diff --git a/bin/idea.sh b/bin/idea.sh index eb37964f396..a184884b61a 100644 --- a/bin/idea.sh +++ b/bin/idea.sh @@ -125,7 +125,8 @@ if [ -d "$TOPLEVEL_DIR/.hg" ] ; then VCS_TYPE="hg4idea" fi -if [ -d "$TOPLEVEL_DIR/.git" ] ; then +# Git worktrees use a '.git' file rather than directory, so test both. +if [ -d "$TOPLEVEL_DIR/.git" -o -f "$TOPLEVEL_DIR/.git" ] ; then VCS_TYPE="Git" fi diff --git a/doc/building.html b/doc/building.html index f7af2648592..99eb3e0c473 100644 --- a/doc/building.html +++ b/doc/building.html @@ -1451,10 +1451,10 @@ of a cross-compiling toolchain and a sysroot environment which can easily be used together with the --with-devkit configure option to cross compile the JDK. On Linux/x86_64, the following command:

-
bash configure --with-devkit=<devkit-path> --openjdk-target=ppc64-linux-gnu && make
-

will configure and build the JDK for Linux/ppc64 assuming that -<devkit-path> points to a Linux/x86_64 to Linux/ppc64 -devkit.

+
bash configure --with-devkit=<devkit-path> --openjdk-target=ppc64le-linux-gnu && make
+

will configure and build the JDK for Linux/ppc64le assuming that +<devkit-path> points to a Linux/x86_64 to +Linux/ppc64le devkit.

Devkits can be created from the make/devkit directory by executing:

make [ TARGETS="<TARGET_TRIPLET>+" ] [ BASE_OS=<OS> ] [ BASE_OS_VERSION=<VER> ]
@@ -1481,10 +1481,10 @@ following targets are known to work:

arm-linux-gnueabihf -ppc64-linux-gnu +ppc64le-linux-gnu -ppc64le-linux-gnu +riscv64-linux-gnu s390x-linux-gnu diff --git a/doc/building.md b/doc/building.md index 32c6ae46540..047255d1848 100644 --- a/doc/building.md +++ b/doc/building.md @@ -1258,11 +1258,11 @@ toolchain and a sysroot environment which can easily be used together with the following command: ``` -bash configure --with-devkit= --openjdk-target=ppc64-linux-gnu && make +bash configure --with-devkit= --openjdk-target=ppc64le-linux-gnu && make ``` -will configure and build the JDK for Linux/ppc64 assuming that `` -points to a Linux/x86_64 to Linux/ppc64 devkit. +will configure and build the JDK for Linux/ppc64le assuming that `` +points to a Linux/x86_64 to Linux/ppc64le devkit. Devkits can be created from the `make/devkit` directory by executing: @@ -1281,8 +1281,8 @@ at least the following targets are known to work: | x86_64-linux-gnu | | aarch64-linux-gnu | | arm-linux-gnueabihf | -| ppc64-linux-gnu | | ppc64le-linux-gnu | +| riscv64-linux-gnu | | s390x-linux-gnu | `BASE_OS` must be one of `OL` for Oracle Enterprise Linux or `Fedora`. If the diff --git a/doc/starting-next-release.html b/doc/starting-next-release.html index 421229f9fbc..6cffdf38b0f 100644 --- a/doc/starting-next-release.html +++ b/doc/starting-next-release.html @@ -11,11 +11,8 @@ div.columns{display: flex; gap: min(4vw, 1.5em);} div.column{flex: auto; overflow-x: auto;} div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} - /* The extra [class] is a hack that increases specificity enough to - override a similar rule in reveal.js */ - ul.task-list[class]{list-style: none;} + ul.task-list{list-style: none;} ul.task-list li input[type="checkbox"] { - font-size: inherit; width: 0.8em; margin: 0 0.8em 0.2em -1.6em; vertical-align: middle; diff --git a/doc/testing.html b/doc/testing.html index b9d1f4ed22f..fa774aa312f 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -72,11 +72,9 @@ id="toc-notes-for-specific-tests">Notes for Specific Tests
  • Non-US locale
  • PKCS11 Tests
  • -
  • ### Testing Ahead-of-time -Optimizations -
      +id="toc-testing-ahead-of-time-optimizations">Testing Ahead-of-time +Optimizations
    • Testing with alternative security providers
    • @@ -435,6 +433,9 @@ the diff between the specified revision and the repository tip.

      The report is stored in build/$BUILD/test-results/jcov-output/diff_coverage_report file.

      +

      AOT_JDK

      +

      See Testing +Ahead-of-time optimizations.

      JTReg keywords

      JOBS

      The test concurrency (-concurrency).

      @@ -457,6 +458,12 @@ class, named Virtual, is currently part of the JDK build in the test/jtreg_test_thread_factory/ directory. This class gets compiled during the test image build. The implementation of the Virtual class creates a new virtual thread for executing each test class.

      +

      JVMTI_STRESS_AGENT

      +

      Executes JTReg tests with JVM TI stress agent. The stress agent is +the part of test library and located in +test/lib/jdk/test/lib/jvmti/libJvmtiStressAgent.cpp. The +value of this argument is set as JVM TI agent options. This mode uses +ProblemList-jvmti-stress-agent.txt as an additional exclude list.

      TEST_MODE

      The test mode (agentvm or othervm).

      Defaults to agentvm.

      @@ -556,6 +563,12 @@ each fork. Same as specifying -wi <num>.

      same values as -rff, i.e., text, csv, scsv, json, or latex.

      +

      TEST_JDK

      +

      The path to the JDK that will be used to run the benchmarks.

      +

      Defaults to build/<CONF-NAME>/jdk.

      +

      BENCHMARKS_JAR

      +

      The path to the JAR containing the benchmarks.

      +

      Defaults to test/micro/benchmarks.jar.

      VM_OPTIONS

      Additional VM arguments to provide to forked off VMs. Same as -jvmArgs <args>

      @@ -601,8 +614,8 @@ element of the appropriate @Artifact class. (See JTREG="JAVA_OPTIONS=-Djdk.test.lib.artifacts.nsslib-linux_aarch64=/path/to/NSS-libs"

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

      -

      ### Testing Ahead-of-time -Optimizations

      +

      Testing Ahead-of-time +Optimizations

      One way to improve test coverage of ahead-of-time (AOT) optimizations in the JDK is to run existing jtreg test cases in a special "AOT_JDK" mode. Example:

      diff --git a/doc/testing.md b/doc/testing.md index bb56c05c295..4cfc36c85f9 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -367,6 +367,10 @@ between the specified revision and the repository tip. The report is stored in `build/$BUILD/test-results/jcov-output/diff_coverage_report` file. +#### AOT_JDK + +See [Testing Ahead-of-time optimizations](#testing-ahead-of-time-optimizations). + ### JTReg keywords #### JOBS @@ -397,6 +401,13 @@ the `test/jtreg_test_thread_factory/` directory. This class gets compiled during the test image build. The implementation of the Virtual class creates a new virtual thread for executing each test class. +#### JVMTI_STRESS_AGENT + +Executes JTReg tests with JVM TI stress agent. The stress agent is the part of +test library and located in `test/lib/jdk/test/lib/jvmti/libJvmtiStressAgent.cpp`. +The value of this argument is set as JVM TI agent options. +This mode uses ProblemList-jvmti-stress-agent.txt as an additional exclude list. + #### TEST_MODE The test mode (`agentvm` or `othervm`). @@ -545,6 +556,18 @@ Amount of time to spend in each warmup iteration. Same as specifying `-w Specify to have the test run save a log of the values. Accepts the same values as `-rff`, i.e., `text`, `csv`, `scsv`, `json`, or `latex`. +#### TEST_JDK + +The path to the JDK that will be used to run the benchmarks. + +Defaults to `build//jdk`. + +#### BENCHMARKS_JAR + +The path to the JAR containing the benchmarks. + +Defaults to `test/micro/benchmarks.jar`. + #### VM_OPTIONS Additional VM arguments to provide to forked off VMs. Same as `-jvmArgs ` @@ -612,7 +635,7 @@ For more notes about the PKCS11 tests, please refer to test/jdk/sun/security/pkcs11/README. ### Testing Ahead-of-time Optimizations -------------------------------------------------------------------------------- + One way to improve test coverage of ahead-of-time (AOT) optimizations in the JDK is to run existing jtreg test cases in a special "AOT_JDK" mode. Example: diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 60ae1bd4763..46f9a2e4047 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -204,8 +204,9 @@ $(eval $(call SetTestOpt,AOT_JDK,JTREG)) $(eval $(call ParseKeywordVariable, JTREG, \ SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR FAILURE_HANDLER_TIMEOUT \ - TEST_MODE ASSERT VERBOSE RETAIN TEST_THREAD_FACTORY MAX_MEM RUN_PROBLEM_LISTS \ - RETRY_COUNT REPEAT_COUNT MAX_OUTPUT REPORT AOT_JDK $(CUSTOM_JTREG_SINGLE_KEYWORDS), \ + TEST_MODE ASSERT VERBOSE RETAIN TEST_THREAD_FACTORY JVMTI_STRESS_AGENT \ + MAX_MEM RUN_PROBLEM_LISTS RETRY_COUNT REPEAT_COUNT MAX_OUTPUT REPORT \ + AOT_JDK $(CUSTOM_JTREG_SINGLE_KEYWORDS), \ STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \ EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS \ $(CUSTOM_JTREG_STRING_KEYWORDS), \ @@ -876,6 +877,15 @@ define SetupRunJtregTestBody )) endif + ifneq ($$(JTREG_JVMTI_STRESS_AGENT), ) + AGENT := $$(LIBRARY_PREFIX)JvmtiStressAgent$$(SHARED_LIBRARY_SUFFIX)=$$(JTREG_JVMTI_STRESS_AGENT) + $1_JTREG_BASIC_OPTIONS += -javaoption:'-agentpath:$(TEST_IMAGE_DIR)/hotspot/jtreg/native/$$(AGENT)' + $1_JTREG_BASIC_OPTIONS += $$(addprefix $$(JTREG_PROBLEM_LIST_PREFIX), $$(wildcard \ + $$(addprefix $$($1_TEST_ROOT)/, ProblemList-jvmti-stress-agent.txt) \ + )) + endif + + ifneq ($$(JTREG_LAUNCHER_OPTIONS), ) $1_JTREG_LAUNCHER_OPTIONS += $$(JTREG_LAUNCHER_OPTIONS) endif @@ -1243,7 +1253,7 @@ UseSpecialTestHandler = \ # Now process each test to run and setup a proper make rule $(foreach test, $(TESTS_TO_RUN), \ $(eval TEST_ID := $(shell $(ECHO) $(strip $(test)) | \ - $(TR) -cs '[a-z][A-Z][0-9]\n' '[_*1000]')) \ + $(TR) -cs '[a-z][A-Z][0-9]\n' '_')) \ $(eval ALL_TEST_IDS += $(TEST_ID)) \ $(if $(call UseCustomTestHandler, $(test)), \ $(eval $(call SetupRunCustomTest, $(TEST_ID), \ @@ -1323,9 +1333,9 @@ run-test-report: post-run-test TEST TOTAL PASS FAIL ERROR SKIP " " $(foreach test, $(TESTS_TO_RUN), \ $(eval TEST_ID := $(shell $(ECHO) $(strip $(test)) | \ - $(TR) -cs '[a-z][A-Z][0-9]\n' '[_*1000]')) \ + $(TR) -cs '[a-z][A-Z][0-9]\n' '_')) \ $(ECHO) >> $(TEST_LAST_IDS) $(TEST_ID) $(NEWLINE) \ - $(eval NAME_PATTERN := $(shell $(ECHO) $(test) | $(TR) -c '\n' '[_*1000]')) \ + $(eval NAME_PATTERN := $(shell $(ECHO) $(test) | $(TR) -c '\n' '_')) \ $(if $(filter __________________________________________________%, $(NAME_PATTERN)), \ $(eval TEST_NAME := ) \ $(PRINTF) >> $(TEST_SUMMARY) "%2s %-49s\n" " " "$(test)" $(NEWLINE) \ diff --git a/make/RunTestsPrebuiltSpec.gmk b/make/RunTestsPrebuiltSpec.gmk index 03c877a4277..e9fd901c9e1 100644 --- a/make/RunTestsPrebuiltSpec.gmk +++ b/make/RunTestsPrebuiltSpec.gmk @@ -176,3 +176,19 @@ ULIMIT := ulimit ifeq ($(OPENJDK_BUILD_OS), windows) PATHTOOL := cygpath endif + +# These settings are needed to run testing with jvmti agent +ifeq ($(OPENJDK_BUILD_OS), linux) + LIBRARY_PREFIX := lib + SHARED_LIBRARY_SUFFIX := .so +endif + +ifeq ($(OPENJDK_BUILD_OS), windows) + LIBRARY_PREFIX := + SHARED_LIBRARY_SUFFIX := .dll +endif + +ifeq ($(OPENJDK_BUILD_OS), macosx) + LIBRARY_PREFIX := lib + SHARED_LIBRARY_SUFFIX := .dylib +endif diff --git a/make/ToolsLangtools.gmk b/make/ToolsLangtools.gmk index 4146652bf8b..1a764d6019b 100644 --- a/make/ToolsLangtools.gmk +++ b/make/ToolsLangtools.gmk @@ -36,7 +36,7 @@ $(eval $(call SetupJavaCompilation, BUILD_TOOLS_LANGTOOLS, \ COMPILER := bootjdk, \ TARGET_RELEASE := $(TARGET_RELEASE_BOOTJDK), \ SRC := $(TOPDIR)/make/langtools/tools, \ - INCLUDES := compileproperties propertiesparser, \ + INCLUDES := compileproperties flagsgenerator propertiesparser, \ COPY := .properties, \ BIN := $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes, \ )) diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index 056334e99c4..3a61840e8c9 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -940,7 +940,7 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_CPU_DEP], # ACLE and this flag are required to build the aarch64 SVE related functions in # libvectormath. Apple Silicon does not support SVE; use macOS as a proxy for # that check. - if test "x$OPENJDK_TARGET_CPU" = "xaarch64" && test "x$OPENJDK_TARGET_CPU" = "xlinux"; then + if test "x$OPENJDK_TARGET_CPU" = "xaarch64" && test "x$OPENJDK_TARGET_OS" = "xlinux"; then if test "x$TOOLCHAIN_TYPE" = xgcc || test "x$TOOLCHAIN_TYPE" = xclang; then AC_LANG_PUSH(C) OLD_CFLAGS="$CFLAGS" @@ -954,6 +954,17 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_CPU_DEP], [ AC_MSG_RESULT([yes]) $2SVE_CFLAGS="-march=armv8-a+sve" + # Switching the initialization mode with gcc from 'pattern' to 'zero' + # avoids the use of unsupported `__builtin_clear_padding` for variable + # length aggregates + if test "x$DEBUG_LEVEL" != xrelease && test "x$TOOLCHAIN_TYPE" = xgcc ; then + INIT_ZERO_FLAG="-ftrivial-auto-var-init=zero" + FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [$INIT_ZERO_FLAG], + IF_TRUE: [ + $2SVE_CFLAGS="${$2SVE_CFLAGS} $INIT_ZERO_FLAG" + ] + ) + fi ], [ AC_MSG_RESULT([no]) diff --git a/make/devkit/Makefile b/make/devkit/Makefile index 5ffa5265aa3..d2167bf33fa 100644 --- a/make/devkit/Makefile +++ b/make/devkit/Makefile @@ -39,7 +39,7 @@ # # make TARGETS="aarch64-linux-gnu" BASE_OS=Fedora # or -# make TARGETS="arm-linux-gnueabihf ppc64-linux-gnu" BASE_OS=Fedora BASE_OS_VERSION=17 +# make TARGETS="arm-linux-gnueabihf ppc64le-linux-gnu" BASE_OS=Fedora BASE_OS_VERSION=17 # # to build several devkits for a specific OS version at once. # You can find the final results under ../../build/devkit/result/-to- @@ -50,7 +50,7 @@ # makefile again for cross compilation. Ex: # # PATH=$PWD/../../build/devkit/result/x86_64-linux-gnu-to-x86_64-linux-gnu/bin:$PATH \ -# make TARGETS="arm-linux-gnueabihf,ppc64-linux-gnu" BASE_OS=Fedora +# make TARGETS="arm-linux-gnueabihf ppc64le-linux-gnu" BASE_OS=Fedora # # This is the makefile which iterates over all host and target platforms. # diff --git a/make/devkit/Tools.gmk b/make/devkit/Tools.gmk index 1b9240df49c..f27d47b822c 100644 --- a/make/devkit/Tools.gmk +++ b/make/devkit/Tools.gmk @@ -69,15 +69,26 @@ else ifeq ($(BASE_OS), Fedora) ifeq ($(BASE_OS_VERSION), ) BASE_OS_VERSION := $(DEFAULT_OS_VERSION) endif + ifeq ($(filter aarch64 armhfp ppc64le riscv64 s390x x86_64, $(ARCH)), ) + $(error Only "aarch64 armhfp ppc64le riscv64 s390x x86_64" architectures are supported for Fedora, but "$(ARCH)" was requested) + endif ifeq ($(ARCH), riscv64) + ifeq ($(filter 38 39 40 41, $(BASE_OS_VERSION)), ) + $(error Only Fedora 38-41 are supported for "$(ARCH)", but Fedora $(BASE_OS_VERSION) was requested) + endif BASE_URL := http://fedora.riscv.rocks/repos-dist/f$(BASE_OS_VERSION)/latest/$(ARCH)/Packages/ else - LATEST_ARCHIVED_OS_VERSION := 35 - ifeq ($(filter x86_64 armhfp, $(ARCH)), ) + LATEST_ARCHIVED_OS_VERSION := 36 + ifeq ($(filter aarch64 armhfp x86_64, $(ARCH)), ) FEDORA_TYPE := fedora-secondary else FEDORA_TYPE := fedora/linux endif + ifeq ($(ARCH), armhfp) + ifneq ($(BASE_OS_VERSION), 36) + $(error Fedora 36 is the last release supporting "armhfp", but $(BASE_OS) was requested) + endif + endif NOT_ARCHIVED := $(shell [ $(BASE_OS_VERSION) -gt $(LATEST_ARCHIVED_OS_VERSION) ] && echo true) ifeq ($(NOT_ARCHIVED),true) BASE_URL := https://dl.fedoraproject.org/pub/$(FEDORA_TYPE)/releases/$(BASE_OS_VERSION)/Everything/$(ARCH)/os/Packages/ @@ -464,7 +475,7 @@ ifeq ($(ARCH), armhfp) $(BUILDDIR)/$(gcc_ver)/Makefile : CONFIG += --with-float=hard endif -ifneq ($(filter riscv64 ppc64 ppc64le s390x, $(ARCH)), ) +ifneq ($(filter riscv64 ppc64le s390x, $(ARCH)), ) # We only support 64-bit on these platforms anyway CONFIG += --disable-multilib endif diff --git a/make/ide/vscode/hotspot/template-workspace.jsonc b/make/ide/vscode/hotspot/template-workspace.jsonc index 30533c7ce84..c582c48047d 100644 --- a/make/ide/vscode/hotspot/template-workspace.jsonc +++ b/make/ide/vscode/hotspot/template-workspace.jsonc @@ -12,12 +12,17 @@ ], "extensions": { "recommendations": [ + "oracle.oracle-java", // {{INDEXER_EXTENSIONS}} ] }, "settings": { // {{INDEXER_SETTINGS}} + // Java extension + "jdk.project.jdkhome": "{{OUTPUTDIR}}/jdk", + "jdk.java.onSave.organizeImports": false, // prevents unnecessary changes + // Additional conventions "files.associations": { "*.gmk": "makefile" diff --git a/make/langtools/tools/flagsgenerator/FlagsGenerator.java b/make/langtools/tools/flagsgenerator/FlagsGenerator.java new file mode 100644 index 00000000000..1c192d214de --- /dev/null +++ b/make/langtools/tools/flagsgenerator/FlagsGenerator.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package flagsgenerator; + +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.util.JavacTask; +import com.sun.source.util.TreePath; +import com.sun.source.util.Trees; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.util.ElementFilter; +import javax.tools.ToolProvider; + +public class FlagsGenerator { + public static void main(String... args) throws IOException { + var compiler = ToolProvider.getSystemJavaCompiler(); + + try (var fm = compiler.getStandardFileManager(null, null, null)) { + JavacTask task = (JavacTask) compiler.getTask(null, null, d -> {}, null, null, fm.getJavaFileObjects(args[0])); + Trees trees = Trees.instance(task); + CompilationUnitTree cut = task.parse().iterator().next(); + + task.analyze(); + + TypeElement clazz = (TypeElement) trees.getElement(new TreePath(new TreePath(cut), cut.getTypeDecls().get(0))); + Map> flag2Names = new TreeMap<>(); + Map>> target2FlagBit2Fields = new EnumMap<>(FlagTarget.class); + Map customToString = new HashMap<>(); + Set noToString = new HashSet<>(); + + for (VariableElement field : ElementFilter.fieldsIn(clazz.getEnclosedElements())) { + String flagName = field.getSimpleName().toString(); + for (AnnotationMirror am : field.getAnnotationMirrors()) { + switch (am.getAnnotationType().toString()) { + case "com.sun.tools.javac.code.Flags.Use" -> { + long flagValue = ((Number) field.getConstantValue()).longValue(); + int flagBit = 63 - Long.numberOfLeadingZeros(flagValue); + + flag2Names.computeIfAbsent(flagBit, _ -> new ArrayList<>()) + .add(flagName); + + List originalTargets = (List) valueOfValueAttribute(am); + originalTargets.stream() + .map(value -> FlagTarget.valueOf(value.toString())) + .forEach(target -> target2FlagBit2Fields.computeIfAbsent(target, _ -> new HashMap<>()) + .computeIfAbsent(flagBit, _ -> new ArrayList<>()) + .add(flagName)); + } + case "com.sun.tools.javac.code.Flags.CustomToStringValue" -> { + customToString.put(flagName, (String) valueOfValueAttribute(am)); + } + case "com.sun.tools.javac.code.Flags.NoToStringValue" -> { + noToString.add(flagName); + } + } + } + } + + //verify there are no flag overlaps: + for (Entry>> targetAndFlag : target2FlagBit2Fields.entrySet()) { + for (Entry> flagAndFields : targetAndFlag.getValue().entrySet()) { + if (flagAndFields.getValue().size() > 1) { + throw new AssertionError("duplicate flag for target: " + targetAndFlag.getKey() + + ", flag: " + flagAndFields.getKey() + + ", flags fields: " + flagAndFields.getValue()); + } + } + } + + try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(Paths.get(args[1])))) { + out.println(""" + package com.sun.tools.javac.code; + + public enum FlagsEnum { + """); + for (Entry> e : flag2Names.entrySet()) { + String constantName = e.getValue().stream().collect(Collectors.joining("_OR_")); + String toString = e.getValue() + .stream() + .filter(n -> !noToString.contains(n)) + .map(n -> customToString.getOrDefault(n, n.toLowerCase(Locale.US))) + .collect(Collectors.joining(" or ")); + out.println(" " + constantName + "(1L<<" + e.getKey() + ", \"" + toString + "\"),"); + } + out.println(""" + ; + + private final long value; + private final String toString; + private FlagsEnum(long value, String toString) { + this.value = value; + this.toString = toString; + } + public long value() { + return value; + } + public String toString() { + return toString; + } + } + """); + } + } + } + + private static Object valueOfValueAttribute(AnnotationMirror am) { + return am.getElementValues() + .values() + .iterator() + .next() + .getValue(); + } + + private enum FlagTarget { + BLOCK, + CLASS, + METHOD, + MODULE, + PACKAGE, + TYPE_VAR, + VARIABLE; + } +} diff --git a/make/langtools/tools/propertiesparser/parser/MessageType.java b/make/langtools/tools/propertiesparser/parser/MessageType.java index ea518dc536b..a4ea0ddc3c0 100644 --- a/make/langtools/tools/propertiesparser/parser/MessageType.java +++ b/make/langtools/tools/propertiesparser/parser/MessageType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,7 +76,7 @@ public interface MessageType { ANNOTATION("annotation", "Compound", "com.sun.tools.javac.code.Attribute"), BOOLEAN("boolean", "boolean", null), COLLECTION("collection", "Collection", "java.util"), - FLAG("flag", "Flag", "com.sun.tools.javac.code.Flags"), + FLAG("flag", "FlagsEnum", "com.sun.tools.javac.code"), FRAGMENT("fragment", "Fragment", null), DIAGNOSTIC("diagnostic", "JCDiagnostic", "com.sun.tools.javac.util"), MODIFIER("modifier", "Modifier", "javax.lang.model.element"), diff --git a/make/modules/jdk.compiler/Gensrc.gmk b/make/modules/jdk.compiler/Gensrc.gmk index c6c5879745c..501ea37ae8b 100644 --- a/make/modules/jdk.compiler/Gensrc.gmk +++ b/make/modules/jdk.compiler/Gensrc.gmk @@ -41,17 +41,17 @@ $(eval $(call SetupCompileProperties, COMPILE_PROPERTIES, \ TARGETS += $(COMPILE_PROPERTIES) -################################################################################ -# -# Compile properties files into enum-like classes using the propertiesparser tool -# - # To avoid reevaluating the compilation setup for the tools each time this file # is included, the following trick is used to be able to declare a dependency on # the built tools. BUILD_TOOLS_LANGTOOLS := $(call SetupJavaCompilationCompileTarget, \ BUILD_TOOLS_LANGTOOLS, $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes) +################################################################################ +# +# Compile properties files into enum-like classes using the propertiesparser tool +# + TOOL_PARSEPROPERTIES_CMD := $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes \ propertiesparser.PropertiesParser @@ -76,3 +76,26 @@ $(eval $(call SetupExecute, PARSEPROPERTIES, \ TARGETS += $(PARSEPROPERTIES) ################################################################################ +# +# Generate FlagsEnum from Flags constants +# + +TOOL_FLAGSGENERATOR_CMD := $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/langtools_tools_classes \ + flagsgenerator.FlagsGenerator + +FLAGS_SRC := \ + $(MODULE_SRC)/share/classes/com/sun/tools/javac/code/Flags.java + +FLAGS_OUT := \ + $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/com/sun/tools/javac/code/FlagsEnum.java + +$(eval $(call SetupExecute, FLAGSGENERATOR, \ + WARN := Generating FlagsEnum, \ + DEPS := $(FLAGS_SRC) $(BUILD_TOOLS_LANGTOOLS), \ + OUTPUT_FILE := $(FLAGS_OUT), \ + COMMAND := $(TOOL_FLAGSGENERATOR_CMD) $(FLAGS_SRC) $(FLAGS_OUT), \ +)) + +TARGETS += $(FLAGSGENERATOR) + +################################################################################ diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 517da8066de..9697ac31350 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -16281,41 +16281,8 @@ instruct branchLoopEnd(cmpOp cmp, rFlagsReg cr, label lbl) // ============================================================================ // inlined locking and unlocking -instruct cmpFastLock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRegPNoSp tmp2, iRegPNoSp tmp3) -%{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set cr (FastLock object box)); - effect(TEMP tmp, TEMP tmp2, TEMP tmp3); - - ins_cost(5 * INSN_COST); - format %{ "fastlock $object,$box\t! kills $tmp,$tmp2,$tmp3" %} - - ins_encode %{ - __ fast_lock($object$$Register, $box$$Register, $tmp$$Register, $tmp2$$Register, $tmp3$$Register); - %} - - ins_pipe(pipe_serial); -%} - -instruct cmpFastUnlock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRegPNoSp tmp2) -%{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set cr (FastUnlock object box)); - effect(TEMP tmp, TEMP tmp2); - - ins_cost(5 * INSN_COST); - format %{ "fastunlock $object,$box\t! kills $tmp, $tmp2" %} - - ins_encode %{ - __ fast_unlock($object$$Register, $box$$Register, $tmp$$Register, $tmp2$$Register); - %} - - ins_pipe(pipe_serial); -%} - instruct cmpFastLockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRegPNoSp tmp2, iRegPNoSp tmp3) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastLock object box)); effect(TEMP tmp, TEMP tmp2, TEMP tmp3); @@ -16331,7 +16298,6 @@ instruct cmpFastLockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp instruct cmpFastUnlockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRegPNoSp tmp2, iRegPNoSp tmp3) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastUnlock object box)); effect(TEMP tmp, TEMP tmp2, TEMP tmp3); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 31f1e97002a..e9bb2350b5b 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -410,11 +410,7 @@ int LIR_Assembler::emit_unwind_handler() { if (method()->is_synchronized()) { monitor_address(0, FrameMap::r0_opr); stub = new MonitorExitStub(FrameMap::r0_opr, true, 0); - if (LockingMode == LM_MONITOR) { - __ b(*stub->entry()); - } else { - __ unlock_object(r5, r4, r0, r6, *stub->entry()); - } + __ unlock_object(r5, r4, r0, r6, *stub->entry()); __ bind(*stub->continuation()); } @@ -2484,13 +2480,7 @@ void LIR_Assembler::emit_lock(LIR_OpLock* op) { Register hdr = op->hdr_opr()->as_register(); Register lock = op->lock_opr()->as_register(); Register temp = op->scratch_opr()->as_register(); - if (LockingMode == LM_MONITOR) { - if (op->info() != nullptr) { - add_debug_info_for_null_check_here(op->info()); - __ null_check(obj, -1); - } - __ b(*op->stub()->entry()); - } else if (op->code() == lir_lock) { + if (op->code() == lir_lock) { assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); // add debug info for NullPointerException only if one is possible int null_check_offset = __ lock_object(hdr, obj, lock, temp, *op->stub()->entry()); @@ -2823,7 +2813,7 @@ void LIR_Assembler::leal(LIR_Opr addr, LIR_Opr dest, LIR_PatchCode patch_code, C return; } - __ lea(dest->as_register_lo(), as_Address(addr->as_address_ptr())); + __ lea(dest->as_pointer_register(), as_Address(addr->as_address_ptr())); } @@ -3133,7 +3123,9 @@ void LIR_Assembler::atomic_op(LIR_Code code, LIR_Opr src, LIR_Opr data, LIR_Opr default: ShouldNotReachHere(); } - __ membar(__ AnyAny); + if(!UseLSE) { + __ membar(__ AnyAny); + } } #undef __ diff --git a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp index f0d09edd0a9..ad26d494b2d 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp @@ -981,7 +981,7 @@ void LIRGenerator::do_update_CRC32(Intrinsic* x) { CallingConvention* cc = frame_map()->c_calling_convention(&signature); const LIR_Opr result_reg = result_register_for(x->type()); - LIR_Opr addr = new_pointer_register(); + LIR_Opr addr = new_register(T_ADDRESS); __ leal(LIR_OprFact::address(a), addr); crc.load_item_force(cc->at(0)); @@ -1058,7 +1058,7 @@ void LIRGenerator::do_update_CRC32C(Intrinsic* x) { CallingConvention* cc = frame_map()->c_calling_convention(&signature); const LIR_Opr result_reg = result_register_for(x->type()); - LIR_Opr addr = new_pointer_register(); + LIR_Opr addr = new_register(T_ADDRESS); __ leal(LIR_OprFact::address(a), addr); crc.load_item_force(cc->at(0)); diff --git a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp index b0f9d6d6c41..8a79274b2ff 100644 --- a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp @@ -60,8 +60,6 @@ void C1_MacroAssembler::float_cmp(bool is_float, int unordered_result, } int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr, Register temp, Label& slow_case) { - const int aligned_mask = BytesPerWord -1; - const int hdr_offset = oopDesc::mark_offset_in_bytes(); assert_different_registers(hdr, obj, disp_hdr, temp, rscratch2); int null_check_offset = -1; @@ -72,95 +70,20 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr null_check_offset = offset(); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(disp_hdr, obj, hdr, temp, rscratch2, slow_case); - } else if (LockingMode == LM_LEGACY) { + lightweight_lock(disp_hdr, obj, hdr, temp, rscratch2, slow_case); - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(hdr, obj); - ldrb(hdr, Address(hdr, Klass::misc_flags_offset())); - tst(hdr, KlassFlags::_misc_is_value_based_class); - br(Assembler::NE, slow_case); - } - - Label done; - // Load object header - ldr(hdr, Address(obj, hdr_offset)); - // and mark it as unlocked - orr(hdr, hdr, markWord::unlocked_value); - // save unlocked object header into the displaced header location on the stack - str(hdr, Address(disp_hdr, 0)); - // test if object header is still the same (i.e. unlocked), and if so, store the - // displaced header address in the object header - if it is not the same, get the - // object header instead - lea(rscratch2, Address(obj, hdr_offset)); - cmpxchgptr(hdr, disp_hdr, rscratch2, rscratch1, done, /*fallthough*/nullptr); - // if the object header was the same, we're done - // if the object header was not the same, it is now in the hdr register - // => test if it is a stack pointer into the same stack (recursive locking), i.e.: - // - // 1) (hdr & aligned_mask) == 0 - // 2) sp <= hdr - // 3) hdr <= sp + page_size - // - // these 3 tests can be done by evaluating the following expression: - // - // (hdr - sp) & (aligned_mask - page_size) - // - // assuming both the stack pointer and page_size have their least - // significant 2 bits cleared and page_size is a power of 2 - mov(rscratch1, sp); - sub(hdr, hdr, rscratch1); - ands(hdr, hdr, aligned_mask - (int)os::vm_page_size()); - // for recursive locking, the result is zero => save it in the displaced header - // location (null in the displaced hdr location indicates recursive locking) - str(hdr, Address(disp_hdr, 0)); - // otherwise we don't care about the result and handle locking via runtime call - cbnz(hdr, slow_case); - // done - bind(done); - inc_held_monitor_count(rscratch1); - } return null_check_offset; } void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_hdr, Register temp, Label& slow_case) { - const int aligned_mask = BytesPerWord -1; - const int hdr_offset = oopDesc::mark_offset_in_bytes(); assert_different_registers(hdr, obj, disp_hdr, temp, rscratch2); - Label done; - - if (LockingMode != LM_LIGHTWEIGHT) { - // load displaced header - ldr(hdr, Address(disp_hdr, 0)); - // if the loaded hdr is null we had recursive locking - // if we had recursive locking, we are done - cbz(hdr, done); - } // load object ldr(obj, Address(disp_hdr, BasicObjectLock::obj_offset())); verify_oop(obj); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_unlock(obj, hdr, temp, rscratch2, slow_case); - } else if (LockingMode == LM_LEGACY) { - // test if object header is pointing to the displaced header, and if so, restore - // the displaced header in the object - if the object header is not pointing to - // the displaced header, get the object header instead - // if the object header was not pointing to the displaced header, - // we do unlocking via runtime call - if (hdr_offset) { - lea(rscratch1, Address(obj, hdr_offset)); - cmpxchgptr(disp_hdr, hdr, rscratch1, rscratch2, done, &slow_case); - } else { - cmpxchgptr(disp_hdr, hdr, obj, rscratch2, done, &slow_case); - } - // done - bind(done); - dec_held_monitor_count(rscratch1); - } + lightweight_unlock(obj, hdr, temp, rscratch2, slow_case); } diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index e87cb478c8f..b1562c54f4e 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -147,215 +147,8 @@ address C2_MacroAssembler::arrays_hashcode(Register ary, Register cnt, Register return pc(); } -void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, Register tmpReg, - Register tmp2Reg, Register tmp3Reg) { - Register oop = objectReg; - Register box = boxReg; - Register disp_hdr = tmpReg; - Register tmp = tmp2Reg; - Label cont; - Label object_has_monitor; - Label count, no_count; - - assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_lock_lightweight"); - assert_different_registers(oop, box, tmp, disp_hdr, rscratch2); - - // Load markWord from object into displaced_header. - ldr(disp_hdr, Address(oop, oopDesc::mark_offset_in_bytes())); - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmp, oop); - ldrb(tmp, Address(tmp, Klass::misc_flags_offset())); - tst(tmp, KlassFlags::_misc_is_value_based_class); - br(Assembler::NE, cont); - } - - // Check for existing monitor - tbnz(disp_hdr, exact_log2(markWord::monitor_value), object_has_monitor); - - if (LockingMode == LM_MONITOR) { - tst(oop, oop); // Set NE to indicate 'failure' -> take slow-path. We know that oop != 0. - b(cont); - } else { - assert(LockingMode == LM_LEGACY, "must be"); - // Set tmp to be (markWord of object | UNLOCK_VALUE). - orr(tmp, disp_hdr, markWord::unlocked_value); - - // Initialize the box. (Must happen before we update the object mark!) - str(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes())); - - // Compare object markWord with an unlocked value (tmp) and if - // equal exchange the stack address of our box with object markWord. - // On failure disp_hdr contains the possibly locked markWord. - cmpxchg(oop, tmp, box, Assembler::xword, /*acquire*/ true, - /*release*/ true, /*weak*/ false, disp_hdr); - br(Assembler::EQ, cont); - - assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); - - // If the compare-and-exchange succeeded, then we found an unlocked - // object, will have now locked it will continue at label cont - - // Check if the owner is self by comparing the value in the - // markWord of object (disp_hdr) with the stack pointer. - mov(rscratch1, sp); - sub(disp_hdr, disp_hdr, rscratch1); - mov(tmp, (address) (~(os::vm_page_size()-1) | markWord::lock_mask_in_place)); - // If condition is true we are cont and hence we can store 0 as the - // displaced header in the box, which indicates that it is a recursive lock. - ands(tmp/*==0?*/, disp_hdr, tmp); // Sets flags for result - str(tmp/*==0, perhaps*/, Address(box, BasicLock::displaced_header_offset_in_bytes())); - b(cont); - } - - // Handle existing monitor. - bind(object_has_monitor); - - // Try to CAS owner (no owner => current thread's _monitor_owner_id). - ldr(rscratch2, Address(rthread, JavaThread::monitor_owner_id_offset())); - add(tmp, disp_hdr, (in_bytes(ObjectMonitor::owner_offset())-markWord::monitor_value)); - cmpxchg(tmp, zr, rscratch2, Assembler::xword, /*acquire*/ true, - /*release*/ true, /*weak*/ false, tmp3Reg); // Sets flags for result - - // Store a non-null value into the box to avoid looking like a re-entrant - // lock. The fast-path monitor unlock code checks for - // markWord::monitor_value so use markWord::unused_mark which has the - // relevant bit set, and also matches ObjectSynchronizer::enter. - mov(tmp, (address)markWord::unused_mark().value()); - str(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes())); - - br(Assembler::EQ, cont); // CAS success means locking succeeded - - cmp(tmp3Reg, rscratch2); - br(Assembler::NE, cont); // Check for recursive locking - - // Recursive lock case - increment(Address(disp_hdr, in_bytes(ObjectMonitor::recursions_offset()) - markWord::monitor_value), 1); - // flag == EQ still from the cmp above, checking if this is a reentrant lock - - bind(cont); - // flag == EQ indicates success - // flag == NE indicates failure - br(Assembler::NE, no_count); - - bind(count); - if (LockingMode == LM_LEGACY) { - inc_held_monitor_count(rscratch1); - } - - bind(no_count); -} - -void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Register tmpReg, - Register tmp2Reg) { - Register oop = objectReg; - Register box = boxReg; - Register disp_hdr = tmpReg; - Register owner_addr = tmpReg; - Register tmp = tmp2Reg; - Label cont; - Label object_has_monitor; - Label count, no_count; - Label unlocked; - - assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_unlock_lightweight"); - assert_different_registers(oop, box, tmp, disp_hdr); - - if (LockingMode == LM_LEGACY) { - // Find the lock address and load the displaced header from the stack. - ldr(disp_hdr, Address(box, BasicLock::displaced_header_offset_in_bytes())); - - // If the displaced header is 0, we have a recursive unlock. - cmp(disp_hdr, zr); - br(Assembler::EQ, cont); - } - - // Handle existing monitor. - ldr(tmp, Address(oop, oopDesc::mark_offset_in_bytes())); - tbnz(tmp, exact_log2(markWord::monitor_value), object_has_monitor); - - if (LockingMode == LM_MONITOR) { - tst(oop, oop); // Set NE to indicate 'failure' -> take slow-path. We know that oop != 0. - b(cont); - } else { - assert(LockingMode == LM_LEGACY, "must be"); - // Check if it is still a light weight lock, this is is true if we - // see the stack address of the basicLock in the markWord of the - // object. - - cmpxchg(oop, box, disp_hdr, Assembler::xword, /*acquire*/ false, - /*release*/ true, /*weak*/ false, tmp); - b(cont); - } - - assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); - - // Handle existing monitor. - bind(object_has_monitor); - STATIC_ASSERT(markWord::monitor_value <= INT_MAX); - add(tmp, tmp, -(int)markWord::monitor_value); // monitor - - ldr(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset())); - - Label notRecursive; - cbz(disp_hdr, notRecursive); - - // Recursive lock - sub(disp_hdr, disp_hdr, 1u); - str(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset())); - cmp(disp_hdr, disp_hdr); // Sets flags for result - b(cont); - - bind(notRecursive); - - // Compute owner address. - lea(owner_addr, Address(tmp, ObjectMonitor::owner_offset())); - - // Set owner to null. - // Release to satisfy the JMM - stlr(zr, owner_addr); - // We need a full fence after clearing owner to avoid stranding. - // StoreLoad achieves this. - membar(StoreLoad); - - // Check if the entry_list is empty. - ldr(rscratch1, Address(tmp, ObjectMonitor::entry_list_offset())); - cmp(rscratch1, zr); - br(Assembler::EQ, cont); // If so we are done. - - // Check if there is a successor. - ldr(rscratch1, Address(tmp, ObjectMonitor::succ_offset())); - cmp(rscratch1, zr); - br(Assembler::NE, unlocked); // If so we are done. - - // Save the monitor pointer in the current thread, so we can try to - // reacquire the lock in SharedRuntime::monitor_exit_helper(). - str(tmp, Address(rthread, JavaThread::unlocked_inflated_monitor_offset())); - - cmp(zr, rthread); // Set Flag to NE => slow path - b(cont); - - bind(unlocked); - cmp(zr, zr); // Set Flag to EQ => fast path - - // Intentional fall-through - - bind(cont); - // flag == EQ indicates success - // flag == NE indicates failure - br(Assembler::NE, no_count); - - bind(count); - if (LockingMode == LM_LEGACY) { - dec_held_monitor_count(rscratch1); - } - - bind(no_count); -} - void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Register t1, Register t2, Register t3) { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); assert_different_registers(obj, box, t1, t2, t3, rscratch2); // Handle inflated monitor. @@ -512,7 +305,6 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register box, Register t1, Register t2, Register t3) { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); assert_different_registers(obj, box, t1, t2, t3); // Handle inflated monitor. diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index 233f600cb14..0403a27910f 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp @@ -51,9 +51,6 @@ FloatRegister vmul3, FloatRegister vpow, FloatRegister vpowm, BasicType eltype); - // Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file. - void fast_lock(Register object, Register box, Register tmp, Register tmp2, Register tmp3); - void fast_unlock(Register object, Register box, Register tmp, Register tmp2); // Code used by cmpFastLockLightweight and cmpFastUnlockLightweight mach instructions in .ad file. void fast_lock_lightweight(Register object, Register box, Register t1, Register t2, Register t3); void fast_unlock_lightweight(Register object, Register box, Register t1, Register t2, Register t3); diff --git a/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp index e82162df4ab..1fd59b81d9e 100644 --- a/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -101,9 +101,12 @@ frame FreezeBase::new_heap_frame(frame& f, frame& caller) { *hf.addr_at(frame::interpreter_frame_locals_offset) = locals_offset; return hf; } else { - // We need to re-read fp out of the frame because it may be an oop and we might have - // had a safepoint in finalize_freeze, after constructing f. - fp = *(intptr_t**)(f.sp() - frame::sender_sp_offset); + // For a compiled frame we need to re-read fp out of the frame because it may be an + // oop and we might have had a safepoint in finalize_freeze, after constructing f. + // For stub/native frames the value is not used while frozen, and will be constructed again + // when thawing the frame (see ThawBase::new_stack_frame). We use a special bad address to + // help with debugging, particularly when inspecting frames and identifying invalid accesses. + fp = FKind::compiled ? *(intptr_t**)(f.sp() - frame::sender_sp_offset) : (intptr_t*)badAddressVal; int fsize = FKind::size(f); sp = caller.unextended_sp() - fsize; @@ -192,6 +195,11 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { } } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { + intptr_t* fp_addr = sp - frame::sender_sp_offset; + *fp_addr = badAddressVal; +} + //////// Thaw // Fast path diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index e14829b7c89..607912e6e49 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -691,104 +691,27 @@ void InterpreterMacroAssembler::leave_jfr_critical_section() { void InterpreterMacroAssembler::lock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1"); - if (LockingMode == LM_MONITOR) { - call_VM_preemptable(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - lock_reg); - } else { - Label count, done; - const Register swap_reg = r0; - const Register tmp = c_rarg2; - const Register obj_reg = c_rarg3; // Will contain the oop - const Register tmp2 = c_rarg4; - const Register tmp3 = c_rarg5; + const Register tmp = c_rarg2; + const Register obj_reg = c_rarg3; // Will contain the oop + const Register tmp2 = c_rarg4; + const Register tmp3 = c_rarg5; - const int obj_offset = in_bytes(BasicObjectLock::obj_offset()); - const int lock_offset = in_bytes(BasicObjectLock::lock_offset()); - const int mark_offset = lock_offset + - BasicLock::displaced_header_offset_in_bytes(); + // Load object pointer into obj_reg %c_rarg3 + ldr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); - Label slow_case; + Label slow_case, done; + lightweight_lock(lock_reg, obj_reg, tmp, tmp2, tmp3, slow_case); + b(done); - // Load object pointer into obj_reg %c_rarg3 - ldr(obj_reg, Address(lock_reg, obj_offset)); + bind(slow_case); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(lock_reg, obj_reg, tmp, tmp2, tmp3, slow_case); - b(done); - } else if (LockingMode == LM_LEGACY) { + // Call the runtime routine for slow case + call_VM_preemptable(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), + lock_reg); - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmp, obj_reg); - ldrb(tmp, Address(tmp, Klass::misc_flags_offset())); - tst(tmp, KlassFlags::_misc_is_value_based_class); - br(Assembler::NE, slow_case); - } - - // Load (object->mark() | 1) into swap_reg - ldr(rscratch1, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - orr(swap_reg, rscratch1, 1); - - // Save (object->mark() | 1) into BasicLock's displaced header - str(swap_reg, Address(lock_reg, mark_offset)); - - assert(lock_offset == 0, - "displached header must be first word in BasicObjectLock"); - - Label fail; - cmpxchg_obj_header(swap_reg, lock_reg, obj_reg, rscratch1, count, /*fallthrough*/nullptr); - - // Fast check for recursive lock. - // - // Can apply the optimization only if this is a stack lock - // allocated in this thread. For efficiency, we can focus on - // recently allocated stack locks (instead of reading the stack - // base and checking whether 'mark' points inside the current - // thread stack): - // 1) (mark & 7) == 0, and - // 2) sp <= mark < mark + os::pagesize() - // - // Warning: sp + os::pagesize can overflow the stack base. We must - // neither apply the optimization for an inflated lock allocated - // just above the thread stack (this is why condition 1 matters) - // nor apply the optimization if the stack lock is inside the stack - // of another thread. The latter is avoided even in case of overflow - // because we have guard pages at the end of all stacks. Hence, if - // we go over the stack base and hit the stack of another thread, - // this should not be in a writeable area that could contain a - // stack lock allocated by that thread. As a consequence, a stack - // lock less than page size away from sp is guaranteed to be - // owned by the current thread. - // - // These 3 tests can be done by evaluating the following - // expression: ((mark - sp) & (7 - os::vm_page_size())), - // assuming both stack pointer and pagesize have their - // least significant 3 bits clear. - // NOTE: the mark is in swap_reg %r0 as the result of cmpxchg - // NOTE2: aarch64 does not like to subtract sp from rn so take a - // copy - mov(rscratch1, sp); - sub(swap_reg, swap_reg, rscratch1); - ands(swap_reg, swap_reg, (uint64_t)(7 - (int)os::vm_page_size())); - - // Save the test result, for recursive case, the result is zero - str(swap_reg, Address(lock_reg, mark_offset)); - br(Assembler::NE, slow_case); - - bind(count); - inc_held_monitor_count(rscratch1); - b(done); - } - bind(slow_case); - - // Call the runtime routine for slow case - call_VM_preemptable(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - lock_reg); - - bind(done); - } + bind(done); } @@ -807,57 +730,29 @@ void InterpreterMacroAssembler::unlock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be rarg1"); - if (LockingMode == LM_MONITOR) { - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - } else { - Label count, done; + const Register swap_reg = r0; + const Register header_reg = c_rarg2; // Will contain the old oopMark + const Register obj_reg = c_rarg3; // Will contain the oop + const Register tmp_reg = c_rarg4; // Temporary used by lightweight_unlock - const Register swap_reg = r0; - const Register header_reg = c_rarg2; // Will contain the old oopMark - const Register obj_reg = c_rarg3; // Will contain the oop - const Register tmp_reg = c_rarg4; // Temporary used by lightweight_unlock + save_bcp(); // Save in case of exception - save_bcp(); // Save in case of exception + // Load oop into obj_reg(%c_rarg3) + ldr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); - if (LockingMode != LM_LIGHTWEIGHT) { - // Convert from BasicObjectLock structure to object and BasicLock - // structure Store the BasicLock address into %r0 - lea(swap_reg, Address(lock_reg, BasicObjectLock::lock_offset())); - } + // Free entry + str(zr, Address(lock_reg, BasicObjectLock::obj_offset())); - // Load oop into obj_reg(%c_rarg3) - ldr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); + Label slow_case, done; + lightweight_unlock(obj_reg, header_reg, swap_reg, tmp_reg, slow_case); + b(done); - // Free entry - str(zr, Address(lock_reg, BasicObjectLock::obj_offset())); - - Label slow_case; - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_unlock(obj_reg, header_reg, swap_reg, tmp_reg, slow_case); - b(done); - } else if (LockingMode == LM_LEGACY) { - // Load the old header from BasicLock structure - ldr(header_reg, Address(swap_reg, - BasicLock::displaced_header_offset_in_bytes())); - - // Test for recursion - cbz(header_reg, count); - - // Atomic swap back the old header - cmpxchg_obj_header(swap_reg, header_reg, obj_reg, rscratch1, count, &slow_case); - - bind(count); - dec_held_monitor_count(rscratch1); - b(done); - } - - bind(slow_case); - // Call the runtime routine for slow case. - str(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); // restore obj - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - bind(done); - restore_bcp(); - } + bind(slow_case); + // Call the runtime routine for slow case. + str(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); // restore obj + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); + bind(done); + restore_bcp(); } void InterpreterMacroAssembler::test_method_data_pointer(Register mdp, diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 6ae6861e38b..fb307c8831a 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -6421,10 +6421,14 @@ void MacroAssembler::fill_words(Register base, Register cnt, Register value) // Intrinsic for // -// - sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray -// return the number of characters copied. -// - java/lang/StringUTF16.compress -// return index of non-latin1 character if copy fails, otherwise 'len'. +// - sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// Encodes char[] to byte[] in ISO-8859-1 +// +// - java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// Encodes byte[] (containing UTF-16) to byte[] in ISO-8859-1 +// +// - java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) +// Encodes char[] to byte[] in ASCII // // This version always returns the number of characters copied, and does not // clobber the 'len' register. A successful copy will complete with the post- @@ -7097,7 +7101,6 @@ void MacroAssembler::double_move(VMRegPair src, VMRegPair dst, Register tmp) { // - t1, t2, t3: temporary registers, will be destroyed // - slow: branched to if locking fails, absolute offset may larger than 32KB (imm14 encoding). void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Register t1, Register t2, Register t3, Label& slow) { - assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking"); assert_different_registers(basic_lock, obj, t1, t2, t3, rscratch1); Label push; @@ -7157,7 +7160,6 @@ void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Registe // - t1, t2, t3: temporary registers // - slow: branched to if unlocking fails, absolute offset may larger than 32KB (imm14 encoding). void MacroAssembler::lightweight_unlock(Register obj, Register t1, Register t2, Register t3, Label& slow) { - assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking"); // cmpxchg clobbers rscratch1. assert_different_registers(obj, t1, t2, t3, rscratch1); diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index 00f97ed04e4..748c3e8fb11 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -1721,7 +1721,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // We use the same pc/oopMap repeatedly when we call out. Label native_return; - if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + if (method->is_object_wait0()) { // For convenience we use the pc we want to resume to in case of preemption on Object.wait. __ set_last_Java_frame(sp, noreg, native_return, rscratch1); } else { @@ -1776,44 +1776,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Load the oop from the handle __ ldr(obj_reg, Address(oop_handle_reg, 0)); - if (LockingMode == LM_MONITOR) { - __ b(slow_path_lock); - } else if (LockingMode == LM_LEGACY) { - // Load (object->mark() | 1) into swap_reg %r0 - __ ldr(rscratch1, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - __ orr(swap_reg, rscratch1, 1); - - // Save (object->mark() | 1) into BasicLock's displaced header - __ str(swap_reg, Address(lock_reg, mark_word_offset)); - - // src -> dest iff dest == r0 else r0 <- dest - __ cmpxchg_obj_header(r0, lock_reg, obj_reg, rscratch1, count, /*fallthrough*/nullptr); - - // Hmm should this move to the slow path code area??? - - // Test if the oopMark is an obvious stack pointer, i.e., - // 1) (mark & 3) == 0, and - // 2) sp <= mark < mark + os::pagesize() - // These 3 tests can be done by evaluating the following - // expression: ((mark - sp) & (3 - os::vm_page_size())), - // assuming both stack pointer and pagesize have their - // least significant 2 bits clear. - // NOTE: the oopMark is in swap_reg %r0 as the result of cmpxchg - - __ sub(swap_reg, sp, swap_reg); - __ neg(swap_reg, swap_reg); - __ ands(swap_reg, swap_reg, 3 - (int)os::vm_page_size()); - - // Save the test result, for recursive case, the result is zero - __ str(swap_reg, Address(lock_reg, mark_word_offset)); - __ br(Assembler::NE, slow_path_lock); - - __ bind(count); - __ inc_held_monitor_count(rscratch1); - } else { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); - __ lightweight_lock(lock_reg, obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock); - } + __ lightweight_lock(lock_reg, obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock); // Slow path will re-enter here __ bind(lock_done); @@ -1888,7 +1851,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ lea(rscratch2, Address(rthread, JavaThread::thread_state_offset())); __ stlrw(rscratch1, rscratch2); - if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + if (method->is_object_wait0()) { // Check preemption for Object.wait() __ ldr(rscratch1, Address(rthread, JavaThread::preempt_alternate_return_offset())); __ cbz(rscratch1, native_return); @@ -1917,48 +1880,18 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Get locked oop from the handle we passed to jni __ ldr(obj_reg, Address(oop_handle_reg, 0)); - Label done, not_recursive; - - if (LockingMode == LM_LEGACY) { - // Simple recursive lock? - __ ldr(rscratch1, Address(sp, lock_slot_offset * VMRegImpl::stack_slot_size)); - __ cbnz(rscratch1, not_recursive); - __ dec_held_monitor_count(rscratch1); - __ b(done); - } - - __ bind(not_recursive); - // Must save r0 if if it is live now because cmpxchg must use it if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { save_native_result(masm, ret_type, stack_slots); } - if (LockingMode == LM_MONITOR) { - __ b(slow_path_unlock); - } else if (LockingMode == LM_LEGACY) { - // get address of the stack lock - __ lea(r0, Address(sp, lock_slot_offset * VMRegImpl::stack_slot_size)); - // get old displaced header - __ ldr(old_hdr, Address(r0, 0)); - - // Atomic swap old header if oop still contains the stack lock - Label count; - __ cmpxchg_obj_header(r0, old_hdr, obj_reg, rscratch1, count, &slow_path_unlock); - __ bind(count); - __ dec_held_monitor_count(rscratch1); - } else { - assert(LockingMode == LM_LIGHTWEIGHT, ""); - __ lightweight_unlock(obj_reg, old_hdr, swap_reg, lock_tmp, slow_path_unlock); - } + __ lightweight_unlock(obj_reg, old_hdr, swap_reg, lock_tmp, slow_path_unlock); // slow path re-enters here __ bind(unlock_done); if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { restore_native_result(masm, ret_type, stack_slots); } - - __ bind(done); } Label dtrace_method_exit, dtrace_method_exit_done; diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index 6593406902f..c1eabed8ade 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -1478,22 +1478,17 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ lea(rscratch2, Address(rthread, JavaThread::thread_state_offset())); __ stlrw(rscratch1, rscratch2); - if (LockingMode != LM_LEGACY) { - // Check preemption for Object.wait() - Label not_preempted; - __ ldr(rscratch1, Address(rthread, JavaThread::preempt_alternate_return_offset())); - __ cbz(rscratch1, not_preempted); - __ str(zr, Address(rthread, JavaThread::preempt_alternate_return_offset())); - __ br(rscratch1); - __ bind(native_return); - __ restore_after_resume(true /* is_native */); - // reload result_handler - __ ldr(result_handler, Address(rfp, frame::interpreter_frame_result_handler_offset*wordSize)); - __ bind(not_preempted); - } else { - // any pc will do so just use this one for LM_LEGACY to keep code together. - __ bind(native_return); - } + // Check preemption for Object.wait() + Label not_preempted; + __ ldr(rscratch1, Address(rthread, JavaThread::preempt_alternate_return_offset())); + __ cbz(rscratch1, not_preempted); + __ str(zr, Address(rthread, JavaThread::preempt_alternate_return_offset())); + __ br(rscratch1); + __ bind(native_return); + __ restore_after_resume(true /* is_native */); + // reload result_handler + __ ldr(result_handler, Address(rfp, frame::interpreter_frame_result_handler_offset*wordSize)); + __ bind(not_preempted); // reset_last_Java_frame __ reset_last_Java_frame(true); diff --git a/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp b/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp index b276a7d70b6..389cf4dc936 100644 --- a/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp +++ b/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp @@ -60,6 +60,10 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { Unimplemented(); } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { + Unimplemented(); +} + inline void FreezeBase::patch_stack_pd(intptr_t* frame_sp, intptr_t* heap_sp) { Unimplemented(); } diff --git a/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp b/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp index 8eb33c3cd0b..fa878b07d43 100644 --- a/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp @@ -334,6 +334,9 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { #endif } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { +} + //////// Thaw // Fast path diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index 81b829bde9a..f60be85a141 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -339,11 +339,7 @@ int LIR_Assembler::emit_unwind_handler() { if (method()->is_synchronized()) { monitor_address(0, FrameMap::r10_opr); stub = new MonitorExitStub(FrameMap::r10_opr, true, 0); - if (LockingMode == LM_MONITOR) { - __ j(*stub->entry()); - } else { - __ unlock_object(x15, x14, x10, x16, *stub->entry()); - } + __ unlock_object(x15, x14, x10, x16, *stub->entry()); __ bind(*stub->continuation()); } @@ -1497,13 +1493,7 @@ void LIR_Assembler::emit_lock(LIR_OpLock* op) { Register hdr = op->hdr_opr()->as_register(); Register lock = op->lock_opr()->as_register(); Register temp = op->scratch_opr()->as_register(); - if (LockingMode == LM_MONITOR) { - if (op->info() != nullptr) { - add_debug_info_for_null_check_here(op->info()); - __ null_check(obj, -1); - } - __ j(*op->stub()->entry()); - } else if (op->code() == lir_lock) { + if (op->code() == lir_lock) { assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); // add debug info for NullPointerException only if one is possible int null_check_offset = __ lock_object(hdr, obj, lock, temp, *op->stub()->entry()); @@ -1831,7 +1821,7 @@ void LIR_Assembler::leal(LIR_Opr addr, LIR_Opr dest, LIR_PatchCode patch_code, C } LIR_Address* adr = addr->as_address_ptr(); - Register dst = dest->as_register_lo(); + Register dst = dest->as_pointer_register(); assert_different_registers(dst, t0); if (adr->base()->is_valid() && dst == adr->base()->as_pointer_register() && (!adr->index()->is_cpu_register())) { diff --git a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp index e450c04c47d..88565d9136f 100644 --- a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp @@ -837,7 +837,7 @@ void LIRGenerator::do_update_CRC32(Intrinsic* x) { CallingConvention* cc = frame_map()->c_calling_convention(&signature); const LIR_Opr result_reg = result_register_for(x->type()); - LIR_Opr addr = new_pointer_register(); + LIR_Opr addr = new_register(T_ADDRESS); __ leal(LIR_OprFact::address(a), addr); crc.load_item_force(cc->at(0)); diff --git a/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp index 3ce764b1861..8198192f506 100644 --- a/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp @@ -49,8 +49,6 @@ void C1_MacroAssembler::float_cmp(bool is_float, int unordered_result, } int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr, Register temp, Label& slow_case) { - const int aligned_mask = BytesPerWord - 1; - const int hdr_offset = oopDesc::mark_offset_in_bytes(); assert_different_registers(hdr, obj, disp_hdr, temp, t0, t1); int null_check_offset = -1; @@ -61,97 +59,19 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr null_check_offset = offset(); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(disp_hdr, obj, hdr, temp, t1, slow_case); - } else if (LockingMode == LM_LEGACY) { - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(hdr, obj); - lbu(hdr, Address(hdr, Klass::misc_flags_offset())); - test_bit(temp, hdr, exact_log2(KlassFlags::_misc_is_value_based_class)); - bnez(temp, slow_case, /* is_far */ true); - } - - Label done; - // Load object header - ld(hdr, Address(obj, hdr_offset)); - // and mark it as unlocked - ori(hdr, hdr, markWord::unlocked_value); - // save unlocked object header into the displaced header location on the stack - sd(hdr, Address(disp_hdr, 0)); - // test if object header is still the same (i.e. unlocked), and if so, store the - // displaced header address in the object header - if it is not the same, get the - // object header instead - la(temp, Address(obj, hdr_offset)); - // if the object header was the same, we're done - cmpxchgptr(hdr, disp_hdr, temp, t1, done, /*fallthough*/nullptr); - // if the object header was not the same, it is now in the hdr register - // => test if it is a stack pointer into the same stack (recursive locking), i.e.: - // - // 1) (hdr & aligned_mask) == 0 - // 2) sp <= hdr - // 3) hdr <= sp + page_size - // - // these 3 tests can be done by evaluating the following expression: - // - // (hdr -sp) & (aligned_mask - page_size) - // - // assuming both the stack pointer and page_size have their least - // significant 2 bits cleared and page_size is a power of 2 - sub(hdr, hdr, sp); - mv(temp, aligned_mask - (int)os::vm_page_size()); - andr(hdr, hdr, temp); - // for recursive locking, the result is zero => save it in the displaced header - // location (null in the displaced hdr location indicates recursive locking) - sd(hdr, Address(disp_hdr, 0)); - // otherwise we don't care about the result and handle locking via runtime call - bnez(hdr, slow_case, /* is_far */ true); - - // done - bind(done); - inc_held_monitor_count(t0); - } + lightweight_lock(disp_hdr, obj, hdr, temp, t1, slow_case); return null_check_offset; } void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_hdr, Register temp, Label& slow_case) { - const int aligned_mask = BytesPerWord - 1; - const int hdr_offset = oopDesc::mark_offset_in_bytes(); assert_different_registers(hdr, obj, disp_hdr, temp, t0, t1); - Label done; - - if (LockingMode != LM_LIGHTWEIGHT) { - // load displaced header - ld(hdr, Address(disp_hdr, 0)); - // if the loaded hdr is null we had recursive locking - // if we had recursive locking, we are done - beqz(hdr, done); - } // load object ld(obj, Address(disp_hdr, BasicObjectLock::obj_offset())); verify_oop(obj); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_unlock(obj, hdr, temp, t1, slow_case); - } else if (LockingMode == LM_LEGACY) { - // test if object header is pointing to the displaced header, and if so, restore - // the displaced header in the object - if the object header is not pointing to - // the displaced header, get the object header instead - // if the object header was not pointing to the displaced header, - // we do unlocking via runtime call - if (hdr_offset) { - la(temp, Address(obj, hdr_offset)); - cmpxchgptr(disp_hdr, hdr, temp, t1, done, &slow_case); - } else { - cmpxchgptr(disp_hdr, hdr, obj, t1, done, &slow_case); - } - - // done - bind(done); - dec_held_monitor_count(t0); - } + lightweight_unlock(obj, hdr, temp, t1, slow_case); } // Defines obj, preserves var_size_in_bytes diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index bf71d2c68f1..e91af885d78 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -43,240 +43,11 @@ #define BIND(label) bind(label); BLOCK_COMMENT(#label ":") -void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, - Register tmp1Reg, Register tmp2Reg, Register tmp3Reg, Register tmp4Reg) { - // Use cr register to indicate the fast_lock result: zero for success; non-zero for failure. - Register flag = t1; - Register oop = objectReg; - Register box = boxReg; - Register disp_hdr = tmp1Reg; - Register tmp = tmp2Reg; - Label object_has_monitor; - // Finish fast lock successfully. MUST branch to with flag == 0 - Label locked; - // Finish fast lock unsuccessfully. slow_path MUST branch to with flag != 0 - Label slow_path; - - assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_lock_lightweight"); - assert_different_registers(oop, box, tmp, disp_hdr, flag, tmp3Reg, t0); - - mv(flag, 1); - - // Load markWord from object into displaced_header. - ld(disp_hdr, Address(oop, oopDesc::mark_offset_in_bytes())); - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmp, oop); - lbu(tmp, Address(tmp, Klass::misc_flags_offset())); - test_bit(tmp, tmp, exact_log2(KlassFlags::_misc_is_value_based_class)); - bnez(tmp, slow_path); - } - - // Check for existing monitor - test_bit(tmp, disp_hdr, exact_log2(markWord::monitor_value)); - bnez(tmp, object_has_monitor); - - if (LockingMode == LM_MONITOR) { - j(slow_path); - } else { - assert(LockingMode == LM_LEGACY, "must be"); - // Set tmp to be (markWord of object | UNLOCK_VALUE). - ori(tmp, disp_hdr, markWord::unlocked_value); - - // Initialize the box. (Must happen before we update the object mark!) - sd(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes())); - - // Compare object markWord with an unlocked value (tmp) and if - // equal exchange the stack address of our box with object markWord. - // On failure disp_hdr contains the possibly locked markWord. - cmpxchg(/*memory address*/oop, /*expected value*/tmp, /*new value*/box, Assembler::int64, - Assembler::aq, Assembler::rl, /*result*/disp_hdr); - beq(disp_hdr, tmp, locked); - - assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); - - // If the compare-and-exchange succeeded, then we found an unlocked - // object, will have now locked it will continue at label locked - // We did not see an unlocked object so try the fast recursive case. - - // Check if the owner is self by comparing the value in the - // markWord of object (disp_hdr) with the stack pointer. - sub(disp_hdr, disp_hdr, sp); - mv(tmp, (intptr_t) (~(os::vm_page_size()-1) | (uintptr_t)markWord::lock_mask_in_place)); - // If (mark & lock_mask) == 0 and mark - sp < page_size, we are stack-locking and goto label - // locked, hence we can store 0 as the displaced header in the box, which indicates that it - // is a recursive lock. - andr(tmp/*==0?*/, disp_hdr, tmp); - sd(tmp/*==0, perhaps*/, Address(box, BasicLock::displaced_header_offset_in_bytes())); - beqz(tmp, locked); - j(slow_path); - } - - // Handle existing monitor. - bind(object_has_monitor); - - // Try to CAS owner (no owner => current thread's _monitor_owner_id). - add(tmp, disp_hdr, (in_bytes(ObjectMonitor::owner_offset()) - markWord::monitor_value)); - Register tid = tmp4Reg; - ld(tid, Address(xthread, JavaThread::monitor_owner_id_offset())); - cmpxchg(/*memory address*/tmp, /*expected value*/zr, /*new value*/tid, Assembler::int64, - Assembler::aq, Assembler::rl, /*result*/tmp3Reg); // cas succeeds if tmp3Reg == zr(expected) - - // Store a non-null value into the box to avoid looking like a re-entrant - // lock. The fast-path monitor unlock code checks for - // markWord::monitor_value so use markWord::unused_mark which has the - // relevant bit set, and also matches ObjectSynchronizer::slow_enter. - mv(tmp, (address)markWord::unused_mark().value()); - sd(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes())); - - beqz(tmp3Reg, locked); // CAS success means locking succeeded - - bne(tmp3Reg, tid, slow_path); // Check for recursive locking - - // Recursive lock case - increment(Address(disp_hdr, in_bytes(ObjectMonitor::recursions_offset()) - markWord::monitor_value), 1, tmp2Reg, tmp3Reg); - - bind(locked); - mv(flag, zr); - if (LockingMode == LM_LEGACY) { - inc_held_monitor_count(t0); - } - -#ifdef ASSERT - // Check that locked label is reached with flag == 0. - Label flag_correct; - beqz(flag, flag_correct); - stop("Fast Lock Flag != 0"); -#endif - - bind(slow_path); -#ifdef ASSERT - // Check that slow_path label is reached with flag != 0. - bnez(flag, flag_correct); - stop("Fast Lock Flag == 0"); - bind(flag_correct); -#endif - // C2 uses the value of flag (0 vs !0) to determine the continuation. -} - -void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, - Register tmp1Reg, Register tmp2Reg) { - // Use cr register to indicate the fast_unlock result: zero for success; non-zero for failure. - Register flag = t1; - Register oop = objectReg; - Register box = boxReg; - Register disp_hdr = tmp1Reg; - Register owner_addr = tmp1Reg; - Register tmp = tmp2Reg; - Label object_has_monitor; - // Finish fast lock successfully. MUST branch to with flag == 0 - Label unlocked; - // Finish fast lock unsuccessfully. slow_path MUST branch to with flag != 0 - Label slow_path; - - assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_unlock_lightweight"); - assert_different_registers(oop, box, tmp, disp_hdr, flag, t0); - - mv(flag, 1); - - if (LockingMode == LM_LEGACY) { - // Find the lock address and load the displaced header from the stack. - ld(disp_hdr, Address(box, BasicLock::displaced_header_offset_in_bytes())); - - // If the displaced header is 0, we have a recursive unlock. - beqz(disp_hdr, unlocked); - } - - // Handle existing monitor. - ld(tmp, Address(oop, oopDesc::mark_offset_in_bytes())); - test_bit(t0, tmp, exact_log2(markWord::monitor_value)); - bnez(t0, object_has_monitor); - - if (LockingMode == LM_MONITOR) { - j(slow_path); - } else { - assert(LockingMode == LM_LEGACY, "must be"); - // Check if it is still a light weight lock, this is true if we - // see the stack address of the basicLock in the markWord of the - // object. - - cmpxchg(/*memory address*/oop, /*expected value*/box, /*new value*/disp_hdr, Assembler::int64, - Assembler::relaxed, Assembler::rl, /*result*/tmp); - beq(box, tmp, unlocked); // box == tmp if cas succeeds - j(slow_path); - } - - assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); - - // Handle existing monitor. - bind(object_has_monitor); - subi(tmp, tmp, (int)markWord::monitor_value); // monitor - ld(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset())); - - Label notRecursive; - beqz(disp_hdr, notRecursive); // Will be 0 if not recursive. - - // Recursive lock - subi(disp_hdr, disp_hdr, 1); - sd(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset())); - j(unlocked); - - bind(notRecursive); - // Compute owner address. - la(owner_addr, Address(tmp, ObjectMonitor::owner_offset())); - - // Set owner to null. - // Release to satisfy the JMM - membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); - sd(zr, Address(owner_addr)); - // We need a full fence after clearing owner to avoid stranding. - // StoreLoad achieves this. - membar(StoreLoad); - - // Check if the entry_list is empty. - ld(t0, Address(tmp, ObjectMonitor::entry_list_offset())); - beqz(t0, unlocked); // If so we are done. - - // Check if there is a successor. - ld(t0, Address(tmp, ObjectMonitor::succ_offset())); - bnez(t0, unlocked); // If so we are done. - - // Save the monitor pointer in the current thread, so we can try to - // reacquire the lock in SharedRuntime::monitor_exit_helper(). - sd(tmp, Address(xthread, JavaThread::unlocked_inflated_monitor_offset())); - - mv(flag, 1); - j(slow_path); - - bind(unlocked); - mv(flag, zr); - if (LockingMode == LM_LEGACY) { - dec_held_monitor_count(t0); - } - -#ifdef ASSERT - // Check that unlocked label is reached with flag == 0. - Label flag_correct; - beqz(flag, flag_correct); - stop("Fast Lock Flag != 0"); -#endif - - bind(slow_path); -#ifdef ASSERT - // Check that slow_path label is reached with flag != 0. - bnez(flag, flag_correct); - stop("Fast Lock Flag == 0"); - bind(flag_correct); -#endif - // C2 uses the value of flag (0 vs !0) to determine the continuation. -} - void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Register tmp1, Register tmp2, Register tmp3, Register tmp4) { // Flag register, zero for success; non-zero for failure. Register flag = t1; - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); assert_different_registers(obj, box, tmp1, tmp2, tmp3, tmp4, flag, t0); mv(flag, 1); @@ -439,7 +210,6 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register box, // Flag register, zero for success; non-zero for failure. Register flag = t1; - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); assert_different_registers(obj, box, tmp1, tmp2, tmp3, flag, t0); mv(flag, 1); @@ -2823,10 +2593,14 @@ void C2_MacroAssembler::char_array_compress_v(Register src, Register dst, Regist // Intrinsic for // -// - sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray -// return the number of characters copied. -// - java/lang/StringUTF16.compress -// return index of non-latin1 character if copy fails, otherwise 'len'. +// - sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// Encodes char[] to byte[] in ISO-8859-1 +// +// - java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// Encodes byte[] (containing UTF-16) to byte[] in ISO-8859-1 +// +// - java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) +// Encodes char[] to byte[] in ASCII // // This version always returns the number of characters copied. A successful // copy will complete with the post-condition: 'res' == 'len', while an diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp index 73fceea3805..309ef8d9d5e 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -49,11 +49,6 @@ const int STUB_THRESHOLD, Label *STUB, Label *DONE); public: - // Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file. - void fast_lock(Register object, Register box, - Register tmp1, Register tmp2, Register tmp3, Register tmp4); - void fast_unlock(Register object, Register box, Register tmp1, Register tmp2); - // Code used by cmpFastLockLightweight and cmpFastUnlockLightweight mach instructions in .ad file. void fast_lock_lightweight(Register object, Register box, Register tmp1, Register tmp2, Register tmp3, Register tmp4); diff --git a/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp b/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp index 78975b8df89..461dc19f383 100644 --- a/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp @@ -194,6 +194,9 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { } } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { +} + //////// Thaw // Fast path diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp index 92f7169f471..7c4b8444407 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp @@ -733,84 +733,26 @@ void InterpreterMacroAssembler::leave_jfr_critical_section() { void InterpreterMacroAssembler::lock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1"); - if (LockingMode == LM_MONITOR) { - call_VM_preemptable(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - lock_reg); - } else { - Label count, done; - const Register swap_reg = x10; - const Register tmp = c_rarg2; - const Register obj_reg = c_rarg3; // Will contain the oop - const Register tmp2 = c_rarg4; - const Register tmp3 = c_rarg5; + const Register tmp = c_rarg2; + const Register obj_reg = c_rarg3; // Will contain the oop + const Register tmp2 = c_rarg4; + const Register tmp3 = c_rarg5; - const int obj_offset = in_bytes(BasicObjectLock::obj_offset()); - const int lock_offset = in_bytes(BasicObjectLock::lock_offset()); - const int mark_offset = lock_offset + - BasicLock::displaced_header_offset_in_bytes(); + // Load object pointer into obj_reg (c_rarg3) + ld(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); - Label slow_case; + Label done, slow_case; + lightweight_lock(lock_reg, obj_reg, tmp, tmp2, tmp3, slow_case); + j(done); - // Load object pointer into obj_reg c_rarg3 - ld(obj_reg, Address(lock_reg, obj_offset)); + bind(slow_case); + // Call the runtime routine for slow case + call_VM_preemptable(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), + lock_reg); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(lock_reg, obj_reg, tmp, tmp2, tmp3, slow_case); - j(done); - } else if (LockingMode == LM_LEGACY) { - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmp, obj_reg); - lbu(tmp, Address(tmp, Klass::misc_flags_offset())); - test_bit(tmp, tmp, exact_log2(KlassFlags::_misc_is_value_based_class)); - bnez(tmp, slow_case); - } - - // Load (object->mark() | 1) into swap_reg - ld(t0, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - ori(swap_reg, t0, 1); - - // Save (object->mark() | 1) into BasicLock's displaced header - sd(swap_reg, Address(lock_reg, mark_offset)); - - assert(lock_offset == 0, - "displached header must be first word in BasicObjectLock"); - - cmpxchg_obj_header(swap_reg, lock_reg, obj_reg, tmp, count, /*fallthrough*/nullptr); - - // Test if the oopMark is an obvious stack pointer, i.e., - // 1) (mark & 7) == 0, and - // 2) sp <= mark < mark + os::pagesize() - // - // These 3 tests can be done by evaluating the following - // expression: ((mark - sp) & (7 - os::vm_page_size())), - // assuming both stack pointer and pagesize have their - // least significant 3 bits clear. - // NOTE: the oopMark is in swap_reg x10 as the result of cmpxchg - sub(swap_reg, swap_reg, sp); - mv(t0, (int64_t)(7 - (int)os::vm_page_size())); - andr(swap_reg, swap_reg, t0); - - // Save the test result, for recursive case, the result is zero - sd(swap_reg, Address(lock_reg, mark_offset)); - bnez(swap_reg, slow_case); - - bind(count); - inc_held_monitor_count(t0); - j(done); - } - - bind(slow_case); - - // Call the runtime routine for slow case - call_VM_preemptable(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - lock_reg); - - bind(done); - } + bind(done); } @@ -829,58 +771,30 @@ void InterpreterMacroAssembler::unlock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be rarg1"); - if (LockingMode == LM_MONITOR) { - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - } else { - Label count, done; + const Register swap_reg = x10; + const Register header_reg = c_rarg2; // Will contain the old oopMark + const Register obj_reg = c_rarg3; // Will contain the oop + const Register tmp_reg = c_rarg4; // Temporary used by lightweight_unlock - const Register swap_reg = x10; - const Register header_reg = c_rarg2; // Will contain the old oopMark - const Register obj_reg = c_rarg3; // Will contain the oop - const Register tmp_reg = c_rarg4; // Temporary used by lightweight_unlock + save_bcp(); // Save in case of exception - save_bcp(); // Save in case of exception + // Load oop into obj_reg (c_rarg3) + ld(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); - if (LockingMode != LM_LIGHTWEIGHT) { - // Convert from BasicObjectLock structure to object and BasicLock - // structure Store the BasicLock address into x10 - la(swap_reg, Address(lock_reg, BasicObjectLock::lock_offset())); - } + // Free entry + sd(zr, Address(lock_reg, BasicObjectLock::obj_offset())); - // Load oop into obj_reg(c_rarg3) - ld(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); + Label done, slow_case; + lightweight_unlock(obj_reg, header_reg, swap_reg, tmp_reg, slow_case); + j(done); - // Free entry - sd(zr, Address(lock_reg, BasicObjectLock::obj_offset())); + bind(slow_case); + // Call the runtime routine for slow case. + sd(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); // restore obj + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - Label slow_case; - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_unlock(obj_reg, header_reg, swap_reg, tmp_reg, slow_case); - j(done); - } else if (LockingMode == LM_LEGACY) { - // Load the old header from BasicLock structure - ld(header_reg, Address(swap_reg, - BasicLock::displaced_header_offset_in_bytes())); - - // Test for recursion - beqz(header_reg, count); - - // Atomic swap back the old header - cmpxchg_obj_header(swap_reg, header_reg, obj_reg, tmp_reg, count, &slow_case); - - bind(count); - dec_held_monitor_count(t0); - j(done); - } - - bind(slow_case); - // Call the runtime routine for slow case. - sd(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); // restore obj - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - - bind(done); - restore_bcp(); - } + bind(done); + restore_bcp(); } diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index a4c44b0b1d1..453538e4a5a 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -6421,7 +6421,6 @@ void MacroAssembler::test_bit(Register Rd, Register Rs, uint32_t bit_pos) { // - tmp1, tmp2, tmp3: temporary registers, will be destroyed // - slow: branched to if locking fails void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Register tmp1, Register tmp2, Register tmp3, Label& slow) { - assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking"); assert_different_registers(basic_lock, obj, tmp1, tmp2, tmp3, t0); Label push; @@ -6481,7 +6480,6 @@ void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Registe // - tmp1, tmp2, tmp3: temporary registers // - slow: branched to if unlocking fails void MacroAssembler::lightweight_unlock(Register obj, Register tmp1, Register tmp2, Register tmp3, Label& slow) { - assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking"); assert_different_registers(obj, tmp1, tmp2, tmp3, t0); #ifdef ASSERT diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 8e7b772414a..7d204007898 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. // Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. // Copyright (c) 2020, 2024, Huawei Technologies Co., Ltd. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -11021,45 +11021,9 @@ instruct tlsLoadP(javaThread_RegP dst) // inlined locking and unlocking // using t1 as the 'flag' register to bridge the BoolNode producers and consumers -instruct cmpFastLock(rFlagsReg cr, iRegP object, iRegP box, - iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegPNoSp tmp4) -%{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set cr (FastLock object box)); - effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4); - - ins_cost(10 * DEFAULT_COST); - format %{ "fastlock $object,$box\t! kills $tmp1,$tmp2,$tmp3,$tmp4 #@cmpFastLock" %} - - ins_encode %{ - __ fast_lock($object$$Register, $box$$Register, - $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register); - %} - - ins_pipe(pipe_serial); -%} - -// using t1 as the 'flag' register to bridge the BoolNode producers and consumers -instruct cmpFastUnlock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2) -%{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set cr (FastUnlock object box)); - effect(TEMP tmp1, TEMP tmp2); - - ins_cost(10 * DEFAULT_COST); - format %{ "fastunlock $object,$box\t! kills $tmp1, $tmp2, #@cmpFastUnlock" %} - - ins_encode %{ - __ fast_unlock($object$$Register, $box$$Register, $tmp1$$Register, $tmp2$$Register); - %} - - ins_pipe(pipe_serial); -%} - instruct cmpFastLockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3, iRegPNoSp tmp4) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastLock object box)); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4); @@ -11074,10 +11038,10 @@ instruct cmpFastLockLightweight(rFlagsReg cr, iRegP object, iRegP box, ins_pipe(pipe_serial); %} +// using t1 as the 'flag' register to bridge the BoolNode producers and consumers instruct cmpFastUnlockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastUnlock object box)); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3); diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index c4e5a871a88..2cc1fb8eeff 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -1637,7 +1637,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // We use the same pc/oopMap repeatedly when we call out. Label native_return; - if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + if (method->is_object_wait0()) { // For convenience we use the pc we want to resume to in case of preemption on Object.wait. __ set_last_Java_frame(sp, noreg, native_return, t0); } else { @@ -1679,8 +1679,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, Label lock_done; if (method->is_synchronized()) { - Label count; - const int mark_word_offset = BasicLock::displaced_header_offset_in_bytes(); // Get the handle (the 2nd argument) @@ -1693,42 +1691,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Load the oop from the handle __ ld(obj_reg, Address(oop_handle_reg, 0)); - if (LockingMode == LM_MONITOR) { - __ j(slow_path_lock); - } else if (LockingMode == LM_LEGACY) { - // Load (object->mark() | 1) into swap_reg % x10 - __ ld(t0, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - __ ori(swap_reg, t0, 1); - - // Save (object->mark() | 1) into BasicLock's displaced header - __ sd(swap_reg, Address(lock_reg, mark_word_offset)); - - // src -> dest if dest == x10 else x10 <- dest - __ cmpxchg_obj_header(x10, lock_reg, obj_reg, lock_tmp, count, /*fallthrough*/nullptr); - - // Test if the oopMark is an obvious stack pointer, i.e., - // 1) (mark & 3) == 0, and - // 2) sp <= mark < mark + os::pagesize() - // These 3 tests can be done by evaluating the following - // expression: ((mark - sp) & (3 - os::vm_page_size())), - // assuming both stack pointer and pagesize have their - // least significant 2 bits clear. - // NOTE: the oopMark is in swap_reg % 10 as the result of cmpxchg - - __ sub(swap_reg, swap_reg, sp); - __ mv(t0, 3 - (int)os::vm_page_size()); - __ andr(swap_reg, swap_reg, t0); - - // Save the test result, for recursive case, the result is zero - __ sd(swap_reg, Address(lock_reg, mark_word_offset)); - __ bnez(swap_reg, slow_path_lock); - - __ bind(count); - __ inc_held_monitor_count(t0); - } else { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); - __ lightweight_lock(lock_reg, obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock); - } + __ lightweight_lock(lock_reg, obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock); // Slow path will re-enter here __ bind(lock_done); @@ -1789,7 +1752,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); __ sw(t0, Address(t1)); - if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + if (method->is_object_wait0()) { // Check preemption for Object.wait() __ ld(t1, Address(xthread, JavaThread::preempt_alternate_return_offset())); __ beqz(t1, native_return); @@ -1818,48 +1781,18 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Get locked oop from the handle we passed to jni __ ld(obj_reg, Address(oop_handle_reg, 0)); - Label done, not_recursive; - - if (LockingMode == LM_LEGACY) { - // Simple recursive lock? - __ ld(t0, Address(sp, lock_slot_offset * VMRegImpl::stack_slot_size)); - __ bnez(t0, not_recursive); - __ dec_held_monitor_count(t0); - __ j(done); - } - - __ bind(not_recursive); - // Must save x10 if if it is live now because cmpxchg must use it if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { save_native_result(masm, ret_type, stack_slots); } - if (LockingMode == LM_MONITOR) { - __ j(slow_path_unlock); - } else if (LockingMode == LM_LEGACY) { - // get address of the stack lock - __ la(x10, Address(sp, lock_slot_offset * VMRegImpl::stack_slot_size)); - // get old displaced header - __ ld(old_hdr, Address(x10, 0)); - - // Atomic swap old header if oop still contains the stack lock - Label count; - __ cmpxchg_obj_header(x10, old_hdr, obj_reg, lock_tmp, count, &slow_path_unlock); - __ bind(count); - __ dec_held_monitor_count(t0); - } else { - assert(LockingMode == LM_LIGHTWEIGHT, ""); - __ lightweight_unlock(obj_reg, old_hdr, swap_reg, lock_tmp, slow_path_unlock); - } + __ lightweight_unlock(obj_reg, old_hdr, swap_reg, lock_tmp, slow_path_unlock); // slow path re-enters here __ bind(unlock_done); if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { restore_native_result(masm, ret_type, stack_slots); } - - __ bind(done); } Label dtrace_method_exit, dtrace_method_exit_done; diff --git a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp index 21164107053..61f4aa3e722 100644 --- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp @@ -1253,22 +1253,17 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { __ mv(t0, _thread_in_Java); __ sw(t0, Address(xthread, JavaThread::thread_state_offset())); - if (LockingMode != LM_LEGACY) { - // Check preemption for Object.wait() - Label not_preempted; - __ ld(t1, Address(xthread, JavaThread::preempt_alternate_return_offset())); - __ beqz(t1, not_preempted); - __ sd(zr, Address(xthread, JavaThread::preempt_alternate_return_offset())); - __ jr(t1); - __ bind(native_return); - __ restore_after_resume(true /* is_native */); - // reload result_handler - __ ld(result_handler, Address(fp, frame::interpreter_frame_result_handler_offset * wordSize)); - __ bind(not_preempted); - } else { - // any pc will do so just use this one for LM_LEGACY to keep code together. - __ bind(native_return); - } + // Check preemption for Object.wait() + Label not_preempted; + __ ld(t1, Address(xthread, JavaThread::preempt_alternate_return_offset())); + __ beqz(t1, not_preempted); + __ sd(zr, Address(xthread, JavaThread::preempt_alternate_return_offset())); + __ jr(t1); + __ bind(native_return); + __ restore_after_resume(true /* is_native */); + // reload result_handler + __ ld(result_handler, Address(fp, frame::interpreter_frame_result_handler_offset * wordSize)); + __ bind(not_preempted); // reset_last_Java_frame __ reset_last_Java_frame(true); diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index 4d90f313267..87dc8b9286d 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -229,11 +229,7 @@ int LIR_Assembler::emit_unwind_handler() { LIR_Opr lock = FrameMap::as_opr(Z_R1_scratch); monitor_address(0, lock); stub = new MonitorExitStub(lock, true, 0); - if (LockingMode == LM_MONITOR) { - __ branch_optimized(Assembler::bcondAlways, *stub->entry()); - } else { - __ unlock_object(Rtmp1, Rtmp2, lock->as_register(), *stub->entry()); - } + __ unlock_object(Rtmp1, Rtmp2, lock->as_register(), *stub->entry()); __ bind(*stub->continuation()); } @@ -2714,13 +2710,7 @@ void LIR_Assembler::emit_lock(LIR_OpLock* op) { Register obj = op->obj_opr()->as_register(); // May not be an oop. Register hdr = op->hdr_opr()->as_register(); Register lock = op->lock_opr()->as_register(); - if (LockingMode == LM_MONITOR) { - if (op->info() != nullptr) { - add_debug_info_for_null_check_here(op->info()); - __ null_check(obj); - } - __ branch_optimized(Assembler::bcondAlways, *op->stub()->entry()); - } else if (op->code() == lir_lock) { + if (op->code() == lir_lock) { assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); // Add debug info for NullPointerException only if one is possible. if (op->info() != nullptr) { diff --git a/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp index 519884c8212..91d52def08d 100644 --- a/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp @@ -58,8 +58,6 @@ void C1_MacroAssembler::verified_entry(bool breakAtEntry) { } void C1_MacroAssembler::lock_object(Register Rmark, Register Roop, Register Rbox, Label& slow_case) { - const int hdr_offset = oopDesc::mark_offset_in_bytes(); - const Register tmp = Z_R1_scratch; assert_different_registers(Rmark, Roop, Rbox, tmp); @@ -69,95 +67,17 @@ void C1_MacroAssembler::lock_object(Register Rmark, Register Roop, Register Rbox // Save object being locked into the BasicObjectLock... z_stg(Roop, Address(Rbox, BasicObjectLock::obj_offset())); - assert(LockingMode != LM_MONITOR, "LM_MONITOR is already handled, by emit_lock()"); - - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(Rbox, Roop, Rmark, tmp, slow_case); - } else if (LockingMode == LM_LEGACY) { - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmp, Roop); - z_tm(Address(tmp, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class); - branch_optimized(Assembler::bcondAllOne, slow_case); - } - - NearLabel done; - - // Load object header. - z_lg(Rmark, Address(Roop, hdr_offset)); - - // and mark it as unlocked. - z_oill(Rmark, markWord::unlocked_value); - // Save unlocked object header into the displaced header location on the stack. - z_stg(Rmark, Address(Rbox, BasicLock::displaced_header_offset_in_bytes())); - // Test if object header is still the same (i.e. unlocked), and if so, store the - // displaced header address in the object header. If it is not the same, get the - // object header instead. - z_csg(Rmark, Rbox, hdr_offset, Roop); - // If the object header was the same, we're done. - branch_optimized(Assembler::bcondEqual, done); - // If the object header was not the same, it is now in the Rmark register. - // => Test if it is a stack pointer into the same stack (recursive locking), i.e.: - // - // 1) (Rmark & markWord::lock_mask_in_place) == 0 - // 2) rsp <= Rmark - // 3) Rmark <= rsp + page_size - // - // These 3 tests can be done by evaluating the following expression: - // - // (Rmark - Z_SP) & (~(page_size-1) | markWord::lock_mask_in_place) - // - // assuming both the stack pointer and page_size have their least - // significant 2 bits cleared and page_size is a power of 2 - z_sgr(Rmark, Z_SP); - - load_const_optimized(Z_R0_scratch, (~(os::vm_page_size() - 1) | markWord::lock_mask_in_place)); - z_ngr(Rmark, Z_R0_scratch); // AND sets CC (result eq/ne 0). - // For recursive locking, the result is zero. => Save it in the displaced header - // location (null in the displaced Rmark location indicates recursive locking). - z_stg(Rmark, Address(Rbox, BasicLock::displaced_header_offset_in_bytes())); - // Otherwise we don't care about the result and handle locking via runtime call. - branch_optimized(Assembler::bcondNotZero, slow_case); - // done - bind(done); - } else { - assert(false, "Unhandled LockingMode:%d", LockingMode); - } + lightweight_lock(Rbox, Roop, Rmark, tmp, slow_case); } void C1_MacroAssembler::unlock_object(Register Rmark, Register Roop, Register Rbox, Label& slow_case) { - const int hdr_offset = oopDesc::mark_offset_in_bytes(); - assert_different_registers(Rmark, Roop, Rbox); - NearLabel done; - - if (LockingMode != LM_LIGHTWEIGHT) { - // Load displaced header. - z_ltg(Rmark, Address(Rbox, BasicLock::displaced_header_offset_in_bytes())); - // If the loaded Rmark is null we had recursive locking, and we are done. - z_bre(done); - } - // Load object. z_lg(Roop, Address(Rbox, BasicObjectLock::obj_offset())); verify_oop(Roop, FILE_AND_LINE); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_unlock(Roop, Rmark, Z_R1_scratch, slow_case); - } else if (LockingMode == LM_LEGACY) { - // Test if object header is pointing to the displaced header, and if so, restore - // the displaced header in the object. If the object header is not pointing to - // the displaced header, get the object header instead. - z_csg(Rbox, Rmark, hdr_offset, Roop); - // If the object header was not pointing to the displaced header, - // we do unlocking via runtime call. - branch_optimized(Assembler::bcondNotEqual, slow_case); - } else { - assert(false, "Unhandled LockingMode:%d", LockingMode); - } - // done - bind(done); + lightweight_unlock(Roop, Rmark, Z_R1_scratch, slow_case); } void C1_MacroAssembler::try_allocate( diff --git a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp index bee7bb72cbf..d39821bd034 100644 --- a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp @@ -60,6 +60,10 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { Unimplemented(); } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { + Unimplemented(); +} + inline void FreezeBase::patch_stack_pd(intptr_t* frame_sp, intptr_t* heap_sp) { Unimplemented(); } diff --git a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp index b384b24d49b..aa3d4849062 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.cpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp @@ -1008,109 +1008,18 @@ void InterpreterMacroAssembler::remove_activation(TosState state, // object (Z_R11, Z_R2) - Address of the object to be locked. // templateTable (monitorenter) is using Z_R2 for object void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { - - if (LockingMode == LM_MONITOR) { - call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor); - return; - } - - // template code: (for LM_LEGACY) - // - // markWord displaced_header = obj->mark().set_unlocked(); - // monitor->lock()->set_displaced_header(displaced_header); - // if (Atomic::cmpxchg(/*addr*/obj->mark_addr(), /*cmp*/displaced_header, /*ex=*/monitor) == displaced_header) { - // // We stored the monitor address into the object's mark word. - // } else if (THREAD->is_lock_owned((address)displaced_header)) - // // Simple recursive case. - // monitor->lock()->set_displaced_header(nullptr); - // } else { - // // Slow path. - // InterpreterRuntime::monitorenter(THREAD, monitor); - // } - - const int hdr_offset = oopDesc::mark_offset_in_bytes(); - const Register header = Z_ARG5; - const Register object_mark_addr = Z_ARG4; - const Register current_header = Z_ARG5; const Register tmp = Z_R1_scratch; NearLabel done, slow_case; - // markWord header = obj->mark().set_unlocked(); - - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(monitor, object, header, tmp, slow_case); - } else if (LockingMode == LM_LEGACY) { - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmp, object); - z_tm(Address(tmp, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class); - z_btrue(slow_case); - } - - // Load markWord from object into header. - z_lg(header, hdr_offset, object); - - // Set header to be (markWord of object | UNLOCK_VALUE). - // This will not change anything if it was unlocked before. - z_oill(header, markWord::unlocked_value); - - // monitor->lock()->set_displaced_header(displaced_header); - const int lock_offset = in_bytes(BasicObjectLock::lock_offset()); - const int mark_offset = lock_offset + BasicLock::displaced_header_offset_in_bytes(); - - // Initialize the box (Must happen before we update the object mark!). - z_stg(header, mark_offset, monitor); - - // if (Atomic::cmpxchg(/*addr*/obj->mark_addr(), /*cmp*/displaced_header, /*ex=*/monitor) == displaced_header) { - - // not necessary, use offset in instruction directly. - // add2reg(object_mark_addr, hdr_offset, object); - - // Store stack address of the BasicObjectLock (this is monitor) into object. - z_csg(header, monitor, hdr_offset, object); - assert(current_header == header, - "must be same register"); // Identified two registers from z/Architecture. - - z_bre(done); - - // } else if (THREAD->is_lock_owned((address)displaced_header)) - // // Simple recursive case. - // monitor->lock()->set_displaced_header(nullptr); - - // We did not see an unlocked object so try the fast recursive case. - - // Check if owner is self by comparing the value in the markWord of object - // (current_header) with the stack pointer. - z_sgr(current_header, Z_SP); - - assert(os::vm_page_size() > 0xfff, "page size too small - change the constant"); - - // The prior sequence "LGR, NGR, LTGR" can be done better - // (Z_R1 is temp and not used after here). - load_const_optimized(Z_R0, (~(os::vm_page_size() - 1) | markWord::lock_mask_in_place)); - z_ngr(Z_R0, current_header); // AND sets CC (result eq/ne 0) - - // If condition is true we are done and hence we can store 0 in the displaced - // header indicating it is a recursive lock and be done. - z_brne(slow_case); - z_release(); // Member unnecessary on zarch AND because the above csg does a sync before and after. - z_stg(Z_R0/*==0!*/, mark_offset, monitor); - } + lightweight_lock(monitor, object, header, tmp, slow_case); z_bru(done); - // } else { - // // Slow path. - // InterpreterRuntime::monitorenter(THREAD, monitor); - // None of the above fast optimizations worked so we have to get into the - // slow case of monitor enter. bind(slow_case); call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor); - // } - bind(done); } @@ -1122,28 +1031,6 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { // // Throw IllegalMonitorException if object is not locked by current thread. void InterpreterMacroAssembler::unlock_object(Register monitor, Register object) { - - if (LockingMode == LM_MONITOR) { - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), monitor); - return; - } - -// else { - // template code: (for LM_LEGACY): - // - // if ((displaced_header = monitor->displaced_header()) == nullptr) { - // // Recursive unlock. Mark the monitor unlocked by setting the object field to null. - // monitor->set_obj(nullptr); - // } else if (Atomic::cmpxchg(obj->mark_addr(), monitor, displaced_header) == monitor) { - // // We swapped the unlocked mark in displaced_header into the object's mark word. - // monitor->set_obj(nullptr); - // } else { - // // Slow path. - // InterpreterRuntime::monitorexit(monitor); - // } - - const int hdr_offset = oopDesc::mark_offset_in_bytes(); - const Register header = Z_ARG4; const Register current_header = Z_R1_scratch; Address obj_entry(monitor, BasicObjectLock::obj_offset()); @@ -1159,56 +1046,16 @@ void InterpreterMacroAssembler::unlock_object(Register monitor, Register object) assert_different_registers(monitor, object, header, current_header); - // if ((displaced_header = monitor->displaced_header()) == nullptr) { - // // Recursive unlock. Mark the monitor unlocked by setting the object field to null. - // monitor->set_obj(nullptr); - - // monitor->lock()->set_displaced_header(displaced_header); - const int lock_offset = in_bytes(BasicObjectLock::lock_offset()); - const int mark_offset = lock_offset + BasicLock::displaced_header_offset_in_bytes(); - clear_mem(obj_entry, sizeof(oop)); - if (LockingMode != LM_LIGHTWEIGHT) { - // Test first if we are in the fast recursive case. - MacroAssembler::load_and_test_long(header, Address(monitor, mark_offset)); - z_bre(done); // header == 0 -> goto done - } - // } else if (Atomic::cmpxchg(obj->mark_addr(), monitor, displaced_header) == monitor) { - // // We swapped the unlocked mark in displaced_header into the object's mark word. - // monitor->set_obj(nullptr); - - // If we still have a lightweight lock, unlock the object and be done. - if (LockingMode == LM_LIGHTWEIGHT) { - - lightweight_unlock(object, header, current_header, slow_case); - - z_bru(done); - } else { - // The markword is expected to be at offset 0. - // This is not required on s390, at least not here. - assert(hdr_offset == 0, "unlock_object: review code below"); - - // We have the displaced header in header. If the lock is still - // lightweight, it will contain the monitor address and we'll store the - // displaced header back into the object's mark word. - z_lgr(current_header, monitor); - z_csg(current_header, header, hdr_offset, object); - z_bre(done); - } - - // } else { - // // Slow path. - // InterpreterRuntime::monitorexit(monitor); + lightweight_unlock(object, header, current_header, slow_case); + z_bru(done); // The lock has been converted into a heavy lock and hence // we need to get into the slow case. bind(slow_case); z_stg(object, obj_entry); // Restore object entry, has been cleared above. call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), monitor); - - // } - bind(done); } diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index 0129e604978..b047ff00044 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -3766,211 +3766,6 @@ void MacroAssembler::increment_counter_eq(address counter_address, Register tmp1 bind(l); } -// "The box" is the space on the stack where we copy the object mark. -void MacroAssembler::compiler_fast_lock_object(Register oop, Register box, Register temp1, Register temp2) { - - assert(LockingMode != LM_LIGHTWEIGHT, "uses fast_lock_lightweight"); - assert_different_registers(oop, box, temp1, temp2, Z_R0_scratch); - - Register displacedHeader = temp1; - Register currentHeader = temp1; - Register temp = temp2; - - NearLabel done, object_has_monitor; - - const int hdr_offset = oopDesc::mark_offset_in_bytes(); - - BLOCK_COMMENT("compiler_fast_lock_object {"); - - // Load markWord from oop into mark. - z_lg(displacedHeader, hdr_offset, oop); - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(temp, oop); - z_tm(Address(temp, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class); - z_brne(done); - } - - // Handle existing monitor. - // The object has an existing monitor iff (mark & monitor_value) != 0. - guarantee(Immediate::is_uimm16(markWord::monitor_value), "must be half-word"); - z_tmll(displacedHeader, markWord::monitor_value); - z_brnaz(object_has_monitor); - - if (LockingMode == LM_MONITOR) { - // Set NE to indicate 'failure' -> take slow-path - // From loading the markWord, we know that oop != nullptr - z_ltgr(oop, oop); - z_bru(done); - } else { - assert(LockingMode == LM_LEGACY, "must be"); - // Set mark to markWord | markWord::unlocked_value. - z_oill(displacedHeader, markWord::unlocked_value); - - // Load Compare Value application register. - - // Initialize the box (must happen before we update the object mark). - z_stg(displacedHeader, BasicLock::displaced_header_offset_in_bytes(), box); - - // Compare object markWord with mark and if equal, exchange box with object markWork. - // If the compare-and-swap succeeds, then we found an unlocked object and have now locked it. - z_csg(displacedHeader, box, hdr_offset, oop); - assert(currentHeader == displacedHeader, "must be same register"); // Identified two registers from z/Architecture. - z_bre(done); - - // We did not see an unlocked object - // currentHeader contains what is currently stored in the oop's markWord. - // We might have a recursive case. Verify by checking if the owner is self. - // To do so, compare the value in the markWord (currentHeader) with the stack pointer. - z_sgr(currentHeader, Z_SP); - load_const_optimized(temp, (~(os::vm_page_size() - 1) | markWord::lock_mask_in_place)); - - z_ngr(currentHeader, temp); - - // result zero: owner is self -> recursive lock. Indicate that by storing 0 in the box. - // result not-zero: attempt failed. We don't hold the lock -> go for slow case. - - z_stg(currentHeader/*==0 or not 0*/, BasicLock::displaced_header_offset_in_bytes(), box); - - z_bru(done); - } - - bind(object_has_monitor); - - Register zero = temp; - Register monitor_tagged = displacedHeader; // Tagged with markWord::monitor_value. - - // Try to CAS owner (no owner => current thread's _monitor_owner_id). - // If csg succeeds then CR=EQ, otherwise, register zero is filled - // with the current owner. - z_lghi(zero, 0); - z_lg(Z_R0_scratch, Address(Z_thread, JavaThread::monitor_owner_id_offset())); - z_csg(zero, Z_R0_scratch, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor_tagged); - - // Store a non-null value into the box. - z_stg(box, BasicLock::displaced_header_offset_in_bytes(), box); - - z_bre(done); // acquired the lock for the first time. - - BLOCK_COMMENT("fast_path_recursive_lock {"); - // Check if we are already the owner (recursive lock) - z_cgr(Z_R0_scratch, zero); // owner is stored in zero by "z_csg" above - z_brne(done); // not a recursive lock - - // Current thread already owns the lock. Just increment recursion count. - z_agsi(Address(monitor_tagged, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 1ll); - z_cgr(zero, zero); // set the CC to EQUAL - BLOCK_COMMENT("} fast_path_recursive_lock"); - bind(done); - - BLOCK_COMMENT("} compiler_fast_lock_object"); - // If locking was successful, CR should indicate 'EQ'. - // The compiler or the native wrapper generates a branch to the runtime call - // _complete_monitor_locking_Java. -} - -void MacroAssembler::compiler_fast_unlock_object(Register oop, Register box, Register temp1, Register temp2) { - - assert(LockingMode != LM_LIGHTWEIGHT, "uses fast_unlock_lightweight"); - assert_different_registers(oop, box, temp1, temp2, Z_R0_scratch); - - Register displacedHeader = temp1; - Register currentHeader = temp2; - Register temp = temp1; - - const int hdr_offset = oopDesc::mark_offset_in_bytes(); - - Label done, object_has_monitor, not_recursive; - - BLOCK_COMMENT("compiler_fast_unlock_object {"); - - if (LockingMode == LM_LEGACY) { - // Find the lock address and load the displaced header from the stack. - // if the displaced header is zero, we have a recursive unlock. - load_and_test_long(displacedHeader, Address(box, BasicLock::displaced_header_offset_in_bytes())); - z_bre(done); - } - - // Handle existing monitor. - // The object has an existing monitor iff (mark & monitor_value) != 0. - z_lg(currentHeader, hdr_offset, oop); - guarantee(Immediate::is_uimm16(markWord::monitor_value), "must be half-word"); - - z_tmll(currentHeader, markWord::monitor_value); - z_brnaz(object_has_monitor); - - if (LockingMode == LM_MONITOR) { - // Set NE to indicate 'failure' -> take slow-path - z_ltgr(oop, oop); - z_bru(done); - } else { - assert(LockingMode == LM_LEGACY, "must be"); - // Check if it is still a lightweight lock, this is true if we see - // the stack address of the basicLock in the markWord of the object - // copy box to currentHeader such that csg does not kill it. - z_lgr(currentHeader, box); - z_csg(currentHeader, displacedHeader, hdr_offset, oop); - z_bru(done); // csg sets CR as desired. - } - - // In case of LM_LIGHTWEIGHT, we may reach here with (temp & ObjectMonitor::ANONYMOUS_OWNER) != 0. - // This is handled like owner thread mismatches: We take the slow path. - - // Handle existing monitor. - bind(object_has_monitor); - - z_lg(Z_R0_scratch, Address(Z_thread, JavaThread::monitor_owner_id_offset())); - z_cg(Z_R0_scratch, Address(currentHeader, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner))); - z_brne(done); - - BLOCK_COMMENT("fast_path_recursive_unlock {"); - load_and_test_long(temp, Address(currentHeader, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); - z_bre(not_recursive); // if 0 then jump, it's not recursive locking - - // Recursive inflated unlock - z_agsi(Address(currentHeader, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), -1ll); - z_cgr(currentHeader, currentHeader); // set the CC to EQUAL - BLOCK_COMMENT("} fast_path_recursive_unlock"); - z_bru(done); - - bind(not_recursive); - - NearLabel set_eq_unlocked; - - // Set owner to null. - // Release to satisfy the JMM - z_release(); - z_lghi(temp, 0); - z_stg(temp, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), currentHeader); - // We need a full fence after clearing owner to avoid stranding. - z_fence(); - - // Check if the entry_list is empty. - load_and_test_long(temp, Address(currentHeader, OM_OFFSET_NO_MONITOR_VALUE_TAG(entry_list))); - z_bre(done); // If so we are done. - - // Check if there is a successor. - load_and_test_long(temp, Address(currentHeader, OM_OFFSET_NO_MONITOR_VALUE_TAG(succ))); - z_brne(set_eq_unlocked); // If so we are done. - - // Save the monitor pointer in the current thread, so we can try to - // reacquire the lock in SharedRuntime::monitor_exit_helper(). - z_xilf(currentHeader, markWord::monitor_value); - z_stg(currentHeader, Address(Z_thread, JavaThread::unlocked_inflated_monitor_offset())); - - z_ltgr(oop, oop); // Set flag = NE - z_bru(done); - - bind(set_eq_unlocked); - z_cr(temp, temp); // Set flag = EQ - - bind(done); - - BLOCK_COMMENT("} compiler_fast_unlock_object"); - // flag == EQ indicates success - // flag == NE indicates failure -} - void MacroAssembler::resolve_jobject(Register value, Register tmp1, Register tmp2) { BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->resolve_jobject(this, value, tmp1, tmp2); @@ -6349,7 +6144,6 @@ void MacroAssembler::zap_from_to(Register low, Register high, Register val, Regi // Note: make sure Z_R1 is not manipulated here when C2 compiler is in play void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Register temp1, Register temp2, Label& slow) { - assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking"); assert_different_registers(basic_lock, obj, temp1, temp2); Label push; @@ -6415,7 +6209,6 @@ void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Registe // - Z_R1_scratch: will be killed in case of Interpreter & C1 Compiler void MacroAssembler::lightweight_unlock(Register obj, Register temp1, Register temp2, Label& slow) { - assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking"); assert_different_registers(obj, temp1, temp2); Label unlocked, push_and_slow; diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index 4aa7bb56f3a..1c4da4d26eb 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -790,8 +790,6 @@ class MacroAssembler: public Assembler { // Kills registers tmp1_reg and tmp2_reg and preserves the condition code. void increment_counter_eq(address counter_address, Register tmp1_reg, Register tmp2_reg); - void compiler_fast_lock_object(Register oop, Register box, Register temp1, Register temp2); - void compiler_fast_unlock_object(Register oop, Register box, Register temp1, Register temp2); void lightweight_lock(Register basic_lock, Register obj, Register tmp1, Register tmp2, Label& slow); void lightweight_unlock(Register obj, Register tmp1, Register tmp2, Label& slow); void compiler_fast_lock_lightweight_object(Register obj, Register box, Register tmp1, Register tmp2); diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index c32064be86d..4c6b0d96e6f 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. // Copyright (c) 2017, 2024 SAP SE. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // @@ -10161,30 +10161,7 @@ instruct partialSubtypeCheckConstSuper(rarg2RegP sub, rarg1RegP super, immP supe // ============================================================================ // inlined locking and unlocking -instruct cmpFastLock(flagsReg pcc, iRegP_N2P oop, iRegP_N2P box, iRegP tmp1, iRegP tmp2) %{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set pcc (FastLock oop box)); - effect(TEMP tmp1, TEMP tmp2); - ins_cost(100); - // TODO: s390 port size(VARIABLE_SIZE); // Uses load_const_optimized. - format %{ "FASTLOCK $oop, $box; KILL Z_ARG4, Z_ARG5" %} - ins_encode %{ __ compiler_fast_lock_object($oop$$Register, $box$$Register, $tmp1$$Register, $tmp2$$Register); %} - ins_pipe(pipe_class_dummy); -%} - -instruct cmpFastUnlock(flagsReg pcc, iRegP_N2P oop, iRegP_N2P box, iRegP tmp1, iRegP tmp2) %{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set pcc (FastUnlock oop box)); - effect(TEMP tmp1, TEMP tmp2); - ins_cost(100); - // TODO: s390 port size(FIXED_SIZE); - format %{ "FASTUNLOCK $oop, $box; KILL Z_ARG4, Z_ARG5" %} - ins_encode %{ __ compiler_fast_unlock_object($oop$$Register, $box$$Register, $tmp1$$Register, $tmp2$$Register); %} - ins_pipe(pipe_class_dummy); -%} - instruct cmpFastLockLightweight(flagsReg pcc, iRegP_N2P oop, iRegP_N2P box, iRegP tmp1, iRegP tmp2) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set pcc (FastLock oop box)); effect(TEMP tmp1, TEMP tmp2); ins_cost(100); @@ -10200,7 +10177,6 @@ instruct cmpFastLockLightweight(flagsReg pcc, iRegP_N2P oop, iRegP_N2P box, iReg %} instruct cmpFastUnlockLightweight(flagsReg pcc, iRegP_N2P oop, iRegP_N2P box, iRegP tmp1, iRegP tmp2) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set pcc (FastUnlock oop box)); effect(TEMP tmp1, TEMP tmp2); ins_cost(100); diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index 90f54b60611..3b1d06cf560 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -1764,13 +1764,8 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, __ add2reg(r_box, lock_offset, Z_SP); // Try fastpath for locking. - if (LockingMode == LM_LIGHTWEIGHT) { - // Fast_lock kills r_temp_1, r_temp_2. - __ compiler_fast_lock_lightweight_object(r_oop, r_box, r_tmp1, r_tmp2); - } else { - // Fast_lock kills r_temp_1, r_temp_2. - __ compiler_fast_lock_object(r_oop, r_box, r_tmp1, r_tmp2); - } + // Fast_lock kills r_temp_1, r_temp_2. + __ compiler_fast_lock_lightweight_object(r_oop, r_box, r_tmp1, r_tmp2); __ z_bre(done); //------------------------------------------------------------------------- @@ -1965,13 +1960,8 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, __ add2reg(r_box, lock_offset, Z_SP); // Try fastpath for unlocking. - if (LockingMode == LM_LIGHTWEIGHT) { - // Fast_unlock kills r_tmp1, r_tmp2. - __ compiler_fast_unlock_lightweight_object(r_oop, r_box, r_tmp1, r_tmp2); - } else { - // Fast_unlock kills r_tmp1, r_tmp2. - __ compiler_fast_unlock_object(r_oop, r_box, r_tmp1, r_tmp2); - } + // Fast_unlock kills r_tmp1, r_tmp2. + __ compiler_fast_unlock_lightweight_object(r_oop, r_box, r_tmp1, r_tmp2); __ z_bre(done); // Slow path for unlocking. diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp index 8261fbd083a..ed925aa23b4 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.cpp +++ b/src/hotspot/cpu/s390/vm_version_s390.cpp @@ -68,12 +68,13 @@ unsigned int VM_Version::_Icache_lineSize = DEFAULT // z14: 2017-09 // z15: 2019-09 // z16: 2022-05 +// z17: 2025-04 -static const char* z_gen[] = {" ", "G1", "G2", "G3", "G4", "G5", "G6", "G7", "G8", "G9", "G10" }; -static const char* z_machine[] = {" ", "2064", "2084", "2094", "2097", "2817", "2827", "2964", "3906", "8561", "3931" }; -static const char* z_name[] = {" ", "z900", "z990", "z9 EC", "z10 EC", "z196 EC", "ec12", "z13", "z14", "z15", "z16" }; -static const char* z_WDFM[] = {" ", "2006-06-30", "2008-06-30", "2010-06-30", "2012-06-30", "2014-06-30", "2016-12-31", "2019-06-30", "2021-06-30", "2024-12-31", "tbd" }; -static const char* z_EOS[] = {" ", "2014-12-31", "2014-12-31", "2017-10-31", "2019-12-31", "2021-12-31", "2023-12-31", "2024-12-31", "tbd", "tbd", "tbd" }; +static const char* z_gen[] = {" ", "G1", "G2", "G3", "G4", "G5", "G6", "G7", "G8", "G9", "G10", "G11" }; +static const char* z_machine[] = {" ", "2064", "2084", "2094", "2097", "2817", "2827", "2964", "3906", "8561", "3931", "9175" }; +static const char* z_name[] = {" ", "z900", "z990", "z9 EC", "z10 EC", "z196 EC", "ec12", "z13", "z14", "z15", "z16", "z17" }; +static const char* z_WDFM[] = {" ", "2006-06-30", "2008-06-30", "2010-06-30", "2012-06-30", "2014-06-30", "2016-12-31", "2019-06-30", "2021-06-30", "2024-12-31", "tbd", "tbd" }; +static const char* z_EOS[] = {" ", "2014-12-31", "2014-12-31", "2017-10-31", "2019-12-31", "2021-12-31", "2023-12-31", "2024-12-31", "tbd", "tbd", "tbd", "tbd" }; static const char* z_features[] = {" ", "system-z, g1-z900, ldisp", "system-z, g2-z990, ldisp_fast", @@ -85,7 +86,9 @@ static const char* z_features[] = {" ", "system-z, g8-z14, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm, vectorinstr, instrext2, venh1", "system-z, g9-z15, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm, vectorinstr, instrext2, venh1, instrext3, venh2", "system-z, g10-z16, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm, vectorinstr, instrext2, venh1, instrext3, venh2," - "bear_enh, sort_enh, nnpa_assist, storage_key_removal, vpack_decimal_enh" + "bear_enh, sort_enh, nnpa_assist, storage_key_removal, vpack_decimal_enh", + "system-z, g11-z17, ldisp_fast, extimm, pcrel_load/store, cmpb, cond_load/store, interlocked_update, txm, vectorinstr, instrext2, venh1, instrext3, venh2," + "bear_enh, sort_enh, nnpa_assist, storage_key_removal, vpack_decimal_enh, concurrent_function" }; void VM_Version::initialize() { @@ -339,6 +342,11 @@ int VM_Version::get_model_index() { // is the index of the oldest detected model. int ambiguity = 0; int model_ix = 0; + if (is_z17()) { + model_ix = 11; + ambiguity++; + } + if (is_z16()) { model_ix = 10; ambiguity++; diff --git a/src/hotspot/cpu/s390/vm_version_s390.hpp b/src/hotspot/cpu/s390/vm_version_s390.hpp index 6c6eb76bf7b..04005ff2cf9 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.hpp +++ b/src/hotspot/cpu/s390/vm_version_s390.hpp @@ -100,7 +100,7 @@ class VM_Version: public Abstract_VM_Version { #define CryptoExtension4Mask 0x0004000000000000UL // z196 (aka message-security assist extension 4, for KMF, KMCTR, KMO) #define DFPPackedConversionMask 0x0000800000000000UL // z13 // ---------------------------------------------- -// --- FeatureBitString Bits 128..192 (DW[2]) --- +// --- FeatureBitString Bits 128..191 (DW[2]) --- // ---------------------------------------------- // 11111111111111111 // 23344455666778889 @@ -118,9 +118,10 @@ class VM_Version: public Abstract_VM_Version { #define NNPAssistFacilityMask 0x0000000004000000UL // z16, Neural-network-processing-assist facility, Bit: 165 // ---------------------------------------------- -// --- FeatureBitString Bits 193..200 (DW[3]) --- +// --- FeatureBitString Bits 192..255 (DW[3]) --- // ---------------------------------------------- #define BEAREnhFacilityMask 0x4000000000000000UL // z16, BEAR-enhancement facility, Bit: 193 +#define ConcurrentFunFacilityMask 0x0040000000000000UL // z17, Concurrent-functions facility, Bit: 201 enum { _max_cache_levels = 8, // As limited by ECAG instruction. @@ -189,7 +190,8 @@ class VM_Version: public Abstract_VM_Version { static bool is_z13() { return has_CryptoExt5() && !has_MiscInstrExt2(); } static bool is_z14() { return has_MiscInstrExt2() && !has_MiscInstrExt3(); } static bool is_z15() { return has_MiscInstrExt3() && !has_BEAR_Enh_Facility(); } - static bool is_z16() { return has_BEAR_Enh_Facility(); } + static bool is_z16() { return has_BEAR_Enh_Facility() && !has_Concurrent_Fun_Facility(); } + static bool is_z17() { return has_Concurrent_Fun_Facility();} // Need to use nested class with unscoped enum. // C++11 declaration "enum class Cipher { ... } is not supported. @@ -497,6 +499,7 @@ class VM_Version: public Abstract_VM_Version { static bool has_BEAR_Enh_Facility() { return (_features[3] & BEAREnhFacilityMask) == BEAREnhFacilityMask; } static bool has_NNP_Assist_Facility() { return (_features[2] & NNPAssistFacilityMask) == NNPAssistFacilityMask; } + static bool has_Concurrent_Fun_Facility() { return (_features[3] & ConcurrentFunFacilityMask) == ConcurrentFunFacilityMask; } // Crypto features query functions. static bool has_Crypto_AES_GCM128() { return has_Crypto() && test_feature_bit(&_cipher_features_KMA[0], Cipher::_AES128, Cipher::_featureBits); } @@ -573,6 +576,7 @@ class VM_Version: public Abstract_VM_Version { static void set_has_VectorPackedDecimalEnh() { _features[2] |= VectorPackedDecimalEnhMask; } static void set_has_BEAR_Enh_Facility() { _features[3] |= BEAREnhFacilityMask;} static void set_has_NNP_Assist_Facility() { _features[2] |= NNPAssistFacilityMask;} + static void set_has_Concurrent_Fun_Facility() { _features[3] |= ConcurrentFunFacilityMask;} static void reset_has_VectorFacility() { _features[2] &= ~VectorFacilityMask; } diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index 0176ff967ce..a30bbe08c55 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -413,11 +413,7 @@ int LIR_Assembler::emit_unwind_handler() { if (method()->is_synchronized()) { monitor_address(0, FrameMap::rax_opr); stub = new MonitorExitStub(FrameMap::rax_opr, true, 0); - if (LockingMode == LM_MONITOR) { - __ jmp(*stub->entry()); - } else { - __ unlock_object(rdi, rsi, rax, *stub->entry()); - } + __ unlock_object(rdi, rsi, rax, *stub->entry()); __ bind(*stub->continuation()); } @@ -2733,15 +2729,9 @@ void LIR_Assembler::emit_lock(LIR_OpLock* op) { Register obj = op->obj_opr()->as_register(); // may not be an oop Register hdr = op->hdr_opr()->as_register(); Register lock = op->lock_opr()->as_register(); - if (LockingMode == LM_MONITOR) { - if (op->info() != nullptr) { - add_debug_info_for_null_check_here(op->info()); - __ null_check(obj); - } - __ jmp(*op->stub()->entry()); - } else if (op->code() == lir_lock) { + if (op->code() == lir_lock) { assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); - Register tmp = LockingMode == LM_LIGHTWEIGHT ? op->scratch_opr()->as_register() : noreg; + Register tmp = op->scratch_opr()->as_register(); // add debug info for NullPointerException only if one is possible int null_check_offset = __ lock_object(hdr, obj, lock, tmp, *op->stub()->entry()); if (op->info() != nullptr) { diff --git a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp index 3bd86d9b7e5..5459e8df229 100644 --- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp @@ -289,7 +289,7 @@ void LIRGenerator::do_MonitorEnter(MonitorEnter* x) { // this CodeEmitInfo must not have the xhandlers because here the // object is already locked (xhandlers expect object to be unlocked) CodeEmitInfo* info = state_for(x, x->state(), true); - LIR_Opr tmp = LockingMode == LM_LIGHTWEIGHT ? new_register(T_ADDRESS) : LIR_OprFact::illegalOpr; + LIR_Opr tmp = new_register(T_ADDRESS); monitor_enter(obj.result(), lock, syncTempOpr(), tmp, x->monitor_no(), info_for_exception, info); } @@ -961,7 +961,7 @@ void LIRGenerator::do_update_CRC32(Intrinsic* x) { CallingConvention* cc = frame_map()->c_calling_convention(&signature); const LIR_Opr result_reg = result_register_for(x->type()); - LIR_Opr addr = new_pointer_register(); + LIR_Opr addr = new_register(T_ADDRESS); __ leal(LIR_OprFact::address(a), addr); crc.load_item_force(cc->at(0)); @@ -1100,10 +1100,10 @@ void LIRGenerator::do_vectorizedMismatch(Intrinsic* x) { CallingConvention* cc = frame_map()->c_calling_convention(&signature); const LIR_Opr result_reg = result_register_for(x->type()); - LIR_Opr ptr_addr_a = new_pointer_register(); + LIR_Opr ptr_addr_a = new_register(T_ADDRESS); __ leal(LIR_OprFact::address(addr_a), ptr_addr_a); - LIR_Opr ptr_addr_b = new_pointer_register(); + LIR_Opr ptr_addr_b = new_register(T_ADDRESS); __ leal(LIR_OprFact::address(addr_b), ptr_addr_b); __ move(ptr_addr_a, cc->at(0)); diff --git a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp index c9d103768b8..36efeafa940 100644 --- a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp @@ -42,8 +42,6 @@ #include "utilities/globalDefinitions.hpp" int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr, Register tmp, Label& slow_case) { - const int aligned_mask = BytesPerWord -1; - const int hdr_offset = oopDesc::mark_offset_in_bytes(); assert(hdr == rax, "hdr must be rax, for the cmpxchg instruction"); assert_different_registers(hdr, obj, disp_hdr, tmp); int null_check_offset = -1; @@ -55,93 +53,20 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr null_check_offset = offset(); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(disp_hdr, obj, hdr, tmp, slow_case); - } else if (LockingMode == LM_LEGACY) { - Label done; - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(hdr, obj, rscratch1); - testb(Address(hdr, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class); - jcc(Assembler::notZero, slow_case); - } - - // Load object header - movptr(hdr, Address(obj, hdr_offset)); - // and mark it as unlocked - orptr(hdr, markWord::unlocked_value); - // save unlocked object header into the displaced header location on the stack - movptr(Address(disp_hdr, 0), hdr); - // test if object header is still the same (i.e. unlocked), and if so, store the - // displaced header address in the object header - if it is not the same, get the - // object header instead - MacroAssembler::lock(); // must be immediately before cmpxchg! - cmpxchgptr(disp_hdr, Address(obj, hdr_offset)); - // if the object header was the same, we're done - jcc(Assembler::equal, done); - // if the object header was not the same, it is now in the hdr register - // => test if it is a stack pointer into the same stack (recursive locking), i.e.: - // - // 1) (hdr & aligned_mask) == 0 - // 2) rsp <= hdr - // 3) hdr <= rsp + page_size - // - // these 3 tests can be done by evaluating the following expression: - // - // (hdr - rsp) & (aligned_mask - page_size) - // - // assuming both the stack pointer and page_size have their least - // significant 2 bits cleared and page_size is a power of 2 - subptr(hdr, rsp); - andptr(hdr, aligned_mask - (int)os::vm_page_size()); - // for recursive locking, the result is zero => save it in the displaced header - // location (null in the displaced hdr location indicates recursive locking) - movptr(Address(disp_hdr, 0), hdr); - // otherwise we don't care about the result and handle locking via runtime call - jcc(Assembler::notZero, slow_case); - // done - bind(done); - inc_held_monitor_count(); - } + lightweight_lock(disp_hdr, obj, hdr, tmp, slow_case); return null_check_offset; } void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_hdr, Label& slow_case) { - const int aligned_mask = BytesPerWord -1; - const int hdr_offset = oopDesc::mark_offset_in_bytes(); assert(disp_hdr == rax, "disp_hdr must be rax, for the cmpxchg instruction"); assert(hdr != obj && hdr != disp_hdr && obj != disp_hdr, "registers must be different"); - Label done; - - if (LockingMode != LM_LIGHTWEIGHT) { - // load displaced header - movptr(hdr, Address(disp_hdr, 0)); - // if the loaded hdr is null we had recursive locking - testptr(hdr, hdr); - // if we had recursive locking, we are done - jcc(Assembler::zero, done); - } // load object movptr(obj, Address(disp_hdr, BasicObjectLock::obj_offset())); verify_oop(obj); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_unlock(obj, disp_hdr, hdr, slow_case); - } else if (LockingMode == LM_LEGACY) { - // test if object header is pointing to the displaced header, and if so, restore - // the displaced header in the object - if the object header is not pointing to - // the displaced header, get the object header instead - MacroAssembler::lock(); // must be immediately before cmpxchg! - cmpxchgptr(hdr, Address(obj, hdr_offset)); - // if the object header was not pointing to the displaced header, - // we do unlocking via runtime call - jcc(Assembler::notEqual, slow_case); - // done - bind(done); - dec_held_monitor_count(); - } + lightweight_unlock(obj, disp_hdr, hdr, slow_case); } diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index b5b65171e32..8c3f33e0aca 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -219,244 +219,11 @@ inline Assembler::AvxVectorLen C2_MacroAssembler::vector_length_encoding(int vle // obj: object to lock -// box: on-stack box address (displaced header location) - KILLED -// rax,: tmp -- KILLED -// scr: tmp -- KILLED -void C2_MacroAssembler::fast_lock(Register objReg, Register boxReg, Register tmpReg, - Register scrReg, Register cx1Reg, Register cx2Reg, Register thread, - Metadata* method_data) { - assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_lock_lightweight"); - // Ensure the register assignments are disjoint - assert(tmpReg == rax, ""); - assert(cx1Reg == noreg, ""); - assert(cx2Reg == noreg, ""); - assert_different_registers(objReg, boxReg, tmpReg, scrReg); - - // Possible cases that we'll encounter in fast_lock - // ------------------------------------------------ - // * Inflated - // -- unlocked - // -- Locked - // = by self - // = by other - // * neutral - // * stack-locked - // -- by self - // = sp-proximity test hits - // = sp-proximity test generates false-negative - // -- by other - // - - Label IsInflated, DONE_LABEL, NO_COUNT, COUNT; - - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmpReg, objReg, scrReg); - testb(Address(tmpReg, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class); - jcc(Assembler::notZero, DONE_LABEL); - } - - movptr(tmpReg, Address(objReg, oopDesc::mark_offset_in_bytes())); // [FETCH] - testptr(tmpReg, markWord::monitor_value); // inflated vs stack-locked|neutral - jcc(Assembler::notZero, IsInflated); - - if (LockingMode == LM_MONITOR) { - // Clear ZF so that we take the slow path at the DONE label. objReg is known to be not 0. - testptr(objReg, objReg); - } else { - assert(LockingMode == LM_LEGACY, "must be"); - // Attempt stack-locking ... - orptr (tmpReg, markWord::unlocked_value); - movptr(Address(boxReg, 0), tmpReg); // Anticipate successful CAS - lock(); - cmpxchgptr(boxReg, Address(objReg, oopDesc::mark_offset_in_bytes())); // Updates tmpReg - jcc(Assembler::equal, COUNT); // Success - - // Recursive locking. - // The object is stack-locked: markword contains stack pointer to BasicLock. - // Locked by current thread if difference with current SP is less than one page. - subptr(tmpReg, rsp); - // Next instruction set ZFlag == 1 (Success) if difference is less then one page. - andptr(tmpReg, (int32_t) (7 - (int)os::vm_page_size()) ); - movptr(Address(boxReg, 0), tmpReg); - } - jmp(DONE_LABEL); - - bind(IsInflated); - // The object is inflated. tmpReg contains pointer to ObjectMonitor* + markWord::monitor_value - - // Unconditionally set box->_displaced_header = markWord::unused_mark(). - // Without cast to int32_t this style of movptr will destroy r10 which is typically obj. - movptr(Address(boxReg, 0), checked_cast(markWord::unused_mark().value())); - - // It's inflated and we use scrReg for ObjectMonitor* in this section. - movptr(boxReg, Address(r15_thread, JavaThread::monitor_owner_id_offset())); - movq(scrReg, tmpReg); - xorq(tmpReg, tmpReg); - lock(); - cmpxchgptr(boxReg, Address(scrReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner))); - - // Propagate ICC.ZF from CAS above into DONE_LABEL. - jccb(Assembler::equal, COUNT); // CAS above succeeded; propagate ZF = 1 (success) - - cmpptr(boxReg, rax); // Check if we are already the owner (recursive lock) - jccb(Assembler::notEqual, NO_COUNT); // If not recursive, ZF = 0 at this point (fail) - incq(Address(scrReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); - xorq(rax, rax); // Set ZF = 1 (success) for recursive lock, denoting locking success - bind(DONE_LABEL); - - // ZFlag == 1 count in fast path - // ZFlag == 0 count in slow path - jccb(Assembler::notZero, NO_COUNT); // jump if ZFlag == 0 - - bind(COUNT); - if (LockingMode == LM_LEGACY) { - // Count monitors in fast path - increment(Address(thread, JavaThread::held_monitor_count_offset())); - } - xorl(tmpReg, tmpReg); // Set ZF == 1 - - bind(NO_COUNT); - - // At NO_COUNT the icc ZFlag is set as follows ... - // fast_unlock uses the same protocol. - // ZFlag == 1 -> Success - // ZFlag == 0 -> Failure - force control through the slow path -} - -// obj: object to unlock -// box: box address (displaced header location), killed. Must be EAX. -// tmp: killed, cannot be obj nor box. -// -// Some commentary on balanced locking: -// -// fast_lock and fast_unlock are emitted only for provably balanced lock sites. -// Methods that don't have provably balanced locking are forced to run in the -// interpreter - such methods won't be compiled to use fast_lock and fast_unlock. -// The interpreter provides two properties: -// I1: At return-time the interpreter automatically and quietly unlocks any -// objects acquired the current activation (frame). Recall that the -// interpreter maintains an on-stack list of locks currently held by -// a frame. -// I2: If a method attempts to unlock an object that is not held by the -// the frame the interpreter throws IMSX. -// -// Lets say A(), which has provably balanced locking, acquires O and then calls B(). -// B() doesn't have provably balanced locking so it runs in the interpreter. -// Control returns to A() and A() unlocks O. By I1 and I2, above, we know that O -// is still locked by A(). -// -// The only other source of unbalanced locking would be JNI. The "Java Native Interface: -// Programmer's Guide and Specification" claims that an object locked by jni_monitorenter -// should not be unlocked by "normal" java-level locking and vice-versa. The specification -// doesn't specify what will occur if a program engages in such mixed-mode locking, however. -// Arguably given that the spec legislates the JNI case as undefined our implementation -// could reasonably *avoid* checking owner in fast_unlock(). -// In the interest of performance we elide m->Owner==Self check in unlock. -// A perfectly viable alternative is to elide the owner check except when -// Xcheck:jni is enabled. - -void C2_MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register tmpReg) { - assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_unlock_lightweight"); - assert(boxReg == rax, ""); - assert_different_registers(objReg, boxReg, tmpReg); - - Label DONE_LABEL, Stacked, COUNT, NO_COUNT; - - if (LockingMode == LM_LEGACY) { - cmpptr(Address(boxReg, 0), NULL_WORD); // Examine the displaced header - jcc (Assembler::zero, COUNT); // 0 indicates recursive stack-lock - } - movptr(tmpReg, Address(objReg, oopDesc::mark_offset_in_bytes())); // Examine the object's markword - if (LockingMode != LM_MONITOR) { - testptr(tmpReg, markWord::monitor_value); // Inflated? - jcc(Assembler::zero, Stacked); - } - - // It's inflated. - - // Despite our balanced locking property we still check that m->_owner == Self - // as java routines or native JNI code called by this thread might - // have released the lock. - // - // If there's no contention try a 1-0 exit. That is, exit without - // a costly MEMBAR or CAS. See synchronizer.cpp for details on how - // we detect and recover from the race that the 1-0 exit admits. - // - // Conceptually fast_unlock() must execute a STST|LDST "release" barrier - // before it STs null into _owner, releasing the lock. Updates - // to data protected by the critical section must be visible before - // we drop the lock (and thus before any other thread could acquire - // the lock and observe the fields protected by the lock). - // IA32's memory-model is SPO, so STs are ordered with respect to - // each other and there's no need for an explicit barrier (fence). - // See also http://gee.cs.oswego.edu/dl/jmm/cookbook.html. - Label LSuccess, LNotRecursive; - - cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 0); - jccb(Assembler::equal, LNotRecursive); - - // Recursive inflated unlock - decrement(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); - jmpb(LSuccess); - - bind(LNotRecursive); - - // Set owner to null. - // Release to satisfy the JMM - movptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), NULL_WORD); - // We need a full fence after clearing owner to avoid stranding. - // StoreLoad achieves this. - membar(StoreLoad); - - // Check if the entry_list is empty. - cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(entry_list)), NULL_WORD); - jccb(Assembler::zero, LSuccess); // If so we are done. - - // Check if there is a successor. - cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(succ)), NULL_WORD); - jccb(Assembler::notZero, LSuccess); // If so we are done. - - // Save the monitor pointer in the current thread, so we can try to - // reacquire the lock in SharedRuntime::monitor_exit_helper(). - andptr(tmpReg, ~(int32_t)markWord::monitor_value); - movptr(Address(r15_thread, JavaThread::unlocked_inflated_monitor_offset()), tmpReg); - - orl (boxReg, 1); // set ICC.ZF=0 to indicate failure - jmpb (DONE_LABEL); - - bind (LSuccess); - testl (boxReg, 0); // set ICC.ZF=1 to indicate success - jmpb (DONE_LABEL); - - if (LockingMode == LM_LEGACY) { - bind (Stacked); - movptr(tmpReg, Address (boxReg, 0)); // re-fetch - lock(); - cmpxchgptr(tmpReg, Address(objReg, oopDesc::mark_offset_in_bytes())); // Uses RAX which is box - // Intentional fall-thru into DONE_LABEL - } - - bind(DONE_LABEL); - - // ZFlag == 1 count in fast path - // ZFlag == 0 count in slow path - jccb(Assembler::notZero, NO_COUNT); - - bind(COUNT); - - if (LockingMode == LM_LEGACY) { - // Count monitors in fast path - decrementq(Address(r15_thread, JavaThread::held_monitor_count_offset())); - } - - xorl(tmpReg, tmpReg); // Set ZF == 1 - - bind(NO_COUNT); -} - +// box: on-stack box address -- KILLED +// rax: tmp -- KILLED +// t : tmp -- KILLED void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Register rax_reg, Register t, Register thread) { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); assert(rax_reg == rax, "Used for CAS"); assert_different_registers(obj, box, rax_reg, t, thread); @@ -616,8 +383,39 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist // C2 uses the value of ZF to determine the continuation. } +// obj: object to lock +// rax: tmp -- KILLED +// t : tmp - cannot be obj nor rax -- KILLED +// +// Some commentary on balanced locking: +// +// fast_lock and fast_unlock are emitted only for provably balanced lock sites. +// Methods that don't have provably balanced locking are forced to run in the +// interpreter - such methods won't be compiled to use fast_lock and fast_unlock. +// The interpreter provides two properties: +// I1: At return-time the interpreter automatically and quietly unlocks any +// objects acquired in the current activation (frame). Recall that the +// interpreter maintains an on-stack list of locks currently held by +// a frame. +// I2: If a method attempts to unlock an object that is not held by the +// frame the interpreter throws IMSX. +// +// Lets say A(), which has provably balanced locking, acquires O and then calls B(). +// B() doesn't have provably balanced locking so it runs in the interpreter. +// Control returns to A() and A() unlocks O. By I1 and I2, above, we know that O +// is still locked by A(). +// +// The only other source of unbalanced locking would be JNI. The "Java Native Interface +// Specification" states that an object locked by JNI's MonitorEnter should not be +// unlocked by "normal" java-level locking and vice-versa. The specification doesn't +// specify what will occur if a program engages in such mixed-mode locking, however. +// Arguably given that the spec legislates the JNI case as undefined our implementation +// could reasonably *avoid* checking owner in fast_unlock(). +// In the interest of performance we elide m->Owner==Self check in unlock. +// A perfectly viable alternative is to elide the owner check except when +// Xcheck:jni is enabled. + void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax, Register t, Register thread) { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); assert(reg_rax == rax, "Used for CAS"); assert_different_registers(obj, reg_rax, t); diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index e1652213688..950fcb75290 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -34,12 +34,7 @@ public: Assembler::AvxVectorLen vector_length_encoding(int vlen_in_bytes); // Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file. - // See full description in macroAssembler_x86.cpp. - void fast_lock(Register obj, Register box, Register tmp, - Register scr, Register cx1, Register cx2, Register thread, - Metadata* method_data); - void fast_unlock(Register obj, Register box, Register tmp); - + // See full description in c2_MacroAssembler_x86.cpp. void fast_lock_lightweight(Register obj, Register box, Register rax_reg, Register t, Register thread); void fast_unlock_lightweight(Register obj, Register reg_rax, Register t, Register thread); diff --git a/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp b/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp index 9591c9f2c96..126f4043cad 100644 --- a/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp +++ b/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -98,9 +98,12 @@ frame FreezeBase::new_heap_frame(frame& f, frame& caller) { *hf.addr_at(frame::interpreter_frame_locals_offset) = locals_offset; return hf; } else { - // We need to re-read fp out of the frame because it may be an oop and we might have - // had a safepoint in finalize_freeze, after constructing f. - fp = *(intptr_t**)(f.sp() - frame::sender_sp_offset); + // For a compiled frame we need to re-read fp out of the frame because it may be an + // oop and we might have had a safepoint in finalize_freeze, after constructing f. + // For stub/native frames the value is not used while frozen, and will be constructed again + // when thawing the frame (see ThawBase::new_stack_frame). We use a special bad address to + // help with debugging, particularly when inspecting frames and identifying invalid accesses. + fp = FKind::compiled ? *(intptr_t**)(f.sp() - frame::sender_sp_offset) : (intptr_t*)badAddressVal; int fsize = FKind::size(f); sp = caller.unextended_sp() - fsize; @@ -183,6 +186,11 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { } } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { + intptr_t* fp_addr = sp - frame::sender_sp_offset; + *fp_addr = badAddressVal; +} + //////// Thaw // Fast path diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp index 92233ee0d07..a6b4efbe4f2 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp @@ -1024,100 +1024,25 @@ void InterpreterMacroAssembler::get_method_counters(Register method, void InterpreterMacroAssembler::lock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1"); - if (LockingMode == LM_MONITOR) { - call_VM_preemptable(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - lock_reg); - } else { - Label count_locking, done, slow_case; + Label done, slow_case; - const Register swap_reg = rax; // Must use rax for cmpxchg instruction - const Register tmp_reg = rbx; - const Register obj_reg = c_rarg3; // Will contain the oop - const Register rklass_decode_tmp = rscratch1; + const Register swap_reg = rax; // Must use rax for cmpxchg instruction + const Register tmp_reg = rbx; + const Register obj_reg = c_rarg3; // Will contain the oop - const int obj_offset = in_bytes(BasicObjectLock::obj_offset()); - const int lock_offset = in_bytes(BasicObjectLock::lock_offset()); - const int mark_offset = lock_offset + - BasicLock::displaced_header_offset_in_bytes(); + // Load object pointer into obj_reg + movptr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); - // Load object pointer into obj_reg - movptr(obj_reg, Address(lock_reg, obj_offset)); + lightweight_lock(lock_reg, obj_reg, swap_reg, tmp_reg, slow_case); + jmp(done); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_lock(lock_reg, obj_reg, swap_reg, tmp_reg, slow_case); - } else if (LockingMode == LM_LEGACY) { - if (DiagnoseSyncOnValueBasedClasses != 0) { - load_klass(tmp_reg, obj_reg, rklass_decode_tmp); - testb(Address(tmp_reg, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class); - jcc(Assembler::notZero, slow_case); - } + bind(slow_case); - // Load immediate 1 into swap_reg %rax - movl(swap_reg, 1); - - // Load (object->mark() | 1) into swap_reg %rax - orptr(swap_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - - // Save (object->mark() | 1) into BasicLock's displaced header - movptr(Address(lock_reg, mark_offset), swap_reg); - - assert(lock_offset == 0, - "displaced header must be first word in BasicObjectLock"); - - lock(); - cmpxchgptr(lock_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - jcc(Assembler::zero, count_locking); - - const int zero_bits = 7; - - // Fast check for recursive lock. - // - // Can apply the optimization only if this is a stack lock - // allocated in this thread. For efficiency, we can focus on - // recently allocated stack locks (instead of reading the stack - // base and checking whether 'mark' points inside the current - // thread stack): - // 1) (mark & zero_bits) == 0, and - // 2) rsp <= mark < mark + os::pagesize() - // - // Warning: rsp + os::pagesize can overflow the stack base. We must - // neither apply the optimization for an inflated lock allocated - // just above the thread stack (this is why condition 1 matters) - // nor apply the optimization if the stack lock is inside the stack - // of another thread. The latter is avoided even in case of overflow - // because we have guard pages at the end of all stacks. Hence, if - // we go over the stack base and hit the stack of another thread, - // this should not be in a writeable area that could contain a - // stack lock allocated by that thread. As a consequence, a stack - // lock less than page size away from rsp is guaranteed to be - // owned by the current thread. - // - // These 3 tests can be done by evaluating the following - // expression: ((mark - rsp) & (zero_bits - os::vm_page_size())), - // assuming both stack pointer and pagesize have their - // least significant bits clear. - // NOTE: the mark is in swap_reg %rax as the result of cmpxchg - subptr(swap_reg, rsp); - andptr(swap_reg, zero_bits - (int)os::vm_page_size()); - - // Save the test result, for recursive case, the result is zero - movptr(Address(lock_reg, mark_offset), swap_reg); - jcc(Assembler::notZero, slow_case); - - bind(count_locking); - inc_held_monitor_count(); - } - jmp(done); - - bind(slow_case); - - // Call the runtime routine for slow case - call_VM_preemptable(noreg, - CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), - lock_reg); - bind(done); - } + // Call the runtime routine for slow case + call_VM_preemptable(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), + lock_reg); + bind(done); } @@ -1136,63 +1061,31 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) { void InterpreterMacroAssembler::unlock_object(Register lock_reg) { assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1"); - if (LockingMode == LM_MONITOR) { - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - } else { - Label count_locking, done, slow_case; + Label done, slow_case; - const Register swap_reg = rax; // Must use rax for cmpxchg instruction - const Register header_reg = c_rarg2; // Will contain the old oopMark - const Register obj_reg = c_rarg3; // Will contain the oop + const Register swap_reg = rax; // Must use rax for cmpxchg instruction + const Register header_reg = c_rarg2; // Will contain the old oopMark + const Register obj_reg = c_rarg3; // Will contain the oop - save_bcp(); // Save in case of exception + save_bcp(); // Save in case of exception - if (LockingMode != LM_LIGHTWEIGHT) { - // Convert from BasicObjectLock structure to object and BasicLock - // structure Store the BasicLock address into %rax - lea(swap_reg, Address(lock_reg, BasicObjectLock::lock_offset())); - } + // Load oop into obj_reg(%c_rarg3) + movptr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); - // Load oop into obj_reg(%c_rarg3) - movptr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset())); + // Free entry + movptr(Address(lock_reg, BasicObjectLock::obj_offset()), NULL_WORD); - // Free entry - movptr(Address(lock_reg, BasicObjectLock::obj_offset()), NULL_WORD); + lightweight_unlock(obj_reg, swap_reg, header_reg, slow_case); + jmp(done); - if (LockingMode == LM_LIGHTWEIGHT) { - lightweight_unlock(obj_reg, swap_reg, header_reg, slow_case); - } else if (LockingMode == LM_LEGACY) { - // Load the old header from BasicLock structure - movptr(header_reg, Address(swap_reg, - BasicLock::displaced_header_offset_in_bytes())); + bind(slow_case); + // Call the runtime routine for slow case. + movptr(Address(lock_reg, BasicObjectLock::obj_offset()), obj_reg); // restore obj + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - // Test for recursion - testptr(header_reg, header_reg); + bind(done); - // zero for recursive case - jcc(Assembler::zero, count_locking); - - // Atomic swap back the old header - lock(); - cmpxchgptr(header_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - - // zero for simple unlock of a stack-lock case - jcc(Assembler::notZero, slow_case); - - bind(count_locking); - dec_held_monitor_count(); - } - jmp(done); - - bind(slow_case); - // Call the runtime routine for slow case. - movptr(Address(lock_reg, BasicObjectLock::obj_offset()), obj_reg); // restore obj - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg); - - bind(done); - - restore_bcp(); - } + restore_bcp(); } void InterpreterMacroAssembler::test_method_data_pointer(Register mdp, diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 2d1005b8438..0b41d594194 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -6027,32 +6027,46 @@ void MacroAssembler::evpbroadcast(BasicType type, XMMRegister dst, Register src, } } -// encode char[] to byte[] in ISO_8859_1 or ASCII - //@IntrinsicCandidate - //private static int implEncodeISOArray(byte[] sa, int sp, - //byte[] da, int dp, int len) { - // int i = 0; - // for (; i < len; i++) { - // char c = StringUTF16.getChar(sa, sp++); - // if (c > '\u00FF') - // break; - // da[dp++] = (byte)c; - // } - // return i; - //} - // - //@IntrinsicCandidate - //private static int implEncodeAsciiArray(char[] sa, int sp, - // byte[] da, int dp, int len) { - // int i = 0; - // for (; i < len; i++) { - // char c = sa[sp++]; - // if (c >= '\u0080') - // break; - // da[dp++] = (byte)c; - // } - // return i; - //} +// Encode given char[]/byte[] to byte[] in ISO_8859_1 or ASCII +// +// @IntrinsicCandidate +// int sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0( +// char[] sa, int sp, byte[] da, int dp, int len) { +// int i = 0; +// for (; i < len; i++) { +// char c = sa[sp++]; +// if (c > '\u00FF') +// break; +// da[dp++] = (byte) c; +// } +// return i; +// } +// +// @IntrinsicCandidate +// int java.lang.StringCoding.encodeISOArray0( +// byte[] sa, int sp, byte[] da, int dp, int len) { +// int i = 0; +// for (; i < len; i++) { +// char c = StringUTF16.getChar(sa, sp++); +// if (c > '\u00FF') +// break; +// da[dp++] = (byte) c; +// } +// return i; +// } +// +// @IntrinsicCandidate +// int java.lang.StringCoding.encodeAsciiArray0( +// char[] sa, int sp, byte[] da, int dp, int len) { +// int i = 0; +// for (; i < len; i++) { +// char c = sa[sp++]; +// if (c >= '\u0080') +// break; +// da[dp++] = (byte) c; +// } +// return i; +// } void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, XMMRegister tmp1Reg, XMMRegister tmp2Reg, XMMRegister tmp3Reg, XMMRegister tmp4Reg, diff --git a/src/hotspot/cpu/x86/methodHandles_x86.cpp b/src/hotspot/cpu/x86/methodHandles_x86.cpp index 0363cb04341..b921a157a52 100644 --- a/src/hotspot/cpu/x86/methodHandles_x86.cpp +++ b/src/hotspot/cpu/x86/methodHandles_x86.cpp @@ -137,7 +137,7 @@ void MethodHandles::verify_method(MacroAssembler* _masm, Register method, Regist case vmIntrinsicID::_invokeBasic: // Require compiled LambdaForm class to be fully initialized. __ cmpb(Address(method_holder, InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized); - __ jccb(Assembler::equal, L_ok); + __ jcc(Assembler::equal, L_ok); break; case vmIntrinsicID::_linkToStatic: @@ -154,7 +154,7 @@ void MethodHandles::verify_method(MacroAssembler* _masm, Register method, Regist // init_state check failed, but it may be an abstract interface method __ load_unsigned_short(temp, Address(method, Method::access_flags_offset())); __ testl(temp, JVM_ACC_ABSTRACT); - __ jccb(Assembler::notZero, L_ok); + __ jcc(Assembler::notZero, L_ok); break; default: diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86.cpp index b8a4b829159..17fdfa61d36 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86.cpp @@ -59,17 +59,10 @@ void SharedRuntime::inline_check_hashcode_from_object_header(MacroAssembler* mas __ movptr(result, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - - if (LockingMode == LM_LIGHTWEIGHT) { - if (!UseObjectMonitorTable) { - // check if monitor - __ testptr(result, markWord::monitor_value); - __ jcc(Assembler::notZero, slowCase); - } - } else { - // check if locked - __ testptr(result, markWord::unlocked_value); - __ jcc(Assembler::zero, slowCase); + if (!UseObjectMonitorTable) { + // check if monitor + __ testptr(result, markWord::monitor_value); + __ jcc(Assembler::notZero, slowCase); } // get hash diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index 7c7ca61f1ae..d60f535fefc 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -2133,7 +2133,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // We use the same pc/oopMap repeatedly when we call out Label native_return; - if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + if (method->is_object_wait0()) { // For convenience we use the pc we want to resume to in case of preemption on Object.wait. __ set_last_Java_frame(rsp, noreg, native_return, rscratch1); } else { @@ -2174,16 +2174,11 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, const Register swap_reg = rax; // Must use rax for cmpxchg instruction const Register obj_reg = rbx; // Will contain the oop const Register lock_reg = r13; // Address of compiler lock object (BasicLock) - const Register old_hdr = r13; // value of old header at unlock time Label slow_path_lock; Label lock_done; if (method->is_synchronized()) { - Label count_mon; - - const int mark_word_offset = BasicLock::displaced_header_offset_in_bytes(); - // Get the handle (the 2nd argument) __ mov(oop_handle_reg, c_rarg1); @@ -2194,47 +2189,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Load the oop from the handle __ movptr(obj_reg, Address(oop_handle_reg, 0)); - if (LockingMode == LM_MONITOR) { - __ jmp(slow_path_lock); - } else if (LockingMode == LM_LEGACY) { - // Load immediate 1 into swap_reg %rax - __ movl(swap_reg, 1); - - // Load (object->mark() | 1) into swap_reg %rax - __ orptr(swap_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - - // Save (object->mark() | 1) into BasicLock's displaced header - __ movptr(Address(lock_reg, mark_word_offset), swap_reg); - - // src -> dest iff dest == rax else rax <- dest - __ lock(); - __ cmpxchgptr(lock_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - __ jcc(Assembler::equal, count_mon); - - // Hmm should this move to the slow path code area??? - - // Test if the oopMark is an obvious stack pointer, i.e., - // 1) (mark & 3) == 0, and - // 2) rsp <= mark < mark + os::pagesize() - // These 3 tests can be done by evaluating the following - // expression: ((mark - rsp) & (3 - os::vm_page_size())), - // assuming both stack pointer and pagesize have their - // least significant 2 bits clear. - // NOTE: the oopMark is in swap_reg %rax as the result of cmpxchg - - __ subptr(swap_reg, rsp); - __ andptr(swap_reg, 3 - (int)os::vm_page_size()); - - // Save the test result, for recursive case, the result is zero - __ movptr(Address(lock_reg, mark_word_offset), swap_reg); - __ jcc(Assembler::notEqual, slow_path_lock); - - __ bind(count_mon); - __ inc_held_monitor_count(); - } else { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); - __ lightweight_lock(lock_reg, obj_reg, swap_reg, rscratch1, slow_path_lock); - } + __ lightweight_lock(lock_reg, obj_reg, swap_reg, rscratch1, slow_path_lock); // Slow path will re-enter here __ bind(lock_done); @@ -2322,7 +2277,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // change thread state __ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_Java); - if (LockingMode != LM_LEGACY && method->is_object_wait0()) { + if (method->is_object_wait0()) { // Check preemption for Object.wait() __ movptr(rscratch1, Address(r15_thread, JavaThread::preempt_alternate_return_offset())); __ cmpptr(rscratch1, NULL_WORD); @@ -2354,38 +2309,12 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // Get locked oop from the handle we passed to jni __ movptr(obj_reg, Address(oop_handle_reg, 0)); - if (LockingMode == LM_LEGACY) { - Label not_recur; - // Simple recursive lock? - __ cmpptr(Address(rsp, lock_slot_offset * VMRegImpl::stack_slot_size), NULL_WORD); - __ jcc(Assembler::notEqual, not_recur); - __ dec_held_monitor_count(); - __ jmpb(fast_done); - __ bind(not_recur); - } - // Must save rax if it is live now because cmpxchg must use it if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) { save_native_result(masm, ret_type, stack_slots); } - if (LockingMode == LM_MONITOR) { - __ jmp(slow_path_unlock); - } else if (LockingMode == LM_LEGACY) { - // get address of the stack lock - __ lea(rax, Address(rsp, lock_slot_offset * VMRegImpl::stack_slot_size)); - // get old displaced header - __ movptr(old_hdr, Address(rax, 0)); - - // Atomic swap old header if oop still contains the stack lock - __ lock(); - __ cmpxchgptr(old_hdr, Address(obj_reg, oopDesc::mark_offset_in_bytes())); - __ jcc(Assembler::notEqual, slow_path_unlock); - __ dec_held_monitor_count(); - } else { - assert(LockingMode == LM_LIGHTWEIGHT, "must be"); - __ lightweight_unlock(obj_reg, swap_reg, lock_reg, slow_path_unlock); - } + __ lightweight_unlock(obj_reg, swap_reg, lock_reg, slow_path_unlock); // slow path re-enters here __ bind(unlock_done); diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp index bd061d45fbd..47ef0aef2bb 100644 --- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp @@ -1017,21 +1017,16 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // change thread state __ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_Java); - if (LockingMode != LM_LEGACY) { - // Check preemption for Object.wait() - Label not_preempted; - __ movptr(rscratch1, Address(r15_thread, JavaThread::preempt_alternate_return_offset())); - __ cmpptr(rscratch1, NULL_WORD); - __ jccb(Assembler::equal, not_preempted); - __ movptr(Address(r15_thread, JavaThread::preempt_alternate_return_offset()), NULL_WORD); - __ jmp(rscratch1); - __ bind(native_return); - __ restore_after_resume(true /* is_native */); - __ bind(not_preempted); - } else { - // any pc will do so just use this one for LM_LEGACY to keep code together. - __ bind(native_return); - } + // Check preemption for Object.wait() + Label not_preempted; + __ movptr(rscratch1, Address(r15_thread, JavaThread::preempt_alternate_return_offset())); + __ cmpptr(rscratch1, NULL_WORD); + __ jccb(Assembler::equal, not_preempted); + __ movptr(Address(r15_thread, JavaThread::preempt_alternate_return_offset()), NULL_WORD); + __ jmp(rscratch1); + __ bind(native_return); + __ restore_after_resume(true /* is_native */); + __ bind(not_preempted); // reset_last_Java_frame __ reset_last_Java_frame(true); diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 5b5292fbde2..932dc9e1ca7 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -14073,33 +14073,7 @@ instruct jmpConUCF2_short(cmpOpUCF2 cop, rFlagsRegUCF cmp, label labl) %{ // ============================================================================ // inlined locking and unlocking -instruct cmpFastLock(rFlagsReg cr, rRegP object, rbx_RegP box, rax_RegI tmp, rRegP scr) %{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set cr (FastLock object box)); - effect(TEMP tmp, TEMP scr, USE_KILL box); - ins_cost(300); - format %{ "fastlock $object,$box\t! kills $box,$tmp,$scr" %} - ins_encode %{ - __ fast_lock($object$$Register, $box$$Register, $tmp$$Register, - $scr$$Register, noreg, noreg, r15_thread, nullptr); - %} - ins_pipe(pipe_slow); -%} - -instruct cmpFastUnlock(rFlagsReg cr, rRegP object, rax_RegP box, rRegP tmp) %{ - predicate(LockingMode != LM_LIGHTWEIGHT); - match(Set cr (FastUnlock object box)); - effect(TEMP tmp, USE_KILL box); - ins_cost(300); - format %{ "fastunlock $object,$box\t! kills $box,$tmp" %} - ins_encode %{ - __ fast_unlock($object$$Register, $box$$Register, $tmp$$Register); - %} - ins_pipe(pipe_slow); -%} - instruct cmpFastLockLightweight(rFlagsReg cr, rRegP object, rbx_RegP box, rax_RegI rax_reg, rRegP tmp) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastLock object box)); effect(TEMP rax_reg, TEMP tmp, USE_KILL box); ins_cost(300); @@ -14111,7 +14085,6 @@ instruct cmpFastLockLightweight(rFlagsReg cr, rRegP object, rbx_RegP box, rax_Re %} instruct cmpFastUnlockLightweight(rFlagsReg cr, rRegP object, rax_RegP rax_reg, rRegP tmp) %{ - predicate(LockingMode == LM_LIGHTWEIGHT); match(Set cr (FastUnlock object rax_reg)); effect(TEMP tmp, USE_KILL rax_reg); ins_cost(300); diff --git a/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp b/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp index de2769fb376..eb6be8772f9 100644 --- a/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp +++ b/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp @@ -60,6 +60,10 @@ inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { Unimplemented(); } +inline void FreezeBase::patch_pd_unused(intptr_t* sp) { + Unimplemented(); +} + inline void FreezeBase::patch_stack_pd(intptr_t* frame_sp, intptr_t* heap_sp) { Unimplemented(); } diff --git a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp index 2c5fb717c40..b2e406c205b 100644 --- a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp +++ b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp @@ -56,11 +56,11 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, const BasicType *sig_bt, const VMRegPair *regs, AdapterHandlerEntry* handler) { + // foil any attempt to call the i2c, c2i or unverified c2i entries handler->set_entry_points(CAST_FROM_FN_PTR(address,zero_null_code_stub), CAST_FROM_FN_PTR(address,zero_null_code_stub), CAST_FROM_FN_PTR(address,zero_null_code_stub), nullptr); - return; } nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, diff --git a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp index 3126cf71460..2357bbb5169 100644 --- a/src/hotspot/cpu/zero/stubDeclarations_zero.hpp +++ b/src/hotspot/cpu/zero/stubDeclarations_zero.hpp @@ -37,7 +37,7 @@ do_arch_blob, \ do_arch_entry, \ do_arch_entry_init) \ - do_arch_blob(initial, 32) \ + do_arch_blob(initial, 0) \ #define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \ @@ -58,7 +58,7 @@ do_arch_blob, \ do_arch_entry, \ do_arch_entry_init) \ - do_arch_blob(final, 32) \ + do_arch_blob(final, 0) \ #endif // CPU_ZERO_STUBDECLARATIONS_HPP diff --git a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp index 28b47877264..f4196d89fe3 100644 --- a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp +++ b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp @@ -209,8 +209,16 @@ frame os::fetch_compiled_frame_from_context(const void* ucVoid) { } intptr_t* os::fetch_bcp_from_context(const void* ucVoid) { - Unimplemented(); - return nullptr; + assert(ucVoid != nullptr, "invariant"); + const ucontext_t* uc = (const ucontext_t*)ucVoid; + assert(os::Posix::ucontext_is_interpreter(uc), "invariant"); +#if (FP_REG_NUM == 11) + assert(Rbcp == R7, "expected FP=R11, Rbcp=R7"); + return (intptr_t*)uc->uc_mcontext.arm_r7; +#else + assert(Rbcp == R11, "expected FP=R7, Rbcp=R11"); + return (intptr_t*)uc->uc_mcontext.arm_fp; // r11 +#endif } frame os::get_sender_for_C_frame(frame* fr) { diff --git a/src/hotspot/share/asm/codeBuffer.hpp b/src/hotspot/share/asm/codeBuffer.hpp index ec9f347e334..57b4aaacdce 100644 --- a/src/hotspot/share/asm/codeBuffer.hpp +++ b/src/hotspot/share/asm/codeBuffer.hpp @@ -33,7 +33,7 @@ #include "utilities/debug.hpp" #include "utilities/growableArray.hpp" #include "utilities/linkedlist.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" #include "utilities/macros.hpp" template @@ -541,7 +541,7 @@ class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) { }; typedef LinkedListImpl Offsets; - typedef ResizeableResourceHashtable SharedTrampolineRequests; + typedef ResizeableHashTable SharedTrampolineRequests; private: enum { diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index adcc4bfb50a..a42d8882099 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -37,7 +37,7 @@ #include "oops/instanceKlass.hpp" #include "oops/objArrayKlass.hpp" #include "oops/trainingData.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" // All the classes that should be included in the AOT cache (in at least the "allocated" state) static GrowableArrayCHeap* _all_cached_classes = nullptr; @@ -47,7 +47,7 @@ static GrowableArrayCHeap* _all_cached_classes = nullptr; static GrowableArrayCHeap* _pending_aot_inited_classes = nullptr; static const int TABLE_SIZE = 15889; // prime number -using ClassesTable = ResourceHashtable; +using ClassesTable = HashTable; static ClassesTable* _seen_classes; // all classes that have been seen by AOTArtifactFinder static ClassesTable* _aot_inited_classes; // all classes that need to be AOT-initialized. diff --git a/src/hotspot/share/cds/aotClassLinker.hpp b/src/hotspot/share/cds/aotClassLinker.hpp index f15684a1ad2..7bbc9ce8138 100644 --- a/src/hotspot/share/cds/aotClassLinker.hpp +++ b/src/hotspot/share/cds/aotClassLinker.hpp @@ -31,8 +31,8 @@ #include "oops/oopsHierarchy.hpp" #include "utilities/exceptions.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" -#include "utilities/resourceHash.hpp" class AOTLinkedClassTable; class InstanceKlass; @@ -69,7 +69,7 @@ enum class AOTLinkedClassCategory : int; // class AOTClassLinker : AllStatic { static const int TABLE_SIZE = 15889; // prime number - using ClassesTable = ResourceHashtable; + using ClassesTable = HashTable; // Classes loaded inside vmClasses::resolve_all() static ClassesTable* _vm_classes; diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.hpp b/src/hotspot/share/cds/aotConstantPoolResolver.hpp index bab9e263a22..e49d9d1ad0b 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.hpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.hpp @@ -31,8 +31,8 @@ #include "oops/oopsHierarchy.hpp" #include "runtime/handles.hpp" #include "utilities/exceptions.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" -#include "utilities/resourceHash.hpp" class ConstantPool; class constantPoolHandle; @@ -53,7 +53,7 @@ template class GrowableArray; // if all of its supertypes are loaded from the CDS archive. class AOTConstantPoolResolver : AllStatic { static const int TABLE_SIZE = 15889; // prime number - using ClassesTable = ResourceHashtable ; + using ClassesTable = HashTable ; static ClassesTable* _processed_classes; #ifdef ASSERT diff --git a/src/hotspot/share/cds/aotReferenceObjSupport.cpp b/src/hotspot/share/cds/aotReferenceObjSupport.cpp index a62e9c7735a..b43c766b8db 100644 --- a/src/hotspot/share/cds/aotReferenceObjSupport.cpp +++ b/src/hotspot/share/cds/aotReferenceObjSupport.cpp @@ -35,7 +35,7 @@ #include "oops/oopHandle.inline.hpp" #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/javaCalls.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" // Handling of java.lang.ref.Reference objects in the AOT cache // ============================================================ @@ -92,7 +92,7 @@ #if INCLUDE_CDS_JAVA_HEAP -class KeepAliveObjectsTable : public ResourceHashtable _src_obj_table; - ResizeableResourceHashtable _buffered_to_src_table; + ResizeableHashTable _src_obj_table; + ResizeableHashTable _buffered_to_src_table; GrowableArray* _klasses; GrowableArray* _symbols; unsigned int _entropy_seed; diff --git a/src/hotspot/share/cds/archiveHeapLoader.cpp b/src/hotspot/share/cds/archiveHeapLoader.cpp index 181fa6b2bb4..673c4baa6f6 100644 --- a/src/hotspot/share/cds/archiveHeapLoader.cpp +++ b/src/hotspot/share/cds/archiveHeapLoader.cpp @@ -348,10 +348,10 @@ bool ArchiveHeapLoader::load_heap_region(FileMapInfo* mapinfo) { } class VerifyLoadedHeapEmbeddedPointers: public BasicOopIterateClosure { - ResourceHashtable* _table; + HashTable* _table; public: - VerifyLoadedHeapEmbeddedPointers(ResourceHashtable* table) : _table(table) {} + VerifyLoadedHeapEmbeddedPointers(HashTable* table) : _table(table) {} virtual void do_oop(narrowOop* p) { // This should be called before the loaded region is modified, so all the embedded pointers @@ -411,7 +411,7 @@ void ArchiveHeapLoader::verify_loaded_heap() { log_info(aot, heap)("Verify all oops and pointers in loaded heap"); ResourceMark rm; - ResourceHashtable table; + HashTable table; VerifyLoadedHeapEmbeddedPointers verifier(&table); HeapWord* bottom = (HeapWord*)_loaded_heap_bottom; HeapWord* top = (HeapWord*)_loaded_heap_top; diff --git a/src/hotspot/share/cds/archiveHeapWriter.cpp b/src/hotspot/share/cds/archiveHeapWriter.cpp index db93027e348..b651da8418b 100644 --- a/src/hotspot/share/cds/archiveHeapWriter.cpp +++ b/src/hotspot/share/cds/archiveHeapWriter.cpp @@ -70,7 +70,7 @@ ArchiveHeapWriter::BufferOffsetToSourceObjectTable* ArchiveHeapWriter::_buffer_offset_to_source_obj_table = nullptr; -typedef ResourceHashtable< +typedef HashTable< size_t, // offset of a filler from ArchiveHeapWriter::buffer_bottom() size_t, // size of this filler (in bytes) 127, // prime number diff --git a/src/hotspot/share/cds/archiveHeapWriter.hpp b/src/hotspot/share/cds/archiveHeapWriter.hpp index 1e1319cccc3..f8f55a745ee 100644 --- a/src/hotspot/share/cds/archiveHeapWriter.hpp +++ b/src/hotspot/share/cds/archiveHeapWriter.hpp @@ -32,8 +32,8 @@ #include "utilities/bitMap.hpp" #include "utilities/exceptions.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" -#include "utilities/resourceHash.hpp" class MemRegion; @@ -152,7 +152,7 @@ private: }; static GrowableArrayCHeap* _source_objs_order; - typedef ResizeableResourceHashtable BufferOffsetToSourceObjectTable; static BufferOffsetToSourceObjectTable* _buffer_offset_to_source_obj_table; diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 85c40df2606..51899490a12 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -643,8 +643,17 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla } if (is_dumping_static_archive()) { - if (is_dumping_preimage_static_archive() || is_dumping_final_static_archive()) { - // Don't tweak execution mode + if (is_dumping_preimage_static_archive()) { + // Don't tweak execution mode during AOT training run + } else if (is_dumping_final_static_archive()) { + if (Arguments::mode() == Arguments::_comp) { + // AOT assembly phase submits the non-blocking compilation requests + // for methods collected during training run, then waits for all compilations + // to complete. With -Xcomp, we block for each compilation request, which is + // counter-productive. Switching back to mixed mode improves testing time + // with AOT and -Xcomp. + Arguments::set_mode_flags(Arguments::_mixed); + } } else if (!mode_flag_cmd_line) { // By default, -Xshare:dump runs in interpreter-only mode, which is required for deterministic archive. // diff --git a/src/hotspot/share/cds/cdsHeapVerifier.hpp b/src/hotspot/share/cds/cdsHeapVerifier.hpp index c53f913e42b..811751e8ca2 100644 --- a/src/hotspot/share/cds/cdsHeapVerifier.hpp +++ b/src/hotspot/share/cds/cdsHeapVerifier.hpp @@ -28,7 +28,7 @@ #include "cds/heapShared.hpp" #include "memory/iterator.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" class InstanceKlass; class Symbol; @@ -47,7 +47,7 @@ class CDSHeapVerifier : public KlassClosure { Symbol* _name; }; - ResourceHashtable ID2KlassTable; enum { diff --git a/src/hotspot/share/cds/classListWriter.cpp b/src/hotspot/share/cds/classListWriter.cpp index c1ac1b47b11..727cc03c216 100644 --- a/src/hotspot/share/cds/classListWriter.cpp +++ b/src/hotspot/share/cds/classListWriter.cpp @@ -66,7 +66,7 @@ void ClassListWriter::write(const InstanceKlass* k, const ClassFileStream* cfs) write_to_stream(k, w.stream(), cfs); } -class ClassListWriter::IDTable : public ResourceHashtable< +class ClassListWriter::IDTable : public HashTable< const InstanceKlass*, int, 15889, // prime number AnyObj::C_HEAP> {}; diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.hpp index 0bb60dfbb29..0feed8682da 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.hpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.hpp @@ -240,7 +240,7 @@ inline unsigned DumpTimeSharedClassTable_hash(T* const& k) { } } -using DumpTimeSharedClassTableBaseType = ResourceHashtable< +using DumpTimeSharedClassTableBaseType = HashTable< InstanceKlass*, DumpTimeClassInfo, 15889, // prime number diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 51572bcace7..ea9368c9277 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -367,7 +367,7 @@ bool HeapShared::archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgra } } -class MetaspaceObjToOopHandleTable: public ResourceHashtable { diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index f4e86aa5895..c0e89809274 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -37,7 +37,7 @@ #include "oops/oopHandle.hpp" #include "oops/oopsHierarchy.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" #if INCLUDE_CDS_JAVA_HEAP class DumpedInternedStrings; @@ -202,14 +202,14 @@ public: private: static const int INITIAL_TABLE_SIZE = 15889; // prime number static const int MAX_TABLE_SIZE = 1000000; - typedef ResizeableResourceHashtable ArchivedObjectCache; static ArchivedObjectCache* _archived_object_cache; class DumpTimeKlassSubGraphInfoTable - : public ResourceHashtable SeenObjectsTable; @@ -468,14 +468,14 @@ private: #if INCLUDE_CDS_JAVA_HEAP class DumpedInternedStrings : - public ResizeableResourceHashtable { public: DumpedInternedStrings(unsigned size, unsigned max_size) : - ResizeableResourceHashtable(size, max_size) {} diff --git a/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp b/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp index 814dda80827..ff7acb15292 100644 --- a/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp +++ b/src/hotspot/share/cds/lambdaProxyClassDictionary.hpp @@ -30,7 +30,7 @@ #include "classfile/javaClasses.hpp" #include "memory/metaspaceClosure.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" // This file contains *legacy* optimization for lambdas before JEP 483. May be removed in the future. // @@ -249,7 +249,7 @@ public: }; class DumpTimeLambdaProxyClassDictionary - : public ResourceHashtable @@ -178,7 +178,7 @@ class DumpClassListCLDClosure : public CLDClosure { static const int MAX_TABLE_SIZE = 61333; fileStream *_stream; - ResizeableResourceHashtable _dumped_classes; void dump(InstanceKlass* ik) { diff --git a/src/hotspot/share/cds/regeneratedClasses.cpp b/src/hotspot/share/cds/regeneratedClasses.cpp index b36f360b82a..ae14866cea5 100644 --- a/src/hotspot/share/cds/regeneratedClasses.cpp +++ b/src/hotspot/share/cds/regeneratedClasses.cpp @@ -32,9 +32,9 @@ #include "runtime/mutexLocker.hpp" #include "runtime/thread.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" -using RegeneratedObjTable = ResourceHashtable; +using RegeneratedObjTable = HashTable; static RegeneratedObjTable* _regenerated_objs = nullptr; // InstanceKlass* and Method* orig_obj -> regen_obj static RegeneratedObjTable* _original_objs = nullptr; // InstanceKlass* and Method* regen_obj -> orig_obj static GrowableArrayCHeap* _regenerated_mirrors = nullptr; diff --git a/src/hotspot/share/classfile/bytecodeAssembler.hpp b/src/hotspot/share/classfile/bytecodeAssembler.hpp index fb51924a9c6..c0383a9678c 100644 --- a/src/hotspot/share/classfile/bytecodeAssembler.hpp +++ b/src/hotspot/share/classfile/bytecodeAssembler.hpp @@ -30,8 +30,7 @@ #include "oops/symbol.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resourceHash.hpp" - +#include "utilities/hashTable.hpp" /** * Bytecode Assembler @@ -125,7 +124,7 @@ class BytecodeCPEntry { class BytecodeConstantPool : public ResourceObj { private: - typedef ResourceHashtable IndexHash; diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 9aca20ee29f..c678fa5e058 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -81,9 +81,9 @@ #include "utilities/formatBuffer.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" #include "utilities/ostream.hpp" -#include "utilities/resourceHash.hpp" #include "utilities/utf8.hpp" #if INCLUDE_CDS #include "classfile/systemDictionaryShared.hpp" @@ -782,7 +782,7 @@ class NameSigHash: public ResourceObj { } }; -using NameSigHashtable = ResourceHashtable; @@ -849,7 +849,7 @@ void ClassFileParser::parse_interfaces(const ClassFileStream* const stream, // Check if there's any duplicates in interfaces ResourceMark rm(THREAD); // Set containing interface names - ResourceHashtable* interface_names = new ResourceHashtable(); + HashTable* interface_names = new HashTable(); for (index = 0; index < itfs_len; index++) { const InstanceKlass* const k = _local_interfaces->at(index); Symbol* interface_name = k->name(); @@ -2022,7 +2022,7 @@ void ClassFileParser::copy_localvariable_table(const ConstMethod* cm, ResourceMark rm(THREAD); - typedef ResourceHashtable LVT_HashTable; diff --git a/src/hotspot/share/classfile/classLoaderStats.hpp b/src/hotspot/share/classfile/classLoaderStats.hpp index 06d375b3e9b..23da6bff63c 100644 --- a/src/hotspot/share/classfile/classLoaderStats.hpp +++ b/src/hotspot/share/classfile/classLoaderStats.hpp @@ -25,15 +25,13 @@ #ifndef SHARE_CLASSFILE_CLASSLOADERSTATS_HPP #define SHARE_CLASSFILE_CLASSLOADERSTATS_HPP - #include "classfile/classLoaderData.hpp" #include "oops/klass.hpp" #include "oops/oop.hpp" #include "oops/oopsHierarchy.hpp" #include "runtime/vmOperation.hpp" #include "services/diagnosticCommand.hpp" -#include "utilities/resourceHash.hpp" - +#include "utilities/hashTable.hpp" class ClassLoaderStatsDCmd : public DCmd { public: @@ -105,7 +103,7 @@ protected: return hash; } - typedef ResourceHashtable StatsTable; diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 28bebee7d9a..20534b93290 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1886,8 +1886,9 @@ JavaThreadStatus java_lang_Thread::get_thread_status(oop java_thread) { // Make sure the caller is operating on behalf of the VM or is // running VM code (state == _thread_in_vm). assert(Threads_lock->owned_by_self() || Thread::current()->is_VM_thread() || - JavaThread::current()->thread_state() == _thread_in_vm, - "Java Thread is not running in vm"); + JavaThread::current()->thread_state() == _thread_in_vm || + JavaThread::current() == java_lang_Thread::thread(java_thread), + "unsafe call to java_lang_Thread::get_thread_status()?"); GET_FIELDHOLDER_FIELD(java_thread, get_thread_status, JavaThreadStatus::NEW /* not initialized */); } @@ -1899,33 +1900,29 @@ oop java_lang_Thread::park_blocker(oop java_thread) { return java_thread->obj_field_access(_park_blocker_offset); } -oop java_lang_Thread::async_get_stack_trace(oop java_thread, TRAPS) { - ThreadsListHandle tlh(JavaThread::current()); - JavaThread* thread; - bool is_virtual = java_lang_VirtualThread::is_instance(java_thread); - if (is_virtual) { - oop carrier_thread = java_lang_VirtualThread::carrier_thread(java_thread); - if (carrier_thread == nullptr) { - return nullptr; - } - thread = java_lang_Thread::thread(carrier_thread); - } else { - thread = java_lang_Thread::thread(java_thread); - } - if (thread == nullptr) { +// Obtain stack trace for platform or mounted virtual thread. +// If jthread is a virtual thread and it has been unmounted (or remounted to different carrier) the method returns null. +// The caller (java.lang.VirtualThread) handles returned nulls via retry. +oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) { + ThreadsListHandle tlh(THREAD); + JavaThread* java_thread = nullptr; + oop thread_oop; + + bool has_java_thread = tlh.cv_internal_thread_to_JavaThread(jthread, &java_thread, &thread_oop); + if (!has_java_thread) { return nullptr; } class GetStackTraceHandshakeClosure : public HandshakeClosure { public: - const Handle _java_thread; + const Handle _thread_h; int _depth; bool _retry_handshake; GrowableArray* _methods; GrowableArray* _bcis; - GetStackTraceHandshakeClosure(Handle java_thread) : - HandshakeClosure("GetStackTraceHandshakeClosure"), _java_thread(java_thread), _depth(0), _retry_handshake(false), + GetStackTraceHandshakeClosure(Handle thread_h) : + HandshakeClosure("GetStackTraceHandshakeClosure"), _thread_h(thread_h), _depth(0), _retry_handshake(false), _methods(nullptr), _bcis(nullptr) { } ~GetStackTraceHandshakeClosure() { @@ -1947,21 +1944,22 @@ oop java_lang_Thread::async_get_stack_trace(oop java_thread, TRAPS) { return; } - JavaThread* thread = JavaThread::cast(th); + JavaThread* java_thread = JavaThread::cast(th); - if (!thread->has_last_Java_frame()) { + if (!java_thread->has_last_Java_frame()) { return; } bool carrier = false; - if (java_lang_VirtualThread::is_instance(_java_thread())) { - // if (thread->vthread() != _java_thread()) // We might be inside a System.executeOnCarrierThread - const ContinuationEntry* ce = thread->vthread_continuation(); - if (ce == nullptr || ce->cont_oop(thread) != java_lang_VirtualThread::continuation(_java_thread())) { - return; // not mounted + if (java_lang_VirtualThread::is_instance(_thread_h())) { + // Ensure _thread_h is still mounted to java_thread. + const ContinuationEntry* ce = java_thread->vthread_continuation(); + if (ce == nullptr || ce->cont_oop(java_thread) != java_lang_VirtualThread::continuation(_thread_h())) { + // Target thread has been unmounted. + return; } } else { - carrier = (thread->vthread_continuation() != nullptr); + carrier = (java_thread->vthread_continuation() != nullptr); } const int max_depth = MaxJavaStackTraceDepth; @@ -1973,7 +1971,7 @@ oop java_lang_Thread::async_get_stack_trace(oop java_thread, TRAPS) { _bcis = new (mtInternal) GrowableArray(init_length, mtInternal); int total_count = 0; - for (vframeStream vfst(thread, false, false, carrier); // we don't process frames as we don't care about oops + for (vframeStream vfst(java_thread, false, false, carrier); // we don't process frames as we don't care about oops !vfst.at_end() && (max_depth == 0 || max_depth != total_count); vfst.next()) { @@ -1994,9 +1992,9 @@ oop java_lang_Thread::async_get_stack_trace(oop java_thread, TRAPS) { // Handshake with target ResourceMark rm(THREAD); HandleMark hm(THREAD); - GetStackTraceHandshakeClosure gsthc(Handle(THREAD, java_thread)); + GetStackTraceHandshakeClosure gsthc(Handle(THREAD, thread_oop)); do { - Handshake::execute(&gsthc, &tlh, thread); + Handshake::execute(&gsthc, &tlh, java_thread); } while (gsthc.read_reset_retry()); // Stop if no stack trace is found. diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index faa325d55dd..70fa519f0f0 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -463,7 +463,7 @@ class java_lang_Thread : AllStatic { static const char* thread_status_name(oop java_thread_oop); // Fill in current stack trace, can cause GC - static oop async_get_stack_trace(oop java_thread, TRAPS); + static oop async_get_stack_trace(jobject jthread, TRAPS); JFR_ONLY(static u2 jfr_epoch(oop java_thread);) JFR_ONLY(static void set_jfr_epoch(oop java_thread, u2 epoch);) diff --git a/src/hotspot/share/classfile/loaderConstraints.cpp b/src/hotspot/share/classfile/loaderConstraints.cpp index 21161f44326..13c1ff7319d 100644 --- a/src/hotspot/share/classfile/loaderConstraints.cpp +++ b/src/hotspot/share/classfile/loaderConstraints.cpp @@ -31,7 +31,7 @@ #include "oops/klass.inline.hpp" #include "oops/symbolHandle.hpp" #include "runtime/mutexLocker.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" // Overview // @@ -147,7 +147,7 @@ class ConstraintSet { // copied into hashtable as }; -using InternalLoaderConstraintTable = ResourceHashtable; +using InternalLoaderConstraintTable = HashTable; static InternalLoaderConstraintTable* _loader_constraint_table; void LoaderConstraint::extend_loader_constraint(Symbol* class_name, diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index 7f9bf09aa51..ce887081cdb 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -45,9 +45,9 @@ #include "runtime/safepoint.hpp" #include "utilities/events.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/ostream.hpp" #include "utilities/quickSort.hpp" -#include "utilities/resourceHash.hpp" ModuleEntry* ModuleEntryTable::_javabase_module = nullptr; @@ -403,7 +403,7 @@ void ModuleEntry::set_loader_data(ClassLoaderData* cld) { } #if INCLUDE_CDS_JAVA_HEAP -typedef ResourceHashtable< +typedef HashTable< const ModuleEntry*, ModuleEntry*, 557, // prime number diff --git a/src/hotspot/share/classfile/moduleEntry.hpp b/src/hotspot/share/classfile/moduleEntry.hpp index 1ae504577e3..1074c88f010 100644 --- a/src/hotspot/share/classfile/moduleEntry.hpp +++ b/src/hotspot/share/classfile/moduleEntry.hpp @@ -31,9 +31,9 @@ #include "oops/symbolHandle.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" #include "utilities/ostream.hpp" -#include "utilities/resourceHash.hpp" #if INCLUDE_JFR #include "jfr/support/jfrTraceIdExtension.hpp" #endif @@ -233,7 +233,7 @@ class ModuleClosure: public StackObj { class ModuleEntryTable : public CHeapObj { private: static ModuleEntry* _javabase_module; - ResourceHashtable _table; public: diff --git a/src/hotspot/share/classfile/packageEntry.cpp b/src/hotspot/share/classfile/packageEntry.cpp index 9a21578630f..ea2e6cd1def 100644 --- a/src/hotspot/share/classfile/packageEntry.cpp +++ b/src/hotspot/share/classfile/packageEntry.cpp @@ -38,9 +38,9 @@ #include "runtime/java.hpp" #include "utilities/events.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/ostream.hpp" #include "utilities/quickSort.hpp" -#include "utilities/resourceHash.hpp" PackageEntry::PackageEntry(Symbol* name, ModuleEntry* module) : _name(name), @@ -212,7 +212,7 @@ PackageEntryTable::~PackageEntryTable() { } #if INCLUDE_CDS_JAVA_HEAP -typedef ResourceHashtable< +typedef HashTable< const PackageEntry*, PackageEntry*, 557, // prime number diff --git a/src/hotspot/share/classfile/packageEntry.hpp b/src/hotspot/share/classfile/packageEntry.hpp index aa572dc15b2..84620785ebb 100644 --- a/src/hotspot/share/classfile/packageEntry.hpp +++ b/src/hotspot/share/classfile/packageEntry.hpp @@ -30,9 +30,9 @@ #include "oops/symbolHandle.hpp" #include "runtime/atomic.hpp" #include "utilities/growableArray.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" #include "utilities/ostream.hpp" -#include "utilities/resourceHash.hpp" #if INCLUDE_JFR #include "jfr/support/jfrTraceIdExtension.hpp" #endif @@ -233,7 +233,7 @@ public: // The PackageEntryTable is a Hashtable containing a list of all packages defined // by a particular class loader. Each package is represented as a PackageEntry node. class PackageEntryTable : public CHeapObj { - ResourceHashtable _table; public: PackageEntryTable(); diff --git a/src/hotspot/share/classfile/placeholders.cpp b/src/hotspot/share/classfile/placeholders.cpp index b2f1ed40106..f4c381eafdf 100644 --- a/src/hotspot/share/classfile/placeholders.cpp +++ b/src/hotspot/share/classfile/placeholders.cpp @@ -31,7 +31,7 @@ #include "oops/symbolHandle.hpp" #include "runtime/javaThread.hpp" #include "runtime/mutexLocker.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" class PlaceholderKey { SymbolHandle _name; @@ -49,7 +49,7 @@ class PlaceholderKey { }; const int _placeholder_table_size = 503; // Does this really have to be prime? -using InternalPlaceholderTable = ResourceHashtable; static InternalPlaceholderTable* _placeholders; diff --git a/src/hotspot/share/classfile/resolutionErrors.cpp b/src/hotspot/share/classfile/resolutionErrors.cpp index 03af71bc26f..25aa370d257 100644 --- a/src/hotspot/share/classfile/resolutionErrors.cpp +++ b/src/hotspot/share/classfile/resolutionErrors.cpp @@ -30,7 +30,7 @@ #include "oops/symbol.hpp" #include "runtime/handles.inline.hpp" #include "runtime/mutexLocker.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" class ResolutionErrorKey { ConstantPool* _cpool; @@ -53,7 +53,7 @@ class ResolutionErrorKey { } }; -using InternalResolutionErrorTable = ResourceHashtable; diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index 957ecd8ebe8..cf5d98650ce 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.cpp @@ -59,7 +59,7 @@ #include "utilities/concurrentHashTable.inline.hpp" #include "utilities/concurrentHashTableTasks.inline.hpp" #include "utilities/macros.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" #include "utilities/utf8.hpp" #if INCLUDE_G1GC #include "gc/g1/g1CollectedHeap.hpp" @@ -810,7 +810,7 @@ class StringTable::VerifyCompStrings : StackObj { return java_lang_String::equals(a, b); } - ResizeableResourceHashtable _table; public: size_t _errors; diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index f482a5bb303..783ae9d35ba 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -111,10 +111,10 @@ class InvokeMethodKey : public StackObj { }; -using InvokeMethodIntrinsicTable = ResourceHashtable; static InvokeMethodIntrinsicTable* _invoke_method_intrinsic_table; -using InvokeMethodTypeTable = ResourceHashtable; +using InvokeMethodTypeTable = HashTable; static InvokeMethodTypeTable* _invoke_method_type_table; OopHandle SystemDictionary::_java_system_loader; diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 7dd3a7d1bb2..1845c28d819 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -22,6 +22,7 @@ * */ + #include "cds/aotClassFilter.hpp" #include "cds/aotClassLocation.hpp" #include "cds/aotLogging.hpp" @@ -74,7 +75,7 @@ #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" #include "runtime/mutexLocker.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" #include "utilities/stringUtils.hpp" SystemDictionaryShared::ArchiveInfo SystemDictionaryShared::_static_archive; @@ -445,7 +446,7 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class( return k; } -class UnregisteredClassesTable : public ResourceHashtable< +class UnregisteredClassesTable : public HashTable< Symbol*, InstanceKlass*, 15889, // prime number AnyObj::C_HEAP> {}; diff --git a/src/hotspot/share/classfile/verifier.hpp b/src/hotspot/share/classfile/verifier.hpp index 7857d472705..87ea8bd2970 100644 --- a/src/hotspot/share/classfile/verifier.hpp +++ b/src/hotspot/share/classfile/verifier.hpp @@ -31,7 +31,7 @@ #include "runtime/handles.hpp" #include "utilities/exceptions.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" // The verifier class class Verifier : AllStatic { @@ -270,7 +270,7 @@ class sig_as_verification_types : public ResourceObj { // This hashtable is indexed by the Utf8 constant pool indexes pointed to // by constant pool (Interface)Method_refs' NameAndType signature entries. -typedef ResourceHashtable +typedef HashTable method_signatures_table_type; // A new instance of this class is created for each class being verified diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 3d110c5706b..c9c5c925f86 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -415,18 +415,18 @@ class methodHandle; \ do_class(java_lang_StringCoding, "java/lang/StringCoding") \ do_intrinsic(_countPositives, java_lang_StringCoding, countPositives_name, countPositives_signature, F_S) \ - do_name( countPositives_name, "countPositives") \ + do_name( countPositives_name, "countPositives0") \ do_signature(countPositives_signature, "([BII)I") \ \ do_class(sun_nio_cs_iso8859_1_Encoder, "sun/nio/cs/ISO_8859_1$Encoder") \ do_intrinsic(_encodeISOArray, sun_nio_cs_iso8859_1_Encoder, encodeISOArray_name, encodeISOArray_signature, F_S) \ - do_name( encodeISOArray_name, "implEncodeISOArray") \ + do_name( encodeISOArray_name, "encodeISOArray0") \ do_signature(encodeISOArray_signature, "([CI[BII)I") \ \ do_intrinsic(_encodeByteISOArray, java_lang_StringCoding, encodeISOArray_name, indexOfI_signature, F_S) \ \ do_intrinsic(_encodeAsciiArray, java_lang_StringCoding, encodeAsciiArray_name, encodeISOArray_signature, F_S) \ - do_name( encodeAsciiArray_name, "implEncodeAsciiArray") \ + do_name( encodeAsciiArray_name, "encodeAsciiArray0") \ \ do_class(java_math_BigInteger, "java/math/BigInteger") \ do_intrinsic(_multiplyToLen, java_math_BigInteger, multiplyToLen_name, multiplyToLen_signature, F_S) \ diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 5b87e8c5670..9ec5478a071 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -450,8 +450,10 @@ AdapterBlob::AdapterBlob(int size, CodeBuffer* cb, int entry_offset[AdapterBlob: BufferBlob("I2C/C2I adapters", CodeBlobKind::Adapter, cb, size, sizeof(AdapterBlob)) { assert(entry_offset[0] == 0, "sanity check"); for (int i = 1; i < AdapterBlob::ENTRY_COUNT; i++) { - assert(entry_offset[i] > 0 && entry_offset[i] < cb->insts()->size(), - "invalid entry offset 0x%x", entry_offset[i]); + // The entry is within the adapter blob or unset. + assert((entry_offset[i] > 0 && entry_offset[i] < cb->insts()->size()) || + (entry_offset[i] == -1), + "invalid entry offset[%d] = 0x%x", i, entry_offset[i]); } _c2i_offset = entry_offset[1]; _c2i_unverified_offset = entry_offset[2]; diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index f7c86ce58fa..8f2932f6abc 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -1185,7 +1185,7 @@ static void check_live_nmethods_dependencies(DepChange& changes) { // Turn off dependency tracing while actually testing dependencies. FlagSetting fs(Dependencies::_verify_in_progress, true); - typedef ResourceHashtable DepTable; diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index d8cbf6b9e66..b8b3a70bf58 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -79,7 +79,7 @@ #include "utilities/dtrace.hpp" #include "utilities/events.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" #include "utilities/xmlstream.hpp" #if INCLUDE_JVMCI #include "jvmci/jvmciRuntime.hpp" diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp index fd1357398f5..9c49d941bbc 100644 --- a/src/hotspot/share/compiler/compilationPolicy.cpp +++ b/src/hotspot/share/compiler/compilationPolicy.cpp @@ -404,7 +404,7 @@ double CompilationPolicy::threshold_scale(CompLevel level, int feedback_k) { return 1; } -void CompilationPolicy::print_counters(const char* prefix, Method* m) { +void CompilationPolicy::print_counters_on(outputStream* st, const char* prefix, Method* m) { int invocation_count = m->invocation_count(); int backedge_count = m->backedge_count(); MethodData* mdh = m->method_data(); @@ -416,133 +416,140 @@ void CompilationPolicy::print_counters(const char* prefix, Method* m) { mdo_invocations_start = mdh->invocation_count_start(); mdo_backedges_start = mdh->backedge_count_start(); } - tty->print(" %stotal=%d,%d %smdo=%d(%d),%d(%d)", prefix, - invocation_count, backedge_count, prefix, - mdo_invocations, mdo_invocations_start, - mdo_backedges, mdo_backedges_start); - tty->print(" %smax levels=%d,%d", prefix, - m->highest_comp_level(), m->highest_osr_comp_level()); + st->print(" %stotal=%d,%d %smdo=%d(%d),%d(%d)", prefix, + invocation_count, backedge_count, prefix, + mdo_invocations, mdo_invocations_start, + mdo_backedges, mdo_backedges_start); + st->print(" %smax levels=%d,%d", prefix, m->highest_comp_level(), m->highest_osr_comp_level()); } -void CompilationPolicy::print_training_data(const char* prefix, Method* method) { +void CompilationPolicy::print_training_data_on(outputStream* st, const char* prefix, Method* method) { methodHandle m(Thread::current(), method); - tty->print(" %smtd: ", prefix); + st->print(" %smtd: ", prefix); MethodTrainingData* mtd = MethodTrainingData::find(m); if (mtd == nullptr) { - tty->print("null"); + st->print("null"); } else { MethodData* md = mtd->final_profile(); - tty->print("mdo="); + st->print("mdo="); if (md == nullptr) { - tty->print("null"); + st->print("null"); } else { int mdo_invocations = md->invocation_count(); int mdo_backedges = md->backedge_count(); int mdo_invocations_start = md->invocation_count_start(); int mdo_backedges_start = md->backedge_count_start(); - tty->print("%d(%d), %d(%d)", mdo_invocations, mdo_invocations_start, mdo_backedges, mdo_backedges_start); + st->print("%d(%d), %d(%d)", mdo_invocations, mdo_invocations_start, mdo_backedges, mdo_backedges_start); } CompileTrainingData* ctd = mtd->last_toplevel_compile(CompLevel_full_optimization); - tty->print(", deps="); + st->print(", deps="); if (ctd == nullptr) { - tty->print("null"); + st->print("null"); } else { - tty->print("%d", ctd->init_deps_left()); + st->print("%d", ctd->init_deps_left()); } } } // Print an event. -void CompilationPolicy::print_event(EventType type, Method* m, Method* im, int bci, CompLevel level) { +void CompilationPolicy::print_event_on(outputStream *st, EventType type, Method* m, Method* im, int bci, CompLevel level) { bool inlinee_event = m != im; - ttyLocker tty_lock; - tty->print("%lf: [", os::elapsedTime()); + st->print("%lf: [", os::elapsedTime()); switch(type) { case CALL: - tty->print("call"); + st->print("call"); break; case LOOP: - tty->print("loop"); + st->print("loop"); break; case COMPILE: - tty->print("compile"); + st->print("compile"); break; case FORCE_COMPILE: - tty->print("force-compile"); + st->print("force-compile"); break; case REMOVE_FROM_QUEUE: - tty->print("remove-from-queue"); + st->print("remove-from-queue"); break; case UPDATE_IN_QUEUE: - tty->print("update-in-queue"); + st->print("update-in-queue"); break; case REPROFILE: - tty->print("reprofile"); + st->print("reprofile"); break; case MAKE_NOT_ENTRANT: - tty->print("make-not-entrant"); + st->print("make-not-entrant"); break; default: - tty->print("unknown"); + st->print("unknown"); } - tty->print(" level=%d ", level); + st->print(" level=%d ", level); ResourceMark rm; char *method_name = m->name_and_sig_as_C_string(); - tty->print("[%s", method_name); + st->print("[%s", method_name); if (inlinee_event) { char *inlinee_name = im->name_and_sig_as_C_string(); - tty->print(" [%s]] ", inlinee_name); + st->print(" [%s]] ", inlinee_name); } - else tty->print("] "); - tty->print("@%d queues=%d,%d", bci, CompileBroker::queue_size(CompLevel_full_profile), - CompileBroker::queue_size(CompLevel_full_optimization)); + else st->print("] "); + st->print("@%d queues=%d,%d", bci, CompileBroker::queue_size(CompLevel_full_profile), + CompileBroker::queue_size(CompLevel_full_optimization)); - tty->print(" rate="); - if (m->prev_time() == 0) tty->print("n/a"); - else tty->print("%f", m->rate()); + st->print(" rate="); + if (m->prev_time() == 0) st->print("n/a"); + else st->print("%f", m->rate()); - tty->print(" k=%.2lf,%.2lf", threshold_scale(CompLevel_full_profile, Tier3LoadFeedback), - threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback)); + st->print(" k=%.2lf,%.2lf", threshold_scale(CompLevel_full_profile, Tier3LoadFeedback), + threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback)); if (type != COMPILE) { - print_counters("", m); + print_counters_on(st, "", m); if (inlinee_event) { - print_counters("inlinee ", im); + print_counters_on(st, "inlinee ", im); } - tty->print(" compilable="); + st->print(" compilable="); bool need_comma = false; if (!m->is_not_compilable(CompLevel_full_profile)) { - tty->print("c1"); + st->print("c1"); need_comma = true; } if (!m->is_not_osr_compilable(CompLevel_full_profile)) { - if (need_comma) tty->print(","); - tty->print("c1-osr"); + if (need_comma) st->print(","); + st->print("c1-osr"); need_comma = true; } if (!m->is_not_compilable(CompLevel_full_optimization)) { - if (need_comma) tty->print(","); - tty->print("c2"); + if (need_comma) st->print(","); + st->print("c2"); need_comma = true; } if (!m->is_not_osr_compilable(CompLevel_full_optimization)) { - if (need_comma) tty->print(","); - tty->print("c2-osr"); + if (need_comma) st->print(","); + st->print("c2-osr"); } - tty->print(" status="); + st->print(" status="); if (m->queued_for_compilation()) { - tty->print("in-queue"); - } else tty->print("idle"); - print_training_data("", m); + st->print("in-queue"); + } else st->print("idle"); + + print_training_data_on(st, "", m); if (inlinee_event) { - print_training_data("inlinee ", im); + print_training_data_on(st, "inlinee ", im); } } - tty->print_cr("]"); + st->print_cr("]"); + +} + +void CompilationPolicy::print_event(EventType type, Method* m, Method* im, int bci, CompLevel level) { + stringStream s; + print_event_on(&s, type, m, im, bci, level); + ResourceMark rm; + tty->print("%s", s.as_string()); } void CompilationPolicy::initialize() { diff --git a/src/hotspot/share/compiler/compilationPolicy.hpp b/src/hotspot/share/compiler/compilationPolicy.hpp index 75374a2f830..f4a7c4c249b 100644 --- a/src/hotspot/share/compiler/compilationPolicy.hpp +++ b/src/hotspot/share/compiler/compilationPolicy.hpp @@ -287,8 +287,8 @@ class CompilationPolicy : AllStatic { // loop_event checks if a method should be OSR compiled at a different // level. static CompLevel loop_event(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD); - static void print_counters(const char* prefix, Method* m); - static void print_training_data(const char* prefix, Method* method); + static void print_counters_on(outputStream* st, const char* prefix, Method* m); + static void print_training_data_on(outputStream* st, const char* prefix, Method* method); // Has a method been long around? // We don't remove old methods from the compile queue even if they have // very low activity (see select_task()). @@ -318,6 +318,7 @@ class CompilationPolicy : AllStatic { static void set_c2_count(int x) { _c2_count = x; } enum EventType { CALL, LOOP, COMPILE, FORCE_COMPILE, FORCE_RECOMPILE, REMOVE_FROM_QUEUE, UPDATE_IN_QUEUE, REPROFILE, MAKE_NOT_ENTRANT }; + static void print_event_on(outputStream *st, EventType type, Method* m, Method* im, int bci, CompLevel level); static void print_event(EventType type, Method* m, Method* im, int bci, CompLevel level); // Check if the method can be compiled, change level if necessary static void compile(const methodHandle& mh, int bci, CompLevel level, TRAPS); diff --git a/src/hotspot/share/compiler/disassembler.cpp b/src/hotspot/share/compiler/disassembler.cpp index f8313db66aa..c79c15e0f32 100644 --- a/src/hotspot/share/compiler/disassembler.cpp +++ b/src/hotspot/share/compiler/disassembler.cpp @@ -22,6 +22,7 @@ * */ + #include "asm/assembler.inline.hpp" #include "asm/macroAssembler.hpp" #include "ci/ciUtilities.hpp" @@ -40,7 +41,7 @@ #include "runtime/os.hpp" #include "runtime/stubCodeGenerator.hpp" #include "runtime/stubRoutines.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" void* Disassembler::_library = nullptr; bool Disassembler::_tried_to_load_library = false; @@ -189,7 +190,7 @@ class decode_env { } }; - typedef ResourceHashtable< + typedef HashTable< address, SourceFileInfo, 15889, // prime number AnyObj::C_HEAP> SourceFileInfoTable; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index e5266a527f0..e964ecefb18 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -2793,8 +2793,6 @@ void G1CollectedHeap::abandon_collection_set() { collection_set()->stop_incremental_building(); collection_set()->abandon_all_candidates(); - - young_regions_cset_group()->clear(); } bool G1CollectedHeap::is_old_gc_alloc_region(G1HeapRegion* hr) { diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index bae8115bb9c..579413768d1 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -246,6 +246,11 @@ void G1FullCollector::complete_collection(size_t allocation_word_size) { _heap->resize_all_tlabs(); + // We clear remembered sets for young regions this late in the full GC because + // G1HeapVerifier expects the remembered sets for all young regions to be complete + // throughout most of the collection process (e.g. G1FullCollector::verify_after_marking). + _heap->young_regions_cset_group()->clear(); + _heap->policy()->record_full_collection_end(); _heap->gc_epilogue(true); diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 3bbc64e0fe7..bb54d344ca0 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -508,12 +508,9 @@ uint G1Policy::calculate_desired_eden_length_before_mixed(double base_time_ms, } double G1Policy::predict_survivor_regions_evac_time() const { - const GrowableArray* survivor_regions = _g1h->survivor()->regions(); double survivor_regions_evac_time = predict_young_region_other_time_ms(_g1h->survivor()->length()); - for (GrowableArrayIterator it = survivor_regions->begin(); - it != survivor_regions->end(); - ++it) { - survivor_regions_evac_time += predict_region_copy_time_ms(*it, _g1h->collector_state()->in_young_only_phase()); + for (G1HeapRegion* r : _g1h->survivor()->regions()) { + survivor_regions_evac_time += predict_region_copy_time_ms(r, _g1h->collector_state()->in_young_only_phase()); } return survivor_regions_evac_time; @@ -1461,16 +1458,13 @@ uint G1Policy::calc_max_old_cset_length() const { void G1Policy::transfer_survivors_to_cset(const G1SurvivorRegions* survivors) { start_adding_survivor_regions(); - for (GrowableArrayIterator it = survivors->regions()->begin(); - it != survivors->regions()->end(); - ++it) { - G1HeapRegion* curr = *it; - set_region_survivor(curr); + for (G1HeapRegion* r : survivors->regions()) { + set_region_survivor(r); // The region is a non-empty survivor so let's add it to // the incremental collection set for the next evacuation // pause. - _collection_set->add_survivor_regions(curr); + _collection_set->add_survivor_regions(r); } stop_adding_survivor_regions(); diff --git a/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp b/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp index edd4f35d5ed..4aa752e5823 100644 --- a/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp +++ b/src/hotspot/share/gc/g1/g1SurvivorRegions.cpp @@ -28,18 +28,18 @@ #include "utilities/growableArray.hpp" G1SurvivorRegions::G1SurvivorRegions() : - _regions(new (mtGC) GrowableArray(8, mtGC)), + _regions(8, mtGC), _used_bytes(0), _regions_on_node() {} uint G1SurvivorRegions::add(G1HeapRegion* hr) { assert(hr->is_survivor(), "should be flagged as survivor region"); - _regions->append(hr); + _regions.append(hr); return _regions_on_node.add(hr); } uint G1SurvivorRegions::length() const { - return (uint)_regions->length(); + return (uint)_regions.length(); } uint G1SurvivorRegions::regions_on_node(uint node_index) const { @@ -47,17 +47,14 @@ uint G1SurvivorRegions::regions_on_node(uint node_index) const { } void G1SurvivorRegions::convert_to_eden() { - for (GrowableArrayIterator it = _regions->begin(); - it != _regions->end(); - ++it) { - G1HeapRegion* hr = *it; - hr->set_eden_pre_gc(); + for (G1HeapRegion* r : _regions) { + r->set_eden_pre_gc(); } clear(); } void G1SurvivorRegions::clear() { - _regions->clear(); + _regions.clear(); _used_bytes = 0; _regions_on_node.clear(); } diff --git a/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp b/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp index cc6bbc44fa8..0532ee12162 100644 --- a/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp +++ b/src/hotspot/share/gc/g1/g1SurvivorRegions.hpp @@ -27,16 +27,17 @@ #include "gc/g1/g1RegionsOnNodes.hpp" #include "runtime/globals.hpp" +#include "utilities/growableArray.hpp" template class GrowableArray; class G1HeapRegion; +// Set of current survivor regions. class G1SurvivorRegions { -private: - GrowableArray* _regions; - volatile size_t _used_bytes; - G1RegionsOnNodes _regions_on_node; + GrowableArray _regions; + volatile size_t _used_bytes; + G1RegionsOnNodes _regions_on_node; public: G1SurvivorRegions(); @@ -50,7 +51,7 @@ public: uint length() const; uint regions_on_node(uint node_index) const; - const GrowableArray* regions() const { + const GrowableArray& regions() const { return _regions; } diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index 48d6a1b74ea..6b9328b8697 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -281,15 +281,6 @@ size_t SerialHeap::max_capacity() const { return _young_gen->max_capacity() + _old_gen->max_capacity(); } -// Return true if any of the following is true: -// . the allocation won't fit into the current young gen heap -// . heap memory is tight -bool SerialHeap::should_try_older_generation_allocation(size_t word_size) const { - size_t young_capacity = _young_gen->capacity_before_gc(); - return (word_size > heap_word_size(young_capacity)) - || _is_heap_almost_full; -} - HeapWord* SerialHeap::expand_heap_and_allocate(size_t size, bool is_tlab) { HeapWord* result = nullptr; if (_old_gen->should_allocate(size, is_tlab)) { @@ -308,32 +299,26 @@ HeapWord* SerialHeap::expand_heap_and_allocate(size_t size, bool is_tlab) { HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) { HeapWord* result = nullptr; - // Loop until the allocation is satisfied, or unsatisfied after GC. - for (uint try_count = 1; /* return or throw */; try_count += 1) { - // First allocation attempt is lock-free. - DefNewGeneration *young = _young_gen; - if (young->should_allocate(size, is_tlab)) { - result = young->par_allocate(size); + for (uint try_count = 1; /* break */; try_count++) { + if (_young_gen->should_allocate(size, is_tlab)) { + result = _young_gen->par_allocate(size); if (result != nullptr) { - assert(is_in_reserved(result), "result not in heap"); - return result; + break; + } + } + // Try old-gen allocation for non-TLAB. + if (!is_tlab) { + // If it's too large for young-gen or heap is too full. + if (size > heap_word_size(_young_gen->capacity_before_gc()) || _is_heap_almost_full) { + result = _old_gen->par_allocate(size); + if (result != nullptr) { + break; + } } } uint gc_count_before; // Read inside the Heap_lock locked region. { MutexLocker ml(Heap_lock); - log_trace(gc, alloc)("SerialHeap::mem_allocate_work: attempting locked slow path allocation"); - // Note that only large objects get a shot at being - // allocated in later generations. - bool first_only = !should_try_older_generation_allocation(size); - - result = attempt_allocation(size, is_tlab, first_only); - if (result != nullptr) { - assert(is_in_reserved(result), "result not in heap"); - return result; - } - - // Read the gc count while the heap lock is held. gc_count_before = total_collections(); } @@ -341,10 +326,7 @@ HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) { VMThread::execute(&op); if (op.gc_succeeded()) { result = op.result(); - - assert(result == nullptr || is_in_reserved(result), - "result not in heap"); - return result; + break; } // Give a warning if we seem to be looping forever. @@ -354,6 +336,9 @@ HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) { " size=%zu %s", try_count, size, is_tlab ? "(TLAB)" : ""); } } + + assert(result == nullptr || is_in_reserved(result), "postcondition"); + return result; } HeapWord* SerialHeap::attempt_allocation(size_t size, diff --git a/src/hotspot/share/gc/serial/serialHeap.hpp b/src/hotspot/share/gc/serial/serialHeap.hpp index e86eac58515..b89edb33307 100644 --- a/src/hotspot/share/gc/serial/serialHeap.hpp +++ b/src/hotspot/share/gc/serial/serialHeap.hpp @@ -229,10 +229,6 @@ public: void save_marks(); private: - // Return true if an allocation should be attempted in the older generation - // if it fails in the younger generation. Return false, otherwise. - bool should_try_older_generation_allocation(size_t word_size) const; - // Try to allocate space by expanding the heap. HeapWord* expand_heap_and_allocate(size_t size, bool is_tlab); diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.hpp b/src/hotspot/share/gc/serial/tenuredGeneration.hpp index 892fc5bb86c..1225005aff4 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.hpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.hpp @@ -126,6 +126,8 @@ public: // Allocate and returns a block of the requested size, or returns "null". // Assumes the caller has done any necessary locking. inline HeapWord* allocate(size_t word_size); + // Multi-threaded version. + inline HeapWord* par_allocate(size_t word_size); // Expand the old-gen then invoke allocate above. HeapWord* expand_and_allocate(size_t size); diff --git a/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp b/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp index d88b2566299..1c82566bdf4 100644 --- a/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp +++ b/src/hotspot/share/gc/serial/tenuredGeneration.inline.hpp @@ -57,4 +57,12 @@ HeapWord* TenuredGeneration::allocate(size_t word_size) { return res; } +HeapWord* TenuredGeneration::par_allocate(size_t word_size) { + HeapWord* res = _the_space->par_allocate(word_size); + if (res != nullptr) { + _bts->update_for_block(res, res + word_size); + } + return res; +} + #endif // SHARE_GC_SERIAL_TENUREDGENERATION_INLINE_HPP diff --git a/src/hotspot/share/gc/shared/gcVMOperations.cpp b/src/hotspot/share/gc/shared/gcVMOperations.cpp index 19a81504722..1299f64995f 100644 --- a/src/hotspot/share/gc/shared/gcVMOperations.cpp +++ b/src/hotspot/share/gc/shared/gcVMOperations.cpp @@ -62,11 +62,6 @@ void VM_Verify::doit() { Universe::verify(); } -VM_GC_Operation::~VM_GC_Operation() { - CollectedHeap* ch = Universe::heap(); - ch->soft_ref_policy()->set_all_soft_refs_clear(false); -} - const char* VM_GC_Operation::cause() const { return GCCause::to_string(_gc_cause); } diff --git a/src/hotspot/share/gc/shared/gcVMOperations.hpp b/src/hotspot/share/gc/shared/gcVMOperations.hpp index 6752e0bc32d..5048bc3c1ed 100644 --- a/src/hotspot/share/gc/shared/gcVMOperations.hpp +++ b/src/hotspot/share/gc/shared/gcVMOperations.hpp @@ -117,8 +117,8 @@ class VM_GC_Operation: public VM_Heap_Sync_Operation { public: VM_GC_Operation(uint gc_count_before, GCCause::Cause _cause, - uint full_gc_count_before = 0, - bool full = false) : VM_Heap_Sync_Operation() { + uint full_gc_count_before, + bool full) : VM_Heap_Sync_Operation() { _full = full; _prologue_succeeded = false; _gc_count_before = gc_count_before; @@ -126,16 +126,7 @@ class VM_GC_Operation: public VM_Heap_Sync_Operation { _gc_cause = _cause; _full_gc_count_before = full_gc_count_before; - // In ParallelScavengeHeap::mem_allocate() collections can be - // executed within a loop and _all_soft_refs_clear can be set - // true after they have been cleared by a collection and another - // collection started so that _all_soft_refs_clear can be true - // when this collection is started. Don't assert that - // _all_soft_refs_clear have to be false here even though - // mutators have run. Soft refs will be cleared again in this - // collection. } - ~VM_GC_Operation(); virtual const char* cause() const; @@ -156,8 +147,8 @@ class VM_GC_Service_Operation : public VM_GC_Operation { public: VM_GC_Service_Operation(uint gc_count_before, GCCause::Cause _cause, - uint full_gc_count_before = 0, - bool full = false) : + uint full_gc_count_before, + bool full) : VM_GC_Operation(gc_count_before, _cause, full_gc_count_before, full) {} }; diff --git a/src/hotspot/share/gc/shared/softRefPolicy.hpp b/src/hotspot/share/gc/shared/softRefPolicy.hpp index fe2706288f7..1e8ec356c40 100644 --- a/src/hotspot/share/gc/shared/softRefPolicy.hpp +++ b/src/hotspot/share/gc/shared/softRefPolicy.hpp @@ -25,37 +25,18 @@ #ifndef SHARE_GC_SHARED_SOFTREFPOLICY_HPP #define SHARE_GC_SHARED_SOFTREFPOLICY_HPP -#include "memory/allocation.hpp" - class SoftRefPolicy { private: // Set to true when policy wants soft refs cleared. // Reset to false by gc after it clears all soft refs. bool _should_clear_all_soft_refs; - // Set to true by the GC if the just-completed gc cleared all - // softrefs. This is set to true whenever a gc clears all softrefs, and - // set to false each time gc returns to the mutator. For example, in the - // ParallelScavengeHeap case the latter would be done toward the end of - // mem_allocate() where it returns op.result() - bool _all_soft_refs_clear; - public: SoftRefPolicy() : - _should_clear_all_soft_refs(false), - _all_soft_refs_clear(false) {} + _should_clear_all_soft_refs(false) {} bool should_clear_all_soft_refs() { return _should_clear_all_soft_refs; } void set_should_clear_all_soft_refs(bool v) { _should_clear_all_soft_refs = v; } - - bool all_soft_refs_clear() { return _all_soft_refs_clear; } - void set_all_soft_refs_clear(bool v) { _all_soft_refs_clear = v; } - - // Called by the GC after Soft Refs have been cleared to indicate - // that the request in _should_clear_all_soft_refs has been fulfilled. - void cleared_all_soft_refs() { - _all_soft_refs_clear = true; - } }; #endif // SHARE_GC_SHARED_SOFTREFPOLICY_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 81154aff9f0..d4c7ad5df50 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -823,7 +823,6 @@ bool ShenandoahConcurrentGC::has_in_place_promotions(ShenandoahHeap* heap) { return heap->mode()->is_generational() && heap->old_generation()->has_in_place_promotions(); } -template class ShenandoahConcurrentEvacThreadClosure : public ThreadClosure { private: OopClosure* const _oops; @@ -833,13 +832,9 @@ public: void do_thread(Thread* thread) override { JavaThread* const jt = JavaThread::cast(thread); StackWatermarkSet::finish_processing(jt, _oops, StackWatermarkKind::gc); - if (GENERATIONAL) { - ShenandoahThreadLocalData::enable_plab_promotions(thread); - } } }; -template class ShenandoahConcurrentEvacUpdateThreadTask : public WorkerTask { private: ShenandoahJavaThreadsIterator _java_threads; @@ -851,30 +846,20 @@ public: } void work(uint worker_id) override { - if (GENERATIONAL) { - Thread* worker_thread = Thread::current(); - ShenandoahThreadLocalData::enable_plab_promotions(worker_thread); - } - // ShenandoahEvacOOMScope has to be setup by ShenandoahContextEvacuateUpdateRootsClosure. // Otherwise, may deadlock with watermark lock ShenandoahContextEvacuateUpdateRootsClosure oops_cl; - ShenandoahConcurrentEvacThreadClosure thr_cl(&oops_cl); + ShenandoahConcurrentEvacThreadClosure thr_cl(&oops_cl); _java_threads.threads_do(&thr_cl, worker_id); } }; void ShenandoahConcurrentGC::op_thread_roots() { - ShenandoahHeap* const heap = ShenandoahHeap::heap(); + const ShenandoahHeap* const heap = ShenandoahHeap::heap(); assert(heap->is_evacuation_in_progress(), "Checked by caller"); ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::conc_thread_roots); - if (heap->mode()->is_generational()) { - ShenandoahConcurrentEvacUpdateThreadTask task(heap->workers()->active_workers()); - heap->workers()->run_task(&task); - } else { - ShenandoahConcurrentEvacUpdateThreadTask task(heap->workers()->active_workers()); - heap->workers()->run_task(&task); - } + ShenandoahConcurrentEvacUpdateThreadTask task(heap->workers()->active_workers()); + heap->workers()->run_task(&task); } void ShenandoahConcurrentGC::op_weak_refs() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 2baf7e25cba..b90468501f6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1216,6 +1216,11 @@ public: // 1. We need to make the plab memory parsable by remembered-set scanning. // 2. We need to establish a trustworthy UpdateWaterMark value within each old-gen heap region ShenandoahGenerationalHeap::heap()->retire_plab(plab, thread); + + // Re-enable promotions for the next evacuation phase. + ShenandoahThreadLocalData::enable_plab_promotions(thread); + + // Reset the fill size for next evacuation phase. if (_resize && ShenandoahThreadLocalData::plab_size(thread) > 0) { ShenandoahThreadLocalData::set_plab_size(thread, 0); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp index dd500462d0f..ace5ab5e69a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp @@ -37,7 +37,6 @@ ShenandoahThreadLocalData::ShenandoahThreadLocalData() : _card_table(nullptr), _gclab(nullptr), _gclab_size(0), - _paced_time(0), _plab(nullptr), _plab_desired_size(0), _plab_actual_size(0), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp index 098e20a72ec..37d935d1f78 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp @@ -58,8 +58,6 @@ private: PLAB* _gclab; size_t _gclab_size; - double _paced_time; - // Thread-local allocation buffer only used in generational mode. // Used both by mutator threads and by GC worker threads // for evacuations within the old generation and @@ -237,18 +235,6 @@ public: return data(thread)->_plab_actual_size; } - static void add_paced_time(Thread* thread, double v) { - data(thread)->_paced_time += v; - } - - static double paced_time(Thread* thread) { - return data(thread)->_paced_time; - } - - static void reset_paced_time(Thread* thread) { - data(thread)->_paced_time = 0; - } - // Evacuation OOM handling static bool is_oom_during_evac(Thread* thread) { return data(thread)->_oom_during_evac; diff --git a/src/hotspot/share/gc/z/zDirector.cpp b/src/hotspot/share/gc/z/zDirector.cpp index e46b1c9e167..380a1e48ab4 100644 --- a/src/hotspot/share/gc/z/zDirector.cpp +++ b/src/hotspot/share/gc/z/zDirector.cpp @@ -725,8 +725,8 @@ static ZWorkerCounts select_worker_threads(const ZDirectorStats& stats, uint you // Adjust down the old workers so the next minor during major will be less sad old_workers = old_workers_clamped; // Since collecting the old generation depends on the initial young collection - // finishing, we don't want it to have fewer workers than the old generation. - young_workers = MAX2(old_workers, young_workers); + // finishing, we ideally don't want it to have fewer workers than the old generation. + young_workers = clamp(MAX2(old_workers, young_workers), 1u, ZYoungGCThreads); } else if (type == ZWorkerSelectionType::minor_during_old) { // Adjust young and old workers for minor during old to fit within ConcGCThreads young_workers = young_workers_clamped; diff --git a/src/hotspot/share/gc/z/zVerify.cpp b/src/hotspot/share/gc/z/zVerify.cpp index 68290c2c009..53cbcd0a421 100644 --- a/src/hotspot/share/gc/z/zVerify.cpp +++ b/src/hotspot/share/gc/z/zVerify.cpp @@ -51,8 +51,8 @@ #include "runtime/thread.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/hashTable.hpp" #include "utilities/preserveException.hpp" -#include "utilities/resourceHash.hpp" #include "utilities/vmError.hpp" #ifdef ASSERT @@ -516,7 +516,7 @@ void ZVerify::after_weak_processing() { // Remembered set verification // -typedef ResourceHashtable ZStoreBarrierBufferTable; +typedef HashTable ZStoreBarrierBufferTable; static ZStoreBarrierBufferTable* z_verify_store_barrier_buffer_table = nullptr; diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index f97374553ca..4d39537e5a1 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -599,16 +599,6 @@ JVM_GetClassDeclaredFields(JNIEnv *env, jclass ofClass, jboolean publicOnly); JNIEXPORT jobjectArray JNICALL JVM_GetClassDeclaredConstructors(JNIEnv *env, jclass ofClass, jboolean publicOnly); - -/* Differs from JVM_GetClassModifiers in treatment of inner classes. - This returns the access flags for the class as specified in the - class file rather than searching the InnerClasses attribute (if - present) to find the source-level access flags. Only the values of - the low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be - valid. */ -JNIEXPORT jint JNICALL -JVM_GetClassAccessFlags(JNIEnv *env, jclass cls); - /* Nestmates - since JDK 11 */ JNIEXPORT jboolean JNICALL diff --git a/src/hotspot/share/interpreter/abstractInterpreter.cpp b/src/hotspot/share/interpreter/abstractInterpreter.cpp index 0d6ccf3a710..05590add8ff 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.cpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp @@ -22,23 +22,22 @@ * */ -#include "asm/macroAssembler.hpp" #include "asm/macroAssembler.inline.hpp" #include "cds/metaspaceShared.hpp" #include "compiler/disassembler.hpp" #include "interpreter/bytecodeHistogram.hpp" #include "interpreter/bytecodeStream.hpp" +#include "interpreter/interp_masm.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" -#include "interpreter/interp_masm.hpp" #include "interpreter/templateTable.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "oops/arrayOop.hpp" #include "oops/constantPool.inline.hpp" #include "oops/cpCache.inline.hpp" -#include "oops/methodData.hpp" #include "oops/method.inline.hpp" +#include "oops/methodData.hpp" #include "oops/oop.inline.hpp" #include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" diff --git a/src/hotspot/share/interpreter/bytecodeStream.cpp b/src/hotspot/share/interpreter/bytecodeStream.cpp index 7454c749828..5a9e32a46b0 100644 --- a/src/hotspot/share/interpreter/bytecodeStream.cpp +++ b/src/hotspot/share/interpreter/bytecodeStream.cpp @@ -22,8 +22,8 @@ * */ -#include "interpreter/bytecodeStream.hpp" #include "interpreter/bytecodes.hpp" +#include "interpreter/bytecodeStream.hpp" #include "runtime/handles.inline.hpp" Bytecodes::Code RawBytecodeStream::raw_next_special(Bytecodes::Code code) { diff --git a/src/hotspot/share/interpreter/bytecodeTracer.cpp b/src/hotspot/share/interpreter/bytecodeTracer.cpp index 1f912b2b00f..34170108dc4 100644 --- a/src/hotspot/share/interpreter/bytecodeTracer.cpp +++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp @@ -25,14 +25,14 @@ #include "classfile/classPrinter.hpp" #include "classfile/javaClasses.inline.hpp" #include "interpreter/bytecodeHistogram.hpp" +#include "interpreter/bytecodes.hpp" #include "interpreter/bytecodeStream.hpp" #include "interpreter/bytecodeTracer.hpp" -#include "interpreter/bytecodes.hpp" #include "interpreter/interpreter.hpp" #include "memory/resourceArea.hpp" #include "oops/constantPool.inline.hpp" -#include "oops/methodData.hpp" #include "oops/method.hpp" +#include "oops/methodData.hpp" #include "runtime/atomic.hpp" #include "runtime/handles.inline.hpp" #include "runtime/mutexLocker.hpp" diff --git a/src/hotspot/share/interpreter/bytecodeUtils.cpp b/src/hotspot/share/interpreter/bytecodeUtils.cpp index eb4557d6ba0..a5503cc4b88 100644 --- a/src/hotspot/share/interpreter/bytecodeUtils.cpp +++ b/src/hotspot/share/interpreter/bytecodeUtils.cpp @@ -28,8 +28,8 @@ #include "gc/shared/gcLocker.hpp" #include "interpreter/bytecodeUtils.hpp" #include "memory/resourceArea.hpp" -#include "runtime/signature.hpp" #include "runtime/safepointVerifiers.hpp" +#include "runtime/signature.hpp" #include "utilities/events.hpp" #include "utilities/ostream.hpp" diff --git a/src/hotspot/share/interpreter/interpreter.cpp b/src/hotspot/share/interpreter/interpreter.cpp index 6272deea349..2cc163186e8 100644 --- a/src/hotspot/share/interpreter/interpreter.cpp +++ b/src/hotspot/share/interpreter/interpreter.cpp @@ -22,18 +22,17 @@ * */ -#include "asm/macroAssembler.hpp" #include "asm/macroAssembler.inline.hpp" #include "compiler/disassembler.hpp" +#include "interpreter/interp_masm.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" -#include "interpreter/interp_masm.hpp" #include "interpreter/templateTable.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "oops/arrayOop.hpp" -#include "oops/methodData.hpp" #include "oops/method.hpp" +#include "oops/methodData.hpp" #include "oops/oop.inline.hpp" #include "prims/forte.hpp" #include "prims/jvmtiExport.hpp" diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index 8e7e5772ba6..50115f842e2 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -46,8 +46,8 @@ #include "oops/cpCache.inline.hpp" #include "oops/instanceKlass.inline.hpp" #include "oops/klass.inline.hpp" -#include "oops/methodData.hpp" #include "oops/method.inline.hpp" +#include "oops/methodData.hpp" #include "oops/objArrayKlass.hpp" #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" diff --git a/src/hotspot/share/interpreter/templateInterpreter.cpp b/src/hotspot/share/interpreter/templateInterpreter.cpp index 0dd82addbfe..44804221dc7 100644 --- a/src/hotspot/share/interpreter/templateInterpreter.cpp +++ b/src/hotspot/share/interpreter/templateInterpreter.cpp @@ -22,9 +22,9 @@ * */ +#include "interpreter/interp_masm.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" -#include "interpreter/interp_masm.hpp" #include "interpreter/templateInterpreter.hpp" #include "interpreter/templateInterpreterGenerator.hpp" #include "interpreter/templateTable.hpp" diff --git a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp index 9a316f3ba46..96f53c517a3 100644 --- a/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp +++ b/src/hotspot/share/interpreter/templateInterpreterGenerator.cpp @@ -23,9 +23,9 @@ */ #include "compiler/disassembler.hpp" +#include "interpreter/interp_masm.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" -#include "interpreter/interp_masm.hpp" #include "interpreter/templateInterpreter.hpp" #include "interpreter/templateInterpreterGenerator.hpp" #include "interpreter/templateTable.hpp" diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp index 07ec57a6ffe..bfed16f2769 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp @@ -29,9 +29,9 @@ #include "gc/shared/threadLocalAllocBuffer.inline.hpp" #include "gc/shared/tlab_globals.hpp" #include "interpreter/bytecodeHistogram.hpp" -#include "interpreter/zero/bytecodeInterpreter.inline.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" +#include "interpreter/zero/bytecodeInterpreter.inline.hpp" #include "jvm_io.h" #include "logging/log.hpp" #include "memory/resourceArea.hpp" diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.inline.hpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.inline.hpp index 2a396aec9ad..effee05c2f1 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.inline.hpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.inline.hpp @@ -27,6 +27,7 @@ #include "interpreter/zero/bytecodeInterpreter.hpp" +#include "bytecodeInterpreter_zero.inline.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/macros.hpp" @@ -42,6 +43,4 @@ #define VERIFY_OOP(o) #endif -# include "bytecodeInterpreter_zero.inline.hpp" - #endif // SHARE_INTERPRETER_BYTECODEINTERPRETER_INLINE_HPP diff --git a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp index 8c745e51949..f8db4a07e9a 100644 --- a/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp +++ b/src/hotspot/share/interpreter/zero/zeroInterpreterGenerator.hpp @@ -29,7 +29,6 @@ // of the Zero interpreter generator. # include "entry_zero.hpp" -// # include "interpreter/interp_masm.hpp" class ZeroInterpreterGenerator: public AbstractInterpreterGenerator { diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp index 3d87e86e9bd..300786daf05 100644 --- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp +++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp @@ -48,14 +48,6 @@ #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" -const unsigned int initial_size = 431; - -static JfrCHeapTraceIdSet* c_heap_allocate_set(int size = initial_size) { - return new JfrCHeapTraceIdSet(size); -} - -static JfrCHeapTraceIdSet* unloaded_thread_id_set = nullptr; - class ThreadIdExclusiveAccess : public StackObj { private: static Semaphore _mutex_semaphore; @@ -66,19 +58,13 @@ class ThreadIdExclusiveAccess : public StackObj { Semaphore ThreadIdExclusiveAccess::_mutex_semaphore(1); -static bool has_thread_exited(traceid tid) { - assert(tid != 0, "invariant"); - if (unloaded_thread_id_set == nullptr) { - return false; - } - ThreadIdExclusiveAccess lock; - return unloaded_thread_id_set->contains(tid); -} +static const unsigned initial_set_size = 512; +static JfrCHeapTraceIdSet* unloaded_thread_id_set = nullptr; static void add_to_unloaded_thread_set(traceid tid) { ThreadIdExclusiveAccess lock; if (unloaded_thread_id_set == nullptr) { - unloaded_thread_id_set = c_heap_allocate_set(); + unloaded_thread_id_set = new (mtTracing) JfrCHeapTraceIdSet(initial_set_size); } unloaded_thread_id_set->add(tid); } @@ -193,12 +179,6 @@ inline void BlobCache::on_unlink(BlobEntry* entry) const { assert(entry != nullptr, "invariant"); } -static JfrResourceAreaTraceIdSet* id_set = nullptr; - -static void prepare_for_resolution() { - id_set = new JfrResourceAreaTraceIdSet(initial_size); -} - static bool stack_trace_precondition(const ObjectSample* sample) { assert(sample != nullptr, "invariant"); return sample->has_stack_trace_id() && !sample->is_dead(); @@ -213,6 +193,8 @@ static void add_to_leakp_set(const ObjectSample* sample) { JfrTraceId::load_leakp(object->klass()); } +static JfrResourceAreaTraceIdSet* resolution_set = nullptr; + class StackTraceBlobInstaller { private: BlobCache _cache; @@ -220,8 +202,9 @@ class StackTraceBlobInstaller { const JfrStackTrace* resolve(const ObjectSample* sample) const; public: StackTraceBlobInstaller() : _cache(JfrOptionSet::old_object_queue_size()) { - prepare_for_resolution(); + resolution_set = new JfrResourceAreaTraceIdSet(initial_set_size); } + void sample_do(ObjectSample* sample) { if (stack_trace_precondition(sample)) { add_to_leakp_set(sample); @@ -314,8 +297,8 @@ static bool is_klass_unloaded(traceid klass_id) { static bool is_processed(traceid method_id) { assert(method_id != 0, "invariant"); - assert(id_set != nullptr, "invariant"); - return !id_set->add(method_id); + assert(resolution_set != nullptr, "invariant"); + return !resolution_set->add(method_id); } void ObjectSampleCheckpoint::add_to_leakp_set(const InstanceKlass* ik, traceid method_id) { @@ -356,7 +339,7 @@ static void write_type_set_blob(const ObjectSample* sample, JfrCheckpointWriter& static void write_thread_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) { assert(sample->has_thread(), "invariant"); - if (sample->is_virtual_thread() || has_thread_exited(sample->thread_id())) { + if (sample->is_virtual_thread() || sample->thread_exited()) { write_blob(sample->thread(), writer); } } @@ -372,13 +355,13 @@ static inline bool should_write(const JfrStackTrace* stacktrace) { class LeakProfilerStackTraceWriter { private: JfrCheckpointWriter& _writer; - int _count; + unsigned _count; public: LeakProfilerStackTraceWriter(JfrCheckpointWriter& writer) : _writer(writer), _count(0) { assert(_stacktrace_id_set != nullptr, "invariant"); } - int count() const { return _count; } + unsigned count() const { return _count; } void operator()(const JfrStackTrace* stacktrace) { if (should_write(stacktrace)) { @@ -394,12 +377,10 @@ void ObjectSampleCheckpoint::write_stacktraces(Thread* thread) { JfrCheckpointWriter writer(thread); writer.write_type(TYPE_STACKTRACE); - const int64_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet - + writer.write_count(_stacktrace_id_set->size()); LeakProfilerStackTraceWriter lpstw(writer); JfrStackTraceRepository::iterate_leakprofiler(lpstw); assert(lpstw.count() == _stacktrace_id_set->size(), "invariant"); - writer.write_count(lpstw.count(), count_offset); } static void write_stacktrace_blob(const ObjectSample* sample, JfrCheckpointWriter& writer) { @@ -422,6 +403,16 @@ static void write_blobs(const ObjectSample* sample, JfrCheckpointWriter& writer) write_type_set_blob(sample, writer); } +static void check_if_thread_exited(const ObjectSample* sample) { + assert(sample != nullptr, "invariant"); + if (sample->thread_exited() || unloaded_thread_id_set == nullptr) { + return; + } + if (unloaded_thread_id_set->contains(sample->thread_id())) { + sample->set_thread_exited(); + } +} + class BlobWriter { private: const ObjectSampler* _sampler; @@ -431,23 +422,36 @@ class BlobWriter { BlobWriter(const ObjectSampler* sampler, JfrCheckpointWriter& writer, jlong last_sweep) : _sampler(sampler), _writer(writer), _last_sweep(last_sweep) {} void sample_do(ObjectSample* sample) { + check_if_thread_exited(sample); if (sample->is_alive_and_older_than(_last_sweep)) { write_blobs(sample, _writer); } } }; +static void delete_unloaded_thread_id_set() { + if (unloaded_thread_id_set != nullptr) { + delete unloaded_thread_id_set; + unloaded_thread_id_set = nullptr; + } +} + static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) { // sample set is predicated on time of last sweep const jlong last_sweep = emit_all ? max_jlong : ObjectSampler::last_sweep(); JfrCheckpointWriter writer(thread, false); BlobWriter cbw(sampler, writer, last_sweep); + ThreadIdExclusiveAccess lock; iterate_samples(cbw, true); + delete_unloaded_thread_id_set(); } -static inline unsigned int set_size() { - const unsigned int queue_size = static_cast(JfrOptionSet::old_object_queue_size()); - return queue_size > initial_size ? queue_size : initial_size; +static inline unsigned stacktrace_id_set_size() { + unsigned queue_size = static_cast(JfrOptionSet::old_object_queue_size()); + if (!is_power_of_2(queue_size)) { + queue_size = next_power_of_2(queue_size); + } + return queue_size > initial_set_size ? queue_size : initial_set_size; } void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) { @@ -456,7 +460,9 @@ void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge assert(thread != nullptr, "invariant"); { ResourceMark rm(thread); - _stacktrace_id_set = new JfrResourceAreaTraceIdSet(set_size()); + const unsigned stacktrace_set_size = stacktrace_id_set_size(); + assert(is_power_of_2(stacktrace_set_size), "invariant"); + _stacktrace_id_set = new JfrResourceAreaTraceIdSet(stacktrace_set_size); write_sample_blobs(sampler, emit_all, thread); if (_stacktrace_id_set->is_nonempty()) { write_stacktraces(thread); diff --git a/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp b/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp index 214de827d03..66ed9145c81 100644 --- a/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp +++ b/src/hotspot/share/jfr/leakprofiler/sampling/objectSample.hpp @@ -59,6 +59,7 @@ class ObjectSample : public JfrCHeapObj { size_t _heap_used_at_last_gc; int _index; bool _virtual_thread; + mutable bool _thread_exited; void release_references() { _stacktrace.~JfrBlobHandle(); @@ -82,7 +83,8 @@ class ObjectSample : public JfrCHeapObj { _allocated(0), _heap_used_at_last_gc(0), _index(0), - _virtual_thread(false) {} + _virtual_thread(false), + _thread_exited(false) {} ObjectSample* next() const { return _next; @@ -225,6 +227,15 @@ class ObjectSample : public JfrCHeapObj { _virtual_thread = true; } + bool thread_exited() const { + return _thread_exited; + } + + void set_thread_exited() const { + assert(!_thread_exited, "invariant"); + _thread_exited = true; + } + const JfrBlobHandle& type_set() const { return _type_set; } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp index bf27fa59031..20e9c1e6798 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.cpp @@ -64,11 +64,11 @@ static void write_metadata_blob(JfrChunkWriter& chunkwriter, JavaThread* thread) chunkwriter.write_unbuffered(data_address, length); } -void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) { +size_t JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) { assert(chunkwriter.is_valid(), "invariant"); check_internal_types(); if (last_metadata_id == metadata_id && chunkwriter.has_metadata()) { - return; + return 0; } JavaThread* const jt = JavaThread::current(); DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); @@ -87,6 +87,7 @@ void JfrMetadataEvent::write(JfrChunkWriter& chunkwriter) { chunkwriter.write_padded_at_offset((u4)size_written, metadata_offset); chunkwriter.set_last_metadata_offset(metadata_offset); last_metadata_id = metadata_id; + return 1; } void JfrMetadataEvent::update(jbyteArray metadata) { diff --git a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp index abadbfb0b13..1b5bd45c946 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrMetadataEvent.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ class JfrChunkWriter; // class JfrMetadataEvent : AllStatic { public: - static void write(JfrChunkWriter& writer); + static size_t write(JfrChunkWriter& writer); static void update(jbyteArray metadata); }; diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp index 69f002138ec..375ab4d04e9 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp @@ -1048,19 +1048,15 @@ class MethodIteratorHost { private: MethodCallback _method_cb; KlassCallback _klass_cb; - KlassUsedPredicate _klass_used_predicate; - MethodUsedPredicate _method_used_predicate; MethodFlagPredicate _method_flag_predicate; public: MethodIteratorHost(JfrCheckpointWriter* writer) : _method_cb(writer, unloading(), false), _klass_cb(writer, unloading(), false), - _klass_used_predicate(current_epoch()), - _method_used_predicate(current_epoch()), _method_flag_predicate(current_epoch()) {} bool operator()(KlassPtr klass) { - if (_method_used_predicate(klass)) { + if (klass->is_instance_klass()) { const InstanceKlass* ik = InstanceKlass::cast(klass); while (ik != nullptr) { const int len = ik->methods()->length(); @@ -1075,7 +1071,7 @@ class MethodIteratorHost { ik = ik->previous_versions(); } } - return _klass_used_predicate(klass) ? _klass_cb(klass) : true; + return _klass_cb(klass); } int count() const { return _method_cb.count(); } @@ -1280,10 +1276,11 @@ static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer _class_unload = class_unload; _flushpoint = flushpoint; if (_artifacts == nullptr) { - _artifacts = new JfrArtifactSet(class_unload); + _artifacts = new JfrArtifactSet(class_unload, previous_epoch()); } else { - _artifacts->initialize(class_unload); + _artifacts->initialize(class_unload, previous_epoch()); } + assert(current_epoch() || _leakp_writer != nullptr, "invariant"); assert(_artifacts != nullptr, "invariant"); assert(!_artifacts->has_klass_entries(), "invariant"); } diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp index d213ecd7d75..c60556927ad 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp @@ -29,18 +29,23 @@ #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" -JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_table(nullptr), - _klass_list(nullptr), - _total_count(0), - _class_unload(class_unload) { - initialize(class_unload); - assert(_klass_list != nullptr, "invariant"); +JfrArtifactSet::JfrArtifactSet(bool class_unload, bool previous_epoch) : _symbol_table(nullptr), + _klass_set(nullptr), + _klass_loader_set(nullptr), + _klass_loader_leakp_set(nullptr), + _total_count(0), + _class_unload(class_unload) { + initialize(class_unload, previous_epoch); + assert(!previous_epoch || _klass_loader_leakp_set != nullptr, "invariant"); + assert(_klass_loader_set != nullptr, "invariant"); + assert(_klass_set != nullptr, "invariant"); } -static const size_t initial_klass_list_size = 4096; -const int initial_klass_loader_set_size = 64; +static unsigned initial_klass_set_size = 4096; +static unsigned initial_klass_loader_set_size = 64; +static unsigned initial_klass_loader_leakp_set_size = 64; -void JfrArtifactSet::initialize(bool class_unload) { +void JfrArtifactSet::initialize(bool class_unload, bool previous_epoch) { _class_unload = class_unload; if (_symbol_table == nullptr) { _symbol_table = JfrSymbolTable::create(); @@ -50,9 +55,11 @@ void JfrArtifactSet::initialize(bool class_unload) { _symbol_table->set_class_unload(class_unload); _total_count = 0; // Resource allocations. Keep in this allocation order. - _klass_loader_leakp_set = new GrowableArray(initial_klass_loader_set_size); - _klass_loader_set = new GrowableArray(initial_klass_loader_set_size); - _klass_list = new GrowableArray(initial_klass_list_size); + if (previous_epoch) { + _klass_loader_leakp_set = new JfrKlassSet(initial_klass_loader_leakp_set_size); + } + _klass_loader_set = new JfrKlassSet(initial_klass_loader_set_size); + _klass_set = new JfrKlassSet(initial_klass_set_size); } void JfrArtifactSet::clear() { @@ -93,17 +100,12 @@ traceid JfrArtifactSet::mark(uintptr_t hash, const char* const str, bool leakp) } bool JfrArtifactSet::has_klass_entries() const { - return _klass_list->is_nonempty(); + return _klass_set->is_nonempty(); } - -int JfrArtifactSet::entries() const { - return _klass_list->length(); -} - -static inline bool not_in_set(GrowableArray* set, const Klass* k) { +static inline bool not_in_set(JfrArtifactSet::JfrKlassSet* set, const Klass* k) { assert(set != nullptr, "invariant"); assert(k != nullptr, "invariant"); - return !JfrMutablePredicate::test(set, k); + return set->add(k); } bool JfrArtifactSet::should_do_cld_klass(const Klass* k, bool leakp) { @@ -116,16 +118,21 @@ bool JfrArtifactSet::should_do_cld_klass(const Klass* k, bool leakp) { void JfrArtifactSet::register_klass(const Klass* k) { assert(k != nullptr, "invariant"); assert(IS_SERIALIZED(k), "invariant"); - assert(_klass_list != nullptr, "invariant"); - _klass_list->append(k); + assert(_klass_set != nullptr, "invariant"); + _klass_set->add(k); } size_t JfrArtifactSet::total_count() const { + assert(_klass_set != nullptr, "invariant"); + initial_klass_set_size = MAX2(initial_klass_set_size, _klass_set->table_size()); + assert(_klass_loader_set != nullptr, "invariant"); + initial_klass_loader_set_size = MAX2(initial_klass_loader_set_size, _klass_loader_set->table_size()); return _total_count; } void JfrArtifactSet::increment_checkpoint_id() { assert(_symbol_table != nullptr, "invariant"); _symbol_table->increment_checkpoint_id(); + assert(_klass_loader_leakp_set != nullptr, "invariant"); + initial_klass_loader_leakp_set_size = MAX2(initial_klass_loader_leakp_set_size, _klass_loader_leakp_set->table_size()); } - diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp index 657aee9dc53..74200aef1f1 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp @@ -28,12 +28,10 @@ #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" #include "jfr/support/jfrSymbolTable.hpp" #include "jfr/utilities/jfrAllocation.hpp" +#include "jfr/utilities/jfrSet.hpp" #include "oops/klass.hpp" #include "oops/method.hpp" -template -class GrowableArray; - // Composite callback/functor building block template class CompositeFunctor { @@ -135,27 +133,6 @@ class SymbolPredicate { } }; -class KlassUsedPredicate { - bool _current_epoch; - public: - KlassUsedPredicate(bool current_epoch) : _current_epoch(current_epoch) {} - bool operator()(const Klass* klass) { - return _current_epoch ? USED_THIS_EPOCH(klass) : USED_PREVIOUS_EPOCH(klass); - } -}; - -class MethodUsedPredicate { - bool _current_epoch; -public: - MethodUsedPredicate(bool current_epoch) : _current_epoch(current_epoch) {} - bool operator()(const Klass* klass) { - if (!klass->is_instance_klass()) { - return false; - } - return _current_epoch ? USED_THIS_EPOCH(klass) : USED_PREVIOUS_EPOCH(klass); - } -}; - template class MethodFlagPredicate { bool _current_epoch; @@ -203,20 +180,46 @@ class LeakPredicate { * in the respective VM subsystems. */ class JfrArtifactSet : public JfrCHeapObj { + public: + class JfrArtifactSetConfig : public AllStatic { + public: + typedef const Klass* KEY_TYPE; + + constexpr static AnyObj::allocation_type alloc_type() { + return AnyObj::RESOURCE_AREA; + } + + constexpr static MemTag memory_tag() { + return mtInternal; + } + + // Knuth multiplicative hashing. + static uint32_t hash(const KEY_TYPE& k) { + const uint32_t v = static_cast(JfrTraceId::load_raw(k)); + return v * UINT32_C(2654435761); + } + + static bool cmp(const KEY_TYPE& lhs, const KEY_TYPE& rhs) { + return lhs == rhs; + } + }; + + typedef JfrSet JfrKlassSet; + private: JfrSymbolTable* _symbol_table; - GrowableArray* _klass_list; - GrowableArray* _klass_loader_set; - GrowableArray* _klass_loader_leakp_set; + JfrKlassSet* _klass_set; + JfrKlassSet* _klass_loader_set; + JfrKlassSet* _klass_loader_leakp_set; size_t _total_count; bool _class_unload; public: - JfrArtifactSet(bool class_unload); + JfrArtifactSet(bool class_unload, bool previous_epoch); ~JfrArtifactSet(); // caller needs ResourceMark - void initialize(bool class_unload); + void initialize(bool class_unload, bool previous_epoch); void clear(); traceid mark(uintptr_t hash, const Symbol* sym, bool leakp); @@ -231,7 +234,6 @@ class JfrArtifactSet : public JfrCHeapObj { const JfrSymbolTable::StringEntry* map_string(uintptr_t hash) const; bool has_klass_entries() const; - int entries() const; size_t total_count() const; void register_klass(const Klass* k); bool should_do_cld_klass(const Klass* k, bool leakp); @@ -254,19 +256,17 @@ class JfrArtifactSet : public JfrCHeapObj { template void iterate_klasses(Functor& functor) const { - if (iterate(functor, _klass_list)) { + if (iterate(functor, _klass_set)) { iterate(functor, _klass_loader_set); } } private: template - bool iterate(Functor& functor, GrowableArray* list) const { - assert(list != nullptr, "invariant"); - for (int i = 0; i < list->length(); ++i) { - if (!functor(list->at(i))) { - return false; - } + bool iterate(Functor& functor, JfrKlassSet* set) const { + assert(set != nullptr, "invariant"); + if (set->is_nonempty()) { + set->iterate(functor); } return true; } diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp index f0170bac460..7784148aee5 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp @@ -367,13 +367,14 @@ static u4 flush_typeset(JfrCheckpointManager& checkpoint_manager, JfrChunkWriter class MetadataEvent : public StackObj { private: JfrChunkWriter& _cw; + size_t _elements; public: - MetadataEvent(JfrChunkWriter& cw) : _cw(cw) {} + MetadataEvent(JfrChunkWriter& cw) : _cw(cw), _elements(0) {} bool process() { - JfrMetadataEvent::write(_cw); + _elements = JfrMetadataEvent::write(_cw); return true; } - size_t elements() const { return 1; } + size_t elements() const { return _elements; } }; typedef WriteContent WriteMetadata; diff --git a/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp b/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp index ce5de54ed16..d136eeab53a 100644 --- a/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp +++ b/src/hotspot/share/jfr/support/jfrKlassUnloading.cpp @@ -30,10 +30,10 @@ #include "runtime/mutexLocker.hpp" #include "utilities/macros.hpp" -static const int initial_size = 1009; +static const int initial_size = 1024; static JfrCHeapTraceIdSet* c_heap_allocate_set(int size = initial_size) { - return new JfrCHeapTraceIdSet(size); + return new (mtTracing) JfrCHeapTraceIdSet(size); } // Track the set of unloaded klasses during a chunk / epoch. @@ -68,18 +68,9 @@ static JfrCHeapTraceIdSet* get_unload_set_previous_epoch() { return get_unload_set(JfrTraceIdEpoch::previous()); } -static bool is_nonempty_set(u1 epoch) { - if (epoch == 0) { - return _unload_set_epoch_0 != nullptr && _unload_set_epoch_0->is_nonempty(); - } - return _unload_set_epoch_1 != nullptr && _unload_set_epoch_1->is_nonempty(); -} - void JfrKlassUnloading::clear() { assert_locked_or_safepoint(ClassLoaderDataGraph_lock); - if (is_nonempty_set(JfrTraceIdEpoch::previous())) { - get_unload_set_previous_epoch()->clear(); - } + get_unload_set_previous_epoch()->clear(); } static void add_to_unloaded_klass_set(traceid klass_id) { diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp index 37f2d438bc6..dc2908e8a81 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.cpp @@ -33,7 +33,7 @@ #include "oops/instanceKlass.inline.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/growableArray.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" constexpr static unsigned int TABLE_SIZE = 1009; constexpr static unsigned int MAX_TABLE_SIZE = 0x3fffffff; diff --git a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.hpp b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.hpp index a23b27eb12a..6cf1b321f1e 100644 --- a/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.hpp +++ b/src/hotspot/share/jfr/support/methodtracer/jfrClassFilterClosure.hpp @@ -40,7 +40,7 @@ template class GrowableArray; template class ResizeableResourceHashtable; + bool (*EQUALS)(K const&, K const&)> class ResizeableHashTable; // Knuth multiplicative hashing. inline uint32_t knuth_hash(const traceid& id) { @@ -48,7 +48,7 @@ inline uint32_t knuth_hash(const traceid& id) { return v * UINT32_C(2654435761); } -typedef ResizeableResourceHashtable* JfrMethodTracer::_instrumented_classes = nullptr; diff --git a/src/hotspot/share/jfr/utilities/jfrSet.hpp b/src/hotspot/share/jfr/utilities/jfrSet.hpp index b4dfc4f6240..b6458d3f9a4 100644 --- a/src/hotspot/share/jfr/utilities/jfrSet.hpp +++ b/src/hotspot/share/jfr/utilities/jfrSet.hpp @@ -25,15 +25,13 @@ #ifndef SHARE_JFR_UTILITIES_JFRSET_HPP #define SHARE_JFR_UTILITIES_JFRSET_HPP -#include "jfr/utilities/jfrAllocation.hpp" +#include "memory/allocation.hpp" #include "jfr/utilities/jfrTypes.hpp" -#include "utilities/resizeableResourceHash.hpp" -template -class ConfigTraceID : public AllStatic { +template +class JfrSetConfig : public AllStatic { public: - typedef AllocPolicy STORAGE; - typedef traceid TYPE; + typedef K KEY_TYPE; constexpr static AnyObj::allocation_type alloc_type() { return AllocType; @@ -44,80 +42,171 @@ class ConfigTraceID : public AllStatic { } // Knuth multiplicative hashing. - static uint32_t hash(const TYPE& id) { - const uint32_t v = static_cast(id); - return v * UINT32_C(2654435761); + static uint32_t hash(const KEY_TYPE& key) { + const uint32_t k = static_cast(key); + return k * UINT32_C(2654435761); } - static bool cmp(const TYPE& lhs, const TYPE& rhs) { + static bool cmp(const KEY_TYPE& lhs, const KEY_TYPE& rhs) { return lhs == rhs; } }; -constexpr static unsigned int MAX_TABLE_SIZE = 0x3fffffff; - template -class JfrSet : public CONFIG::STORAGE { - public: - typedef typename CONFIG::TYPE TYPE; - typedef ResizeableResourceHashtable HashMap; +class JfrSetStorage : public AnyObj { + typedef typename CONFIG::KEY_TYPE K; + protected: + K* _table; + unsigned _table_size; + unsigned _elements; - constexpr static bool is_cheap() { - return CONFIG::alloc_type() == AnyObj::C_HEAP; + static K* alloc_table(unsigned table_size) { + K* table; + if (CONFIG::alloc_type() == C_HEAP) { + table = NEW_C_HEAP_ARRAY(K, table_size, CONFIG::memory_tag()); + } else { + table = NEW_RESOURCE_ARRAY(K, table_size); + } + memset(table, 0, table_size * sizeof(K)); + return table; } - JfrSet(unsigned int initial_size, unsigned int max_size = MAX_TABLE_SIZE) : - _map(is_cheap() ? new (CONFIG::memory_tag()) HashMap(initial_size, max_size) : new HashMap(initial_size, max_size)) {} + JfrSetStorage(unsigned table_size) : + _table(alloc_table(table_size)), + _table_size(table_size), + _elements(0) {} - ~JfrSet() { - if (is_cheap()) { - delete _map; + ~JfrSetStorage() { + if (CONFIG::alloc_type() == C_HEAP) { + FREE_C_HEAP_ARRAY(K, _table); } } - bool add(const TYPE& k) { - bool inserted; - _map->put_if_absent(k, &inserted); - return inserted; + public: + template + void iterate(Functor& functor) { + assert(is_nonempty(), "invariant"); + for (unsigned i = 0; i < _table_size; ++i) { + K k = _table[i]; + if (k != 0) { + functor(k); + } + } } - bool remove(const TYPE& k) { - return _map->remove(k); + unsigned table_size() const { + return _table_size; } - bool contains(const TYPE& k) const { - return _map->contains(k); - } - - bool is_empty() const { - return _map->number_of_entries() == 0; + unsigned size() const { + return _elements; } bool is_nonempty() const { - return !is_empty(); - } - - int size() const { - return _map->number_of_entries(); + return _elements > 0; } void clear() { - if (is_nonempty()) { - _map->unlink(this); - } - assert(is_empty(), "invariant"); + memset(_table, 0, _table_size * sizeof(K)); } - - // Callback for node deletion, used by clear(). - bool do_entry(const TYPE& k, const TYPE& v) { - return true; - } - - private: - HashMap* _map; }; -typedef JfrSet > JfrCHeapTraceIdSet; -typedef JfrSet > JfrResourceAreaTraceIdSet; +template +class JfrSet : public JfrSetStorage { + typedef typename CONFIG::KEY_TYPE K; + static_assert(sizeof(K) > 1, "invalid size of CONFIG::KEY_TYPE"); + private: + static const constexpr unsigned max_initial_size = 1 << 30; + unsigned _table_mask; + unsigned _resize_threshold; // 0.5 load factor + + uint32_t slot_idx(const uint32_t hash) const { + return hash & _table_mask; + } + + void resize() { + assert(this->_elements == _resize_threshold, "invariant"); + K* const old_table = this->_table; + assert(old_table != nullptr, "invariant"); + const unsigned old_table_size = this->table_size(); + guarantee(old_table_size <= max_initial_size, "overflow"); + this->_table_size = old_table_size << 1; + this->_table = JfrSetStorage::alloc_table(this->_table_size); + _table_mask = this->_table_size - 1; + _resize_threshold = old_table_size; + for (unsigned i = 0; i < old_table_size; ++i) { + const K k = old_table[i]; + if (k != 0) { + uint32_t idx = slot_idx(CONFIG::hash(k)); + do { + K v = this->_table[idx]; + if (v == 0) { + this->_table[idx] = k; + break; + } + idx = slot_idx(idx + 1); + } while (true); + } + } + if (CONFIG::alloc_type() == AnyObj::C_HEAP) { + FREE_C_HEAP_ARRAY(K, old_table); + } + assert(_table_mask + 1 == this->_table_size, "invariant"); + assert(_resize_threshold << 1 == this->_table_size, "invariant"); + } + + K* find_slot(K const& k) const { + uint32_t idx = slot_idx(CONFIG::hash(k)); + assert(idx < this->table_size(), "invariant"); + K* result = nullptr; + while (true) { + K v = this->_table[idx]; + if (v == 0) { + result = &this->_table[idx]; + break; + } + if (CONFIG::cmp(v, k)) { + result = reinterpret_cast(p2i(&this->_table[idx]) | 1); + break; + } + idx = slot_idx(idx + 1); + } + assert(result != nullptr, "invariant"); + return result; + } + + public: + JfrSet(unsigned size) : + JfrSetStorage(size), + _table_mask(size - 1), + _resize_threshold(size >> 1) { + assert(size >= 2, "invariant"); + assert(size % 2 == 0, "invariant"); + assert(size <= max_initial_size, "avoid overflow in resize"); + } + + bool contains(K const& k) const { + K* const slot = find_slot(k); + return p2i(slot) & 1; + } + + bool add(K const& k) { + K* const slot = find_slot(k); + if (p2i(slot) & 1) { + // Already exists. + return false; + } + assert(*slot == 0, "invariant"); + *slot = k; + if (++this->_elements == _resize_threshold) { + resize(); + } + assert(this->_elements < _resize_threshold, "invariant"); + return true; + } +}; + +typedef JfrSet > JfrCHeapTraceIdSet; +typedef JfrSet > JfrResourceAreaTraceIdSet; #endif // SHARE_JFR_UTILITIES_JFRSET_HPP diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index 753d673f54c..801a9bf8eb4 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -51,7 +51,7 @@ #include "runtime/flags/jvmFlag.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" #if INCLUDE_SHENANDOAHGC #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" @@ -437,8 +437,8 @@ JVMCIObjectArray CompilerToVM::initialize_intrinsics(JVMCI_TRAPS) { jobjectArray readConfiguration0(JNIEnv *env, JVMCI_TRAPS) { JavaThread* THREAD = JavaThread::current(); // For exception macros. - ResourceHashtable longs; - ResourceHashtable longs; + HashTable strings; diff --git a/src/hotspot/share/logging/logAsyncWriter.hpp b/src/hotspot/share/logging/logAsyncWriter.hpp index 5ffd9dc7b33..a93b1ca58f6 100644 --- a/src/hotspot/share/logging/logAsyncWriter.hpp +++ b/src/hotspot/share/logging/logAsyncWriter.hpp @@ -29,10 +29,10 @@ #include "logging/logMessageBuffer.hpp" #include "memory/allocation.hpp" #include "runtime/mutex.hpp" -#include "runtime/os.inline.hpp" #include "runtime/nonJavaThread.hpp" +#include "runtime/os.inline.hpp" #include "runtime/semaphore.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" class LogFileStreamOutput; @@ -66,7 +66,7 @@ class AsyncLogWriter : public NonJavaThread { // account for dropped messages template - using AsyncLogMap = ResourceHashtable; diff --git a/src/hotspot/share/memory/metaspaceClosure.hpp b/src/hotspot/share/memory/metaspaceClosure.hpp index 8b419034093..c35363eb71f 100644 --- a/src/hotspot/share/memory/metaspaceClosure.hpp +++ b/src/hotspot/share/memory/metaspaceClosure.hpp @@ -33,7 +33,7 @@ #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" #include "utilities/macros.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" #include // The metadata hierarchy is separate from the oop hierarchy @@ -374,7 +374,7 @@ public: UniqueMetaspaceClosure() : _has_been_visited(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {} private: - ResizeableResourceHashtable _has_been_visited; }; diff --git a/src/hotspot/share/memory/oopFactory.cpp b/src/hotspot/share/memory/oopFactory.cpp index b2a468d3a23..e0b94a8caf8 100644 --- a/src/hotspot/share/memory/oopFactory.cpp +++ b/src/hotspot/share/memory/oopFactory.cpp @@ -73,7 +73,6 @@ typeArrayOop oopFactory::new_longArray(int length, TRAPS) { // create java.lang.Object[] objArrayOop oopFactory::new_objectArray(int length, TRAPS) { - assert(Universe::objectArrayKlass() != nullptr, "Too early?"); return Universe::objectArrayKlass()->allocate_instance(length, THREAD); } diff --git a/src/hotspot/share/memory/universe.hpp b/src/hotspot/share/memory/universe.hpp index 35c31330f08..5d481e889c4 100644 --- a/src/hotspot/share/memory/universe.hpp +++ b/src/hotspot/share/memory/universe.hpp @@ -185,15 +185,24 @@ class Universe: AllStatic { static TypeArrayKlass* floatArrayKlass() { return typeArrayKlass(T_FLOAT); } static TypeArrayKlass* doubleArrayKlass() { return typeArrayKlass(T_DOUBLE); } - static ObjArrayKlass* objectArrayKlass() { return _objectArrayKlass; } + static ObjArrayKlass* objectArrayKlass() { + ObjArrayKlass* k = _objectArrayKlass; + assert(k != nullptr, "Object array klass should be initialized; too early?"); + return k; + } - static Klass* fillerArrayKlass() { return _fillerArrayKlass; } + static Klass* fillerArrayKlass() { + Klass* k = _fillerArrayKlass; + assert(k != nullptr, "Filler array class should be initialized; too early?"); + return k; + } static TypeArrayKlass* typeArrayKlass(BasicType t) { assert((uint)t >= T_BOOLEAN, "range check for type: %s", type2name(t)); assert((uint)t < T_LONG+1, "range check for type: %s", type2name(t)); - assert(_typeArrayKlasses[t] != nullptr, "domain check"); - return _typeArrayKlasses[t]; + TypeArrayKlass* k = _typeArrayKlasses[t]; + assert(k != nullptr, "Type array class should be initialized; too early?"); + return k; } // Known objects in the VM diff --git a/src/hotspot/share/nmt/mallocLimit.cpp b/src/hotspot/share/nmt/mallocLimit.cpp index ed479725cf9..2bce2e459e0 100644 --- a/src/hotspot/share/nmt/mallocLimit.cpp +++ b/src/hotspot/share/nmt/mallocLimit.cpp @@ -61,7 +61,7 @@ public: // Check if string at position matches a malloclimit_mode_t. // Advance position on match. - bool match_mode_flag(MallocLimitMode* out) { + bool match_mode(MallocLimitMode* out) { if (eof()) { return false; } @@ -77,9 +77,9 @@ public: return false; } - // Check if string at position matches a category name. + // Check if string at position matches a MemTag name. // Advances position on match. - bool match_category(MemTag* out) { + bool match_mem_tag(MemTag* out) { if (eof()) { return false; } @@ -130,9 +130,9 @@ void MallocLimitSet::set_global_limit(size_t s, MallocLimitMode flag) { _glob.sz = s; _glob.mode = flag; } -void MallocLimitSet::set_category_limit(MemTag mem_tag, size_t s, MallocLimitMode flag) { +void MallocLimitSet::set_category_limit(MemTag mem_tag, size_t s, MallocLimitMode mode) { const int i = NMTUtil::tag_to_index(mem_tag); - _cat[i].sz = s; _cat[i].mode = flag; + _mtag[i].sz = s; _mtag[i].mode = mode; } void MallocLimitSet::reset() { @@ -144,16 +144,15 @@ void MallocLimitSet::reset() { } void MallocLimitSet::print_on(outputStream* st) const { - static const char* flagnames[] = { MODE_FATAL, MODE_OOM }; if (_glob.sz > 0) { st->print_cr("MallocLimit: total limit: " PROPERFMT " (%s)", PROPERFMTARGS(_glob.sz), mode_to_name(_glob.mode)); } else { for (int i = 0; i < mt_number_of_tags; i++) { - if (_cat[i].sz > 0) { + if (_mtag[i].sz > 0) { st->print_cr("MallocLimit: category \"%s\" limit: " PROPERFMT " (%s)", NMTUtil::tag_to_enum_name(NMTUtil::index_to_tag(i)), - PROPERFMTARGS(_cat[i].sz), mode_to_name(_cat[i].mode)); + PROPERFMTARGS(_mtag[i].sz), mode_to_name(_mtag[i].mode)); } } } @@ -164,10 +163,10 @@ bool MallocLimitSet::parse_malloclimit_option(const char* v, const char** err) { #define BAIL_UNLESS(condition, errormessage) if (!(condition)) { *err = errormessage; return false; } // Global form: - // MallocLimit=[:flag] + // MallocLimit=[:mode] - // Category-specific form: - // MallocLimit=:[:flag][,:[:flag]...] + // MemTag-specific form: + // MallocLimit=:[:mode][,:[:mode]...] reset(); @@ -177,29 +176,29 @@ bool MallocLimitSet::parse_malloclimit_option(const char* v, const char** err) { // Global form? if (sst.match_size(&_glob.sz)) { - // Match optional mode flag (e.g. 1g:oom) + // Match optional mode (e.g. 1g:oom) if (!sst.eof()) { BAIL_UNLESS(sst.match_char(':'), "Expected colon"); - BAIL_UNLESS(sst.match_mode_flag(&_glob.mode), "Expected flag"); + BAIL_UNLESS(sst.match_mode(&_glob.mode), "Expected mode"); } } - // Category-specific form? + // MemTag-specific form? else { while (!sst.eof()) { MemTag mem_tag; - // Match category, followed by : - BAIL_UNLESS(sst.match_category(&mem_tag), "Expected category name"); + // Match MemTag, followed by : + BAIL_UNLESS(sst.match_mem_tag(&mem_tag), "Expected category name"); BAIL_UNLESS(sst.match_char(':'), "Expected colon following category"); - malloclimit* const modified_limit = &_cat[NMTUtil::tag_to_index(mem_tag)]; + malloclimit* const modified_limit = &_mtag[NMTUtil::tag_to_index(mem_tag)]; // Match size BAIL_UNLESS(sst.match_size(&modified_limit->sz), "Expected size"); - // Match optional flag + // Match optional mode if (!sst.eof() && sst.match_char(':')) { - BAIL_UNLESS(sst.match_mode_flag(&modified_limit->mode), "Expected flag"); + BAIL_UNLESS(sst.match_mode(&modified_limit->mode), "Expected mode"); } // More to come? diff --git a/src/hotspot/share/nmt/mallocLimit.hpp b/src/hotspot/share/nmt/mallocLimit.hpp index ec6799b41a3..d41eaabea46 100644 --- a/src/hotspot/share/nmt/mallocLimit.hpp +++ b/src/hotspot/share/nmt/mallocLimit.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2023 SAP SE. All rights reserved. - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,7 +46,7 @@ class outputStream; class MallocLimitSet { malloclimit _glob; // global limit - malloclimit _cat[mt_number_of_tags]; // per-category limit + malloclimit _mtag[mt_number_of_tags]; // per-memtag limit public: MallocLimitSet(); @@ -57,7 +57,7 @@ public: void set_category_limit(MemTag mem_tag, size_t s, MallocLimitMode mode); const malloclimit* global_limit() const { return &_glob; } - const malloclimit* category_limit(MemTag mem_tag) const { return &_cat[(int)mem_tag]; } + const malloclimit* mem_tag_limit(MemTag mem_tag) const { return &_mtag[(int)mem_tag]; } void print_on(outputStream* st) const; }; @@ -69,7 +69,7 @@ class MallocLimitHandler : public AllStatic { public: static const malloclimit* global_limit() { return _limits.global_limit(); } - static const malloclimit* category_limit(MemTag mem_tag) { return _limits.category_limit(mem_tag); } + static const malloclimit* mem_tag_limit(MemTag mem_tag) { return _limits.mem_tag_limit(mem_tag); } static void initialize(const char* options); static void print_on(outputStream* st); diff --git a/src/hotspot/share/nmt/mallocTracker.cpp b/src/hotspot/share/nmt/mallocTracker.cpp index 6a2da5f79cd..d919f3ce873 100644 --- a/src/hotspot/share/nmt/mallocTracker.cpp +++ b/src/hotspot/share/nmt/mallocTracker.cpp @@ -207,6 +207,12 @@ void* MallocTracker::record_free_block(void* memblock) { deaccount(header->free_info()); + if (ZapCHeap) { + // To do this zapping, we need to know the block size. + // This is why we have to do it here, and not in os::free. + memset(memblock, freeBlockPad, header->size()); + } + header->mark_block_as_dead(); return (void*)header; diff --git a/src/hotspot/share/nmt/mallocTracker.inline.hpp b/src/hotspot/share/nmt/mallocTracker.inline.hpp index b2aec345eff..51a7f28cf99 100644 --- a/src/hotspot/share/nmt/mallocTracker.inline.hpp +++ b/src/hotspot/share/nmt/mallocTracker.inline.hpp @@ -49,8 +49,8 @@ inline bool MallocMemorySummary::check_exceeds_limit(size_t s, MemTag mem_tag) { return total_limit_reached(s, so_far, l); } } else { - // Category Limit? - l = MallocLimitHandler::category_limit(mem_tag); + // MemTag Limit? + l = MallocLimitHandler::mem_tag_limit(mem_tag); if (l->sz > 0) { const MallocMemory* mm = as_snapshot()->by_tag(mem_tag); size_t so_far = mm->malloc_size() + mm->arena_size(); diff --git a/src/hotspot/share/nmt/nativeCallStackPrinter.hpp b/src/hotspot/share/nmt/nativeCallStackPrinter.hpp index 78fd541fc98..12bd1176485 100644 --- a/src/hotspot/share/nmt/nativeCallStackPrinter.hpp +++ b/src/hotspot/share/nmt/nativeCallStackPrinter.hpp @@ -29,7 +29,7 @@ #include "memory/arena.hpp" #include "nmt/memTag.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" class outputStream; class NativeCallStack; @@ -41,7 +41,7 @@ class NativeCallStackPrinter { // Cache-related data are mutable to be able to use NativeCallStackPrinter as // inline member in classes with const printing methods. mutable Arena _text_storage; - mutable ResourceHashtable _cache; + mutable HashTable _cache; outputStream* const _out; public: NativeCallStackPrinter(outputStream* out); diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp index be4a7a474d4..8999acf4d3a 100644 --- a/src/hotspot/share/oops/constantPool.hpp +++ b/src/hotspot/share/oops/constantPool.hpp @@ -37,8 +37,8 @@ #include "utilities/align.hpp" #include "utilities/bytes.hpp" #include "utilities/constantTag.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" -#include "utilities/resourceHash.hpp" // A ConstantPool is an array containing class constants as described in the // class file. @@ -872,7 +872,7 @@ private: private: class SymbolHash: public CHeapObj { - ResourceHashtable _table; + HashTable _table; public: void add_if_absent(const Symbol* sym, u2 value) { diff --git a/src/hotspot/share/oops/cpCache.inline.hpp b/src/hotspot/share/oops/cpCache.inline.hpp index 791f0a1f5ce..8dec47a3c73 100644 --- a/src/hotspot/share/oops/cpCache.inline.hpp +++ b/src/hotspot/share/oops/cpCache.inline.hpp @@ -73,6 +73,9 @@ inline ResolvedIndyEntry* ConstantPoolCache::resolved_indy_entry_at(int index) c } inline int ConstantPoolCache::resolved_indy_entries_length() const { + if (_resolved_indy_entries == nullptr) { + return 0; + } return _resolved_indy_entries->length(); } #endif // SHARE_OOPS_CPCACHE_INLINE_HPP diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 9266e8ca091..dcec45ff4e2 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -1116,7 +1116,7 @@ void InstanceKlass::initialize_super_interfaces(TRAPS) { } } -using InitializationErrorTable = ResourceHashtable; +using InitializationErrorTable = HashTable; static InitializationErrorTable* _initialization_error_table; void InstanceKlass::add_initialization_error(JavaThread* current, Handle exception) { diff --git a/src/hotspot/share/oops/trainingData.hpp b/src/hotspot/share/oops/trainingData.hpp index b4fd5fd61ab..baf19773a7b 100644 --- a/src/hotspot/share/oops/trainingData.hpp +++ b/src/hotspot/share/oops/trainingData.hpp @@ -37,7 +37,7 @@ #include "oops/objArrayKlass.hpp" #include "runtime/handles.hpp" #include "runtime/mutexLocker.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" class ciEnv; class ciBaseObject; @@ -173,7 +173,7 @@ public: // A set of TD objects that we collect during the training run. class TrainingDataSet { friend TrainingData; - ResizeableResourceHashtable @@ -229,7 +229,7 @@ public: // A widget to ensure that we visit TD object only once (TD objects can have pointer to // other TD object that are sometimes circular). class Visitor { - ResizeableResourceHashtable _visited; + ResizeableHashTable _visited; public: Visitor(unsigned size) : _visited(size, 0x3fffffff) { } bool is_visited(TrainingData* td) { diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 540b6600a0f..3a2d4cbdf96 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -666,6 +666,9 @@ product(bool, PrintIntrinsics, false, DIAGNOSTIC, \ "prints attempted and successful inlining of intrinsics") \ \ + develop(bool, VerifyIntrinsicChecks, false, \ + "Verify in intrinsic that Java level checks work as expected") \ + \ develop(bool, StressReflectiveCode, false, \ "Use inexact types at allocations, etc., to test reflection") \ \ diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 5a4a6c2667d..cef7aa61219 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -87,8 +87,8 @@ #include "runtime/timer.hpp" #include "utilities/align.hpp" #include "utilities/copy.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" -#include "utilities/resourceHash.hpp" // -------------------- Compile::mach_constant_base_node ----------------------- // Constant table base node singleton. @@ -2786,7 +2786,7 @@ uint Compile::eval_macro_logic_op(uint func, uint in1 , uint in2, uint in3) { return res; } -static uint eval_operand(Node* n, ResourceHashtable& eval_map) { +static uint eval_operand(Node* n, HashTable& eval_map) { assert(n != nullptr, ""); assert(eval_map.contains(n), "absent"); return *(eval_map.get(n)); @@ -2794,7 +2794,7 @@ static uint eval_operand(Node* n, ResourceHashtable& eval_map) { static void eval_operands(Node* n, uint& func1, uint& func2, uint& func3, - ResourceHashtable& eval_map) { + HashTable& eval_map) { assert(is_vector_bitwise_op(n), ""); if (is_vector_unary_bitwise_op(n)) { @@ -2818,7 +2818,7 @@ uint Compile::compute_truth_table(Unique_Node_List& partition, Unique_Node_List& assert(inputs.size() <= 3, "sanity"); ResourceMark rm; uint res = 0; - ResourceHashtable eval_map; + HashTable eval_map; // Populate precomputed functions for inputs. // Each input corresponds to one column of 3 input truth-table. diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 902968ef4d4..d65239ab0f8 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -2309,16 +2309,18 @@ Node* GraphKit::record_profiled_receiver_for_speculation(Node* n) { if (!data->as_BitData()->null_seen()) { ptr_kind = ProfileNeverNull; } else { - assert(data->is_ReceiverTypeData(), "bad profile data type"); - ciReceiverTypeData* call = (ciReceiverTypeData*)data->as_ReceiverTypeData(); - uint i = 0; - for (; i < call->row_limit(); i++) { - ciKlass* receiver = call->receiver(i); - if (receiver != nullptr) { - break; + if (TypeProfileCasts) { + assert(data->is_ReceiverTypeData(), "bad profile data type"); + ciReceiverTypeData* call = (ciReceiverTypeData*)data->as_ReceiverTypeData(); + uint i = 0; + for (; i < call->row_limit(); i++) { + ciKlass* receiver = call->receiver(i); + if (receiver != nullptr) { + break; + } } + ptr_kind = (i == call->row_limit()) ? ProfileAlwaysNull : ProfileMaybeNull; } - ptr_kind = (i == call->row_limit()) ? ProfileAlwaysNull : ProfileMaybeNull; } } } diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 6c5efaafc21..da04d6b01ac 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -939,7 +939,11 @@ inline Node* LibraryCallKit::generate_limit_guard(Node* offset, } // Emit range checks for the given String.value byte array -void LibraryCallKit::generate_string_range_check(Node* array, Node* offset, Node* count, bool char_count) { +void LibraryCallKit::generate_string_range_check(Node* array, + Node* offset, + Node* count, + bool char_count, + bool halt_on_oob) { if (stopped()) { return; // already stopped } @@ -957,10 +961,17 @@ void LibraryCallKit::generate_string_range_check(Node* array, Node* offset, Node generate_limit_guard(offset, count, load_array_length(array), bailout); if (bailout->req() > 1) { - PreserveJVMState pjvms(this); - set_control(_gvn.transform(bailout)); - uncommon_trap(Deoptimization::Reason_intrinsic, - Deoptimization::Action_maybe_recompile); + if (halt_on_oob) { + bailout = _gvn.transform(bailout)->as_Region(); + Node* frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr)); + Node* halt = _gvn.transform(new HaltNode(bailout, frame, "unexpected guard failure in intrinsic")); + C->root()->add_req(halt); + } else { + PreserveJVMState pjvms(this); + set_control(_gvn.transform(bailout)); + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_maybe_recompile); + } } } @@ -1118,6 +1129,7 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) { //------------------------------inline_countPositives------------------------------ +// int java.lang.StringCoding#countPositives0(byte[] ba, int off, int len) bool LibraryCallKit::inline_countPositives() { if (too_many_traps(Deoptimization::Reason_intrinsic)) { return false; @@ -1129,13 +1141,14 @@ bool LibraryCallKit::inline_countPositives() { Node* offset = argument(1); Node* len = argument(2); - ba = must_be_not_null(ba, true); - - // Range checks - generate_string_range_check(ba, offset, len, false); - if (stopped()) { - return true; + if (VerifyIntrinsicChecks) { + ba = must_be_not_null(ba, true); + generate_string_range_check(ba, offset, len, false, true); + if (stopped()) { + return true; + } } + Node* ba_start = array_element_address(ba, offset, T_BYTE); Node* result = new CountPositivesNode(control(), memory(TypeAryPtr::BYTES), ba_start, len); set_result(_gvn.transform(result)); @@ -3275,7 +3288,7 @@ bool LibraryCallKit::inline_native_jvm_commit() { lease_compare_io->init_req(_true_path, i_o()); lease_compare_io->init_req(_false_path, input_io_state); - lease_result_value->init_req(_true_path, null()); // if the lease was returned, return 0. + lease_result_value->init_req(_true_path, _gvn.longcon(0)); // if the lease was returned, return 0L. lease_result_value->init_req(_false_path, arg); // if not lease, return new updated position. RegionNode* result_rgn = new RegionNode(PATH_LIMIT); @@ -6128,6 +6141,9 @@ CallStaticJavaNode* LibraryCallKit::get_uncommon_trap_from_success_proj(Node* no } //-------------inline_encodeISOArray----------------------------------- +// int sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// int java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// int java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) // encode char[] to byte[] in ISO_8859_1 or ASCII bool LibraryCallKit::inline_encodeISOArray(bool ascii) { assert(callee()->signature()->size() == 5, "encodeISOArray has 5 parameters"); @@ -6138,8 +6154,14 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) { Node *dst_offset = argument(3); Node *length = argument(4); - src = must_be_not_null(src, true); - dst = must_be_not_null(dst, true); + // Cast source & target arrays to not-null + if (VerifyIntrinsicChecks) { + src = must_be_not_null(src, true); + dst = must_be_not_null(dst, true); + if (stopped()) { + return true; + } + } const TypeAryPtr* src_type = src->Value(&_gvn)->isa_aryptr(); const TypeAryPtr* dst_type = dst->Value(&_gvn)->isa_aryptr(); @@ -6156,6 +6178,15 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) { return false; } + // Check source & target bounds + if (VerifyIntrinsicChecks) { + generate_string_range_check(src, src_offset, length, src_elem == T_BYTE, true); + generate_string_range_check(dst, dst_offset, length, false, true); + if (stopped()) { + return true; + } + } + Node* src_start = array_element_address(src, src_offset, T_CHAR); Node* dst_start = array_element_address(dst, dst_offset, dst_elem); // 'src_start' points to src array + scaled offset diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index fbc6007d4e1..fbac1363dae 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -163,7 +163,8 @@ class LibraryCallKit : public GraphKit { Node* array_length, RegionNode* region); void generate_string_range_check(Node* array, Node* offset, - Node* length, bool char_count); + Node* length, bool char_count, + bool halt_on_oob = false); Node* current_thread_helper(Node* &tls_output, ByteSize handle_offset, bool is_immutable); Node* generate_current_thread(Node* &tls_output); diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 27e397790d4..8863f37699a 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1630,6 +1630,64 @@ public: // same block. Split thru the Region. void do_split_if(Node *iff, RegionNode** new_false_region = nullptr, RegionNode** new_true_region = nullptr); +private: + // Class to keep track of wins in split_thru_phi. + class SplitThruPhiWins { + private: + // Region containing the phi we are splitting through. + const Node* _region; + + // Sum of all wins regardless of where they happen. This applies to Loops phis as well as non-loop phis. + int _total_wins; + + // For Loops, wins have different impact depending on if they happen on loop entry or on the backedge. + // Number of wins on a loop entry edge if the split is through a loop head, + // otherwise 0. Entry edge wins only pay dividends once on loop entry. + int _loop_entry_wins; + // Number of wins on a loop back-edge, which pay dividends on every iteration. + int _loop_back_wins; + + public: + SplitThruPhiWins(const Node* region) : + _region(region), + _total_wins(0), + _loop_entry_wins(0), + _loop_back_wins(0) {}; + + void reset() {_total_wins = 0; _loop_entry_wins = 0; _loop_back_wins = 0;} + void add_win(int ctrl_index) { + if (_region->is_Loop() && ctrl_index == LoopNode::EntryControl) { + _loop_entry_wins++; + } else if (_region->is_Loop() && ctrl_index == LoopNode::LoopBackControl) { + _loop_back_wins++; + } + _total_wins++; + } + // Is this split profitable with respect to the policy? + bool profitable(int policy) const { + assert(_region->is_Loop() || (_loop_entry_wins == 0 && _loop_back_wins == 0), "wins on loop edges without a loop"); + assert(!_region->is_Loop() || _total_wins == _loop_entry_wins + _loop_back_wins, "missed some win"); + // In general this means that the split has to have more wins than specified + // in the policy. However, for loops we need to take into account where the + // wins happen. We need to be careful when splitting, because splitting nodes + // related to the iv through the phi can sufficiently rearrange the loop + // structure to prevent RCE and thus vectorization. Thus, we only deem splitting + // profitable if the win of a split is not on the entry edge, as such wins + // only pay off once and have a high chance of messing up the loop structure. + return (_loop_entry_wins == 0 && _total_wins > policy) || + // If there are wins on the entry edge but the backadge also has sufficient wins, + // there is sufficient profitability to spilt regardless of the risk of messing + // up the loop structure. + _loop_back_wins > policy || + // If the policy is less than 0, a split is always profitable, i.e. we always + // split. This is needed when we split a node and then must also split a + // dependant node, i.e. spliting a Bool node after splitting a Cmp node. + policy < 0; + } + }; + +public: + // Conversion of fill/copy patterns into intrinsic versions bool do_intrinsify_fill(); bool intrinsify_fill(IdealLoopTree* lpt); @@ -1982,7 +2040,7 @@ class DataNodeGraph : public StackObj { DataNodeGraph(const Unique_Node_List& data_nodes, PhaseIdealLoop* phase) : _phase(phase), _data_nodes(data_nodes), - // Use 107 as best guess which is the first resize value in ResizeableResourceHashtable::large_table_sizes. + // Use 107 as best guess which is the first resize value in ResizeableHashTable::large_table_sizes. _orig_to_new(107, MaxNodeLimit) { #ifdef ASSERT diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index e2696336238..a09eef0bb81 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -41,6 +41,7 @@ #include "opto/subtypenode.hpp" #include "opto/superword.hpp" #include "opto/vectornode.hpp" +#include "utilities/checkedCast.hpp" #include "utilities/macros.hpp" //============================================================================= @@ -66,7 +67,7 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { return nullptr; } - int wins = 0; + SplitThruPhiWins wins(region); assert(!n->is_CFG(), ""); assert(region->is_Region(), ""); @@ -119,7 +120,7 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { } if (singleton) { - wins++; + wins.add_win(i); x = makecon(t); } else { // We now call Identity to try to simplify the cloned node. @@ -134,7 +135,7 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { x->raise_bottom_type(t); Node* y = x->Identity(&_igvn); if (y != x) { - wins++; + wins.add_win(i); x = y; } else { y = _igvn.hash_find(x); @@ -142,7 +143,7 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { y = similar_subtype_check(x, region->in(i)); } if (y) { - wins++; + wins.add_win(i); x = y; } else { // Else x is a new node we are keeping @@ -165,12 +166,12 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { n->is_Load() && can_move_to_inner_loop(n, region->as_Loop(), x)) { // it is not a win if 'x' moved from an outer to an inner loop // this edge case can only happen for Load nodes - wins = 0; + wins.reset(); break; } } // Too few wins? - if (wins <= policy) { + if (!wins.profitable(policy)) { _igvn.remove_dead_node(phi); return nullptr; } @@ -227,6 +228,13 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { } } +#ifndef PRODUCT + if (TraceLoopOpts) { + tty->print("Split %d %s through %d Phi in %d %s", + n->_idx, n->Name(), phi->_idx, region->_idx, region->Name()); + } +#endif // !PRODUCT + return phi; } diff --git a/src/hotspot/share/opto/mempointer.cpp b/src/hotspot/share/opto/mempointer.cpp index e400e40e4a2..0dadc14a686 100644 --- a/src/hotspot/share/opto/mempointer.cpp +++ b/src/hotspot/share/opto/mempointer.cpp @@ -25,7 +25,7 @@ #include "classfile/vmSymbols.hpp" #include "opto/addnode.hpp" #include "opto/mempointer.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" MemPointerParserCallback MemPointerParserCallback::_empty; diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index dc0ac474c4b..c2b3c4fb0ad 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -222,7 +222,7 @@ typedef Node** DUIterator_Fast; typedef Node** DUIterator_Last; #endif -typedef ResizeableResourceHashtable OrigToNewHashtable; +typedef ResizeableHashTable OrigToNewHashtable; // Node Sentinel #define NodeSentinel (Node*)-1 diff --git a/src/hotspot/share/opto/superwordVTransformBuilder.hpp b/src/hotspot/share/opto/superwordVTransformBuilder.hpp index ecec380fbfe..f2198113d31 100644 --- a/src/hotspot/share/opto/superwordVTransformBuilder.hpp +++ b/src/hotspot/share/opto/superwordVTransformBuilder.hpp @@ -35,7 +35,7 @@ private: const PackSet& _packset; VTransform& _vtransform; - ResourceHashtable _idx_to_vtnode; + HashTable _idx_to_vtnode; public: SuperWordVTransformBuilder(const PackSet& packset, diff --git a/src/hotspot/share/prims/foreignGlobals.cpp b/src/hotspot/share/prims/foreignGlobals.cpp index d3d0c2cefae..f8aa73bed05 100644 --- a/src/hotspot/share/prims/foreignGlobals.cpp +++ b/src/hotspot/share/prims/foreignGlobals.cpp @@ -26,7 +26,7 @@ #include "memory/resourceArea.hpp" #include "prims/foreignGlobals.inline.hpp" #include "runtime/jniHandles.inline.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" StubLocations::StubLocations() { for (uint32_t i = 0; i < LOCATION_LIMIT; i++) { @@ -201,7 +201,7 @@ class ArgumentShuffle::ComputeMoveOrder: public StackObj { return a.type() == b.type() && a.index_or_offset() == b.index_or_offset(); } - using KillerTable = ResourceHashtable< + using KillerTable = HashTable< VMStorage, MoveOperation*, 32, // doesn't need to be big. don't have that many argument registers (in known ABIs) AnyObj::RESOURCE_AREA, diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 005776b00c2..098bd729c0b 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -2945,7 +2945,7 @@ JVM_ENTRY(jboolean, JVM_HoldsLock(JNIEnv* env, jclass threadClass, jobject obj)) JVM_END JVM_ENTRY(jobject, JVM_GetStackTrace(JNIEnv *env, jobject jthread)) - oop trace = java_lang_Thread::async_get_stack_trace(JNIHandles::resolve(jthread), THREAD); + oop trace = java_lang_Thread::async_get_stack_trace(jthread, THREAD); return JNIHandles::make_local(THREAD, trace); JVM_END diff --git a/src/hotspot/share/prims/jvmtiTagMapTable.hpp b/src/hotspot/share/prims/jvmtiTagMapTable.hpp index f86b6386a9a..f717552da2b 100644 --- a/src/hotspot/share/prims/jvmtiTagMapTable.hpp +++ b/src/hotspot/share/prims/jvmtiTagMapTable.hpp @@ -28,7 +28,7 @@ #include "gc/shared/collectedHeap.hpp" #include "memory/allocation.hpp" #include "oops/weakHandle.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" class JvmtiEnv; class JvmtiTagMapKeyClosure; @@ -65,14 +65,14 @@ class JvmtiTagMapKey : public CHeapObj { }; typedef -ResizeableResourceHashtable ResizableResourceHT; + JvmtiTagMapKey::equals> ResizableHT; class JvmtiTagMapTable : public CHeapObj { private: - ResizableResourceHT _table; + ResizableHT _table; public: JvmtiTagMapTable(); diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index 5eb9601e4c5..3d83959d76a 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -27,8 +27,8 @@ #include "logging/logLevel.hpp" #include "logging/logTag.hpp" -#include "memory/allStatic.hpp" #include "memory/allocation.hpp" +#include "memory/allStatic.hpp" #include "runtime/globals.hpp" #include "runtime/java.hpp" #include "runtime/os.hpp" diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index a928b0443ee..1027b4447c5 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -460,6 +460,7 @@ private: template frame new_heap_frame(frame& f, frame& caller); inline void set_top_frame_metadata_pd(const frame& hf); inline void patch_pd(frame& callee, const frame& caller); + inline void patch_pd_unused(intptr_t* sp); void adjust_interpreted_frame_unextended_sp(frame& f); static inline void prepare_freeze_interpreted_top_frame(frame& f); static inline void relativize_interpreted_frame_metadata(const frame& f, const frame& hf); @@ -783,9 +784,24 @@ void FreezeBase::freeze_fast_copy(stackChunkOop chunk, int chunk_start_sp CONT_J log_develop_trace(continuations)("freeze_fast start: " INTPTR_FORMAT " sp: %d chunk_top: " INTPTR_FORMAT, p2i(chunk->start_address()), chunk_new_sp, p2i(chunk_top)); - intptr_t* from = _cont_stack_top - frame::metadata_words_at_bottom; - intptr_t* to = chunk_top - frame::metadata_words_at_bottom; - copy_to_chunk(from, to, cont_size() + frame::metadata_words_at_bottom); + + int adjust = frame::metadata_words_at_bottom; +#if INCLUDE_ASAN && defined(AARCH64) + // Reading at offset frame::metadata_words_at_bottom from _cont_stack_top + // will accesss memory at the callee frame, which on preemption cases will + // be the VM native method being called. The Arm 64-bit ABI doesn't specify + // a location where the frame record (returnpc+fp) has to be stored within + // a stack frame, and GCC currently chooses to save it at the top of the + // frame (lowest address). ASan treats this memory access in the callee as + // an overflow access to one of the locals stored in that frame. For these + // preemption cases we don't need to read these words anyways so we avoid it. + if (_preempt) { + adjust = 0; + } +#endif + intptr_t* from = _cont_stack_top - adjust; + intptr_t* to = chunk_top - adjust; + copy_to_chunk(from, to, cont_size() + adjust); // Because we're not patched yet, the chunk is now in a bad state // patch return pc of the bottom-most frozen frame (now in the chunk) @@ -816,6 +832,11 @@ void FreezeBase::freeze_fast_copy(stackChunkOop chunk, int chunk_start_sp CONT_J address last_pc = _last_frame.pc(); ContinuationHelper::patch_return_address_at(chunk_top - frame::sender_sp_ret_address_offset(), last_pc); chunk->set_pc(last_pc); + // For stub/native frames the fp is not used while frozen, and will be constructed + // again when thawing the frame (see ThawBase::handle_preempted_continuation). We + // patch it with a special bad address to help with debugging, particularly when + // inspecting frames and identifying invalid accesses. + patch_pd_unused(chunk_top); } else { chunk->set_pc(ContinuationHelper::return_address_at( _cont_stack_top - frame::sender_sp_ret_address_offset())); @@ -2484,7 +2505,7 @@ intptr_t* ThawBase::handle_preempted_continuation(intptr_t* sp, Continuation::pr if (fast_case) { // If we thawed in the slow path the runtime stub/native wrapper frame already // has the correct fp (see ThawBase::new_stack_frame). On the fast path though, - // we copied the original fp at the time of freeze which now will have to be fixed. + // we copied the fp patched during freeze, which will now have to be fixed. assert(top.is_runtime_frame() || top.is_native_frame(), ""); int fsize = top.cb()->frame_size(); patch_pd(top, sp + fsize); diff --git a/src/hotspot/share/runtime/continuationHelper.hpp b/src/hotspot/share/runtime/continuationHelper.hpp index 0b3e6c72452..dffbf333bcc 100644 --- a/src/hotspot/share/runtime/continuationHelper.hpp +++ b/src/hotspot/share/runtime/continuationHelper.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,6 +65,7 @@ public: class ContinuationHelper::Frame : public AllStatic { public: static const bool interpreted = false; + static const bool compiled = false; static const bool stub = false; static const bool native = false; @@ -127,6 +128,8 @@ public: class ContinuationHelper::CompiledFrame : public ContinuationHelper::NonInterpretedFrame { public: + static const bool compiled = true; + static bool is_instance(const frame& f); #ifdef ASSERT diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 5f9a80cf100..243903ed233 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -561,7 +561,8 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread #endif // !PRODUCT GrowableArray* expressions = trap_scope->expressions(); - guarantee(expressions != nullptr && expressions->length() > 0, "must have exception to throw"); + guarantee(expressions != nullptr && expressions->length() == 1, "should have only exception on stack"); + guarantee(exec_mode != Unpack_exception, "rethrow_exception set with Unpack_exception"); ScopeValue* topOfStack = expressions->top(); exceptionObject = StackValue::create_stack_value(&deoptee, &map, topOfStack)->get_obj(); guarantee(exceptionObject() != nullptr, "exception oop can not be null"); @@ -737,6 +738,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread if (exceptionObject() != nullptr) { current->set_exception_oop(exceptionObject()); exec_mode = Unpack_exception; + assert(array->element(0)->rethrow_exception(), "must be"); } #endif @@ -844,6 +846,7 @@ void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_arr } #ifndef PRODUCT +#ifdef ASSERT // Return true if the execution after the provided bytecode continues at the // next bytecode in the code. This is not the case for gotos, returns, and // throws. @@ -868,6 +871,7 @@ static bool falls_through(Bytecodes::Code bc) { } } #endif +#endif // Return BasicType of value being returned JRT_LEAF(BasicType, Deoptimization::unpack_frames(JavaThread* thread, int exec_mode)) @@ -932,116 +936,114 @@ JRT_LEAF(BasicType, Deoptimization::unpack_frames(JavaThread* thread, int exec_m RegisterMap::ProcessFrames::include, RegisterMap::WalkContinuation::skip); rm.set_include_argument_oops(false); - bool is_top_frame = true; int callee_size_of_parameters = 0; - int callee_max_locals = 0; - for (int i = 0; i < cur_array->frames(); i++) { - vframeArrayElement* el = cur_array->element(i); + for (int frame_idx = 0; frame_idx < cur_array->frames(); frame_idx++) { + bool is_top_frame = (frame_idx == 0); + vframeArrayElement* el = cur_array->element(frame_idx); frame* iframe = el->iframe(); guarantee(iframe->is_interpreted_frame(), "Wrong frame type"); + methodHandle mh(thread, iframe->interpreter_frame_method()); + bool reexecute = el->should_reexecute(); + + int cur_invoke_parameter_size = 0; + int top_frame_expression_stack_adjustment = 0; + int max_bci = mh->code_size(); + BytecodeStream str(mh, iframe->interpreter_frame_bci()); + assert(str.bci() < max_bci, "bci in interpreter frame out of bounds"); + Bytecodes::Code cur_code = str.next(); + + if (!reexecute && !Bytecodes::is_invoke(cur_code)) { + // We can only compute OopMaps for the before state, so we need to roll forward + // to the next bytecode. + assert(is_top_frame, "must be"); + assert(falls_through(cur_code), "must be"); + assert(cur_code != Bytecodes::_illegal, "illegal bytecode"); + assert(str.bci() < max_bci, "bci in interpreter frame out of bounds"); + + // Need to subtract off the size of the result type of + // the bytecode because this is not described in the + // debug info but returned to the interpreter in the TOS + // caching register + BasicType bytecode_result_type = Bytecodes::result_type(cur_code); + if (bytecode_result_type != T_ILLEGAL) { + top_frame_expression_stack_adjustment = type2size[bytecode_result_type]; + } + assert(top_frame_expression_stack_adjustment >= 0, "stack adjustment must be positive"); + + cur_code = str.next(); + // Reflect the fact that we have rolled forward and now need + // top_frame_expression_stack_adjustment + reexecute = true; + } + + assert(cur_code != Bytecodes::_illegal, "illegal bytecode"); + assert(str.bci() < max_bci, "bci in interpreter frame out of bounds"); // Get the oop map for this bci InterpreterOopMap mask; - int cur_invoke_parameter_size = 0; - bool try_next_mask = false; - int next_mask_expression_stack_size = -1; - int top_frame_expression_stack_adjustment = 0; - methodHandle mh(thread, iframe->interpreter_frame_method()); - OopMapCache::compute_one_oop_map(mh, iframe->interpreter_frame_bci(), &mask); - BytecodeStream str(mh, iframe->interpreter_frame_bci()); - int max_bci = mh->code_size(); - // Get to the next bytecode if possible - assert(str.bci() < max_bci, "bci in interpreter frame out of bounds"); + OopMapCache::compute_one_oop_map(mh, str.bci(), &mask); // Check to see if we can grab the number of outgoing arguments // at an uncommon trap for an invoke (where the compiler // generates debug info before the invoke has executed) - Bytecodes::Code cur_code = str.next(); - Bytecodes::Code next_code = Bytecodes::_shouldnotreachhere; if (Bytecodes::is_invoke(cur_code)) { - Bytecode_invoke invoke(mh, iframe->interpreter_frame_bci()); + Bytecode_invoke invoke(mh, str.bci()); cur_invoke_parameter_size = invoke.size_of_parameters(); - if (i != 0 && invoke.has_member_arg()) { + if (!is_top_frame && invoke.has_member_arg()) { callee_size_of_parameters++; } } - if (str.bci() < max_bci) { - next_code = str.next(); - if (next_code >= 0) { - // The interpreter oop map generator reports results before - // the current bytecode has executed except in the case of - // calls. It seems to be hard to tell whether the compiler - // has emitted debug information matching the "state before" - // a given bytecode or the state after, so we try both - if (!Bytecodes::is_invoke(cur_code) && falls_through(cur_code)) { - // Get expression stack size for the next bytecode - InterpreterOopMap next_mask; - OopMapCache::compute_one_oop_map(mh, str.bci(), &next_mask); - next_mask_expression_stack_size = next_mask.expression_stack_size(); - if (Bytecodes::is_invoke(next_code)) { - Bytecode_invoke invoke(mh, str.bci()); - next_mask_expression_stack_size += invoke.size_of_parameters(); - } - // Need to subtract off the size of the result type of - // the bytecode because this is not described in the - // debug info but returned to the interpreter in the TOS - // caching register - BasicType bytecode_result_type = Bytecodes::result_type(cur_code); - if (bytecode_result_type != T_ILLEGAL) { - top_frame_expression_stack_adjustment = type2size[bytecode_result_type]; - } - assert(top_frame_expression_stack_adjustment >= 0, "stack adjustment must be positive"); - try_next_mask = true; - } - } - } // Verify stack depth and oops in frame - // This assertion may be dependent on the platform we're running on and may need modification (tested on x86 and sparc) - if (!( - /* SPARC */ - (iframe->interpreter_frame_expression_stack_size() == mask.expression_stack_size() + callee_size_of_parameters) || - /* x86 */ - (iframe->interpreter_frame_expression_stack_size() == mask.expression_stack_size() + callee_max_locals) || - (try_next_mask && - (iframe->interpreter_frame_expression_stack_size() == (next_mask_expression_stack_size - - top_frame_expression_stack_adjustment))) || - (is_top_frame && (exec_mode == Unpack_exception) && iframe->interpreter_frame_expression_stack_size() == 0) || - (is_top_frame && (exec_mode == Unpack_uncommon_trap || exec_mode == Unpack_reexecute || el->should_reexecute()) && - (iframe->interpreter_frame_expression_stack_size() == mask.expression_stack_size() + cur_invoke_parameter_size)) - )) { - { - // Print out some information that will help us debug the problem - tty->print_cr("Wrong number of expression stack elements during deoptimization"); - tty->print_cr(" Error occurred while verifying frame %d (0..%d, 0 is topmost)", i, cur_array->frames() - 1); - tty->print_cr(" Current code %s", Bytecodes::name(cur_code)); - if (try_next_mask) { - tty->print_cr(" Next code %s", Bytecodes::name(next_code)); - } - tty->print_cr(" Fabricated interpreter frame had %d expression stack elements", - iframe->interpreter_frame_expression_stack_size()); - tty->print_cr(" Interpreter oop map had %d expression stack elements", mask.expression_stack_size()); - tty->print_cr(" try_next_mask = %d", try_next_mask); - tty->print_cr(" next_mask_expression_stack_size = %d", next_mask_expression_stack_size); - tty->print_cr(" callee_size_of_parameters = %d", callee_size_of_parameters); - tty->print_cr(" callee_max_locals = %d", callee_max_locals); - tty->print_cr(" top_frame_expression_stack_adjustment = %d", top_frame_expression_stack_adjustment); - tty->print_cr(" exec_mode = %d", exec_mode); - tty->print_cr(" cur_invoke_parameter_size = %d", cur_invoke_parameter_size); - tty->print_cr(" Thread = " INTPTR_FORMAT ", thread ID = %d", p2i(thread), thread->osthread()->thread_id()); - tty->print_cr(" Interpreted frames:"); - for (int k = 0; k < cur_array->frames(); k++) { - vframeArrayElement* el = cur_array->element(k); - tty->print_cr(" %s (bci %d)", el->method()->name_and_sig_as_C_string(), el->bci()); - } - cur_array->print_on_2(tty); + auto match = [&]() { + int iframe_expr_ssize = iframe->interpreter_frame_expression_stack_size(); +#if INCLUDE_JVMCI + if (is_top_frame && el->rethrow_exception()) { + return iframe_expr_ssize == 1; } +#endif + // This should only be needed for C1 + if (is_top_frame && exec_mode == Unpack_exception && iframe_expr_ssize == 0) { + return true; + } + if (reexecute) { + int expr_ssize_before = iframe_expr_ssize + top_frame_expression_stack_adjustment; + int oopmap_expr_invoke_ssize = mask.expression_stack_size() + cur_invoke_parameter_size; + return expr_ssize_before == oopmap_expr_invoke_ssize; + } else { + int oopmap_expr_callee_ssize = mask.expression_stack_size() + callee_size_of_parameters; + return iframe_expr_ssize == oopmap_expr_callee_ssize; + } + }; + if (!match()) { + // Print out some information that will help us debug the problem + tty->print_cr("Wrong number of expression stack elements during deoptimization"); + tty->print_cr(" Error occurred while verifying frame %d (0..%d, 0 is topmost)", frame_idx, cur_array->frames() - 1); + tty->print_cr(" Current code %s", Bytecodes::name(cur_code)); + tty->print_cr(" Fabricated interpreter frame had %d expression stack elements", + iframe->interpreter_frame_expression_stack_size()); + tty->print_cr(" Interpreter oop map had %d expression stack elements", mask.expression_stack_size()); + tty->print_cr(" callee_size_of_parameters = %d", callee_size_of_parameters); + tty->print_cr(" top_frame_expression_stack_adjustment = %d", top_frame_expression_stack_adjustment); + tty->print_cr(" exec_mode = %d", exec_mode); + tty->print_cr(" original should_reexecute = %s", el->should_reexecute() ? "true" : "false"); + tty->print_cr(" reexecute = %s%s", reexecute ? "true" : "false", + (reexecute != el->should_reexecute()) ? " (changed)" : ""); +#if INCLUDE_JVMCI + tty->print_cr(" rethrow_exception = %s", el->rethrow_exception() ? "true" : "false"); +#endif + tty->print_cr(" cur_invoke_parameter_size = %d", cur_invoke_parameter_size); + tty->print_cr(" Thread = " INTPTR_FORMAT ", thread ID = %d", p2i(thread), thread->osthread()->thread_id()); + tty->print_cr(" Interpreted frames:"); + for (int k = 0; k < cur_array->frames(); k++) { + vframeArrayElement* el = cur_array->element(k); + tty->print_cr(" %s (bci %d)", el->method()->name_and_sig_as_C_string(), el->bci()); + } + cur_array->print_on_2(tty); guarantee(false, "wrong number of expression stack elements during deopt"); } VerifyOopClosure verify; iframe->oops_interpreted_do(&verify, &rm, false); callee_size_of_parameters = mh->size_of_parameters(); - callee_max_locals = mh->max_locals(); - is_top_frame = false; } } #endif // !PRODUCT diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp index 26a46462db4..6a618cd7fea 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp @@ -136,6 +136,16 @@ JVMFlag::Error NUMAInterleaveGranularityConstraintFunc(size_t value, bool verbos return JVMFlag::SUCCESS; } +JVMFlag::Error LargePageSizeInBytesConstraintFunc(size_t value, bool verbose) { + if (!is_power_of_2(value)) { + JVMFlag::printError(verbose, "LargePageSizeInBytes ( %zu ) must be " + "a power of 2\n", + value); + return JVMFlag::VIOLATES_CONSTRAINT; + } + return JVMFlag::SUCCESS; +} + JVMFlag::Error OnSpinWaitInstNameConstraintFunc(ccstr value, bool verbose) { #ifdef AARCH64 if (value == nullptr) { diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp index 31512af940d..5fe4c32afb9 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp @@ -42,6 +42,7 @@ f(int, ContendedPaddingWidthConstraintFunc) \ f(size_t, VMPageSizeConstraintFunc) \ f(size_t, NUMAInterleaveGranularityConstraintFunc) \ + f(size_t, LargePageSizeInBytesConstraintFunc) \ f(ccstr, OnSpinWaitInstNameConstraintFunc) RUNTIME_CONSTRAINTS(DECLARE_CONSTRAINT) diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 24dda9ac6d8..9149e389e21 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -237,8 +237,10 @@ const int ObjectAlignmentInBytes = 8; \ product(size_t, LargePageSizeInBytes, 0, \ "Maximum large page size used (0 will use the default large " \ - "page size for the environment as the maximum)") \ + "page size for the environment as the maximum) " \ + "(must be a power of 2)") \ range(0, max_uintx) \ + constraint(LargePageSizeInBytesConstraintFunc, AtParse) \ \ product(size_t, LargePageHeapSizeThreshold, 128*M, \ "Use large pages if maximum heap is at least this big") \ @@ -483,6 +485,9 @@ const int ObjectAlignmentInBytes = 8; develop(bool, ZapFillerObjects, trueInDebug, \ "Zap filler objects") \ \ + develop(bool, ZapCHeap, trueInDebug, \ + "Zap allocated/freed C heap space") \ + \ develop(bool, ZapTLAB, trueInDebug, \ "Zap allocated TLABs") \ develop(bool, TestingAsyncLoggingDeathTest, false, \ diff --git a/src/hotspot/share/runtime/handshake.cpp b/src/hotspot/share/runtime/handshake.cpp index 39ae4f18998..e6a12677a19 100644 --- a/src/hotspot/share/runtime/handshake.cpp +++ b/src/hotspot/share/runtime/handshake.cpp @@ -202,7 +202,7 @@ static void handle_timeout(HandshakeOperation* op, JavaThread* target) { } if (target != nullptr) { - VMError::set_handshake_timed_out_thread(p2i(target)); + VMError::set_handshake_timed_out_thread(target); if (os::signal_thread(target, SIGILL, "cannot be handshaked")) { // Give target a chance to report the error and terminate the VM. os::naked_sleep(3000); diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index ceac0b42acf..39ba328b0d0 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -711,6 +711,7 @@ void ObjectMonitor::add_to_entry_list(JavaThread* current, ObjectWaiter* node) { // if we added current to _entry_list. Once on _entry_list, current // stays on-queue until it acquires the lock. bool ObjectMonitor::try_lock_or_add_to_entry_list(JavaThread* current, ObjectWaiter* node) { + assert(node->TState == ObjectWaiter::TS_RUN, ""); node->_prev = nullptr; node->TState = ObjectWaiter::TS_ENTER; @@ -726,6 +727,7 @@ bool ObjectMonitor::try_lock_or_add_to_entry_list(JavaThread* current, ObjectWai if (try_lock(current) == TryLockResult::Success) { assert(!has_successor(current), "invariant"); assert(has_owner(current), "invariant"); + node->TState = ObjectWaiter::TS_RUN; return true; } } diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index db2256d70cb..04a363a7aab 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -666,8 +666,8 @@ void* os::malloc(size_t size, MemTag mem_tag, const NativeCallStack& stack) { if (CDSConfig::is_dumping_static_archive()) { // Need to deterministically fill all the alignment gaps in C++ structures. ::memset(inner_ptr, 0, size); - } else { - DEBUG_ONLY(::memset(inner_ptr, uninitBlockPad, size);) + } else if (ZapCHeap) { + ::memset(inner_ptr, uninitBlockPad, size); } DEBUG_ONLY(break_if_ptr_caught(inner_ptr);) return inner_ptr; @@ -740,7 +740,7 @@ void* os::realloc(void *memblock, size_t size, MemTag mem_tag, const NativeCallS #ifdef ASSERT assert(old_size == free_info.size, "Sanity"); - if (old_size < size) { + if (ZapCHeap && old_size < size) { // We also zap the newly extended region. ::memset((char*)new_inner_ptr + old_size, uninitBlockPad, size - old_size); } diff --git a/src/hotspot/share/runtime/safepoint.cpp b/src/hotspot/share/runtime/safepoint.cpp index 6fcf1e6c0f5..ef9e0981913 100644 --- a/src/hotspot/share/runtime/safepoint.cpp +++ b/src/hotspot/share/runtime/safepoint.cpp @@ -651,7 +651,7 @@ void SafepointSynchronize::print_safepoint_timeout() { // Send the blocking thread a signal to terminate and write an error file. for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur_thread = jtiwh.next(); ) { if (cur_thread->safepoint_state()->is_running()) { - VMError::set_safepoint_timed_out_thread(p2i(cur_thread)); + VMError::set_safepoint_timed_out_thread(cur_thread); if (!os::signal_thread(cur_thread, SIGILL, "blocking a safepoint")) { break; // Could not send signal. Report fatal error. } diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 149ebef4294..13612496fbd 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -80,7 +80,7 @@ #include "utilities/dtrace.hpp" #include "utilities/events.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" #include "utilities/xmlstream.hpp" #ifdef COMPILER1 @@ -2450,7 +2450,7 @@ class ArchivedAdapterTable : public OffsetCompactHashtable< #endif // INCLUDE_CDS // A hashtable mapping from AdapterFingerPrints to AdapterHandlerEntries -using AdapterHandlerTable = ResourceHashtable; @@ -2602,19 +2602,24 @@ void AdapterHandlerLibrary::initialize() { BasicType obj_obj_args[] = { T_OBJECT, T_OBJECT }; _obj_obj_arg_handler = create_adapter(obj_obj_arg_blob, 2, obj_obj_args); - assert(no_arg_blob != nullptr && - obj_arg_blob != nullptr && - int_arg_blob != nullptr && - obj_int_arg_blob != nullptr && - obj_obj_arg_blob != nullptr, "Initial adapters must be properly created"); + // we should always get an entry back but we don't have any + // associated blob on Zero + assert(_no_arg_handler != nullptr && + _obj_arg_handler != nullptr && + _int_arg_handler != nullptr && + _obj_int_arg_handler != nullptr && + _obj_obj_arg_handler != nullptr, "Initial adapter handlers must be properly created"); } // Outside of the lock +#ifndef ZERO + // no blobs to register when we are on Zero post_adapter_creation(no_arg_blob, _no_arg_handler); post_adapter_creation(obj_arg_blob, _obj_arg_handler); post_adapter_creation(int_arg_blob, _int_arg_handler); post_adapter_creation(obj_int_arg_blob, _obj_int_arg_handler); post_adapter_creation(obj_obj_arg_blob, _obj_obj_arg_handler); +#endif // ZERO } AdapterHandlerEntry* AdapterHandlerLibrary::new_entry(AdapterFingerPrint* fingerprint) { @@ -2709,12 +2714,15 @@ const char* AdapterHandlerEntry::_entry_names[] = { #ifdef ASSERT void AdapterHandlerLibrary::verify_adapter_sharing(int total_args_passed, BasicType* sig_bt, AdapterHandlerEntry* cached_entry) { + // we can only check for the same code if there is any +#ifndef ZERO AdapterBlob* comparison_blob = nullptr; AdapterHandlerEntry* comparison_entry = create_adapter(comparison_blob, total_args_passed, sig_bt, true); assert(comparison_blob == nullptr, "no blob should be created when creating an adapter for comparison"); assert(comparison_entry->compare_code(cached_entry), "code must match"); // Release the one just created AdapterHandlerEntry::deallocate(comparison_entry); +# endif // ZERO } #endif /* ASSERT*/ @@ -2778,7 +2786,12 @@ AdapterBlob* AdapterHandlerLibrary::lookup_aot_cache(AdapterHandlerEntry* handle adapter_blob->get_offsets(offsets); address i2c_entry = adapter_blob->content_begin(); assert(offsets[0] == 0, "sanity check"); - handler->set_entry_points(i2c_entry, i2c_entry + offsets[1], i2c_entry + offsets[2], i2c_entry + offsets[3]); + handler->set_entry_points( + i2c_entry, + (offsets[1] != -1) ? (i2c_entry + offsets[1]) : nullptr, + (offsets[2] != -1) ? (i2c_entry + offsets[2]) : nullptr, + (offsets[3] != -1) ? (i2c_entry + offsets[3]) : nullptr + ); } return adapter_blob; } @@ -2787,8 +2800,13 @@ AdapterBlob* AdapterHandlerLibrary::lookup_aot_cache(AdapterHandlerEntry* handle void AdapterHandlerLibrary::print_adapter_handler_info(outputStream* st, AdapterHandlerEntry* handler, AdapterBlob* adapter_blob) { ttyLocker ttyl; ResourceMark rm; - int insts_size = adapter_blob->code_size(); + int insts_size; + // on Zero the blob may be null handler->print_adapter_on(tty); + if (adapter_blob == nullptr) { + return; + } + insts_size = adapter_blob->code_size(); st->print_cr("i2c argument handler for: %s %s (%d bytes generated)", handler->fingerprint()->as_basic_args_string(), handler->fingerprint()->as_string(), insts_size); @@ -2829,6 +2847,11 @@ bool AdapterHandlerLibrary::generate_adapter_code(AdapterBlob*& adapter_blob, sig_bt, regs, handler); +#ifdef ZERO + // On zero there is no code to save and no need to create a blob and + // or relocate the handler. + adapter_blob = nullptr; +#else #ifdef ASSERT if (VerifyAdapterSharing) { handler->save_code(buf->code_begin(), buffer.insts_size()); @@ -2842,9 +2865,12 @@ bool AdapterHandlerLibrary::generate_adapter_code(AdapterBlob*& adapter_blob, assert(AdapterBlob::ENTRY_COUNT == 4, "sanity"); address i2c_entry = handler->get_i2c_entry(); entry_offset[0] = 0; // i2c_entry offset - entry_offset[1] = handler->get_c2i_entry() - i2c_entry; - entry_offset[2] = handler->get_c2i_unverified_entry() - i2c_entry; - entry_offset[3] = handler->get_c2i_no_clinit_check_entry() - i2c_entry; + entry_offset[1] = (handler->get_c2i_entry() != nullptr) ? + (handler->get_c2i_entry() - i2c_entry) : -1; + entry_offset[2] = (handler->get_c2i_unverified_entry() != nullptr) ? + (handler->get_c2i_unverified_entry() - i2c_entry) : -1; + entry_offset[3] = (handler->get_c2i_no_clinit_check_entry() != nullptr) ? + (handler->get_c2i_no_clinit_check_entry() - i2c_entry) : -1; adapter_blob = AdapterBlob::create(&buffer, entry_offset); if (adapter_blob == nullptr) { @@ -2861,12 +2887,15 @@ bool AdapterHandlerLibrary::generate_adapter_code(AdapterBlob*& adapter_blob, assert(success || !AOTCodeCache::is_dumping_adapter(), "caching of adapter must be disabled"); } handler->relocate(adapter_blob->content_begin()); +#endif // ZERO + #ifndef PRODUCT // debugging support if (PrintAdapterHandlers || PrintStubCode) { print_adapter_handler_info(tty, handler, adapter_blob); } #endif + return true; } diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index 86975f7d0a6..82608737be8 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -168,14 +168,6 @@ static BufferBlob* initialize_stubs(BlobId blob_id, const char* assert_msg) { assert(StubInfo::is_stubgen(blob_id), "not a stubgen blob %s", StubInfo::name(blob_id)); ResourceMark rm; - if (code_size == 0) { - LogTarget(Info, stubs) lt; - if (lt.is_enabled()) { - LogStream ls(lt); - ls.print_cr("%s\t not generated", buffer_name); - } - return nullptr; - } TraceTime timer(timer_msg, TRACETIME_LOG(Info, startuptime)); // Add extra space for large CodeEntryAlignment int size = code_size + CodeEntryAlignment * max_aligned_stubs; @@ -196,9 +188,18 @@ static BufferBlob* initialize_stubs(BlobId blob_id, } CodeBuffer buffer(stubs_code); StubGenerator_generate(&buffer, blob_id); + if (code_size == 0) { + assert(buffer.insts_size() == 0, "should not write into buffer when bob size declared as 0"); + LogTarget(Info, stubs) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr("%s\t not generated", buffer_name); + } + return nullptr; + } // When new stubs added we need to make sure there is some space left // to catch situation when we should increase size again. - assert(code_size == 0 || buffer.insts_remaining() > 200, + assert(buffer.insts_remaining() > 200, "increase %s, code_size: %d, used: %d, free: %d", assert_msg, code_size, buffer.total_content_size(), buffer.insts_remaining()); diff --git a/src/hotspot/share/runtime/synchronizer.hpp b/src/hotspot/share/runtime/synchronizer.hpp index 089a432a8b5..e35c3651f5b 100644 --- a/src/hotspot/share/runtime/synchronizer.hpp +++ b/src/hotspot/share/runtime/synchronizer.hpp @@ -30,7 +30,7 @@ #include "runtime/basicLock.hpp" #include "runtime/handles.hpp" #include "runtime/javaThread.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" template class GrowableArray; class LogStream; diff --git a/src/hotspot/share/runtime/threadSMR.cpp b/src/hotspot/share/runtime/threadSMR.cpp index c8af260f66e..6247b351573 100644 --- a/src/hotspot/share/runtime/threadSMR.cpp +++ b/src/hotspot/share/runtime/threadSMR.cpp @@ -39,9 +39,9 @@ #include "utilities/copy.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/hashTable.hpp" #include "utilities/ostream.hpp" #include "utilities/powerOfTwo.hpp" -#include "utilities/resourceHash.hpp" #include "utilities/vmError.hpp" // The '_cnt', '_max' and '_times" fields are enabled via @@ -191,15 +191,15 @@ class ThreadScanHashtable : public CHeapObj { return (unsigned int)(((uint32_t)(uintptr_t)s1) * 2654435761u); } - // ResourceHashtable SIZE is specified at compile time so we + // HashTable SIZE is specified at compile time so we // use 1031 which is the first prime after 1024. - typedef ResourceHashtable PtrTable; PtrTable * _ptrs; public: - // ResourceHashtable is passed to various functions and populated in + // HashTable is passed to various functions and populated in // different places so we allocate it using C_HEAP to make it immune // from any ResourceMarks that happen to be in the code paths. ThreadScanHashtable() : _ptrs(new (mtThread) PtrTable()) {} diff --git a/src/hotspot/share/runtime/vframeArray.cpp b/src/hotspot/share/runtime/vframeArray.cpp index e17961fc424..224bce4513a 100644 --- a/src/hotspot/share/runtime/vframeArray.cpp +++ b/src/hotspot/share/runtime/vframeArray.cpp @@ -23,6 +23,7 @@ */ #include "classfile/vmSymbols.hpp" +#include "code/scopeDesc.hpp" #include "code/vmreg.inline.hpp" #include "interpreter/bytecode.hpp" #include "interpreter/bytecode.inline.hpp" @@ -61,7 +62,10 @@ void vframeArrayElement::fill_in(compiledVFrame* vf, bool realloc_failures) { _method = vf->method(); _bci = vf->raw_bci(); - _reexecute = vf->should_reexecute(); + _reexecute = vf->should_reexecute(); // initial value, updated in unpack_on_stack +#if INCLUDE_JVMCI + _rethrow = vf->scope()->rethrow_exception(); +#endif #ifdef ASSERT _removed_monitors = false; #endif @@ -171,7 +175,34 @@ void vframeArrayElement::fill_in(compiledVFrame* vf, bool realloc_failures) { } } -int unpack_counter = 0; +static int unpack_counter = 0; + +bool vframeArrayElement::should_reexecute(bool is_top_frame, int exec_mode) const { + if (is_top_frame) { + switch (exec_mode) { + case Deoptimization::Unpack_uncommon_trap: + case Deoptimization::Unpack_reexecute: + return true; + case Deoptimization::Unpack_exception: + assert(raw_bci() >= 0, "bad bci %d for Unpack_exception", raw_bci()); + default: + break; + } + } + if (raw_bci() == SynchronizationEntryBCI) { + return true; + } + bool reexec = should_reexecute(); + assert(is_top_frame || reexec == false, "unexepected should_reexecute()"); +#ifdef ASSERT + if (!reexec) { + address bcp = method()->bcp_from(bci()); + Bytecodes::Code code = Bytecodes::code_at(method(), bcp); + assert(!Interpreter::bytecode_should_reexecute(code), "should_reexecute mismatch"); + } +#endif + return reexec; +} void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, int callee_parameters, @@ -189,20 +220,37 @@ void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, // C++ interpreter doesn't need a pc since it will figure out what to do when it // begins execution address pc; - bool use_next_mdp = false; // true if we should use the mdp associated with the next bci - // rather than the one associated with bcp - if (raw_bci() == SynchronizationEntryBCI) { + bool reexecute = should_reexecute(is_top_frame, exec_mode); + if (is_top_frame && exec_mode == Deoptimization::Unpack_exception) { + assert(raw_bci() >= 0, "bad bci %d for Unpack_exception", raw_bci()); + bcp = method()->bcp_from(bci()); + // exception is pending + pc = Interpreter::rethrow_exception_entry(); + // [phh] We're going to end up in some handler or other, so it doesn't + // matter what mdp we point to. See exception_handler_for_exception() + // in interpreterRuntime.cpp. + } else if (raw_bci() == SynchronizationEntryBCI) { // We are deoptimizing while hanging in prologue code for synchronized method bcp = method()->bcp_from(0); // first byte code pc = Interpreter::deopt_entry(vtos, 0); // step = 0 since we don't skip current bytecode - } else if (should_reexecute()) { //reexecute this bytecode + assert(reexecute, "must be"); + } else if (reexecute) { //reexecute this bytecode assert(is_top_frame, "reexecute allowed only for the top frame"); bcp = method()->bcp_from(bci()); - pc = Interpreter::deopt_reexecute_entry(method(), bcp); + switch (exec_mode) { + case Deoptimization::Unpack_uncommon_trap: + case Deoptimization::Unpack_reexecute: + // Do not special-case _athrow or _return_register_finalizer + pc = Interpreter::deopt_entry(vtos, 0); + break; + default: + // Yes, special-case _athrow and _return_register_finalizer + pc = Interpreter::deopt_reexecute_entry(method(), bcp); + } } else { bcp = method()->bcp_from(bci()); + assert(!reexecute, "must be"); pc = Interpreter::deopt_continue_after_entry(method(), bcp, callee_parameters, is_top_frame); - use_next_mdp = true; } assert(Bytecodes::is_defined(*bcp), "must be a valid bytecode"); @@ -239,44 +287,32 @@ void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, } else { // Reexecute invoke in top frame pc = Interpreter::deopt_entry(vtos, 0); - use_next_mdp = false; +#ifdef ASSERT + Bytecodes::Code code = Bytecodes::code_at(method(), bcp); + assert(Bytecodes::is_invoke(code), "must be"); + assert(!reexecute, "must be"); +#endif + // It would be nice if the VerifyStack logic in unpack_frames() was refactored so + // we could check the stack before and after changing the reexecute mode, but + // it should pass either way because an invoke uses the same stack state for both modes, + // which is: args popped but result not yet pushed. + reexecute = true; popframe_preserved_args_size_in_bytes = in_bytes(thread->popframe_preserved_args_size()); // Note: the PopFrame-related extension of the expression stack size is done in // Deoptimization::fetch_unroll_info_helper popframe_preserved_args_size_in_words = in_words(thread->popframe_preserved_args_size_in_words()); } - } else if (!realloc_failure_exception && JvmtiExport::can_force_early_return() && state != nullptr && - state->is_earlyret_pending()) { - // Force early return from top frame after deoptimization - pc = Interpreter::remove_activation_early_entry(state->earlyret_tos()); - } else { - if (realloc_failure_exception && JvmtiExport::can_force_early_return() && state != nullptr && state->is_earlyret_pending()) { + } else if (JvmtiExport::can_force_early_return() && state != nullptr && state->is_earlyret_pending()) { + if (!realloc_failure_exception) { + // Force early return from top frame after deoptimization + pc = Interpreter::remove_activation_early_entry(state->earlyret_tos()); + } else { state->clr_earlyret_pending(); state->set_earlyret_oop(nullptr); state->clr_earlyret_value(); } - // Possibly override the previous pc computation of the top (youngest) frame - switch (exec_mode) { - case Deoptimization::Unpack_deopt: - // use what we've got - break; - case Deoptimization::Unpack_exception: - // exception is pending - pc = SharedRuntime::raw_exception_handler_for_return_address(thread, pc); - // [phh] We're going to end up in some handler or other, so it doesn't - // matter what mdp we point to. See exception_handler_for_exception() - // in interpreterRuntime.cpp. - break; - case Deoptimization::Unpack_uncommon_trap: - case Deoptimization::Unpack_reexecute: - // redo last byte code - pc = Interpreter::deopt_entry(vtos, 0); - use_next_mdp = false; - break; - default: - ShouldNotReachHere(); - } } + _reexecute = reexecute; } // Setup the interpreter frame @@ -317,17 +353,16 @@ void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, assert(src->obj() != nullptr || ObjectSynchronizer::current_thread_holds_lock(thread, Handle(thread, src->obj())), "should be held, after move_to"); } - if (ProfileInterpreter) { - iframe()->interpreter_frame_set_mdp(nullptr); // clear out the mdp. - } iframe()->interpreter_frame_set_bcp(bcp); if (ProfileInterpreter) { MethodData* mdo = method()->method_data(); - if (mdo != nullptr) { + if (mdo != nullptr && exec_mode != Deoptimization::Unpack_exception) { int bci = iframe()->interpreter_frame_bci(); - if (use_next_mdp) ++bci; + if (!reexecute) ++bci; address mdp = mdo->bci_to_dp(bci); iframe()->interpreter_frame_set_mdp(mdp); + } else { + iframe()->interpreter_frame_set_mdp(nullptr); // clear out the mdp. } } diff --git a/src/hotspot/share/runtime/vframeArray.hpp b/src/hotspot/share/runtime/vframeArray.hpp index b270046252d..d7390bedff4 100644 --- a/src/hotspot/share/runtime/vframeArray.hpp +++ b/src/hotspot/share/runtime/vframeArray.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,6 +56,9 @@ class vframeArrayElement { frame _frame; // the interpreter frame we will unpack into int _bci; // raw bci for this vframe bool _reexecute; // whether we should reexecute this bytecode +#if INCLUDE_JVMCI + bool _rethrow; // from ScopeDesc::rethrow_exception() +#endif Method* _method; // the method for this vframe MonitorChunk* _monitors; // active monitors for this vframe StackValueCollection* _locals; @@ -71,7 +74,11 @@ class vframeArrayElement { int bci(void) const; int raw_bci(void) const { return _bci; } + bool should_reexecute(bool is_top_frame, int exec_mode) const; bool should_reexecute(void) const { return _reexecute; } +#if INCLUDE_JVMCI + bool rethrow_exception(void) const { return _rethrow; } +#endif Method* method(void) const { return _method; } diff --git a/src/hotspot/share/runtime/vmOperations.cpp b/src/hotspot/share/runtime/vmOperations.cpp index da833150d74..2cfc84376af 100644 --- a/src/hotspot/share/runtime/vmOperations.cpp +++ b/src/hotspot/share/runtime/vmOperations.cpp @@ -291,9 +291,9 @@ class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView { AnyObj::C_HEAP, mtThread, AllocFailStrategy::RETURN_NULL> {}; - // ResourceHashtable SIZE is specified at compile time so we + // HashTable SIZE is specified at compile time so we // use 1031 which is the first prime after 1024. - typedef ResourceHashtable PtrTable; PtrTable* _ptrs; size_t _key_count; @@ -326,7 +326,7 @@ class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView { } public: - // ResourceHashtable is passed to various functions and populated in + // HashTable is passed to various functions and populated in // different places so we allocate it using C_HEAP to make it immune // from any ResourceMarks that happen to be in the code paths. ObjectMonitorsDump() : _ptrs(new (mtThread) PtrTable), _key_count(0), _om_count(0) {} diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp index 2e299083ac3..051096ae24a 100644 --- a/src/hotspot/share/services/heapDumper.cpp +++ b/src/hotspot/share/services/heapDumper.cpp @@ -831,7 +831,7 @@ public: class DumperClassCacheTable { private: - // ResourceHashtable SIZE is specified at compile time so we + // HashTable SIZE is specified at compile time so we // use 1031 which is the first prime after 1024. static constexpr size_t TABLE_SIZE = 1031; @@ -841,7 +841,7 @@ private: // sized table from overloading. static constexpr int CACHE_TOP = 256; - typedef ResourceHashtable PtrTable; PtrTable* _ptrs; diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 181786cd238..2a179f5814f 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -1332,7 +1332,7 @@ typedef const char* ccstr; typedef const char* ccstrlist; // represents string arguments which accumulate //---------------------------------------------------------------------------------------------------- -// Default hash/equals functions used by ResourceHashtable +// Default hash/equals functions used by HashTable template unsigned primitive_hash(const K& k) { unsigned hash = (unsigned)((uintptr_t)k); diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index 759b9451490..53403ca5cf1 100644 --- a/src/hotspot/share/utilities/growableArray.hpp +++ b/src/hotspot/share/utilities/growableArray.hpp @@ -93,12 +93,6 @@ public: bool is_empty() const { return _len == 0; } bool is_nonempty() const { return _len != 0; } bool is_full() const { return _len == _capacity; } - - void clear() { _len = 0; } - void trunc_to(int length) { - assert(length <= _len,"cannot increase length"); - _len = length; - } }; template class GrowableArrayIterator; @@ -189,11 +183,6 @@ public: return GrowableArrayIterator(this, length()); } - E pop() { - assert(_len > 0, "empty list"); - return _data[--_len]; - } - void at_put(int i, const E& elem) { assert(0 <= i && i < _len, "illegal index %d for length %d", i, _len); _data[i] = elem; @@ -247,59 +236,6 @@ public: return -1; } - // Order preserving remove operations. - - void remove(const E& elem) { - // Assuming that element does exist. - bool removed = remove_if_existing(elem); - if (removed) return; - ShouldNotReachHere(); - } - - bool remove_if_existing(const E& elem) { - // Returns TRUE if elem is removed. - for (int i = 0; i < _len; i++) { - if (_data[i] == elem) { - remove_at(i); - return true; - } - } - return false; - } - - void remove_at(int index) { - assert(0 <= index && index < _len, "illegal index %d for length %d", index, _len); - for (int j = index + 1; j < _len; j++) { - _data[j-1] = _data[j]; - } - _len--; - } - - // Remove all elements up to the index (exclusive). The order is preserved. - void remove_till(int idx) { - remove_range(0, idx); - } - - // Remove all elements in the range [start - end). The order is preserved. - void remove_range(int start, int end) { - assert(0 <= start, "illegal start index %d", start); - assert(start < end && end <= _len, "erase called with invalid range (%d, %d) for length %d", start, end, _len); - - for (int i = start, j = end; j < length(); i++, j++) { - at_put(i, at(j)); - } - trunc_to(length() - (end - start)); - } - - // The order is changed. - void delete_at(int index) { - assert(0 <= index && index < _len, "illegal index %d for length %d", index, _len); - if (index < --_len) { - // Replace removed element with last one. - _data[index] = _data[_len]; - } - } - void sort(int f(E*, E*)) { if (_data == nullptr) return; qsort(_data, length(), sizeof(E), (_sort_Fn)f); @@ -427,6 +363,11 @@ public: void push(const E& elem) { append(elem); } + E pop() { + assert(this->_len > 0, "empty list"); + return this->_data[--this->_len]; + } + E& at_grow(int i, const E& fill = E()) { assert(0 <= i, "negative index %d", i); if (i >= this->_len) { @@ -514,9 +455,71 @@ public: // Ensure capacity is at least new_capacity. void reserve(int new_capacity); + void trunc_to(int length) { + assert(length <= this->_len,"cannot increase length"); + this->_len = length; + } + + // Order preserving remove operations. + + void remove_at(int index) { + assert(0 <= index && index < this->_len, + "illegal index %d for length %d", index, this->_len); + for (int j = index + 1; j < this->_len; j++) { + this->_data[j-1] = this->_data[j]; + } + this->_len--; + } + + void remove(const E& elem) { + // Assuming that element does exist. + bool removed = this->remove_if_existing(elem); + if (removed) return; + ShouldNotReachHere(); + } + + bool remove_if_existing(const E& elem) { + // Returns TRUE if elem is removed. + for (int i = 0; i < this->_len; i++) { + if (this->_data[i] == elem) { + this->remove_at(i); + return true; + } + } + return false; + } + + // Remove all elements up to the index (exclusive). The order is preserved. + void remove_till(int idx) { + remove_range(0, idx); + } + + // Remove all elements in the range [start - end). The order is preserved. + void remove_range(int start, int end) { + assert(0 <= start, "illegal start index %d", start); + assert(start < end && end <= this->_len, + "erase called with invalid range (%d, %d) for length %d", + start, end, this->_len); + + for (int i = start, j = end; j < this->length(); i++, j++) { + this->at_put(i, this->at(j)); + } + this->_len -= (end - start); + } + + // Replaces the designated element with the last element and shrinks by 1. + void delete_at(int index) { + assert(0 <= index && index < this->_len, "illegal index %d for length %d", index, this->_len); + if (index < --this->_len) { + // Replace removed element with last one. + this->_data[index] = this->_data[this->_len]; + } + } + // Reduce capacity to length. void shrink_to_fit(); + void clear() { this->_len = 0; } void clear_and_deallocate(); }; diff --git a/src/hotspot/share/utilities/resourceHash.hpp b/src/hotspot/share/utilities/hashTable.hpp similarity index 89% rename from src/hotspot/share/utilities/resourceHash.hpp rename to src/hotspot/share/utilities/hashTable.hpp index a99239b21a0..cd85328657a 100644 --- a/src/hotspot/share/utilities/resourceHash.hpp +++ b/src/hotspot/share/utilities/hashTable.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_UTILITIES_RESOURCEHASH_HPP -#define SHARE_UTILITIES_RESOURCEHASH_HPP +#ifndef SHARE_UTILITIES_HASHTABLE_HPP +#define SHARE_UTILITIES_HASHTABLE_HPP #include "memory/allocation.hpp" #include "utilities/globalDefinitions.hpp" @@ -33,20 +33,20 @@ #include template -class ResourceHashtableNode : public AnyObj { +class HashTableNode : public AnyObj { public: unsigned _hash; K _key; V _value; - ResourceHashtableNode* _next; + HashTableNode* _next; - ResourceHashtableNode(unsigned hash, K const& key, V const& value, - ResourceHashtableNode* next = nullptr) : + HashTableNode(unsigned hash, K const& key, V const& value, + HashTableNode* next = nullptr) : _hash(hash), _key(key), _value(value), _next(next) {} // Create a node with a default-constructed value. - ResourceHashtableNode(unsigned hash, K const& key, - ResourceHashtableNode* next = nullptr) : + HashTableNode(unsigned hash, K const& key, + HashTableNode* next = nullptr) : _hash(hash), _key(key), _value(), _next(next) {} }; @@ -58,12 +58,12 @@ template< unsigned (*HASH) (K const&), bool (*EQUALS)(K const&, K const&) > -class ResourceHashtableBase : public STORAGE { +class HashTableBase : public STORAGE { static_assert(ALLOC_TYPE == AnyObj::C_HEAP || std::is_trivially_destructible::value, "Destructor for K is only called with C_HEAP"); static_assert(ALLOC_TYPE == AnyObj::C_HEAP || std::is_trivially_destructible::value, "Destructor for V is only called with C_HEAP"); - using Node = ResourceHashtableNode; + using Node = HashTableNode; private: int _number_of_entries; @@ -94,17 +94,17 @@ class ResourceHashtableBase : public STORAGE { Node const** lookup_node(unsigned hash, K const& key) const { return const_cast( - const_cast(this)->lookup_node(hash, key)); + const_cast(this)->lookup_node(hash, key)); } protected: Node** table() const { return STORAGE::table(); } - ResourceHashtableBase() : STORAGE(), _number_of_entries(0) {} - ResourceHashtableBase(unsigned size) : STORAGE(size), _number_of_entries(0) {} - NONCOPYABLE(ResourceHashtableBase); + HashTableBase() : STORAGE(), _number_of_entries(0) {} + HashTableBase(unsigned size) : STORAGE(size), _number_of_entries(0) {} + NONCOPYABLE(HashTableBase); - ~ResourceHashtableBase() { + ~HashTableBase() { if (ALLOC_TYPE == AnyObj::C_HEAP) { Node* const* bucket = table(); const unsigned sz = table_size(); @@ -343,13 +343,13 @@ class ResourceHashtableBase : public STORAGE { }; template -class FixedResourceHashtableStorage : public AnyObj { - using Node = ResourceHashtableNode; +class FixedHashTableStorage : public AnyObj { + using Node = HashTableNode; Node* _table[TABLE_SIZE]; protected: - FixedResourceHashtableStorage() { memset(_table, 0, sizeof(_table)); } - ~FixedResourceHashtableStorage() = default; + FixedHashTableStorage() { memset(_table, 0, sizeof(_table)); } + ~FixedHashTableStorage() = default; constexpr unsigned table_size() const { return TABLE_SIZE; @@ -368,13 +368,13 @@ template< unsigned (*HASH) (K const&) = primitive_hash, bool (*EQUALS)(K const&, K const&) = primitive_equals > -class ResourceHashtable : public ResourceHashtableBase< - FixedResourceHashtableStorage, +class HashTable : public HashTableBase< + FixedHashTableStorage, K, V, ALLOC_TYPE, MEM_TAG, HASH, EQUALS> { - NONCOPYABLE(ResourceHashtable); + NONCOPYABLE(HashTable); public: - ResourceHashtable() : ResourceHashtableBase, + HashTable() : HashTableBase, K, V, ALLOC_TYPE, MEM_TAG, HASH, EQUALS>() {} }; -#endif // SHARE_UTILITIES_RESOURCEHASH_HPP +#endif // SHARE_UTILITIES_HASHTABLE_HPP diff --git a/src/hotspot/share/utilities/nativeCallStack.hpp b/src/hotspot/share/utilities/nativeCallStack.hpp index 5038fe31aef..32635a72d51 100644 --- a/src/hotspot/share/utilities/nativeCallStack.hpp +++ b/src/hotspot/share/utilities/nativeCallStack.hpp @@ -27,8 +27,8 @@ #include "memory/allocation.hpp" #include "nmt/nmtCommon.hpp" +#include "utilities/hashTable.hpp" #include "utilities/ostream.hpp" -#include "utilities/resourceHash.hpp" /* * This class represents a native call path (does not include Java frame) diff --git a/src/hotspot/share/utilities/objectBitSet.hpp b/src/hotspot/share/utilities/objectBitSet.hpp index 124188cd321..73f1c89dd40 100644 --- a/src/hotspot/share/utilities/objectBitSet.hpp +++ b/src/hotspot/share/utilities/objectBitSet.hpp @@ -29,7 +29,7 @@ #include "oops/oop.hpp" #include "oops/oopsHierarchy.hpp" #include "utilities/bitMap.hpp" -#include "utilities/resizeableResourceHash.hpp" +#include "utilities/resizableHashTable.hpp" class MemRegion; @@ -52,7 +52,7 @@ class ObjectBitSet : public CHeapObj { return hash ^ (hash >> 3); } - typedef ResizeableResourceHashtable BitMapFragmentTable; CHeapBitMap* get_fragment_bits(uintptr_t addr); diff --git a/src/hotspot/share/utilities/objectBitSet.inline.hpp b/src/hotspot/share/utilities/objectBitSet.inline.hpp index 482a97bc2d1..e1b4d728b10 100644 --- a/src/hotspot/share/utilities/objectBitSet.inline.hpp +++ b/src/hotspot/share/utilities/objectBitSet.inline.hpp @@ -52,8 +52,8 @@ ObjectBitSet::~ObjectBitSet() { delete current; current = next; } - // destructors for ResourceHashtable base deletes nodes, and - // ResizeableResourceHashtableStorage deletes the table. + // destructors for HashTable base deletes nodes, and + // ResizeableHashTableStorage deletes the table. } template diff --git a/src/hotspot/share/utilities/resizeableResourceHash.hpp b/src/hotspot/share/utilities/resizableHashTable.hpp similarity index 86% rename from src/hotspot/share/utilities/resizeableResourceHash.hpp rename to src/hotspot/share/utilities/resizableHashTable.hpp index b0c992bf1ef..b445ed8a55e 100644 --- a/src/hotspot/share/utilities/resizeableResourceHash.hpp +++ b/src/hotspot/share/utilities/resizableHashTable.hpp @@ -22,28 +22,28 @@ * */ -#ifndef SHARE_UTILITIES_RESIZEABLERESOURCEHASH_HPP -#define SHARE_UTILITIES_RESIZEABLERESOURCEHASH_HPP +#ifndef SHARE_UTILITIES_RESIZEABLEHASHTABLE_HPP +#define SHARE_UTILITIES_RESIZEABLEHASHTABLE_HPP -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" template< typename K, typename V, AnyObj::allocation_type ALLOC_TYPE, MemTag MEM_TAG> -class ResizeableResourceHashtableStorage : public AnyObj { - using Node = ResourceHashtableNode; +class ResizeableHashTableStorage : public AnyObj { + using Node = HashTableNode; protected: unsigned _table_size; Node** _table; - ResizeableResourceHashtableStorage(unsigned table_size) { + ResizeableHashTableStorage(unsigned table_size) { _table_size = table_size; _table = alloc_table(table_size); } - ~ResizeableResourceHashtableStorage() { + ~ResizeableHashTableStorage() { if (ALLOC_TYPE == C_HEAP) { FREE_C_HEAP_ARRAY(Node*, _table); } @@ -76,15 +76,15 @@ template< unsigned (*HASH) (K const&) = primitive_hash, bool (*EQUALS)(K const&, K const&) = primitive_equals > -class ResizeableResourceHashtable : public ResourceHashtableBase< - ResizeableResourceHashtableStorage, +class ResizeableHashTable : public HashTableBase< + ResizeableHashTableStorage, K, V, ALLOC_TYPE, MEM_TAG, HASH, EQUALS> { unsigned _max_size; - using BASE = ResourceHashtableBase, + using BASE = HashTableBase, K, V, ALLOC_TYPE, MEM_TAG, HASH, EQUALS>; - using Node = ResourceHashtableNode; - NONCOPYABLE(ResizeableResourceHashtable); + using Node = HashTableNode; + NONCOPYABLE(ResizeableHashTable); // Calculate next "good" hashtable size based on requested count int calculate_resize(bool use_large_table_sizes) const { @@ -111,7 +111,7 @@ class ResizeableResourceHashtable : public ResourceHashtableBase< } public: - ResizeableResourceHashtable(unsigned size, unsigned max_size) + ResizeableHashTable(unsigned size, unsigned max_size) : BASE(size), _max_size(max_size) { assert(size <= 0x3fffffff && max_size <= 0x3fffffff, "avoid overflow in resize"); } @@ -180,4 +180,4 @@ public: #endif // ASSERT }; -#endif // SHARE_UTILITIES_RESIZEABLERESOURCEHASH_HPP +#endif // SHARE_UTILITIES_RESIZABLEHASHTABLE_HPP diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index bd882a7ef3c..19c9773dfe5 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -104,8 +104,8 @@ int VMError::_lineno; size_t VMError::_size; const size_t VMError::_reattempt_required_stack_headroom = 64 * K; const intptr_t VMError::segfault_address = pd_segfault_address; -volatile intptr_t VMError::_handshake_timed_out_thread = p2i(nullptr); -volatile intptr_t VMError::_safepoint_timed_out_thread = p2i(nullptr); +Thread* volatile VMError::_handshake_timed_out_thread = nullptr; +Thread* volatile VMError::_safepoint_timed_out_thread = nullptr; // List of environment variables that should be reported in error log file. static const char* env_list[] = { @@ -821,10 +821,10 @@ void VMError::report(outputStream* st, bool _verbose) { st->print(" (0x%x)", _id); // signal number st->print(" at pc=" PTR_FORMAT, p2i(_pc)); if (_siginfo != nullptr && os::signal_sent_by_kill(_siginfo)) { - if (_handshake_timed_out_thread == p2i(_thread)) { - st->print(" (sent by handshake timeout handler"); - } else if (_safepoint_timed_out_thread == p2i(_thread)) { - st->print(" (sent by safepoint timeout handler"); + if (get_handshake_timed_out_thread() == _thread) { + st->print(" (sent by handshake timeout handler)"); + } else if (get_safepoint_timed_out_thread() == _thread) { + st->print(" (sent by safepoint timeout handler)"); } else { st->print(" (sent by kill)"); } @@ -1338,12 +1338,24 @@ void VMError::report(outputStream* st, bool _verbose) { # undef END } -void VMError::set_handshake_timed_out_thread(intptr_t thread_addr) { - _handshake_timed_out_thread = thread_addr; +void VMError::set_handshake_timed_out_thread(Thread* thread) { + // Only preserve the first thread to time-out this way. The atomic operation ensures + // visibility to the target thread. + Atomic::replace_if_null(&_handshake_timed_out_thread, thread); } -void VMError::set_safepoint_timed_out_thread(intptr_t thread_addr) { - _safepoint_timed_out_thread = thread_addr; +void VMError::set_safepoint_timed_out_thread(Thread* thread) { + // Only preserve the first thread to time-out this way. The atomic operation ensures + // visibility to the target thread. + Atomic::replace_if_null(&_safepoint_timed_out_thread, thread); +} + +Thread* VMError::get_handshake_timed_out_thread() { + return Atomic::load(&_handshake_timed_out_thread); +} + +Thread* VMError::get_safepoint_timed_out_thread() { + return Atomic::load(&_safepoint_timed_out_thread); } // Report for the vm_info_cmd. This prints out the information above omitting diff --git a/src/hotspot/share/utilities/vmError.hpp b/src/hotspot/share/utilities/vmError.hpp index 96e37acff57..5e5e074fb20 100644 --- a/src/hotspot/share/utilities/vmError.hpp +++ b/src/hotspot/share/utilities/vmError.hpp @@ -143,8 +143,8 @@ class VMError : public AllStatic { static void clear_step_start_time(); // Handshake/safepoint timed out threads - static volatile intptr_t _handshake_timed_out_thread; - static volatile intptr_t _safepoint_timed_out_thread; + static Thread* volatile _handshake_timed_out_thread; + static Thread* volatile _safepoint_timed_out_thread; WINDOWS_ONLY([[noreturn]] static void raise_fail_fast(const void* exrecord, const void* context);) @@ -223,8 +223,10 @@ public: static bool was_assert_poison_crash(const void* sigInfo); - static void set_handshake_timed_out_thread(intptr_t thread_addr); - static void set_safepoint_timed_out_thread(intptr_t thread_addr); + static void set_handshake_timed_out_thread(Thread* thread); + static void set_safepoint_timed_out_thread(Thread* thread); + static Thread* get_handshake_timed_out_thread(); + static Thread* get_safepoint_timed_out_thread(); }; class VMErrorCallback { diff --git a/src/java.base/share/classes/java/io/DataInputStream.java b/src/java.base/share/classes/java/io/DataInputStream.java index daf75b1318f..e019589ac61 100644 --- a/src/java.base/share/classes/java/io/DataInputStream.java +++ b/src/java.base/share/classes/java/io/DataInputStream.java @@ -599,7 +599,7 @@ loop: while (true) { if (ascii == utflen) { String str; if (trusted) { - str = JLA.uncheckedNewStringNoRepl(bytearr, StandardCharsets.ISO_8859_1); + str = JLA.uncheckedNewStringWithLatin1Bytes(bytearr); } else { str = new String(bytearr, 0, utflen, StandardCharsets.ISO_8859_1); } diff --git a/src/java.base/share/classes/java/lang/FdLibm.java b/src/java.base/share/classes/java/lang/FdLibm.java index 70d728a16db..73e1da46af4 100644 --- a/src/java.base/share/classes/java/lang/FdLibm.java +++ b/src/java.base/share/classes/java/lang/FdLibm.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package java.lang; +import jdk.internal.vm.annotation.Stable; + /** * Port of the "Freely Distributable Math Library", version 5.3, from * C to Java. @@ -451,8 +453,10 @@ final class FdLibm { */ private static final double pio4 = 0x1.921fb54442d18p-1, // 7.85398163397448278999e-01 - pio4lo= 0x1.1a62633145c07p-55, // 3.06161699786838301793e-17 - T[] = { + pio4lo= 0x1.1a62633145c07p-55; // 3.06161699786838301793e-17 + @Stable + private static final double[] + T = { 0x1.5555555555563p-2, // 3.33333333333334091986e-01 0x1.111111110fe7ap-3, // 1.33333333333201242699e-01 0x1.ba1ba1bb341fep-5, // 5.39682539762260521377e-02 @@ -546,6 +550,7 @@ final class FdLibm { /* * Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi */ + @Stable private static final int[] two_over_pi = { 0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, 0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, @@ -560,6 +565,7 @@ final class FdLibm { 0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, }; + @Stable private static final int[] npio2_hw = { 0x3FF921FB, 0x400921FB, 0x4012D97C, 0x401921FB, 0x401F6A7A, 0x4022D97C, 0x4025FDBB, 0x402921FB, 0x402C463A, 0x402F6A7A, 0x4031475C, 0x4032D97C, @@ -807,8 +813,10 @@ final class FdLibm { * to produce the hexadecimal values shown. */ + @Stable private static final int init_jk[] = {2, 3, 4, 6}; // initial value for jk + @Stable private static final double PIo2[] = { 0x1.921fb4p0, // 1.57079625129699707031e+00 0x1.4442dp-24, // 7.54978941586159635335e-08 @@ -1232,6 +1240,7 @@ final class FdLibm { static final class Atan { private Atan() {throw new UnsupportedOperationException();} + @Stable private static final double atanhi[] = { 0x1.dac670561bb4fp-2, // atan(0.5)hi 4.63647609000806093515e-01 0x1.921fb54442d18p-1, // atan(1.0)hi 7.85398163397448278999e-01 @@ -1239,6 +1248,7 @@ final class FdLibm { 0x1.921fb54442d18p0, // atan(inf)hi 1.57079632679489655800e+00 }; + @Stable private static final double atanlo[] = { 0x1.a2b7f222f65e2p-56, // atan(0.5)lo 2.26987774529616870924e-17 0x1.1a62633145c07p-55, // atan(1.0)lo 3.06161699786838301793e-17 @@ -1246,6 +1256,7 @@ final class FdLibm { 0x1.1a62633145c07p-54, // atan(inf)lo 6.12323399573676603587e-17 }; + @Stable private static final double aT[] = { 0x1.555555555550dp-2, // 3.33333333333329318027e-01 -0x1.999999998ebc4p-3, // -1.99999999998764832476e-01 @@ -2245,12 +2256,8 @@ final class FdLibm { // Compute ss = s_h + s_l = (x-1)/(x+1) or (x-1.5)/(x+1.5) - final double BP[] = {1.0, - 1.5}; - final double DP_H[] = {0.0, - 0x1.2b80_34p-1}; // 5.84962487220764160156e-01 - final double DP_L[] = {0.0, - 0x1.cfde_b43c_fd006p-27};// 1.35003920212974897128e-08 + final double DP_H = 0x1.2b80_34p-1; // 5.84962487220764160156e-01 + final double DP_L = 0x1.cfde_b43c_fd006p-27; // 1.35003920212974897128e-08 // Poly coefs for (3/2)*(log(x)-2s-2/3*s**3 final double L1 = 0x1.3333_3333_33303p-1; // 5.99999999999994648725e-01 @@ -2259,15 +2266,17 @@ final class FdLibm { final double L4 = 0x1.1746_0a91_d4101p-2; // 2.72728123808534006489e-01 final double L5 = 0x1.d864_a93c_9db65p-3; // 2.30660745775561754067e-01 final double L6 = 0x1.a7e2_84a4_54eefp-3; // 2.06975017800338417784e-01 - u = x_abs - BP[k]; // BP[0]=1.0, BP[1]=1.5 - v = 1.0 / (x_abs + BP[k]); + + double BP_k = 1.0 + 0.5*k; // BP[0]=1.0, BP[1]=1.5 + u = x_abs - BP_k; + v = 1.0 / (x_abs + BP_k); ss = u * v; s_h = ss; s_h = __LO(s_h, 0); // t_h=x_abs + BP[k] High t_h = 0.0; t_h = __HI(t_h, ((ix >> 1) | 0x20000000) + 0x00080000 + (k << 18) ); - t_l = x_abs - (t_h - BP[k]); + t_l = x_abs - (t_h - BP_k); s_l = v * ((u - s_h * t_h) - s_h * t_l); // Compute log(x_abs) s2 = ss * ss; @@ -2285,12 +2294,12 @@ final class FdLibm { p_h = __LO(p_h, 0); p_l = v - (p_h - u); z_h = CP_H * p_h; // CP_H + CP_L = 2/(3*log2) - z_l = CP_L * p_h + p_l * CP + DP_L[k]; + z_l = CP_L * p_h + p_l * CP + DP_L*k; // log2(x_abs) = (ss + ..)*2/(3*log2) = n + DP_H + z_h + z_l t = (double)n; - t1 = (((z_h + z_l) + DP_H[k]) + t); + t1 = (((z_h + z_l) + DP_H*k) + t); t1 = __LO(t1, 0); - t2 = z_l - (((t1 - t) - DP_H[k]) - z_h); + t2 = z_l - (((t1 - t) - DP_H*k) - z_h); } // Split up y into (y1 + y2) and compute (y1 + y2) * (t1 + t2) @@ -2430,13 +2439,13 @@ final class FdLibm { static final class Exp { private Exp() {throw new UnsupportedOperationException();} - private static final double[] half = {0.5, -0.5,}; private static final double huge = 1.0e+300; private static final double twom1000= 0x1.0p-1000; // 9.33263618503218878990e-302 = 2^-1000 private static final double o_threshold= 0x1.62e42fefa39efp9; // 7.09782712893383973096e+02 private static final double u_threshold= -0x1.74910d52d3051p9; // -7.45133219101941108420e+02; - private static final double[] ln2HI ={ 0x1.62e42feep-1, // 6.93147180369123816490e-01 - -0x1.62e42feep-1}; // -6.93147180369123816490e-01 + private static final double ln2HI = 0x1.62e42feep-1; // 6.93147180369123816490e-01 + + @Stable private static final double[] ln2LO ={ 0x1.a39ef35793c76p-33, // 1.90821492927058770002e-10 -0x1.a39ef35793c76p-33}; // -1.90821492927058770002e-10 private static final double invln2 = 0x1.71547652b82fep0; // 1.44269504088896338700e+00 @@ -2478,13 +2487,13 @@ final class FdLibm { /* argument reduction */ if (hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */ if(hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */ - hi = x - ln2HI[xsb]; + hi = x - ln2HI*(1 - 2*xsb); /* +/- ln2HI */ lo=ln2LO[xsb]; k = 1 - xsb - xsb; } else { - k = (int)(invln2 * x + half[xsb]); + k = (int)(invln2 * x + 0.5 * (1 - 2*xsb) /* +/- 0.5 */ ); t = k; - hi = x - t*ln2HI[0]; /* t*ln2HI is exact here */ + hi = x - t*ln2HI; /* t*ln2HI is exact here */ lo = t*ln2LO[0]; } x = hi - lo; diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index eac8a1355b7..15b8e98369e 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -690,13 +690,10 @@ public final class String /* * Throws iae, instead of replacing, if malformed or unmappable. - * - * @param noShare - * {@code true} if the resulting string MUST NOT share the byte array, - * {@code false} if the byte array can be exclusively used to construct - * the string and is not modified or used for any other purpose. + * The byte array can be exclusively used to construct + * the string and is not modified or used for any other purpose. */ - static String newStringUTF8NoRepl(byte[] bytes, int offset, int length, boolean noShare) { + private static String newStringUTF8NoRepl(byte[] bytes, int offset, int length) { checkBoundsOffCount(offset, length, bytes.length); if (length == 0) { return ""; @@ -707,7 +704,7 @@ public final class String dp = StringCoding.countPositives(bytes, offset, length); int sl = offset + length; if (dp == length) { - if (noShare || length != bytes.length) { + if (length != bytes.length) { return new String(Arrays.copyOfRange(bytes, offset, offset + length), LATIN1); } else { return new String(bytes, LATIN1); @@ -759,6 +756,34 @@ public final class String return new String(dst, UTF16); } + /** + * {@return a new String created using the supplied latin1 bytes} + * @param src a byte array with the bytes for a latin1 string + */ + static String newStringWithLatin1Bytes(byte[] src) { + int len = src.length; + if (len == 0) { + return ""; + } + + if (COMPACT_STRINGS) + return new String(src, LATIN1); + return new String(StringLatin1.inflate(src, 0, src.length), UTF16); + } + + /** + * {@return a new {@code String} created using the given byte array that is + * encoded in specified charset} + *

      + * WARNING: The caller of this method is assumed to have relinquished + * and transferred the ownership of the byte array. It can thus be + * exclusively used to construct the {@code String}. + * + * @param src byte array containing encoded characters + * @param cs charset the byte array encoded in + * + * @throws CharacterCodingException for malformed input or unmappable characters + */ static String newStringNoRepl(byte[] src, Charset cs) throws CharacterCodingException { try { return newStringNoRepl1(src, cs); @@ -778,7 +803,7 @@ public final class String return ""; } if (cs == UTF_8.INSTANCE) { - return newStringUTF8NoRepl(src, 0, src.length, false); + return newStringUTF8NoRepl(src, 0, src.length); } if (cs == ISO_8859_1.INSTANCE) { if (COMPACT_STRINGS) @@ -1019,7 +1044,7 @@ public final class String int sp = 0; int sl = len; while (sp < sl) { - int ret = StringCoding.implEncodeISOArray(val, sp, dst, dp, len); + int ret = StringCoding.encodeISOArray(val, sp, dst, dp, len); sp = sp + ret; dp = dp + ret; if (ret != len) { @@ -1284,13 +1309,18 @@ public final class String return encodeUTF8_UTF16(val, doReplace); } - if (!StringCoding.hasNegatives(val, 0, val.length)) { + int positives = StringCoding.countPositives(val, 0, val.length); + if (positives == val.length) { return val.clone(); } - int dp = 0; byte[] dst = StringUTF16.newBytesFor(val.length); - for (byte c : val) { + if (positives > 0) { + System.arraycopy(val, 0, dst, 0, positives); + } + int dp = positives; + for (int i = dp; i < val.length; i++) { + byte c = val[i]; if (c < 0) { dst[dp++] = (byte) (0xc0 | ((c & 0xff) >> 6)); dst[dp++] = (byte) (0x80 | (c & 0x3f)); diff --git a/src/java.base/share/classes/java/lang/StringCoding.java b/src/java.base/share/classes/java/lang/StringCoding.java index c02af28c37d..545f216b755 100644 --- a/src/java.base/share/classes/java/lang/StringCoding.java +++ b/src/java.base/share/classes/java/lang/StringCoding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,8 +26,11 @@ package java.lang; +import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.IntrinsicCandidate; +import java.util.function.BiFunction; + /** * Utility class for string encoding and decoding. */ @@ -38,7 +41,7 @@ class StringCoding { /** * Count the number of leading non-zero ascii chars in the range. */ - public static int countNonZeroAscii(String s) { + static int countNonZeroAscii(String s) { byte[] value = s.value(); if (s.isLatin1()) { return countNonZeroAsciiLatin1(value, 0, value.length); @@ -50,7 +53,7 @@ class StringCoding { /** * Count the number of non-zero ascii chars in the range. */ - public static int countNonZeroAsciiLatin1(byte[] ba, int off, int len) { + private static int countNonZeroAsciiLatin1(byte[] ba, int off, int len) { int limit = off + len; for (int i = off; i < limit; i++) { if (ba[i] <= 0) { @@ -63,7 +66,7 @@ class StringCoding { /** * Count the number of leading non-zero ascii chars in the range. */ - public static int countNonZeroAsciiUTF16(byte[] ba, int off, int strlen) { + private static int countNonZeroAsciiUTF16(byte[] ba, int off, int strlen) { int limit = off + strlen; for (int i = off; i < limit; i++) { char c = StringUTF16.charAt(ba, i); @@ -74,7 +77,7 @@ class StringCoding { return strlen; } - public static boolean hasNegatives(byte[] ba, int off, int len) { + static boolean hasNegatives(byte[] ba, int off, int len) { return countPositives(ba, off, len) != len; } @@ -85,9 +88,24 @@ class StringCoding { * bytes in the range. If there are negative bytes, the implementation must return * a value that is less than or equal to the index of the first negative byte * in the range. + * + * @param ba a byte array + * @param off the index of the first byte to start reading from + * @param len the total number of bytes to read + * @throws NullPointerException if {@code ba} is null + * @throws ArrayIndexOutOfBoundsException if the provided sub-range is + * {@linkplain Preconditions#checkFromIndexSize(int, int, int, BiFunction) out of bounds} */ + static int countPositives(byte[] ba, int off, int len) { + Preconditions.checkFromIndexSize( + off, len, + ba.length, // Implicit null check on `ba` + Preconditions.AIOOBE_FORMATTER); + return countPositives0(ba, off, len); + } + @IntrinsicCandidate - public static int countPositives(byte[] ba, int off, int len) { + private static int countPositives0(byte[] ba, int off, int len) { int limit = off + len; for (int i = off; i < limit; i++) { if (ba[i] < 0) { @@ -97,9 +115,37 @@ class StringCoding { return len; } + /** + * Encodes as many ISO-8859-1 codepoints as possible from the source byte + * array containing characters encoded in UTF-16, into the destination byte + * array, assuming that the encoding is ISO-8859-1 compatible. + * + * @param sa the source byte array containing characters encoded in UTF-16 + * @param sp the index of the character (not byte!) from the source array to start reading from + * @param da the target byte array + * @param dp the index of the target array to start writing to + * @param len the maximum number of characters (not bytes!) to be encoded + * @return the total number of characters (not bytes!) successfully encoded + * @throws NullPointerException if any of the provided arrays is null + */ + static int encodeISOArray(byte[] sa, int sp, + byte[] da, int dp, int len) { + // This method should tolerate invalid arguments, matching the lenient behavior of the VM intrinsic. + // Hence, using operator expressions instead of `Preconditions`, which throw on failure. + int sl; + if ((sp | dp | len) < 0 || + // Halving the length of `sa` to obtain the number of characters: + sp >= (sl = sa.length >>> 1) || // Implicit null check on `sa` + dp >= da.length) { // Implicit null check on `da` + return 0; + } + int minLen = Math.min(len, Math.min(sl - sp, da.length - dp)); + return encodeISOArray0(sa, sp, da, dp, minLen); + } + @IntrinsicCandidate - public static int implEncodeISOArray(byte[] sa, int sp, - byte[] da, int dp, int len) { + private static int encodeISOArray0(byte[] sa, int sp, + byte[] da, int dp, int len) { int i = 0; for (; i < len; i++) { char c = StringUTF16.getChar(sa, sp++); @@ -110,10 +156,35 @@ class StringCoding { return i; } + /** + * Encodes as many ASCII codepoints as possible from the source + * character array into the destination byte array, assuming that + * the encoding is ASCII compatible. + * + * @param sa the source character array + * @param sp the index of the source array to start reading from + * @param da the target byte array + * @param dp the index of the target array to start writing to + * @param len the maximum number of characters to be encoded + * @return the total number of characters successfully encoded + * @throws NullPointerException if any of the provided arrays is null + */ + static int encodeAsciiArray(char[] sa, int sp, + byte[] da, int dp, int len) { + // This method should tolerate invalid arguments, matching the lenient behavior of the VM intrinsic. + // Hence, using operator expressions instead of `Preconditions`, which throw on failure. + if ((sp | dp | len) < 0 || + sp >= sa.length || // Implicit null check on `sa` + dp >= da.length) { // Implicit null check on `da` + return 0; + } + int minLen = Math.min(len, Math.min(sa.length - sp, da.length - dp)); + return encodeAsciiArray0(sa, sp, da, dp, minLen); + } + @IntrinsicCandidate - public static int implEncodeAsciiArray(char[] sa, int sp, - byte[] da, int dp, int len) - { + static int encodeAsciiArray0(char[] sa, int sp, + byte[] da, int dp, int len) { int i = 0; for (; i < len; i++) { char c = sa[sp++]; diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 1d62d698896..f046ed4111a 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -55,7 +55,6 @@ import java.util.Properties; import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.Executor; -import java.util.concurrent.ScheduledExecutorService; import java.util.function.Supplier; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; @@ -2127,6 +2126,11 @@ public final class System { public int countNonZeroAscii(String s) { return StringCoding.countNonZeroAscii(s); } + + public String uncheckedNewStringWithLatin1Bytes(byte[] bytes) { + return String.newStringWithLatin1Bytes(bytes); + } + public String uncheckedNewStringNoRepl(byte[] bytes, Charset cs) throws CharacterCodingException { return String.newStringNoRepl(bytes, cs); } @@ -2140,10 +2144,6 @@ public final class System { return String.getBytesNoRepl(s, cs); } - public String newStringUTF8NoRepl(byte[] bytes, int off, int len) { - return String.newStringUTF8NoRepl(bytes, off, len, true); - } - public byte[] getBytesUTF8NoRepl(String s) { return String.getBytesUTF8NoRepl(s); } @@ -2156,8 +2156,8 @@ public final class System { return String.decodeASCII(src, srcOff, dst, dstOff, len); } - public int uncheckedEncodeASCII(char[] src, int srcOff, byte[] dst, int dstOff, int len) { - return StringCoding.implEncodeAsciiArray(src, srcOff, dst, dstOff, len); + public int encodeASCII(char[] sa, int sp, byte[] da, int dp, int len) { + return StringCoding.encodeAsciiArray(sa, sp, da, dp, len); } public InputStream initialSystemIn() { diff --git a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java index e640700c2e9..c1070bbcc77 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeBuilder.java @@ -175,6 +175,11 @@ public sealed interface CodeBuilder * A builder for blocks of code. Its {@link #startLabel()} and {@link * #endLabel()} do not enclose the entire method body, but from the start to * the end of the block. + *

      + * The location where a block of code merges back to its parent block, as + * represented by the {@link #breakLabel()}, is expected to be reachable, + * either from this block or the parent block. The built code may be + * malformed if there is no executable code at that location. * * @since 24 */ @@ -325,6 +330,11 @@ public sealed interface CodeBuilder /** * A builder to add catch blocks. + *

      + * The order of catch blocks is significant. When an exception is thrown + * by the try block, the first catch block whose exception type is {@linkplain + * Class#isAssignableFrom(Class) the same class as or a superclass of} the + * class of exception thrown is branched to (JVMS {@jvms 2.10}). * * @see #trying * @see ExceptionCatch @@ -343,13 +353,16 @@ public sealed interface CodeBuilder * If the type of exception is {@code null} then the catch block catches * all exceptions. * + * @apiNote + * If the type of exception to catch is already handled by previous + * catch blocks, this block will never be executed. + * * @param exceptionType the type of exception to catch, may be {@code null} * @param catchHandler handler that receives a {@link BlockCodeBuilder} to * generate the body of the catch block * @return this builder - * @throws IllegalArgumentException if an existing catch block catches - * an exception of the given type or {@code exceptionType} - * represents a primitive type + * @throws IllegalArgumentException if {@code exceptionType} represents + * a primitive type * @see #catchingMulti * @see #catchingAll */ @@ -367,12 +380,16 @@ public sealed interface CodeBuilder * If list of exception types is empty then the catch block catches all * exceptions. * + * @apiNote + * If every type of exception to catch is already handled by previous + * catch blocks, this block will never be executed. + * * @param exceptionTypes the types of exception to catch * @param catchHandler handler that receives a {@link BlockCodeBuilder} * to generate the body of the catch block * @return this builder - * @throws IllegalArgumentException if an existing catch block catches - * one or more exceptions of the given types + * @throws IllegalArgumentException if any exception type represents a + * primitive type * @see #catching * @see #catchingAll */ @@ -387,10 +404,12 @@ public sealed interface CodeBuilder * The caught exception will be on top of the operand stack when the * catch block is entered. * + * @apiNote + * Since this block intercepts all exceptions, all subsequent catch + * blocks will never be executed. + * * @param catchAllHandler handler that receives a {@link BlockCodeBuilder} * to generate the body of the catch block - * @throws IllegalArgumentException if an existing catch block catches - * all exceptions * @see #catching * @see #catchingMulti */ diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java b/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java index e924ca718e7..b0e5af2941e 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/ExceptionCatch.java @@ -39,8 +39,10 @@ import jdk.internal.classfile.impl.AbstractPseudoInstruction; * A pseudo-instruction modeling an entry in the {@code exception_table} array * of a {@link CodeAttribute Code} attribute. Catch (JVMS {@jvms 3.12}) and * finally (JVMS {@jvms 3.14}) blocks in Java source code compile to exception - * table entries. Delivered as a {@link CodeElement} when traversing the - * contents of a {@link CodeModel}. + * table entries. The order of exception table entries is significant: when an + * exception is thrown in a method, execution branches to the first matching + * exception handler if such a handler exists (JVMS {@jvms 2.10}). Delivered as + * a {@link CodeElement} when traversing the contents of a {@link CodeModel}. *

      * An exception table entry is composite: * {@snippet lang=text : diff --git a/src/java.base/share/classes/java/lang/constant/ConstantDescs.java b/src/java.base/share/classes/java/lang/constant/ConstantDescs.java index c976342033f..0ef45ec34bb 100644 --- a/src/java.base/share/classes/java/lang/constant/ConstantDescs.java +++ b/src/java.base/share/classes/java/lang/constant/ConstantDescs.java @@ -44,7 +44,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import static java.lang.constant.DirectMethodHandleDesc.*; import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC; /** @@ -338,9 +337,6 @@ public final class ConstantDescs { */ public static final MethodTypeDesc MTD_void = MethodTypeDesc.of(CD_void); - static final DirectMethodHandleDesc MHD_METHODHANDLE_ASTYPE - = MethodHandleDesc.ofMethod(Kind.VIRTUAL, CD_MethodHandle, "asType", - MethodTypeDesc.of(CD_MethodHandle, CD_MethodType)); /** * Returns a {@link MethodHandleDesc} corresponding to a bootstrap method for * an {@code invokedynamic} callsite, which is a static method whose leading diff --git a/src/java.base/share/classes/java/lang/constant/MethodHandleDesc.java b/src/java.base/share/classes/java/lang/constant/MethodHandleDesc.java index 6a3541bf5e9..18786481fa6 100644 --- a/src/java.base/share/classes/java/lang/constant/MethodHandleDesc.java +++ b/src/java.base/share/classes/java/lang/constant/MethodHandleDesc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import jdk.internal.constant.AsTypeMethodHandleDesc; import jdk.internal.constant.DirectMethodHandleDescImpl; import static java.lang.constant.ConstantDescs.CD_void; diff --git a/src/java.base/share/classes/java/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java index 3b7d9e0d65b..c24998344c1 100644 --- a/src/java.base/share/classes/java/math/BigDecimal.java +++ b/src/java.base/share/classes/java/math/BigDecimal.java @@ -35,8 +35,6 @@ import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectStreamException; import java.io.StreamCorruptedException; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Objects; @@ -4149,11 +4147,7 @@ public class BigDecimal extends Number implements Comparable { DecimalDigits.uncheckedGetCharsLatin1(highInt, highIntSize, buf); buf[highIntSize] = '.'; DecimalDigits.uncheckedPutPairLatin1(buf, highIntSize + 1, lowInt); - try { - return JLA.uncheckedNewStringNoRepl(buf, StandardCharsets.ISO_8859_1); - } catch (CharacterCodingException cce) { - throw new AssertionError(cce); - } + return JLA.uncheckedNewStringWithLatin1Bytes(buf); } char[] coeff; diff --git a/src/java.base/share/classes/java/nio/StringCharBuffer.java b/src/java.base/share/classes/java/nio/StringCharBuffer.java index a62e8023363..39cd6910f5d 100644 --- a/src/java.base/share/classes/java/nio/StringCharBuffer.java +++ b/src/java.base/share/classes/java/nio/StringCharBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -97,7 +97,13 @@ final class StringCharBuffer // package-private return str.charAt(index + offset); } - // ## Override bulk get methods for better performance + @Override + CharBuffer getArray(int index, char[] dst, int dstOffset, int length) { + // Note: the variable "offset" is defined and set in the superclass + int srcOffset = offset + index; + str.getChars(srcOffset, srcOffset + length, dst, dstOffset); + return this; + } public final CharBuffer put(char c) { throw new ReadOnlyBufferException(); diff --git a/src/java.base/share/classes/java/nio/X-Buffer.java.template b/src/java.base/share/classes/java/nio/X-Buffer.java.template index c43004cc0ac..b2b96288d57 100644 --- a/src/java.base/share/classes/java/nio/X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/X-Buffer.java.template @@ -950,12 +950,15 @@ public abstract sealed class $Type$Buffer return get(index, dst, 0, dst.length); } - private $Type$Buffer getArray(int index, $type$[] dst, int offset, int length) { - if ( + // + // This method does not perform bounds checking which it expects + // will have been done by the caller + // + $Type$Buffer getArray(int index, $type$[] dst, int offset, int length) { #if[char] - isAddressable() && + assert isAddressable(); #end[char] - ((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) { + if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) { long bufAddr = address + ((long)index << $LG_BYTES_PER_VALUE$); long dstOffset = ARRAY_BASE_OFFSET + ((long)offset << $LG_BYTES_PER_VALUE$); @@ -1116,6 +1119,10 @@ public abstract sealed class $Type$Buffer return this; } + // + // This method does not perform bounds checking which it expects + // will have been done by the caller + // void putBuffer(int pos, $Type$Buffer src, int srcPos, int n) { #if[rw] Object srcBase = src.base(); @@ -1150,9 +1157,13 @@ public abstract sealed class $Type$Buffer #if[char] } else { // src.isAddressable() == false assert StringCharBuffer.class.isInstance(src); - int posMax = pos + n; - for (int i = pos, j = srcPos; i < posMax; i++, j++) - put(i, src.get(j)); + if (this.hb != null && src instanceof StringCharBuffer scb) { + scb.getArray(srcPos, this.hb, pos, n); + } else { + int posMax = pos + n; + for (int i = pos, j = srcPos; i < posMax; i++, j++) + put(i, src.get(j)); + } } #end[char] #else[rw] @@ -1342,6 +1353,10 @@ public abstract sealed class $Type$Buffer return put(index, src, 0, src.length); } + // + // This method does not perform bounds checking which it expects + // will have been done by the caller + // $Type$Buffer putArray(int index, $type$[] src, int offset, int length) { #if[rw] if ( diff --git a/src/java.base/share/classes/java/nio/charset/Charset.java b/src/java.base/share/classes/java/nio/charset/Charset.java index b36e11cdc36..1eb3c9c2094 100644 --- a/src/java.base/share/classes/java/nio/charset/Charset.java +++ b/src/java.base/share/classes/java/nio/charset/Charset.java @@ -37,13 +37,16 @@ import java.nio.charset.spi.CharsetProvider; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.ServiceLoader; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; +import java.util.function.Supplier; /** @@ -381,17 +384,7 @@ public abstract class Charset }; } - private static class ThreadTrackHolder { - static final ThreadTracker TRACKER = new ThreadTracker(); - } - - private static Object tryBeginLookup() { - return ThreadTrackHolder.TRACKER.tryBegin(); - } - - private static void endLookup(Object key) { - ThreadTrackHolder.TRACKER.end(key); - } + private static final ScopedValue IN_LOOKUP = ScopedValue.newInstance(); private static Charset lookupViaProviders(final String charsetName) { @@ -406,48 +399,54 @@ public abstract class Charset if (!VM.isBooted()) return null; - Object key = tryBeginLookup(); - if (key == null) { + if (IN_LOOKUP.isBound()) { // Avoid recursive provider lookups return null; } try { - for (Iterator i = providers(); i.hasNext();) { - CharsetProvider cp = i.next(); - Charset cs = cp.charsetForName(charsetName); - if (cs != null) - return cs; - } - return null; - } finally { - endLookup(key); + return ScopedValue.where(IN_LOOKUP, true).call( + new ScopedValue.CallableOp() { + @Override + public Charset call() { + for (Iterator i = providers(); i.hasNext(); ) { + CharsetProvider cp = i.next(); + Charset cs = cp.charsetForName(charsetName); + if (cs != null) + return cs; + } + return null; + } + } + ); + } catch (Exception t) { + // Should not happen + throw new RuntimeException(t); } + } /* The extended set of charsets */ - private static class ExtendedProviderHolder { - static final CharsetProvider[] extendedProviders = extendedProviders(); - // returns ExtendedProvider, if installed - private static CharsetProvider[] extendedProviders() { - CharsetProvider[] cps = new CharsetProvider[1]; - int n = 0; - ServiceLoader sl = + private static final Supplier> EXTENDED_PROVIDERS = StableValue.supplier( + new Supplier<>() { public List get() { return extendedProviders0(); }}); + + private static List extendedProviders0() { + CharsetProvider[] cps = new CharsetProvider[1]; + int n = 0; + final ServiceLoader sl = ServiceLoader.loadInstalled(CharsetProvider.class); - for (CharsetProvider cp : sl) { - if (n + 1 > cps.length) { - cps = Arrays.copyOf(cps, cps.length << 1); - } - cps[n++] = cp; + for (CharsetProvider cp : sl) { + if (n + 1 > cps.length) { + cps = Arrays.copyOf(cps, cps.length << 1); } - return n == cps.length ? cps : Arrays.copyOf(cps, n); + cps[n++] = cp; } + return List.of(n == cps.length ? cps : Arrays.copyOf(cps, n)); } private static Charset lookupExtendedCharset(String charsetName) { if (!VM.isBooted()) // see lookupViaProviders() return null; - CharsetProvider[] ecps = ExtendedProviderHolder.extendedProviders; - for (CharsetProvider cp : ecps) { + for (CharsetProvider cp : EXTENDED_PROVIDERS.get()) { Charset cs = cp.charsetForName(charsetName); if (cs != null) return cs; @@ -608,8 +607,7 @@ public abstract class Charset new TreeMap<>( String.CASE_INSENSITIVE_ORDER); put(standardProvider.charsets(), m); - CharsetProvider[] ecps = ExtendedProviderHolder.extendedProviders; - for (CharsetProvider ecp :ecps) { + for (CharsetProvider ecp : EXTENDED_PROVIDERS.get()) { put(ecp.charsets(), m); } for (Iterator i = providers(); i.hasNext();) { @@ -619,7 +617,16 @@ public abstract class Charset return Collections.unmodifiableSortedMap(m); } - private @Stable static Charset defaultCharset; + private static final Supplier defaultCharset = StableValue.supplier( + new Supplier<>() { public Charset get() { return defaultCharset0(); }}); + + private static Charset defaultCharset0() { + // do not look for providers other than the standard one + final Charset cs = standardProvider.charsetForName(StaticProperty.fileEncoding()); + return (cs == null) + ? sun.nio.cs.UTF_8.INSTANCE + : cs; + } /** * Returns the default charset of this Java virtual machine. @@ -640,25 +647,19 @@ public abstract class Charset * @since 1.5 */ public static Charset defaultCharset() { - if (defaultCharset == null) { - synchronized (Charset.class) { - // do not look for providers other than the standard one - Charset cs = standardProvider.charsetForName(StaticProperty.fileEncoding()); - if (cs != null) - defaultCharset = cs; - else - defaultCharset = sun.nio.cs.UTF_8.INSTANCE; - } - } - return defaultCharset; + return defaultCharset.get(); } /* -- Instance fields and methods -- */ - private final String name; // tickles a bug in oldjavac - private final String[] aliases; // tickles a bug in oldjavac - private Set aliasSet; + @Stable + private final String name; + @Stable + private final String[] aliases; + @Stable + private final Supplier> aliasSet = StableValue.supplier( + new Supplier<>() { public Set get() { return Set.of(aliases); }}); /** * Initializes a new charset with the given canonical name and alias @@ -710,12 +711,7 @@ public abstract class Charset * @return An immutable set of this charset's aliases */ public final Set aliases() { - Set set = this.aliasSet; - if (set == null) { - set = Set.of(aliases); - this.aliasSet = set; - } - return set; + return aliasSet.get(); } /** diff --git a/src/java.base/share/classes/java/text/DecimalFormat.java b/src/java.base/share/classes/java/text/DecimalFormat.java index 185d0baad4b..50bb1e01336 100644 --- a/src/java.base/share/classes/java/text/DecimalFormat.java +++ b/src/java.base/share/classes/java/text/DecimalFormat.java @@ -652,17 +652,10 @@ public class DecimalFormat extends NumberFormat { return result; } - /* Detecting whether a double is negative is easy with the exception of - * the value -0.0. This is a double which has a zero mantissa (and - * exponent), but a negative sign bit. It is semantically distinct from - * a zero with a positive sign bit, and this distinction is important - * to certain kinds of computations. However, it's a little tricky to - * detect, since (-0.0 == 0.0) and !(-0.0 < 0.0). How then, you may - * ask, does it behave distinctly from +0.0? Well, 1/(-0.0) == - * -Infinity. Proper detection of -0.0 is needed to deal with the + /* Proper detection of -0.0 is needed to deal with the * issues raised by bugs 4106658, 4106667, and 4147706. Liu 7/6/98. */ - boolean isNegative = ((number < 0.0) || (number == 0.0 && 1/number < 0.0)) ^ (multiplier < 0); + boolean isNegative = Double.doubleToRawLongBits(number) < 0 ^ multiplier < 0; if (multiplier != 1) { number *= multiplier; diff --git a/src/java.base/share/classes/java/text/DecimalFormatSymbols.java b/src/java.base/share/classes/java/text/DecimalFormatSymbols.java index 54e045e0a85..da20af60662 100644 --- a/src/java.base/share/classes/java/text/DecimalFormatSymbols.java +++ b/src/java.base/share/classes/java/text/DecimalFormatSymbols.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,9 +59,12 @@ import sun.util.locale.provider.ResourceBundleBasedAdapter; * of these symbols, you can get the {@code DecimalFormatSymbols} object from * your {@code DecimalFormat} and modify it. * - *

      If the locale contains "rg" (region override) - * Unicode extension, - * the symbols are overridden for the designated region. + *

      The "rg" (region override), "nu" (numbering system), and "cu" (currency) + * {@code Locale} Unicode + * extensions are supported which may override values within the symbols. + * For both "nu" and "cu", if they are specified in addition to "rg" by the + * backing {@code Locale}, the respective values from the "nu" and "cu" extension + * supersede the implicit ones from the "rg" extension. * * @see java.util.Locale * @see DecimalFormat @@ -88,7 +91,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { * @see java.util.Locale.Category#FORMAT */ public DecimalFormatSymbols() { - initialize( Locale.getDefault(Locale.Category.FORMAT) ); + initialize(Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -111,8 +114,8 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { * @param locale the desired locale * @throws NullPointerException if {@code locale} is null */ - public DecimalFormatSymbols( Locale locale ) { - initialize( locale ); + public DecimalFormatSymbols(Locale locale) { + initialize(locale); } /** @@ -812,7 +815,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { /** * Initializes the symbols from the FormatData resource bundle. */ - private void initialize( Locale locale ) { + private void initialize(Locale locale) { this.locale = locale; // check for region override diff --git a/src/java.base/share/classes/java/text/DigitList.java b/src/java.base/share/classes/java/text/DigitList.java index 3eeabb7e77e..41c143178da 100644 --- a/src/java.base/share/classes/java/text/DigitList.java +++ b/src/java.base/share/classes/java/text/DigitList.java @@ -42,6 +42,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; import jdk.internal.math.FloatingDecimal; +import jdk.internal.util.ArraysSupport; /** * Digit List. Private to DecimalFormat. @@ -51,11 +52,11 @@ import jdk.internal.math.FloatingDecimal; * DecimalFormat is that DigitList handles the radix 10 representation * issues; DecimalFormat handles the locale-specific issues such as * positive/negative, grouping, decimal point, currency, and so on. - * + *

      * A DigitList is really a representation of a floating point value. * It may be an integer value; we assume that a double has sufficient * precision to represent all digits of a long. - * + *

      * The DigitList representation consists of a string of characters, * which are the digits radix 10, from '0' to '9'. It also has a radix * 10 exponent associated with it. The value represented by a DigitList @@ -81,22 +82,22 @@ final class DigitList implements Cloneable { /** * These data members are intentionally public and can be set directly. - * + *

      * The value represented is given by placing the decimal point before * digits[decimalAt]. If decimalAt is < 0, then leading zeros between * the decimal point and the first nonzero digit are implied. If decimalAt * is > count, then trailing zeros between the digits[count-1] and the * decimal point are implied. - * + *

      * Equivalently, the represented value is given by f * 10^decimalAt. Here * f is a value 0.1 <= f < 1 arrived at by placing the digits in Digits to * the right of the decimal. - * + *

      * DigitList is normalized, so if it is non-zero, digits[0] is non-zero. We * don't allow denormalized numbers because our exponent is effectively of * unlimited magnitude. The count value contains the number of significant * digits present in digits[]. - * + *

      * Zero is represented by any DigitList with count == 0 or with each digits[i] * for all i <= count == '0'. */ @@ -153,7 +154,7 @@ final class DigitList implements Cloneable { */ public void append(char digit) { if (count == digits.length) { - char[] data = new char[count + 100]; + char[] data = new char[ArraysSupport.newLength(count, 1, count)]; System.arraycopy(digits, 0, data, 0, count); digits = data; } @@ -165,7 +166,7 @@ final class DigitList implements Cloneable { * If (count == 0) this returns 0.0, * unlike Double.parseDouble("") which throws NumberFormatException. */ - public final double getDouble() { + public double getDouble() { if (count == 0) { return 0.0; } @@ -177,7 +178,7 @@ final class DigitList implements Cloneable { * If (count == 0) this returns 0, * unlike Long.parseLong("") which throws NumberFormatException. */ - public final long getLong() { + public long getLong() { // for now, simple implementation; later, do proper IEEE native stuff if (count == 0) { @@ -207,7 +208,7 @@ final class DigitList implements Cloneable { * If (count == 0) this does not throw a NumberFormatException, * unlike BigDecimal(""). */ - public final BigDecimal getBigDecimal() { + public BigDecimal getBigDecimal() { if (count == 0) { return BigDecimal.valueOf(0, -decimalAt); } @@ -279,10 +280,26 @@ final class DigitList implements Cloneable { * @param maximumFractionDigits The most fractional digits which should * be converted. */ - final void set(boolean isNegative, double source, int maximumFractionDigits) { + void set(boolean isNegative, double source, int maximumFractionDigits) { set(isNegative, source, maximumFractionDigits, true); } + /* + * This compatibility option will only be available for a *very* limited + * number of releases. + * It restores the original behavior to help migrating to the new one, + * and is used by adding + * -Djdk.compat.DecimalFormat=true + * to the launcher's command line. + * + * The new behavior differs from the old one only in very rare cases, + * so migration should be painless. + * + * When this option is removed, the old behavior, including relevant + * fields and methods, will be removed as well. + */ + private static final boolean COMPAT = Boolean.getBoolean("jdk.compat.DecimalFormat"); + /** * Set the digit list to a representation of the given double value. * This method supports both fixed-point and exponential notation. @@ -294,9 +311,9 @@ final class DigitList implements Cloneable { * @param fixedPoint If true, then maximumDigits is the maximum * fractional digits to be converted. If false, total digits. */ - final void set(boolean isNegative, double source, int maximumDigits, boolean fixedPoint) { - - FloatingDecimal.BinaryToASCIIConverter fdConverter = FloatingDecimal.getBinaryToASCIIConverter(source); + void set(boolean isNegative, double source, int maximumDigits, boolean fixedPoint) { + FloatingDecimal.BinaryToASCIIConverter fdConverter = + FloatingDecimal.getBinaryToASCIIConverter(source, COMPAT); boolean hasBeenRoundedUp = fdConverter.digitsRoundedUp(); boolean valueExactAsDecimal = fdConverter.decimalDigitsExact(); assert !fdConverter.isExceptional(); @@ -414,7 +431,7 @@ final class DigitList implements Cloneable { * * Upon return, count will be less than or equal to maximumDigits. */ - private final void round(int maximumDigits, + private void round(int maximumDigits, boolean alreadyRounded, boolean valueExactAsDecimal) { // Eliminate digits beyond maximum digits to be displayed. @@ -581,7 +598,7 @@ final class DigitList implements Cloneable { /** * Utility routine to set the value of the digit list from a long */ - final void set(boolean isNegative, long source) { + void set(boolean isNegative, long source) { set(isNegative, source, 0); } @@ -594,7 +611,7 @@ final class DigitList implements Cloneable { * If maximumDigits is lower than the number of significant digits * in source, the representation will be rounded. Ignored if <= 0. */ - final void set(boolean isNegative, long source, int maximumDigits) { + void set(boolean isNegative, long source, int maximumDigits) { this.isNegative = isNegative; // This method does not expect a negative number. However, @@ -644,7 +661,7 @@ final class DigitList implements Cloneable { * @param fixedPoint If true, then maximumDigits is the maximum * fractional digits to be converted. If false, total digits. */ - final void set(boolean isNegative, BigDecimal source, int maximumDigits, boolean fixedPoint) { + void set(boolean isNegative, BigDecimal source, int maximumDigits, boolean fixedPoint) { String s = source.toString(); extendDigits(s.length()); @@ -661,7 +678,7 @@ final class DigitList implements Cloneable { * If maximumDigits is lower than the number of significant digits * in source, the representation will be rounded. Ignored if <= 0. */ - final void set(boolean isNegative, BigInteger source, int maximumDigits) { + void set(boolean isNegative, BigInteger source, int maximumDigits) { this.isNegative = isNegative; String s = source.toString(); int len = s.length(); @@ -771,7 +788,7 @@ final class DigitList implements Cloneable { } } - private final char[] getDataChars(int length) { + private char[] getDataChars(int length) { if (data == null || data.length < length) { data = new char[length]; } diff --git a/src/java.base/share/classes/java/text/NumberFormat.java b/src/java.base/share/classes/java/text/NumberFormat.java index bd4ec53656a..0f4da56de0c 100644 --- a/src/java.base/share/classes/java/text/NumberFormat.java +++ b/src/java.base/share/classes/java/text/NumberFormat.java @@ -103,10 +103,13 @@ import sun.util.locale.provider.LocaleServiceProviderPool; *

    • "cf" * ( * Currency Format style) - Overrides the Currency Format style used + *
    • "cu" + * ( + * Currency Type) - Overrides the Currency used *
    *

    - * If both "nu" and "rg" are specified, the decimal digits from the "nu" - * extension supersedes the implicit one from the "rg" extension. + * For both "nu" and "cu", if they are specified in addition to "rg", the respective + * values from the "nu" and "cu" extension supersede the implicit ones from the "rg" extension. * Although Unicode extensions * defines various keys and values, actual locale-sensitive service implementations * in a Java Runtime Environment might not support any particular Unicode locale diff --git a/src/java.base/share/classes/java/util/HexFormat.java b/src/java.base/share/classes/java/util/HexFormat.java index 99d047995fd..aebb8b9af52 100644 --- a/src/java.base/share/classes/java/util/HexFormat.java +++ b/src/java.base/share/classes/java/util/HexFormat.java @@ -33,8 +33,6 @@ import jdk.internal.util.HexDigits; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.CharBuffer; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.StandardCharsets; /** * {@code HexFormat} converts between bytes and chars and hex-encoded strings which may include @@ -460,12 +458,7 @@ public final class HexFormat { // Delimiter formatting not to a single byte return null; } - try { - // Return a new string using the bytes without making a copy - return jla.uncheckedNewStringNoRepl(rep, StandardCharsets.ISO_8859_1); - } catch (CharacterCodingException cce) { - throw new AssertionError(cce); - } + return jla.uncheckedNewStringWithLatin1Bytes(rep); } /** @@ -695,11 +688,7 @@ public final class HexFormat { byte[] rep = new byte[2]; rep[0] = (byte)toHighHexDigit(value); rep[1] = (byte)toLowHexDigit(value); - try { - return jla.uncheckedNewStringNoRepl(rep, StandardCharsets.ISO_8859_1); - } catch (CharacterCodingException cce) { - throw new AssertionError(cce); - } + return jla.uncheckedNewStringWithLatin1Bytes(rep); } /** @@ -731,11 +720,7 @@ public final class HexFormat { rep[2] = (byte)toHighHexDigit((byte)value); rep[3] = (byte)toLowHexDigit((byte)value); - try { - return jla.uncheckedNewStringNoRepl(rep, StandardCharsets.ISO_8859_1); - } catch (CharacterCodingException cce) { - throw new AssertionError(cce); - } + return jla.uncheckedNewStringWithLatin1Bytes(rep); } /** @@ -759,11 +744,7 @@ public final class HexFormat { rep[6] = (byte)toHighHexDigit((byte)value); rep[7] = (byte)toLowHexDigit((byte)value); - try { - return jla.uncheckedNewStringNoRepl(rep, StandardCharsets.ISO_8859_1); - } catch (CharacterCodingException cce) { - throw new AssertionError(cce); - } + return jla.uncheckedNewStringWithLatin1Bytes(rep); } /** @@ -795,11 +776,7 @@ public final class HexFormat { rep[14] = (byte)toHighHexDigit((byte)value); rep[15] = (byte)toLowHexDigit((byte)value); - try { - return jla.uncheckedNewStringNoRepl(rep, StandardCharsets.ISO_8859_1); - } catch (CharacterCodingException cce) { - throw new AssertionError(cce); - } + return jla.uncheckedNewStringWithLatin1Bytes(rep); } /** @@ -823,11 +800,7 @@ public final class HexFormat { rep[i] = (byte)toLowHexDigit((byte)(value)); value = value >>> 4; } - try { - return jla.uncheckedNewStringNoRepl(rep, StandardCharsets.ISO_8859_1); - } catch (CharacterCodingException cce) { - throw new AssertionError(cce); - } + return jla.uncheckedNewStringWithLatin1Bytes(rep); } /** diff --git a/src/java.base/share/classes/java/util/UUID.java b/src/java.base/share/classes/java/util/UUID.java index 5961fce9cb2..d1c81badba6 100644 --- a/src/java.base/share/classes/java/util/UUID.java +++ b/src/java.base/share/classes/java/util/UUID.java @@ -25,8 +25,6 @@ package java.util; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.StandardCharsets; import java.security.*; import jdk.internal.access.JavaLangAccess; @@ -479,11 +477,7 @@ public final class UUID implements java.io.Serializable, Comparable { ByteArrayLittleEndian.setInt(buf, 24, (int) (x1 >>> 32)); ByteArrayLittleEndian.setLong(buf, 28, hex8(leastSigBits)); - try { - return jla.uncheckedNewStringNoRepl(buf, StandardCharsets.ISO_8859_1); - } catch (CharacterCodingException cce) { - throw new AssertionError(cce); - } + return jla.uncheckedNewStringWithLatin1Bytes(buf); } /** diff --git a/src/java.base/share/classes/java/util/zip/ZipCoder.java b/src/java.base/share/classes/java/util/zip/ZipCoder.java index 0c3282e3518..8b812eba202 100644 --- a/src/java.base/share/classes/java/util/zip/ZipCoder.java +++ b/src/java.base/share/classes/java/util/zip/ZipCoder.java @@ -32,6 +32,7 @@ import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CharacterCodingException; import java.nio.charset.CodingErrorAction; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import jdk.internal.util.ArraysSupport; @@ -252,7 +253,13 @@ class ZipCoder { @Override String toString(byte[] ba, int off, int length) { - return JLA.newStringUTF8NoRepl(ba, off, length); + try { + // Copy subrange for exclusive use by the string being created + byte[] bytes = Arrays.copyOfRange(ba, off, off + length); + return JLA.uncheckedNewStringNoRepl(bytes, StandardCharsets.UTF_8); + } catch (CharacterCodingException cce) { + throw new IllegalArgumentException(cce); + } } @Override @@ -273,7 +280,7 @@ class ZipCoder { // shared and that decoder is not thread safe. // We use the JLA.newStringUTF8NoRepl variant to throw // exceptions eagerly when opening ZipFiles - return hash(JLA.newStringUTF8NoRepl(a, off, len)); + return hash(toString(a, off, len)); } int h = ArraysSupport.hashCodeOfUnsigned(a, off, len, 0); if (a[end - 1] != '/') { diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index efa36b5b2d8..aa5b6e438f5 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -45,6 +45,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; +import java.util.function.BiFunction; import java.util.stream.Stream; import jdk.internal.loader.NativeLibraries; @@ -317,6 +318,18 @@ public interface JavaLangAccess { */ int countNonZeroAscii(String s); + /** + * Constructs a new {@code String} with the supplied Latin1 bytes. + *

    + * WARNING: The caller of this method shall relinquish and transfer the + * ownership of the byte array to the callee, since the latter will not + * make a copy. + * + * @param bytes the byte array source + * @return the newly created string + */ + String uncheckedNewStringWithLatin1Bytes(byte[] bytes); + /** * Constructs a new {@code String} by decoding the specified byte array * using the specified {@linkplain java.nio.charset.Charset charset}. @@ -350,16 +363,6 @@ public interface JavaLangAccess { */ byte[] uncheckedGetBytesNoRepl(String s, Charset cs) throws CharacterCodingException; - /** - * Returns a new string by decoding from the given UTF-8 bytes array. - * - * @param off the index of the first byte to decode - * @param len the number of bytes to decode - * @return the newly created string - * @throws IllegalArgumentException for malformed or unmappable bytes. - */ - String newStringUTF8NoRepl(byte[] bytes, int off, int len); - /** * Get the {@code char} at {@code index} in a {@code byte[]} in internal * UTF-16 representation. @@ -422,15 +425,19 @@ public interface JavaLangAccess { PrintStream initialSystemErr(); /** - * Encodes as many ASCII codepoints as possible from the source array into - * the destination byte array, assuming that the encoding is ASCII - * compatible. - *

    - * WARNING: This method does not perform any bound checks. + * Encodes as many ASCII codepoints as possible from the source + * character array into the destination byte array, assuming that + * the encoding is ASCII compatible. * - * @return the number of bytes successfully encoded, or 0 if none + * @param sa the source character array + * @param sp the index of the source array to start reading from + * @param da the target byte array + * @param dp the index of the target array to start writing to + * @param len the total number of characters to be encoded + * @return the total number of characters successfully encoded + * @throws NullPointerException if any of the provided arrays is null */ - int uncheckedEncodeASCII(char[] src, int srcOff, byte[] dst, int dstOff, int len); + int encodeASCII(char[] sa, int sp, byte[] da, int dp, int len); /** * Set the cause of Throwable diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CatchBuilderImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CatchBuilderImpl.java index d520753326e..89d43135551 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CatchBuilderImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CatchBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,9 @@ package jdk.internal.classfile.impl; import java.lang.classfile.CodeBuilder; import java.lang.classfile.Label; import java.lang.classfile.Opcode; +import java.lang.classfile.constantpool.ClassEntry; import java.lang.constant.ClassDesc; -import java.lang.constant.ConstantDesc; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -39,14 +40,12 @@ public final class CatchBuilderImpl implements CodeBuilder.CatchBuilder { final CodeBuilder b; final BlockCodeBuilderImpl tryBlock; final Label tryCatchEnd; - final Set catchTypes; BlockCodeBuilderImpl catchBlock; public CatchBuilderImpl(CodeBuilder b, BlockCodeBuilderImpl tryBlock, Label tryCatchEnd) { this.b = b; this.tryBlock = tryBlock; this.tryCatchEnd = tryCatchEnd; - this.catchTypes = new HashSet<>(); } @Override @@ -59,18 +58,24 @@ public final class CatchBuilderImpl implements CodeBuilder.CatchBuilder { Objects.requireNonNull(exceptionTypes); Objects.requireNonNull(catchHandler); + // nullable list of CP entries - null means catching all (0) + List entries = new ArrayList<>(Math.max(1, exceptionTypes.size())); + if (exceptionTypes.isEmpty()) { + entries.add(null); + } else { + for (var exceptionType : exceptionTypes) { + var entry = b.constantPool().classEntry(exceptionType); // throws IAE + entries.add(entry); + } + } + // End validation + if (catchBlock == null) { if (tryBlock.reachable()) { b.branch(Opcode.GOTO, tryCatchEnd); } } - for (var exceptionType : exceptionTypes) { - if (!catchTypes.add(exceptionType)) { - throw new IllegalArgumentException("Existing catch block catches exception of type: " + exceptionType); - } - } - // Finish prior catch block if (catchBlock != null) { catchBlock.end(); @@ -82,13 +87,9 @@ public final class CatchBuilderImpl implements CodeBuilder.CatchBuilder { catchBlock = new BlockCodeBuilderImpl(b, tryCatchEnd); Label tryStart = tryBlock.startLabel(); Label tryEnd = tryBlock.endLabel(); - if (exceptionTypes.isEmpty()) { - catchBlock.exceptionCatchAll(tryStart, tryEnd, catchBlock.startLabel()); - } - else { - for (var exceptionType : exceptionTypes) { - catchBlock.exceptionCatch(tryStart, tryEnd, catchBlock.startLabel(), exceptionType); - } + for (var entry : entries) { + // This accepts null for catching all + catchBlock.exceptionCatch(tryStart, tryEnd, catchBlock.startLabel(), entry); } catchBlock.start(); catchHandler.accept(catchBlock); diff --git a/src/java.base/share/classes/java/lang/constant/AsTypeMethodHandleDesc.java b/src/java.base/share/classes/jdk/internal/constant/AsTypeMethodHandleDesc.java similarity index 75% rename from src/java.base/share/classes/java/lang/constant/AsTypeMethodHandleDesc.java rename to src/java.base/share/classes/jdk/internal/constant/AsTypeMethodHandleDesc.java index 32818a9bc00..8b250c0fff8 100644 --- a/src/java.base/share/classes/java/lang/constant/AsTypeMethodHandleDesc.java +++ b/src/java.base/share/classes/jdk/internal/constant/AsTypeMethodHandleDesc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package java.lang.constant; +package jdk.internal.constant; +import java.lang.constant.ConstantDescs; +import java.lang.constant.DirectMethodHandleDesc; +import java.lang.constant.DynamicConstantDesc; +import java.lang.constant.MethodHandleDesc; +import java.lang.constant.MethodTypeDesc; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -37,15 +42,19 @@ import static java.util.Objects.requireNonNull; * {@link MethodHandle} constant that performs a {@link MethodHandle#asType(MethodType)} * adaptation on another {@link MethodHandle}. */ -final class AsTypeMethodHandleDesc extends DynamicConstantDesc +public final class AsTypeMethodHandleDesc extends DynamicConstantDesc implements MethodHandleDesc { + private static final DirectMethodHandleDesc MHD_METHODHANDLE_ASTYPE + = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.VIRTUAL, CD_MethodHandle, "asType", + MethodTypeDesc.of(CD_MethodHandle, ConstantDescs.CD_MethodType)); + private final MethodHandleDesc underlying; private final MethodTypeDesc type; - AsTypeMethodHandleDesc(MethodHandleDesc underlying, MethodTypeDesc type) { + public AsTypeMethodHandleDesc(MethodHandleDesc underlying, MethodTypeDesc type) { super(BSM_INVOKE, ConstantDescs.DEFAULT_NAME, CD_MethodHandle, - ConstantDescs.MHD_METHODHANDLE_ASTYPE, underlying, type); + MHD_METHODHANDLE_ASTYPE, underlying, type); this.underlying = requireNonNull(underlying); this.type = requireNonNull(type); } diff --git a/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java b/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java index f12c39f3e81..79e718c76e5 100644 --- a/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java +++ b/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,16 +25,14 @@ package jdk.internal.jimage; import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.nio.file.Files; -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.attribute.FileTime; import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -42,9 +40,36 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; /** + * A view over the entries of a jimage file with a unified namespace suitable + * for file system use. The jimage entries (resources, module and package + * information) are mapped into a unified hierarchy of named nodes, which serve + * as the underlying structure for {@code JrtFileSystem} and other utilities. + * + *

    Entries in jimage are expressed as one of three {@link Node} types; + * resource nodes, directory nodes and link nodes. + * + *

    When remapping jimage entries, jimage location names (e.g. {@code + * "/java.base/java/lang/Integer.class"}) are prefixed with {@code "/modules"} + * to form the names of resource nodes. This aligns with the naming of module + * entries in jimage (e.g. "/modules/java.base/java/lang"), which appear as + * directory nodes in {@code ImageReader}. + * + *

    Package entries (e.g. {@code "/packages/java.lang"} appear as directory + * nodes containing link nodes, which resolve back to the root directory of the + * module in which that package exists (e.g. {@code "/modules/java.base"}). + * Unlike other nodes, the jimage file does not contain explicit entries for + * link nodes, and their existence is derived only from the contents of the + * parent directory. + * + *

    While similar to {@code BasicImageReader}, this class is not a conceptual + * subtype of it, and deliberately hides types such as {@code ImageLocation} to + * give a focused API based only on nodes. + * * @implNote This class needs to maintain JDK 8 source compatibility. * * It is used internally in the JDK to implement jimage/jrtfs access, @@ -60,6 +85,10 @@ public final class ImageReader implements AutoCloseable { this.reader = reader; } + /** + * Opens an image reader for a jimage file at the specified path, using the + * given byte order. + */ public static ImageReader open(Path imagePath, ByteOrder byteOrder) throws IOException { Objects.requireNonNull(imagePath); Objects.requireNonNull(byteOrder); @@ -67,6 +96,10 @@ public final class ImageReader implements AutoCloseable { return SharedImageReader.open(imagePath, byteOrder); } + /** + * Opens an image reader for a jimage file at the specified path, using the + * platform native byte order. + */ public static ImageReader open(Path imagePath) throws IOException { return open(imagePath, ByteOrder.nativeOrder()); } @@ -92,146 +125,113 @@ public final class ImageReader implements AutoCloseable { } } - // directory management interface - public Directory getRootDirectory() throws IOException { - ensureOpen(); - return reader.getRootDirectory(); - } - - + /** + * Finds the node with the given name. + * + * @param name a node name of the form {@code "/modules//...} or + * {@code "/packages//...}. + * @return a node representing a resource, directory or symbolic link. + */ public Node findNode(String name) throws IOException { ensureOpen(); return reader.findNode(name); } + /** + * Returns a copy of the content of a resource node. The buffer returned by + * this method is not cached by the node, and each call returns a new array + * instance. + * + * @throws IOException if the content cannot be returned (including if the + * given node is not a resource node). + */ public byte[] getResource(Node node) throws IOException { ensureOpen(); return reader.getResource(node); } - public byte[] getResource(Resource rs) throws IOException { - ensureOpen(); - return reader.getResource(rs); - } - - public ImageHeader getHeader() { - requireOpen(); - return reader.getHeader(); - } - + /** + * Releases a (possibly cached) {@link ByteBuffer} obtained via + * {@link #getResourceBuffer(Node)}. + * + *

    Note that no testing is performed to check whether the buffer about + * to be released actually came from a call to {@code getResourceBuffer()}. + */ public static void releaseByteBuffer(ByteBuffer buffer) { BasicImageReader.releaseByteBuffer(buffer); } - public String getName() { + /** + * Returns the content of a resource node in a possibly cached byte buffer. + * Callers of this method must call {@link #releaseByteBuffer(ByteBuffer)} + * when they are finished with it. + */ + public ByteBuffer getResourceBuffer(Node node) { requireOpen(); - return reader.getName(); - } - - public ByteOrder getByteOrder() { - requireOpen(); - return reader.getByteOrder(); - } - - public Path getImagePath() { - requireOpen(); - return reader.getImagePath(); - } - - public ImageStringsReader getStrings() { - requireOpen(); - return reader.getStrings(); - } - - public ImageLocation findLocation(String mn, String rn) { - requireOpen(); - return reader.findLocation(mn, rn); - } - - public boolean verifyLocation(String mn, String rn) { - requireOpen(); - return reader.verifyLocation(mn, rn); - } - - public ImageLocation findLocation(String name) { - requireOpen(); - return reader.findLocation(name); - } - - public String[] getEntryNames() { - requireOpen(); - return reader.getEntryNames(); - } - - public String[] getModuleNames() { - requireOpen(); - int off = "/modules/".length(); - return reader.findNode("/modules") - .getChildren() - .stream() - .map(Node::getNameString) - .map(s -> s.substring(off, s.length())) - .toArray(String[]::new); - } - - public long[] getAttributes(int offset) { - requireOpen(); - return reader.getAttributes(offset); - } - - public String getString(int offset) { - requireOpen(); - return reader.getString(offset); - } - - public byte[] getResource(String name) { - requireOpen(); - return reader.getResource(name); - } - - public byte[] getResource(ImageLocation loc) { - requireOpen(); - return reader.getResource(loc); - } - - public ByteBuffer getResourceBuffer(ImageLocation loc) { - requireOpen(); - return reader.getResourceBuffer(loc); - } - - public InputStream getResourceStream(ImageLocation loc) { - requireOpen(); - return reader.getResourceStream(loc); + if (!node.isResource()) { + throw new IllegalArgumentException("Not a resource node: " + node); + } + return reader.getResourceBuffer(node.getLocation()); } private static final class SharedImageReader extends BasicImageReader { - static final int SIZE_OF_OFFSET = Integer.BYTES; - - static final Map OPEN_FILES = new HashMap<>(); + private static final Map OPEN_FILES = new HashMap<>(); + private static final String MODULES_ROOT = "/modules"; + private static final String PACKAGES_ROOT = "/packages"; + // There are >30,000 nodes in a complete jimage tree, and even relatively + // common tasks (e.g. starting up javac) load somewhere in the region of + // 1000 classes. Thus, an initial capacity of 2000 is a reasonable guess. + private static final int INITIAL_NODE_CACHE_CAPACITY = 2000; // List of openers for this shared image. - final Set openers; + private final Set openers = new HashSet<>(); - // attributes of the .jimage file. jimage file does not contain + // Attributes of the jimage file. The jimage file does not contain // attributes for the individual resources (yet). We use attributes // of the jimage file itself (creation, modification, access times). - // Iniitalized lazily, see {@link #imageFileAttributes()}. - BasicFileAttributes imageFileAttributes; + private final BasicFileAttributes imageFileAttributes; - // directory management implementation - final HashMap nodes; - volatile Directory rootDir; - - Directory packagesDir; - Directory modulesDir; + // Cache of all user visible nodes, guarded by synchronizing 'this' instance. + private final Map nodes; + // Used to classify ImageLocation instances without string comparison. + private final int modulesStringOffset; + private final int packagesStringOffset; private SharedImageReader(Path imagePath, ByteOrder byteOrder) throws IOException { super(imagePath, byteOrder); - this.openers = new HashSet<>(); - this.nodes = new HashMap<>(); + this.imageFileAttributes = Files.readAttributes(imagePath, BasicFileAttributes.class); + this.nodes = new HashMap<>(INITIAL_NODE_CACHE_CAPACITY); + // Pick stable jimage names from which to extract string offsets (we cannot + // use "/modules" or "/packages", since those have a module offset of zero). + this.modulesStringOffset = getModuleOffset("/modules/java.base"); + this.packagesStringOffset = getModuleOffset("/packages/java.lang"); + + // Node creation is very lazy, so we can just make the top-level directories + // now without the risk of triggering the building of lots of other nodes. + Directory packages = newDirectory(PACKAGES_ROOT); + nodes.put(packages.getName(), packages); + Directory modules = newDirectory(MODULES_ROOT); + nodes.put(modules.getName(), modules); + + Directory root = newDirectory("/"); + root.setChildren(Arrays.asList(packages, modules)); + nodes.put(root.getName(), root); } - public static ImageReader open(Path imagePath, ByteOrder byteOrder) throws IOException { + /** + * Returns the offset of the string denoting the leading "module" segment in + * the given path (e.g. {@code /}). We can't just pass in the + * {@code /} string here because that has a module offset of zero. + */ + private int getModuleOffset(String path) { + ImageLocation location = findLocation(path); + assert location != null : "Cannot find expected jimage location: " + path; + int offset = location.getModuleOffset(); + assert offset != 0 : "Invalid module offset for jimage location: " + path; + return offset; + } + + private static ImageReader open(Path imagePath, ByteOrder byteOrder) throws IOException { Objects.requireNonNull(imagePath); Objects.requireNonNull(byteOrder); @@ -264,7 +264,6 @@ public final class ImageReader implements AutoCloseable { if (openers.isEmpty()) { close(); nodes.clear(); - rootDir = null; if (!OPEN_FILES.remove(this.getImagePath(), this)) { throw new IOException("image file not found in open list"); @@ -273,448 +272,412 @@ public final class ImageReader implements AutoCloseable { } } - void addOpener(ImageReader reader) { - synchronized (OPEN_FILES) { - openers.add(reader); + /** + * Returns a node with the given name, or null if no resource or directory of + * that name exists. + * + *

    This is the only public API by which anything outside this class can access + * {@code Node} instances either directly, or by resolving symbolic links. + * + *

    Note also that there is no reentrant calling back to this method from within + * the node handling code. + * + * @param name an absolute, {@code /}-separated path string, prefixed with either + * "/modules" or "/packages". + */ + synchronized Node findNode(String name) { + Node node = nodes.get(name); + if (node == null) { + // We cannot get the root paths ("/modules" or "/packages") here + // because those nodes are already in the nodes cache. + if (name.startsWith(MODULES_ROOT + "/")) { + node = buildModulesNode(name); + } else if (name.startsWith(PACKAGES_ROOT + "/")) { + node = buildPackagesNode(name); + } + if (node != null) { + nodes.put(node.getName(), node); + } + } else if (!node.isCompleted()) { + // Only directories can be incomplete. + assert node instanceof Directory : "Invalid incomplete node: " + node; + completeDirectory((Directory) node); } - } - - boolean removeOpener(ImageReader reader) { - synchronized (OPEN_FILES) { - return openers.remove(reader); - } - } - - // directory management interface - Directory getRootDirectory() { - return buildRootDirectory(); + assert node == null || node.isCompleted() : "Incomplete node: " + node; + return node; } /** - * Lazily build a node from a name. + * Builds a node in the "/modules/..." namespace. + * + *

    Called by {@link #findNode(String)} if a {@code /modules/...} node + * is not present in the cache. */ - synchronized Node buildNode(String name) { - Node n; - boolean isPackages = name.startsWith("/packages"); - boolean isModules = !isPackages && name.startsWith("/modules"); - - if (!(isModules || isPackages)) { - return null; - } - + private Node buildModulesNode(String name) { + assert name.startsWith(MODULES_ROOT + "/") : "Invalid module node name: " + name; + // Returns null for non-directory resources, since the jimage name does not + // start with "/modules" (e.g. "/java.base/java/lang/Object.class"). ImageLocation loc = findLocation(name); - - if (loc != null) { // A sub tree node - if (isPackages) { - n = handlePackages(name, loc); - } else { // modules sub tree - n = handleModulesSubTree(name, loc); - } - } else { // Asking for a resource? /modules/java.base/java/lang/Object.class - if (isModules) { - n = handleResource(name); - } else { - // Possibly ask for /packages/java.lang/java.base - // although /packages/java.base not created - n = handleModuleLink(name); - } + if (loc != null) { + assert name.equals(loc.getFullName()) : "Mismatched location for directory: " + name; + assert isModulesSubdirectory(loc) : "Invalid modules directory: " + name; + return completeModuleDirectory(newDirectory(name), loc); } - return n; - } - - synchronized Directory buildRootDirectory() { - Directory root = rootDir; // volatile read - if (root != null) { - return root; - } - - root = newDirectory(null, "/"); - root.setIsRootDir(); - - // /packages dir - packagesDir = newDirectory(root, "/packages"); - packagesDir.setIsPackagesDir(); - - // /modules dir - modulesDir = newDirectory(root, "/modules"); - modulesDir.setIsModulesDir(); - - root.setCompleted(true); - return rootDir = root; + // Now try the non-prefixed resource name, but be careful to avoid false + // positives for names like "/modules/modules/xxx" which could return a + // location of a directory entry. + loc = findLocation(name.substring(MODULES_ROOT.length())); + return loc != null && isResource(loc) ? newResource(name, loc) : null; } /** - * To visit sub tree resources. + * Builds a node in the "/packages/..." namespace. + * + *

    Called by {@link #findNode(String)} if a {@code /packages/...} node + * is not present in the cache. */ - interface LocationVisitor { - void visit(ImageLocation loc); - } - - void visitLocation(ImageLocation loc, LocationVisitor visitor) { - byte[] offsets = getResource(loc); - ByteBuffer buffer = ByteBuffer.wrap(offsets); - buffer.order(getByteOrder()); - IntBuffer intBuffer = buffer.asIntBuffer(); - for (int i = 0; i < offsets.length / SIZE_OF_OFFSET; i++) { - int offset = intBuffer.get(i); - ImageLocation pkgLoc = getLocation(offset); - visitor.visit(pkgLoc); - } - } - - void visitPackageLocation(ImageLocation loc) { - // Retrieve package name - String pkgName = getBaseExt(loc); - // Content is array of offsets in Strings table - byte[] stringsOffsets = getResource(loc); - ByteBuffer buffer = ByteBuffer.wrap(stringsOffsets); - buffer.order(getByteOrder()); - IntBuffer intBuffer = buffer.asIntBuffer(); - // For each module, create a link node. - for (int i = 0; i < stringsOffsets.length / SIZE_OF_OFFSET; i++) { - // skip empty state, useless. - intBuffer.get(i); - i++; - int offset = intBuffer.get(i); - String moduleName = getString(offset); - Node targetNode = findNode("/modules/" + moduleName); - if (targetNode != null) { - String pkgDirName = packagesDir.getName() + "/" + pkgName; - Directory pkgDir = (Directory) nodes.get(pkgDirName); - newLinkNode(pkgDir, pkgDir.getName() + "/" + moduleName, targetNode); - } - } - } - - Node handlePackages(String name, ImageLocation loc) { - long size = loc.getUncompressedSize(); - Node n = null; - // Only possibilities are /packages, /packages/package/module - if (name.equals("/packages")) { - visitLocation(loc, (childloc) -> { - findNode(childloc.getFullName()); - }); - packagesDir.setCompleted(true); - n = packagesDir; + private Node buildPackagesNode(String name) { + // There are only locations for the root "/packages" or "/packages/xxx" + // directories, but not the symbolic links below them (the links can be + // entirely derived from the name information in the parent directory). + // However, unlike resources this means that we do not have a constant + // time lookup for link nodes when creating them. + int packageStart = PACKAGES_ROOT.length() + 1; + int packageEnd = name.indexOf('/', packageStart); + if (packageEnd == -1) { + ImageLocation loc = findLocation(name); + return loc != null ? completePackageDirectory(newDirectory(name), loc) : null; } else { - if (size != 0) { // children are offsets to module in StringsTable - String pkgName = getBaseExt(loc); - Directory pkgDir = newDirectory(packagesDir, packagesDir.getName() + "/" + pkgName); - visitPackageLocation(loc); - pkgDir.setCompleted(true); - n = pkgDir; - } else { // Link to module - String pkgName = loc.getParent(); - String modName = getBaseExt(loc); - Node targetNode = findNode("/modules/" + modName); - if (targetNode != null) { - String pkgDirName = packagesDir.getName() + "/" + pkgName; - Directory pkgDir = (Directory) nodes.get(pkgDirName); - Node linkNode = newLinkNode(pkgDir, pkgDir.getName() + "/" + modName, targetNode); - n = linkNode; + // We cannot assume that the parent directory exists for a link node, since + // the given name is untrusted and could reference a non-existent link. + // However, if the parent directory is present, we can conclude that the + // given name was not a valid link (or else it would already be cached). + String dirName = name.substring(0, packageEnd); + if (!nodes.containsKey(dirName)) { + ImageLocation loc = findLocation(dirName); + // If the parent location doesn't exist, the link node cannot exist. + if (loc != null) { + nodes.put(dirName, completePackageDirectory(newDirectory(dirName), loc)); + // When the parent is created its child nodes are created and cached, + // but this can still return null if given name wasn't a valid link. + return nodes.get(name); } } } - return n; + return null; } - // Asking for /packages/package/module although - // /packages// not yet created, need to create it - // prior to return the link to module node. - Node handleModuleLink(String name) { - // eg: unresolved /packages/package/module - // Build /packages/package node - Node ret = null; - String radical = "/packages/"; - String path = name; - if (path.startsWith(radical)) { - int start = radical.length(); - int pkgEnd = path.indexOf('/', start); - if (pkgEnd != -1) { - String pkg = path.substring(start, pkgEnd); - String pkgPath = radical + pkg; - Node n = findNode(pkgPath); - // If not found means that this is a symbolic link such as: - // /packages/java.util/java.base/java/util/Vector.class - // and will be done by a retry of the filesystem - for (Node child : n.getChildren()) { - if (child.name.equals(name)) { - ret = child; - break; - } - } - } + /** Completes a directory by ensuring its child list is populated correctly. */ + private void completeDirectory(Directory dir) { + String name = dir.getName(); + // Since the node exists, we can assert that its name starts with + // either "/modules" or "/packages", making differentiation easy. + // It also means that the name is valid, so it must yield a location. + assert name.startsWith(MODULES_ROOT) || name.startsWith(PACKAGES_ROOT); + ImageLocation loc = findLocation(name); + assert loc != null && name.equals(loc.getFullName()) : "Invalid location for name: " + name; + // We cannot use 'isXxxSubdirectory()' methods here since we could + // be given a top-level directory (for which that test doesn't work). + // The string MUST start "/modules" or "/packages" here. + if (name.charAt(1) == 'm') { + completeModuleDirectory(dir, loc); + } else { + completePackageDirectory(dir, loc); } - return ret; + assert dir.isCompleted() : "Directory must be complete by now: " + dir; } - Node handleModulesSubTree(String name, ImageLocation loc) { - Node n; - assert (name.equals(loc.getFullName())); - Directory dir = makeDirectories(name); - visitLocation(loc, (childloc) -> { - String path = childloc.getFullName(); - if (path.startsWith("/modules")) { // a package - makeDirectories(path); - } else { // a resource - makeDirectories(childloc.buildName(true, true, false)); - // if we have already created a resource for this name previously, then don't - // recreate it - if (!nodes.containsKey(childloc.getFullName(true))) { - newResource(dir, childloc); - } + /** + * Completes a modules directory by setting the list of child nodes. + * + *

    The given directory can be the top level {@code /modules} directory, + * so it is NOT safe to use {@code isModulesSubdirectory(loc)} here. + */ + private Directory completeModuleDirectory(Directory dir, ImageLocation loc) { + assert dir.getName().equals(loc.getFullName()) : "Mismatched location for directory: " + dir; + List children = createChildNodes(loc, childLoc -> { + if (isModulesSubdirectory(childLoc)) { + return nodes.computeIfAbsent(childLoc.getFullName(), this::newDirectory); + } else { + // Add "/modules" prefix to image location paths to get node names. + String resourceName = childLoc.getFullName(true); + return nodes.computeIfAbsent(resourceName, n -> newResource(n, childLoc)); } }); - dir.setCompleted(true); - n = dir; - return n; - } - - Node handleResource(String name) { - Node n = null; - if (!name.startsWith("/modules/")) { - return null; - } - // Make sure that the thing that follows "/modules/" is a module name. - int moduleEndIndex = name.indexOf('/', "/modules/".length()); - if (moduleEndIndex == -1) { - return null; - } - ImageLocation moduleLoc = findLocation(name.substring(0, moduleEndIndex)); - if (moduleLoc == null || moduleLoc.getModuleOffset() == 0) { - return null; - } - - String locationPath = name.substring("/modules".length()); - ImageLocation resourceLoc = findLocation(locationPath); - if (resourceLoc != null) { - Directory dir = makeDirectories(resourceLoc.buildName(true, true, false)); - Resource res = newResource(dir, resourceLoc); - n = res; - } - return n; - } - - String getBaseExt(ImageLocation loc) { - String base = loc.getBase(); - String ext = loc.getExtension(); - if (ext != null && !ext.isEmpty()) { - base = base + "." + ext; - } - return base; - } - - synchronized Node findNode(String name) { - buildRootDirectory(); - Node n = nodes.get(name); - if (n == null || !n.isCompleted()) { - n = buildNode(name); - } - return n; - } - - /** - * Returns the file attributes of the image file. - */ - BasicFileAttributes imageFileAttributes() { - BasicFileAttributes attrs = imageFileAttributes; - if (attrs == null) { - try { - Path file = getImagePath(); - attrs = Files.readAttributes(file, BasicFileAttributes.class); - } catch (IOException ioe) { - throw new UncheckedIOException(ioe); - } - imageFileAttributes = attrs; - } - return attrs; - } - - Directory newDirectory(Directory parent, String name) { - Directory dir = Directory.create(parent, name, imageFileAttributes()); - nodes.put(dir.getName(), dir); + dir.setChildren(children); return dir; } - Resource newResource(Directory parent, ImageLocation loc) { - Resource res = Resource.create(parent, loc, imageFileAttributes()); - nodes.put(res.getName(), res); - return res; - } - - LinkNode newLinkNode(Directory dir, String name, Node link) { - LinkNode linkNode = LinkNode.create(dir, name, link); - nodes.put(linkNode.getName(), linkNode); - return linkNode; - } - - Directory makeDirectories(String parent) { - Directory last = rootDir; - for (int offset = parent.indexOf('/', 1); - offset != -1; - offset = parent.indexOf('/', offset + 1)) { - String dir = parent.substring(0, offset); - last = makeDirectory(dir, last); + /** + * Completes a package directory by setting the list of child nodes. + * + *

    The given directory can be the top level {@code /packages} directory, + * so it is NOT safe to use {@code isPackagesSubdirectory(loc)} here. + */ + private Directory completePackageDirectory(Directory dir, ImageLocation loc) { + assert dir.getName().equals(loc.getFullName()) : "Mismatched location for directory: " + dir; + // The only directories in the "/packages" namespace are "/packages" or + // "/packages/". However, unlike "/modules" directories, the + // location offsets mean different things. + List children; + if (dir.getName().equals(PACKAGES_ROOT)) { + // Top-level directory just contains a list of subdirectories. + children = createChildNodes(loc, c -> nodes.computeIfAbsent(c.getFullName(), this::newDirectory)); + } else { + // A package directory's content is array of offset PAIRS in the + // Strings table, but we only need the 2nd value of each pair. + IntBuffer intBuffer = getOffsetBuffer(loc); + int offsetCount = intBuffer.capacity(); + assert (offsetCount & 0x1) == 0 : "Offset count must be even: " + offsetCount; + children = new ArrayList<>(offsetCount / 2); + // Iterate the 2nd offset in each pair (odd indices). + for (int i = 1; i < offsetCount; i += 2) { + String moduleName = getString(intBuffer.get(i)); + children.add(nodes.computeIfAbsent( + dir.getName() + "/" + moduleName, + n -> newLinkNode(n, MODULES_ROOT + "/" + moduleName))); + } } - return makeDirectory(parent, last); - + // This only happens once and "completes" the directory. + dir.setChildren(children); + return dir; } - Directory makeDirectory(String dir, Directory last) { - Directory nextDir = (Directory) nodes.get(dir); - if (nextDir == null) { - nextDir = newDirectory(last, dir); + /** + * Creates the list of child nodes for a {@code Directory} based on a given + * + *

    Note: This cannot be used for package subdirectories as they have + * child offsets stored differently to other directories. + */ + private List createChildNodes(ImageLocation loc, Function newChildFn) { + IntBuffer offsets = getOffsetBuffer(loc); + int childCount = offsets.capacity(); + List children = new ArrayList<>(childCount); + for (int i = 0; i < childCount; i++) { + children.add(newChildFn.apply(getLocation(offsets.get(i)))); } - return nextDir; + return children; } - byte[] getResource(Node node) throws IOException { + /** Helper to extract the integer offset buffer from a directory location. */ + private IntBuffer getOffsetBuffer(ImageLocation dir) { + assert !isResource(dir) : "Not a directory: " + dir.getFullName(); + byte[] offsets = getResource(dir); + ByteBuffer buffer = ByteBuffer.wrap(offsets); + buffer.order(getByteOrder()); + return buffer.asIntBuffer(); + } + + /** + * Efficiently determines if an image location is a resource. + * + *

    A resource must have a valid module associated with it, so its + * module offset must be non-zero, and not equal to the offsets for + * "/modules/..." or "/packages/..." entries. + */ + private boolean isResource(ImageLocation loc) { + int moduleOffset = loc.getModuleOffset(); + return moduleOffset != 0 + && moduleOffset != modulesStringOffset + && moduleOffset != packagesStringOffset; + } + + /** + * Determines if an image location is a directory in the {@code /modules} + * namespace (if so, the location name is the node name). + * + *

    In jimage, every {@code ImageLocation} under {@code /modules/} is a + * directory and has the same value for {@code getModule()}, and {@code + * getModuleOffset()}. + */ + private boolean isModulesSubdirectory(ImageLocation loc) { + return loc.getModuleOffset() == modulesStringOffset; + } + + /** + * Creates an "incomplete" directory node with no child nodes set. + * Directories need to be "completed" before they are returned by + * {@link #findNode(String)}. + */ + private Directory newDirectory(String name) { + return new Directory(name, imageFileAttributes); + } + + /** + * Creates a new resource from an image location. This is the only case + * where the image location name does not match the requested node name. + * In image files, resource locations are NOT prefixed by {@code /modules}. + */ + private Resource newResource(String name, ImageLocation loc) { + assert name.equals(loc.getFullName(true)) : "Mismatched location for resource: " + name; + return new Resource(name, loc, imageFileAttributes); + } + + /** + * Creates a new link node pointing at the given target name. + * + *

    Note that target node is resolved each time {@code resolve()} is called, + * so if a link node is retained after its reader is closed, it will fail. + */ + private LinkNode newLinkNode(String name, String targetName) { + return new LinkNode(name, () -> findNode(targetName), imageFileAttributes); + } + + /** Returns the content of a resource node. */ + private byte[] getResource(Node node) throws IOException { + // We could have been given a non-resource node here. if (node.isResource()) { return super.getResource(node.getLocation()); } throw new IOException("Not a resource: " + node); } - - byte[] getResource(Resource rs) throws IOException { - return super.getResource(rs.getLocation()); - } } - // jimage file does not store directory structure. We build nodes - // using the "path" strings found in the jimage file. - // Node can be a directory or a resource + /** + * A directory, resource or symbolic link. + * + *

    Node Equality

    + * + * Nodes are identified solely by their name, and it is not valid to attempt + * to compare nodes from different reader instances. Different readers may + * produce nodes with the same names, but different contents. + * + *

    Furthermore, since a {@link ImageReader} provides "perfect" caching of + * nodes, equality of nodes from the same reader is equivalent to instance + * identity. + */ public abstract static class Node { - private static final int ROOT_DIR = 0b0000_0000_0000_0001; - private static final int PACKAGES_DIR = 0b0000_0000_0000_0010; - private static final int MODULES_DIR = 0b0000_0000_0000_0100; - - private int flags; private final String name; private final BasicFileAttributes fileAttrs; - private boolean completed; + /** + * Creates an abstract {@code Node}, which is either a resource, directory + * or symbolic link. + * + *

    This constructor is only non-private so it can be used by the + * {@code ExplodedImage} class, and must not be used otherwise. + */ protected Node(String name, BasicFileAttributes fileAttrs) { this.name = Objects.requireNonNull(name); this.fileAttrs = Objects.requireNonNull(fileAttrs); } + // A node is completed when all its direct children have been built. + // As such, non-directory nodes are always complete. + boolean isCompleted() { + return true; + } + + // Only resources can return a location. + ImageLocation getLocation() { + throw new IllegalStateException("not a resource: " + getName()); + } + /** - * A node is completed when all its direct children have been built. + * Returns the name of this node (e.g. {@code + * "/modules/java.base/java/lang/Object.class"} or {@code + * "/packages/java.lang"}). * - * @return + *

    Note that for resource nodes this is NOT the underlying jimage + * resource name (it is prefixed with {@code "/modules"}). */ - public boolean isCompleted() { - return completed; - } - - public void setCompleted(boolean completed) { - this.completed = completed; - } - - public final void setIsRootDir() { - flags |= ROOT_DIR; - } - - public final boolean isRootDir() { - return (flags & ROOT_DIR) != 0; - } - - public final void setIsPackagesDir() { - flags |= PACKAGES_DIR; - } - - public final boolean isPackagesDir() { - return (flags & PACKAGES_DIR) != 0; - } - - public final void setIsModulesDir() { - flags |= MODULES_DIR; - } - - public final boolean isModulesDir() { - return (flags & MODULES_DIR) != 0; - } - public final String getName() { return name; } + /** + * Returns file attributes for this node. The value returned may be the + * same for all nodes, and should not be relied upon for accuracy. + */ public final BasicFileAttributes getFileAttributes() { return fileAttrs; } - // resolve this Node (if this is a soft link, get underlying Node) + /** + * Resolves a symbolic link to its target node. If this code is not a + * symbolic link, then it resolves to itself. + */ public final Node resolveLink() { return resolveLink(false); } + /** + * Resolves a symbolic link to its target node. If this code is not a + * symbolic link, then it resolves to itself. + */ public Node resolveLink(boolean recursive) { return this; } - // is this a soft link Node? + /** Returns whether this node is a symbolic link. */ public boolean isLink() { return false; } + /** + * Returns whether this node is a directory. Directory nodes can have + * {@link #getChildNames()} invoked to get the fully qualified names + * of any child nodes. + */ public boolean isDirectory() { return false; } - public List getChildren() { - throw new IllegalArgumentException("not a directory: " + getNameString()); - } - + /** + * Returns whether this node is a resource. Resource nodes can have + * their contents obtained via {@link ImageReader#getResource(Node)} + * or {@link ImageReader#getResourceBuffer(Node)}. + */ public boolean isResource() { return false; } - public ImageLocation getLocation() { - throw new IllegalArgumentException("not a resource: " + getNameString()); + /** + * Returns the fully qualified names of any child nodes for a directory. + * + *

    By default, this method throws {@link IllegalStateException} and + * is overridden for directories. + */ + public Stream getChildNames() { + throw new IllegalStateException("not a directory: " + getName()); } + /** + * Returns the uncompressed size of this node's content. If this node is + * not a resource, this method returns zero. + */ public long size() { return 0L; } + /** + * Returns the compressed size of this node's content. If this node is + * not a resource, this method returns zero. + */ public long compressedSize() { return 0L; } + /** + * Returns the extension string of a resource node. If this node is not + * a resource, this method returns null. + */ public String extension() { return null; } - public long contentOffset() { - return 0L; - } - - public final FileTime creationTime() { - return fileAttrs.creationTime(); - } - - public final FileTime lastAccessTime() { - return fileAttrs.lastAccessTime(); - } - - public final FileTime lastModifiedTime() { - return fileAttrs.lastModifiedTime(); - } - - public final String getNameString() { - return name; - } - @Override public final String toString() { - return getNameString(); + return getName(); } + /** See Node Equality. */ @Override public final int hashCode() { return name.hashCode(); } + /** See Node Equality. */ @Override public final boolean equals(Object other) { if (this == other) { @@ -729,21 +692,40 @@ public final class ImageReader implements AutoCloseable { } } - // directory node - directory has full path name without '/' at end. - static final class Directory extends Node { - private final List children; + /** + * Directory node (referenced from a full path, without a trailing '/'). + * + *

    Directory nodes have two distinct states: + *

      + *
    • Incomplete: The child list has not been set. + *
    • Complete: The child list has been set. + *
    + * + *

    When a directory node is returned by {@link ImageReader#findNode(String)} + * it is always complete, but this DOES NOT mean that its child nodes are + * complete yet. + * + *

    To avoid users being able to access incomplete child nodes, the + * {@code Node} API offers only a way to obtain child node names, forcing + * callers to invoke {@code findNode()} if they need to access the child + * node itself. + * + *

    This approach allows directories to be implemented lazily with respect + * to child nodes, while retaining efficiency when child nodes are accessed + * (since any incomplete nodes will be created and placed in the node cache + * when the parent was first returned to the user). + */ + private static final class Directory extends Node { + // Monotonic reference, will be set to the unmodifiable child list exactly once. + private List children = null; private Directory(String name, BasicFileAttributes fileAttrs) { super(name, fileAttrs); - children = new ArrayList<>(); } - static Directory create(Directory parent, String name, BasicFileAttributes fileAttrs) { - Directory d = new Directory(name, fileAttrs); - if (parent != null) { - parent.addChild(d); - } - return d; + @Override + boolean isCompleted() { + return children != null; } @Override @@ -752,46 +734,41 @@ public final class ImageReader implements AutoCloseable { } @Override - public List getChildren() { - return Collections.unmodifiableList(children); - } - - void addChild(Node node) { - assert !children.contains(node) : "Child " + node + " already added"; - children.add(node); - } - - public void walk(Consumer consumer) { - consumer.accept(this); - for (Node child : children) { - if (child.isDirectory()) { - ((Directory)child).walk(consumer); - } else { - consumer.accept(child); - } + public Stream getChildNames() { + if (children != null) { + return children.stream().map(Node::getName); } + throw new IllegalStateException("Cannot get child nodes of an incomplete directory: " + getName()); + } + + private void setChildren(List children) { + assert this.children == null : this + ": Cannot set child nodes twice!"; + this.children = Collections.unmodifiableList(children); } } - - // "resource" is .class or any other resource (compressed/uncompressed) in a jimage. - // full path of the resource is the "name" of the resource. - static class Resource extends Node { + /** + * Resource node (e.g. a ".class" entry, or any other data resource). + * + *

    Resources are leaf nodes referencing an underlying image location. They + * are lightweight, and do not cache their contents. + * + *

    Unlike directories (where the node name matches the jimage path for the + * corresponding {@code ImageLocation}), resource node names are NOT the same + * as the corresponding jimage path. The difference is that node names for + * resources are prefixed with "/modules", which is missing from the + * equivalent jimage path. + */ + private static class Resource extends Node { private final ImageLocation loc; - private Resource(ImageLocation loc, BasicFileAttributes fileAttrs) { - super(loc.getFullName(true), fileAttrs); + private Resource(String name, ImageLocation loc, BasicFileAttributes fileAttrs) { + super(name, fileAttrs); this.loc = loc; } - static Resource create(Directory parent, ImageLocation loc, BasicFileAttributes fileAttrs) { - Resource rs = new Resource(loc, fileAttrs); - parent.addChild(rs); - return rs; - } - @Override - public boolean isCompleted() { - return true; + ImageLocation getLocation() { + return loc; } @Override @@ -799,11 +776,6 @@ public final class ImageReader implements AutoCloseable { return true; } - @Override - public ImageLocation getLocation() { - return loc; - } - @Override public long size() { return loc.getUncompressedSize(); @@ -818,36 +790,29 @@ public final class ImageReader implements AutoCloseable { public String extension() { return loc.getExtension(); } - - @Override - public long contentOffset() { - return loc.getContentOffset(); - } } - // represents a soft link to another Node - static class LinkNode extends Node { - private final Node link; + /** + * Link node (a symbolic link to a top-level modules directory). + * + *

    Link nodes resolve their target by invoking a given supplier, and do + * not cache the result. Since nodes are cached by the {@code ImageReader}, + * this means that only the first call to {@link #resolveLink(boolean)} + * could do any significant work. + */ + private static class LinkNode extends Node { + private final Supplier link; - private LinkNode(String name, Node link) { - super(name, link.getFileAttributes()); + private LinkNode(String name, Supplier link, BasicFileAttributes fileAttrs) { + super(name, fileAttrs); this.link = link; } - static LinkNode create(Directory parent, String name, Node link) { - LinkNode ln = new LinkNode(name, link); - parent.addChild(ln); - return ln; - } - - @Override - public boolean isCompleted() { - return true; - } - @Override public Node resolveLink(boolean recursive) { - return (recursive && link instanceof LinkNode) ? ((LinkNode)link).resolveLink(true) : link; + // No need to use or propagate the recursive flag, since the target + // cannot possibly be a link node (links only point to directories). + return link.get(); } @Override diff --git a/src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java b/src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java index e2c17f8ca25..87a00da4393 100644 --- a/src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java +++ b/src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java @@ -119,9 +119,9 @@ class ExplodedImage extends SystemImage { } @Override - public List getChildren() { + public Stream getChildNames() { if (!isDirectory()) - throw new IllegalArgumentException("not a directory: " + getNameString()); + throw new IllegalArgumentException("not a directory: " + getName()); if (children == null) { List list = new ArrayList<>(); try (DirectoryStream stream = Files.newDirectoryStream(path)) { @@ -138,7 +138,7 @@ class ExplodedImage extends SystemImage { } children = list; } - return children; + return children.stream().map(Node::getName); } @Override diff --git a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java index f0804b58c1c..e1eb1115260 100644 --- a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java +++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,7 +49,7 @@ final class JrtFileAttributes implements BasicFileAttributes { //-------- basic attributes -------- @Override public FileTime creationTime() { - return node.creationTime(); + return node.getFileAttributes().creationTime(); } @Override @@ -69,12 +69,12 @@ final class JrtFileAttributes implements BasicFileAttributes { @Override public FileTime lastAccessTime() { - return node.lastAccessTime(); + return node.getFileAttributes().lastAccessTime(); } @Override public FileTime lastModifiedTime() { - return node.lastModifiedTime(); + return node.getFileAttributes().lastModifiedTime(); } @Override diff --git a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java index 9a8d9d22dfa..6530bd1f90a 100644 --- a/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java +++ b/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java @@ -54,7 +54,6 @@ import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileTime; import java.nio.file.attribute.UserPrincipalLookupService; import java.nio.file.spi.FileSystemProvider; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -64,7 +63,6 @@ import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; import jdk.internal.jimage.ImageReader.Node; -import static java.util.stream.Collectors.toList; /** * jrt file system implementation built on System jimage files. @@ -225,19 +223,19 @@ class JrtFileSystem extends FileSystem { throw new NotDirectoryException(path.getName()); } if (filter == null) { - return node.getChildren() - .stream() - .map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName()))) - .iterator(); + return node.getChildNames() + .map(child -> (Path) (path.resolve(new JrtPath(this, child).getFileName()))) + .iterator(); } - return node.getChildren() - .stream() - .map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName()))) - .filter(p -> { try { return filter.accept(p); - } catch (IOException x) {} - return false; - }) - .iterator(); + return node.getChildNames() + .map(child -> (Path) (path.resolve(new JrtPath(this, child).getFileName()))) + .filter(p -> { + try { + return filter.accept(p); + } catch (IOException x) {} + return false; + }) + .iterator(); } // returns the content of the file resource specified by the path diff --git a/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java b/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java index 88cdf724e7d..6813c7e627f 100644 --- a/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java +++ b/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java @@ -58,7 +58,6 @@ abstract class SystemImage { if (modulesImageExists) { // open a .jimage and build directory structure final ImageReader image = ImageReader.open(moduleImageFile); - image.getRootDirectory(); return new SystemImage() { @Override Node findNode(String path) throws IOException { diff --git a/src/java.base/share/classes/jdk/internal/math/DoubleToDecimal.java b/src/java.base/share/classes/jdk/internal/math/DoubleToDecimal.java index 2db099e93ed..11830e4774f 100644 --- a/src/java.base/share/classes/jdk/internal/math/DoubleToDecimal.java +++ b/src/java.base/share/classes/jdk/internal/math/DoubleToDecimal.java @@ -246,7 +246,7 @@ public final class DoubleToDecimal extends ToDecimal { if (0 < mq & mq < P) { long f = c >> mq; if (f << mq == c) { - return toChars(str, index, f, 0, fd) - start; + return toChars(str, index, f, 0, fd, true, false) - start; } } return toDecimal(str, index, -mq, c, 0, fd) - start; @@ -324,13 +324,17 @@ public final class DoubleToDecimal extends ToDecimal { * upin iff u' = sp10 10^k in Rv * wpin iff w' = tp10 10^k in Rv * See section 9.3 of [1]. + * + * Also, + * d_v = v iff 4 sp10 = vb */ long sp10 = 10 * multiplyHigh(s, 115_292_150_460_684_698L << 4); long tp10 = sp10 + 10; boolean upin = vbl + out <= sp10 << 2; boolean wpin = (tp10 << 2) + out <= vbr; if (upin != wpin) { - return toChars(str, index, upin ? sp10 : tp10, k, fd); + /* Exactly one of u' or w' lies in Rv */ + return toChars(str, index, upin ? sp10 : tp10, k, fd, sp10 << 2 == vb, wpin); } } @@ -339,20 +343,24 @@ public final class DoubleToDecimal extends ToDecimal { * uin iff u = s 10^k in Rv * win iff w = t 10^k in Rv * See section 9.3 of [1]. + * + * Also, + * d_v = v iff 4 s = vb */ long t = s + 1; boolean uin = vbl + out <= s << 2; boolean win = (t << 2) + out <= vbr; if (uin != win) { /* Exactly one of u or w lies in Rv */ - return toChars(str, index, uin ? s : t, k + dk, fd); + return toChars(str, index, uin ? s : t, k + dk, fd, s << 2 == vb, win); } /* * Both u and w lie in Rv: determine the one closest to v. * See section 9.3 of [1]. */ long cmp = vb - (s + t << 1); - return toChars(str, index, cmp < 0 || cmp == 0 && (s & 0x1) == 0 ? s : t, k + dk, fd); + boolean away = cmp > 0 || cmp == 0 && (s & 0x1) != 0; + return toChars(str, index, away ? t : s, k + dk, fd, s << 2 == vb, away); } /* @@ -371,7 +379,8 @@ public final class DoubleToDecimal extends ToDecimal { /* * Formats the decimal f 10^e. */ - private int toChars(byte[] str, int index, long f, int e, FormattedFPDecimal fd) { + private int toChars(byte[] str, int index, long f, int e, + FormattedFPDecimal fd, boolean exact, boolean away) { /* * For details not discussed here see section 10 of [1]. * @@ -383,7 +392,7 @@ public final class DoubleToDecimal extends ToDecimal { len += 1; } if (fd != null) { - fd.set(f, e, len); + fd.set(f, e, len, exact, away); return index; } diff --git a/src/java.base/share/classes/jdk/internal/math/FloatToDecimal.java b/src/java.base/share/classes/jdk/internal/math/FloatToDecimal.java index f0ce15db4dd..3d0c01543aa 100644 --- a/src/java.base/share/classes/jdk/internal/math/FloatToDecimal.java +++ b/src/java.base/share/classes/jdk/internal/math/FloatToDecimal.java @@ -304,6 +304,7 @@ public final class FloatToDecimal extends ToDecimal { boolean upin = vbl + out <= sp10 << 2; boolean wpin = (tp10 << 2) + out <= vbr; if (upin != wpin) { + /* Exactly one of u' or w' lies in Rv */ return toChars(str, index, upin ? sp10 : tp10, k); } } @@ -326,7 +327,8 @@ public final class FloatToDecimal extends ToDecimal { * See section 9.3 of [1]. */ int cmp = vb - (s + t << 1); - return toChars(str, index, cmp < 0 || cmp == 0 && (s & 0x1) == 0 ? s : t, k + dk); + boolean away = cmp > 0 || cmp == 0 && (s & 0x1) != 0; + return toChars(str, index, away ? t : s, k + dk); } /* diff --git a/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java b/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java index cc68ce1282b..168f76fad8f 100644 --- a/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java +++ b/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java @@ -1742,22 +1742,44 @@ public class FloatingDecimal{ * Returns a BinaryToASCIIConverter for a double. * The returned object is a ThreadLocal variable of this class. * - * @param d The double precision value to convert. + * @param d The double precision value to convert. + * @param compat compatibility with releases < JDK 21 * @return The converter. */ - public static BinaryToASCIIConverter getBinaryToASCIIConverter(double d) { - return getBinaryToASCIIConverter(d, true); + public static BinaryToASCIIConverter getBinaryToASCIIConverter(double d, boolean compat) { + return compat + ? getCompatBinaryToASCIIConverter(d, true) + : getBinaryToASCIIConverter(d); } - /** - * Returns a BinaryToASCIIConverter for a double. - * The returned object is a ThreadLocal variable of this class. - * - * @param d The double precision value to convert. - * @param isCompatibleFormat - * @return The converter. + private static BinaryToASCIIConverter getBinaryToASCIIConverter(double d) { + assert Double.isFinite(d); + + FormattedFPDecimal dec = FormattedFPDecimal.split(d); + BinaryToASCIIBuffer buf = getBinaryToASCIIBuffer(); + + buf.nDigits = dec.getPrecision(); + buf.decExponent = dec.getExp() + buf.nDigits; + buf.firstDigitIndex = 0; + buf.exactDecimalConversion = dec.getExact(); + buf.decimalDigitsRoundedUp = dec.getAway(); + + long f = dec.getSignificand(); + char[] digits = buf.digits; + for (int i = buf.nDigits - 1; i >= 0; --i) { + long q = f / 10; + digits[i] = (char) ((f - 10 * q) + '0'); + f = q; + } + return buf; + } + + /* + * The old implementation of getBinaryToASCIIConverter(). + * Should be removed in the future, along with its dependent methods and + * fields (> 550 lines). */ - static BinaryToASCIIConverter getBinaryToASCIIConverter(double d, boolean isCompatibleFormat) { + private static BinaryToASCIIConverter getCompatBinaryToASCIIConverter(double d, boolean isCompatibleFormat) { long dBits = Double.doubleToRawLongBits(d); boolean isNegative = (dBits&DoubleConsts.SIGN_BIT_MASK) != 0; // discover sign long fractBits = dBits & DoubleConsts.SIGNIF_BIT_MASK; diff --git a/src/java.base/share/classes/jdk/internal/math/FormattedFPDecimal.java b/src/java.base/share/classes/jdk/internal/math/FormattedFPDecimal.java index 7bfe63d0507..1595c7574f5 100644 --- a/src/java.base/share/classes/jdk/internal/math/FormattedFPDecimal.java +++ b/src/java.base/share/classes/jdk/internal/math/FormattedFPDecimal.java @@ -47,9 +47,16 @@ public final class FormattedFPDecimal { public static final char PLAIN = 'f'; public static final char GENERAL = 'g'; - private long f; + /* Whether the decimal exactly represents the double */ + private boolean exact; + /* + * When not exact, whether the magnitude of the decimal is larger than + * the magnitude of the double. Aka "away from zero". + */ + private boolean away; private int e; // normalized to 0 when f = 0 private int n; + private long f; private char[] digits; // ... and often the decimal separator as well private char[] exp; // [+-][e]ee, that is, sign and minimum 2 digits @@ -68,7 +75,7 @@ public final class FormattedFPDecimal { }; } - private static FormattedFPDecimal split(double v) { + static FormattedFPDecimal split(double v) { FormattedFPDecimal fd = new FormattedFPDecimal(); DoubleToDecimal.split(v, fd); return fd; @@ -119,7 +126,7 @@ public final class FormattedFPDecimal { // Calculate new e based on updated precision final int eNew = expR - prec + 1; // expR is defined as prec + e - 1 - fd.set(s, eNew, prec); + fd.set(s, eNew, prec, fd.exact, fd.away); return fd; } @@ -132,15 +139,25 @@ public final class FormattedFPDecimal { return n; } + public boolean getAway() { + return away; + } + + public boolean getExact() { + return exact; + } + public int getExp() { return e; } - public void set(long f, int e, int n) { + public void set(long f, int e, int n, boolean exact, boolean away) { /* Initially, n = 0 if f = 0, and 10^{n-1} <= f < 10^n if f != 0 */ this.f = f; this.e = e; this.n = n; + this.exact = exact; + this.away = away; } public char[] getExponent() { diff --git a/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java b/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java index c520e6e636a..39f433d4041 100644 --- a/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java +++ b/src/java.base/share/classes/jdk/internal/module/SystemModuleFinders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,6 @@ import java.lang.module.ModuleReader; import java.lang.module.ModuleReference; import java.lang.reflect.Constructor; import java.net.URI; -import java.net.URLConnection; import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; @@ -54,7 +53,6 @@ import java.util.function.Supplier; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import jdk.internal.jimage.ImageLocation; import jdk.internal.jimage.ImageReader; import jdk.internal.jimage.ImageReaderFactory; import jdk.internal.access.JavaNetUriAccess; @@ -210,7 +208,7 @@ public final class SystemModuleFinders { } /** - * Parses the module-info.class of all module in the runtime image and + * Parses the {@code module-info.class} of all modules in the runtime image and * returns a ModuleFinder to find the modules. * * @apiNote The returned ModuleFinder is thread safe. @@ -219,20 +217,16 @@ public final class SystemModuleFinders { // parse the module-info.class in every module Map nameToAttributes = new HashMap<>(); Map nameToHash = new HashMap<>(); - ImageReader reader = SystemImage.reader(); - for (String mn : reader.getModuleNames()) { - ImageLocation loc = reader.findLocation(mn, "module-info.class"); - ModuleInfo.Attributes attrs - = ModuleInfo.read(reader.getResourceBuffer(loc), null); - nameToAttributes.put(mn, attrs); + allModuleAttributes().forEach(attrs -> { + nameToAttributes.put(attrs.descriptor().name(), attrs); ModuleHashes hashes = attrs.recordedHashes(); if (hashes != null) { for (String name : hashes.names()) { nameToHash.computeIfAbsent(name, k -> hashes.hashFor(name)); } } - } + }); // create a ModuleReference for each module Set mrefs = new HashSet<>(); @@ -253,6 +247,40 @@ public final class SystemModuleFinders { return new SystemModuleFinder(mrefs, nameToModule); } + /** + * Parses the {@code module-info.class} of all modules in the runtime image and + * returns a stream of {@link ModuleInfo.Attributes Attributes} for them. The + * returned attributes are in no specific order. + */ + private static Stream allModuleAttributes() { + // System-wide image reader. + ImageReader reader = SystemImage.reader(); + try { + return reader.findNode("/modules") + .getChildNames() + .map(mn -> readModuleAttributes(reader, mn)); + } catch (IOException e) { + throw new Error("Error reading root /modules entry", e); + } + } + + /** + * Returns the module's "module-info", returning a holder for its class file + * attributes. Every module is required to have a valid {@code module-info.class}. + */ + private static ModuleInfo.Attributes readModuleAttributes(ImageReader reader, String moduleName) { + Exception err = null; + try { + ImageReader.Node node = reader.findNode(moduleName + "/module-info.class"); + if (node != null && node.isResource()) { + return ModuleInfo.read(reader.getResourceBuffer(node), null); + } + } catch (IOException | UncheckedIOException e) { + err = e; + } + throw new Error("Missing or invalid module-info.class for module: " + moduleName, err); + } + /** * A ModuleFinder that finds module in an array or set of modules. */ @@ -382,34 +410,18 @@ public final class SystemModuleFinders { this.module = module; } - /** - * Returns the ImageLocation for the given resource, {@code null} - * if not found. - */ - private ImageLocation findImageLocation(String name) throws IOException { - Objects.requireNonNull(name); - if (closed) - throw new IOException("ModuleReader is closed"); - ImageReader imageReader = SystemImage.reader(); - if (imageReader != null) { - return imageReader.findLocation(module, name); - } else { - // not an images build - return null; - } - } - /** * Returns {@code true} if the given resource exists, {@code false} * if not found. */ - private boolean containsImageLocation(String name) throws IOException { - Objects.requireNonNull(name); + private boolean containsResource(String resourcePath) throws IOException { + Objects.requireNonNull(resourcePath); if (closed) throw new IOException("ModuleReader is closed"); ImageReader imageReader = SystemImage.reader(); if (imageReader != null) { - return imageReader.verifyLocation(module, name); + ImageReader.Node node = imageReader.findNode("/modules" + resourcePath); + return node != null && node.isResource(); } else { // not an images build return false; @@ -418,8 +430,10 @@ public final class SystemModuleFinders { @Override public Optional find(String name) throws IOException { - if (containsImageLocation(name)) { - URI u = JNUA.create("jrt", "/" + module + "/" + name); + Objects.requireNonNull(name); + String resourcePath = "/" + module + "/" + name; + if (containsResource(resourcePath)) { + URI u = JNUA.create("jrt", resourcePath); return Optional.of(u); } else { return Optional.empty(); @@ -442,14 +456,25 @@ public final class SystemModuleFinders { } } + /** + * Returns the node for the given resource if found. If the name references + * a non-resource node, then {@code null} is returned. + */ + private ImageReader.Node findResource(ImageReader reader, String name) throws IOException { + Objects.requireNonNull(name); + if (closed) { + throw new IOException("ModuleReader is closed"); + } + String nodeName = "/modules/" + module + "/" + name; + ImageReader.Node node = reader.findNode(nodeName); + return (node != null && node.isResource()) ? node : null; + } + @Override public Optional read(String name) throws IOException { - ImageLocation location = findImageLocation(name); - if (location != null) { - return Optional.of(SystemImage.reader().getResourceBuffer(location)); - } else { - return Optional.empty(); - } + ImageReader reader = SystemImage.reader(); + return Optional.ofNullable(findResource(reader, name)) + .map(reader::getResourceBuffer); } @Override @@ -481,7 +506,7 @@ public final class SystemModuleFinders { private static class ModuleContentSpliterator implements Spliterator { final String moduleRoot; final Deque stack; - Iterator iterator; + Iterator iterator; ModuleContentSpliterator(String module) throws IOException { moduleRoot = "/modules/" + module; @@ -502,13 +527,10 @@ public final class SystemModuleFinders { private String next() throws IOException { for (;;) { while (iterator.hasNext()) { - ImageReader.Node node = iterator.next(); - String name = node.getName(); + String name = iterator.next(); + ImageReader.Node node = SystemImage.reader().findNode(name); if (node.isDirectory()) { - // build node - ImageReader.Node dir = SystemImage.reader().findNode(name); - assert dir.isDirectory(); - stack.push(dir); + stack.push(node); } else { // strip /modules/$MODULE/ prefix return name.substring(moduleRoot.length() + 1); @@ -520,7 +542,7 @@ public final class SystemModuleFinders { } else { ImageReader.Node dir = stack.poll(); assert dir.isDirectory(); - iterator = dir.getChildren().iterator(); + iterator = dir.getChildNames().iterator(); } } } diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 43d148a3428..2a51a0af38d 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -154,14 +154,7 @@ module java.base { // module declaration be annotated with jdk.internal.javac.ParticipatesInPreview exports jdk.internal.javac to java.compiler, - java.desktop, // for ScopedValue - jdk.compiler, - jdk.incubator.vector, // participates in preview features - jdk.jartool, // participates in preview features - jdk.jdeps, // participates in preview features - jdk.jfr, // participates in preview features - jdk.jlink, // participates in preview features - jdk.jshell; // participates in preview features + jdk.compiler; exports jdk.internal.access to java.desktop, java.logging, diff --git a/src/java.base/share/classes/sun/nio/cs/CESU_8.java b/src/java.base/share/classes/sun/nio/cs/CESU_8.java index a9a25e151ad..9b907bcbc65 100644 --- a/src/java.base/share/classes/sun/nio/cs/CESU_8.java +++ b/src/java.base/share/classes/sun/nio/cs/CESU_8.java @@ -446,7 +446,7 @@ class CESU_8 extends Unicode int dl = dst.arrayOffset() + dst.limit(); // Handle ASCII-only prefix - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); sp += n; dp += n; @@ -551,7 +551,7 @@ class CESU_8 extends Unicode int dp = 0; // Handle ASCII-only prefix - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(len, da.length)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(len, da.length)); sp += n; dp += n; diff --git a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java index 4738f51515b..165e1e21c0f 100644 --- a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java @@ -600,7 +600,7 @@ public class DoubleByte { try { if (isASCIICompatible) { - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); sp += n; dp += n; } @@ -686,7 +686,7 @@ public class DoubleByte { int dp = 0; int sl = sp + len; if (isASCIICompatible) { - int n = JLA.uncheckedEncodeASCII(src, sp, dst, dp, len); + int n = JLA.encodeASCII(src, sp, dst, dp, len); sp += n; dp += n; } diff --git a/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java b/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java index 39215bfa93d..ff5970e92a8 100644 --- a/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java +++ b/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java @@ -35,7 +35,6 @@ import java.util.Objects; import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; -import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.IntrinsicCandidate; public class ISO_8859_1 @@ -142,20 +141,34 @@ public class ISO_8859_1 private final Surrogate.Parser sgp = new Surrogate.Parser(); - // Method possible replaced with a compiler intrinsic. + /** + * Encodes as many ISO-8859-1 codepoints as possible from the source + * character array into the destination byte array, assuming that + * the encoding is ISO-8859-1 compatible. + * + * @param sa the source character array + * @param sp the index of the source array to start reading from + * @param da the target byte array + * @param dp the index of the target array to start writing to + * @param len the maximum number of characters to be encoded + * @return the total number of characters successfully encoded + * @throws NullPointerException if any of the provided arrays is null + */ private static int encodeISOArray(char[] sa, int sp, byte[] da, int dp, int len) { - if (len <= 0) { + // This method should tolerate invalid arguments, matching the lenient behavior of the VM intrinsic. + // Hence, using operator expressions instead of `Preconditions`, which throw on failure. + if ((sp | dp | len) < 0 || + sp >= sa.length || // Implicit null check on `sa` + dp >= da.length) { // Implicit null check on `da` return 0; } - encodeISOArrayCheck(sa, sp, da, dp, len); - return implEncodeISOArray(sa, sp, da, dp, len); + int minLen = Math.min(len, Math.min(sa.length - sp, da.length - dp)); + return encodeISOArray0(sa, sp, da, dp, minLen); } @IntrinsicCandidate - private static int implEncodeISOArray(char[] sa, int sp, - byte[] da, int dp, int len) - { + private static int encodeISOArray0(char[] sa, int sp, byte[] da, int dp, int len) { int i = 0; for (; i < len; i++) { char c = sa[sp++]; @@ -166,17 +179,6 @@ public class ISO_8859_1 return i; } - private static void encodeISOArrayCheck(char[] sa, int sp, - byte[] da, int dp, int len) { - Objects.requireNonNull(sa); - Objects.requireNonNull(da); - Preconditions.checkIndex(sp, sa.length, Preconditions.AIOOBE_FORMATTER); - Preconditions.checkIndex(dp, da.length, Preconditions.AIOOBE_FORMATTER); - - Preconditions.checkIndex(sp + len - 1, sa.length, Preconditions.AIOOBE_FORMATTER); - Preconditions.checkIndex(dp + len - 1, da.length, Preconditions.AIOOBE_FORMATTER); - } - private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { diff --git a/src/java.base/share/classes/sun/nio/cs/SingleByte.java b/src/java.base/share/classes/sun/nio/cs/SingleByte.java index d802cc85aa8..8efa6b295ff 100644 --- a/src/java.base/share/classes/sun/nio/cs/SingleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/SingleByte.java @@ -217,7 +217,7 @@ public class SingleByte int len = Math.min(dl - dp, sl - sp); if (isASCIICompatible) { - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, len); + int n = JLA.encodeASCII(sa, sp, da, dp, len); sp += n; dp += n; len -= n; diff --git a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java index bb84ab1bd4b..3886978d209 100644 --- a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java +++ b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java @@ -159,7 +159,7 @@ public class US_ASCII assert (dp <= dl); dp = (dp <= dl ? dp : dl); - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); sp += n; dp += n; diff --git a/src/java.base/share/classes/sun/nio/cs/UTF_8.java b/src/java.base/share/classes/sun/nio/cs/UTF_8.java index 54e479f838a..d2d6d7e485d 100644 --- a/src/java.base/share/classes/sun/nio/cs/UTF_8.java +++ b/src/java.base/share/classes/sun/nio/cs/UTF_8.java @@ -452,7 +452,7 @@ public final class UTF_8 extends Unicode { int dl = dst.arrayOffset() + dst.limit(); // Handle ASCII-only prefix - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); sp += n; dp += n; diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c index fbf996886ae..5f81241b7dd 100644 --- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c +++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c @@ -343,22 +343,21 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this) /* system calls that might not be available at run time */ -#if defined(_ALLBSD_SOURCE) - my_openat_func = (openat_func*) openat; - my_fstatat_func = (fstatat_func*) fstatat; -#else - // Make sure we link to the 64-bit version of the functions - my_openat_func = (openat_func*) dlsym(RTLD_DEFAULT, "openat64"); - my_fstatat_func = (fstatat_func*) dlsym(RTLD_DEFAULT, "fstatat64"); -#endif my_unlinkat_func = (unlinkat_func*) dlsym(RTLD_DEFAULT, "unlinkat"); my_renameat_func = (renameat_func*) dlsym(RTLD_DEFAULT, "renameat"); #if defined(_AIX) // Make sure we link to the 64-bit version of the function + my_openat_func = (openat_func*) dlsym(RTLD_DEFAULT, "open64at"); + my_fstatat_func = (fstatat_func*) dlsym(RTLD_DEFAULT, "stat64at"); my_fdopendir_func = (fdopendir_func*) dlsym(RTLD_DEFAULT, "fdopendir64"); #elif defined(_ALLBSD_SOURCE) + my_openat_func = (openat_func*) openat; + my_fstatat_func = (fstatat_func*) fstatat; my_fdopendir_func = (fdopendir_func*) fdopendir; #else + // Make sure we link to the 64-bit version of the functions + my_openat_func = (openat_func*) dlsym(RTLD_DEFAULT, "openat64"); + my_fstatat_func = (fstatat_func*) dlsym(RTLD_DEFAULT, "fstatat64"); my_fdopendir_func = (fdopendir_func*) dlsym(RTLD_DEFAULT, "fdopendir"); #endif diff --git a/src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java b/src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java index f94b8968614..f5e8c8666a2 100644 --- a/src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java +++ b/src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java @@ -33,9 +33,10 @@ import static sun.font.FontUtilities.isIgnorableWhitespace; public final class CCharToGlyphMapper extends CharToGlyphMapper { private static native int countGlyphs(final long nativeFontPtr); - private Cache cache = new Cache(); - CFont fFont; - int numGlyphs = -1; + private final Cache rawCache = new Cache(true); + private final Cache modCache = new Cache(false); + private final CFont fFont; + private int numGlyphs = -1; public CCharToGlyphMapper(CFont font) { fFont = font; @@ -101,13 +102,18 @@ public final class CCharToGlyphMapper extends CharToGlyphMapper { } private int charToGlyph(char unicode, boolean raw) { - int glyph = cache.get(unicode, raw); + Cache cache = raw ? rawCache : modCache; + int glyph = cache.get(unicode); if (glyph != 0) return glyph; - final char[] unicodeArray = new char[] { unicode }; - final int[] glyphArray = new int[1]; - nativeCharsToGlyphs(fFont.getNativeFontPtr(), 1, unicodeArray, glyphArray); - glyph = glyphArray[0]; + if (isIgnorableWhitespace(unicode) || (isDefaultIgnorable(unicode) && !raw)) { + glyph = INVISIBLE_GLYPH_ID; + } else { + final char[] unicodeArray = new char[]{unicode}; + final int[] glyphArray = new int[1]; + nativeCharsToGlyphs(fFont.getNativeFontPtr(), 1, unicodeArray, glyphArray); + glyph = glyphArray[0]; + } cache.put(unicode, glyph); @@ -131,7 +137,8 @@ public final class CCharToGlyphMapper extends CharToGlyphMapper { int base = unicode - 0x10000; surrogates[0] = (char)((base >>> 10) + HI_SURROGATE_START); surrogates[1] = (char)((base % 0x400) + LO_SURROGATE_START); - cache.get(2, surrogates, glyphs, raw); + Cache cache = raw ? rawCache : modCache; + cache.get(2, surrogates, glyphs); return glyphs[0]; } else { return charToGlyph((char) unicode, raw); @@ -140,7 +147,7 @@ public final class CCharToGlyphMapper extends CharToGlyphMapper { @Override public synchronized void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { - cache.get(count, unicodes, glyphs, false); + modCache.get(count, unicodes, glyphs); } @Override @@ -164,20 +171,18 @@ public final class CCharToGlyphMapper extends CharToGlyphMapper { private static final int FIRST_LAYER_SIZE = 256; private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128 + private final boolean raw; private final int[] firstLayerCache = new int[FIRST_LAYER_SIZE]; private SparseBitShiftingTwoLayerArray secondLayerCache; private HashMap generalCache; - Cache() { + Cache(boolean raw) { + this.raw = raw; // need to prevent getting '-1' stuck in the cache firstLayerCache[1] = 1; } - public synchronized int get(final int index, final boolean raw) { - if (isIgnorableWhitespace(index) || (isDefaultIgnorable(index) && !raw)) { - return INVISIBLE_GLYPH_ID; - } - + public synchronized int get(final int index) { if (index < FIRST_LAYER_SIZE) { // catch common glyphcodes return firstLayerCache[index]; @@ -248,7 +253,7 @@ public final class CCharToGlyphMapper extends CharToGlyphMapper { } } - public synchronized void get(int count, char[] indices, int[] values, boolean raw) + public synchronized void get(int count, char[] indices, int[] values) { // "missed" is the count of 'char' that are not mapped. // Surrogates count for 2. @@ -270,13 +275,16 @@ public final class CCharToGlyphMapper extends CharToGlyphMapper { } } - final int value = get(code, raw); + final int value = get(code); if (value != 0 && value != -1) { values[i] = value; if (code >= 0x10000) { values[i+1] = INVISIBLE_GLYPH_ID; i++; } + } else if (isIgnorableWhitespace(code) || (isDefaultIgnorable(code) && !raw)) { + values[i] = INVISIBLE_GLYPH_ID; + put(code, INVISIBLE_GLYPH_ID); } else { values[i] = 0; put(code, -1); diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SubImageInputStream.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SubImageInputStream.java index 10211c6636c..5d6bd401dc6 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SubImageInputStream.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SubImageInputStream.java @@ -71,10 +71,4 @@ public final class SubImageInputStream extends ImageInputStreamImpl { stream.seek(pos - startingPos); streamPos = pos; } - - @SuppressWarnings("removal") - protected void finalize() throws Throwable { - // Empty finalizer (for improved performance; no need to call - // super.finalize() in this case) - } } diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java index 38f8829bae1..b88574edd6e 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java @@ -146,13 +146,6 @@ final class ChunkStream extends ImageOutputStreamImpl { stream.seek(pos); stream.flushBefore(pos); } - - @Override - @SuppressWarnings("removal") - protected void finalize() throws Throwable { - // Empty finalizer (for improved performance; no need to call - // super.finalize() in this case) - } } // Compress output and write as a series of 'IDAT' chunks of @@ -283,13 +276,6 @@ final class IDATOutputStream extends ImageOutputStreamImpl { def.end(); } } - - @Override - @SuppressWarnings("removal") - protected void finalize() throws Throwable { - // Empty finalizer (for improved performance; no need to call - // super.finalize() in this case) - } } diff --git a/src/java.desktop/share/classes/com/sun/imageio/stream/StreamFinalizer.java b/src/java.desktop/share/classes/com/sun/imageio/stream/StreamFinalizer.java deleted file mode 100644 index 012d7e10645..00000000000 --- a/src/java.desktop/share/classes/com/sun/imageio/stream/StreamFinalizer.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute 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 com.sun.imageio.stream; - -import java.io.IOException; -import javax.imageio.stream.ImageInputStream; - -/** - * Small class to assist in properly closing an ImageInputStream instance - * prior to garbage collection. The ImageInputStreamImpl class defines a - * finalize() method, but in a number of its public subclasses - * (e.g. FileImageInputStream) we override the finalize() method to be - * empty for performance reasons, and instead rely on the Disposer mechanism - * for closing/disposing resources. This is fine when one of these classes - * is instantiated directly (e.g. new FileImageInputStream()) but in the - * unlikely case where a user defines their own subclass of one of those - * streams, we need some way to get back to the behavior of - * ImageInputStreamImpl, which will call close() as part of finalization. - * - * Typically an Image{Input,Output}Stream will construct an instance of - * StreamFinalizer in its constructor if it detects that it has been - * subclassed by the user. The ImageInputStream instance will hold a - * reference to the StreamFinalizer, and the StreamFinalizer will hold a - * reference back to the ImageInputStream from which it was created. When - * both are no longer reachable, the StreamFinalizer.finalize() method will - * be called, which will take care of closing down the ImageInputStream. - * - * Clearly this is a bit of a hack, but it will likely only be used in the - * rarest of circumstances: when a user has subclassed one of the public - * stream classes. (It should be no worse than the old days when the public - * stream classes had non-empty finalize() methods.) - */ -public class StreamFinalizer { - private ImageInputStream stream; - - public StreamFinalizer(ImageInputStream stream) { - this.stream = stream; - } - - @SuppressWarnings("removal") - protected void finalize() throws Throwable { - try { - stream.close(); - } catch (IOException e) { - } finally { - stream = null; - super.finalize(); - } - } -} diff --git a/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java b/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java index a17f1f480ed..5e1e149eb9e 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java @@ -248,7 +248,7 @@ public class SwingUtilities3 { } public static void setAcceleratorSelectionForeground(Color acceleratorSelectionFg) { - acceleratorForeground = acceleratorSelectionFg; + acceleratorSelectionForeground = acceleratorSelectionFg; } public static void setAcceleratorForeground(Color acceleratorFg) { diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java index 3793b138a96..8bf09195627 100644 --- a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java +++ b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java @@ -51,11 +51,11 @@ import java.util.Objects; import java.util.StringTokenizer; import sun.awt.AWTAccessor; +import sun.java2d.cmm.BuiltinProfileInfo; import sun.java2d.cmm.CMSManager; import sun.java2d.cmm.PCMM; import sun.java2d.cmm.Profile; import sun.java2d.cmm.ProfileDataVerifier; -import sun.java2d.cmm.ProfileDeferralInfo; import static sun.java2d.cmm.ProfileDataVerifier.HEADER_SIZE; @@ -102,20 +102,11 @@ public sealed class ICC_Profile implements Serializable private transient volatile Profile cmmProfile; /** - * Stores some information about {@code ICC_Profile} without causing a - * deferred profile to be loaded. Note that we can defer the loading of - * standard profiles only. If this field is null, then {@link #cmmProfile} - * should be used to access profile information. + * Stores information about a built-in profile without triggering profile + * loading. If this field is null, {@link #cmmProfile} should be used to + * access profile data. If not null, the profile is considered immutable. */ - private transient volatile ProfileDeferralInfo deferralInfo; - - - /** - * Set to {@code true} for {@code BuiltInProfile}, {@code false} otherwise. - * This flag is used in {@link #setData(int, byte[])} to prevent modifying - * built-in profiles. - */ - private final transient boolean builtIn; + private transient final BuiltinProfileInfo builtInInfo; /** * The lazy registry of singleton profile objects for specific built-in @@ -124,22 +115,22 @@ public sealed class ICC_Profile implements Serializable */ private interface BuiltInProfile { /* - * ProfileDeferralInfo is used for built-in profile creation only, + * BuiltinProfileInfo is used for built-in profile creation only, * and all built-in profiles should be constructed using it. */ - ICC_Profile SRGB = new ICC_ProfileRGB(new ProfileDeferralInfo( + ICC_Profile SRGB = new ICC_ProfileRGB(new BuiltinProfileInfo( "sRGB.pf", ColorSpace.TYPE_RGB, 3, CLASS_DISPLAY)); - ICC_Profile LRGB = new ICC_ProfileRGB(new ProfileDeferralInfo( + ICC_Profile LRGB = new ICC_ProfileRGB(new BuiltinProfileInfo( "LINEAR_RGB.pf", ColorSpace.TYPE_RGB, 3, CLASS_DISPLAY)); - ICC_Profile XYZ = new ICC_Profile(new ProfileDeferralInfo( + ICC_Profile XYZ = new ICC_Profile(new BuiltinProfileInfo( "CIEXYZ.pf", ColorSpace.TYPE_XYZ, 3, CLASS_ABSTRACT)); - ICC_Profile PYCC = new ICC_Profile(new ProfileDeferralInfo( + ICC_Profile PYCC = new ICC_Profile(new BuiltinProfileInfo( "PYCC.pf", ColorSpace.TYPE_3CLR, 3, CLASS_COLORSPACECONVERSION)); - ICC_Profile GRAY = new ICC_ProfileGray(new ProfileDeferralInfo( + ICC_Profile GRAY = new ICC_ProfileGray(new BuiltinProfileInfo( "GRAY.pf", ColorSpace.TYPE_GRAY, 1, CLASS_DISPLAY)); } @@ -771,7 +762,7 @@ public sealed class ICC_Profile implements Serializable */ ICC_Profile(Profile p) { cmmProfile = p; - builtIn = false; + builtInInfo = null; } /** @@ -779,12 +770,11 @@ public sealed class ICC_Profile implements Serializable * The ID will be 0 until the profile is loaded. * *

    - * Note: {@code ProfileDeferralInfo} is used for built-in profile + * Note: {@code BuiltinProfileInfo} is used for built-in profile * creation only, and all built-in profiles should be constructed using it. */ - ICC_Profile(ProfileDeferralInfo pdi) { - deferralInfo = pdi; - builtIn = true; + ICC_Profile(BuiltinProfileInfo bpi) { + builtInInfo = bpi; } /** @@ -934,7 +924,7 @@ public sealed class ICC_Profile implements Serializable if (cmmProfile != null) { return cmmProfile; } - var is = getStandardProfileInputStream(deferralInfo.filename); + var is = getStandardProfileInputStream(builtInInfo.filename); if (is == null) { return null; } @@ -942,8 +932,6 @@ public sealed class ICC_Profile implements Serializable byte[] data = getProfileDataFromStream(is); if (data != null) { p = cmmProfile = CMSManager.getModule().loadProfile(data); - // from now we cannot use the deferred value, drop it - deferralInfo = null; } } catch (CMMException | IOException ignore) { } @@ -975,9 +963,8 @@ public sealed class ICC_Profile implements Serializable * @return one of the predefined profile class constants */ public int getProfileClass() { - ProfileDeferralInfo info = deferralInfo; - if (info != null) { - return info.profileClass; + if (builtInInfo != null) { + return builtInInfo.profileClass; } byte[] theHeader = getData(cmmProfile(), icSigHead); return getProfileClass(theHeader); @@ -1012,9 +999,8 @@ public sealed class ICC_Profile implements Serializable * {@code ColorSpace} class */ public int getColorSpaceType() { - ProfileDeferralInfo info = deferralInfo; - if (info != null) { - return info.colorSpaceType; + if (builtInInfo != null) { + return builtInInfo.colorSpaceType; } byte[] theHeader = getData(cmmProfile(), icSigHead); return getColorSpaceType(theHeader); @@ -1160,8 +1146,8 @@ public sealed class ICC_Profile implements Serializable * @see ColorSpace */ public void setData(int tagSignature, byte[] tagData) { - if (builtIn) { - throw new IllegalArgumentException("Built-in profile cannot be modified"); + if (builtInInfo != null) { + throw new IllegalArgumentException("Can't modify built-in profile"); } if (tagSignature == ICC_Profile.icSigHead) { @@ -1205,9 +1191,8 @@ public sealed class ICC_Profile implements Serializable * @throws ProfileDataException if color space is in the profile is invalid */ public int getNumComponents() { - ProfileDeferralInfo info = deferralInfo; - if (info != null) { - return info.numComponents; + if (builtInInfo != null) { + return builtInInfo.numComponents; } byte[] theHeader = getData(cmmProfile(), icSigHead); int theColorSpaceSig = intFromBigEndian(theHeader, icHdrColorSpace); diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_ProfileGray.java b/src/java.desktop/share/classes/java/awt/color/ICC_ProfileGray.java index 523668d37ec..f8de7d1581d 100644 --- a/src/java.desktop/share/classes/java/awt/color/ICC_ProfileGray.java +++ b/src/java.desktop/share/classes/java/awt/color/ICC_ProfileGray.java @@ -37,8 +37,8 @@ package java.awt.color; import java.io.Serial; +import sun.java2d.cmm.BuiltinProfileInfo; import sun.java2d.cmm.Profile; -import sun.java2d.cmm.ProfileDeferralInfo; /** * The {@code ICC_ProfileGray} class is a subclass of the {@code ICC_Profile} @@ -87,10 +87,10 @@ public final class ICC_ProfileGray extends ICC_Profile { /** * Constructs a new {@code ICC_ProfileGray} from a - * {@code ProfileDeferralInfo} object. + * {@code BuiltinProfileInfo} object. */ - ICC_ProfileGray(ProfileDeferralInfo pdi) { - super(pdi); + ICC_ProfileGray(BuiltinProfileInfo bpi) { + super(bpi); } /** diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_ProfileRGB.java b/src/java.desktop/share/classes/java/awt/color/ICC_ProfileRGB.java index d46852380f3..1c7fab265ac 100644 --- a/src/java.desktop/share/classes/java/awt/color/ICC_ProfileRGB.java +++ b/src/java.desktop/share/classes/java/awt/color/ICC_ProfileRGB.java @@ -37,8 +37,8 @@ package java.awt.color; import java.io.Serial; +import sun.java2d.cmm.BuiltinProfileInfo; import sun.java2d.cmm.Profile; -import sun.java2d.cmm.ProfileDeferralInfo; /** * The {@code ICC_ProfileRGB} class is a subclass of the {@code ICC_Profile} @@ -122,12 +122,10 @@ public final class ICC_ProfileRGB extends ICC_Profile { /** * Constructs a new {@code ICC_ProfileRGB} from a - * {@code ProfileDeferralInfo} object. - * - * @param pdi + * {@code BuiltinProfileInfo} object. */ - ICC_ProfileRGB(ProfileDeferralInfo pdi) { - super(pdi); + ICC_ProfileRGB(BuiltinProfileInfo bpi) { + super(bpi); } /** diff --git a/src/java.desktop/share/classes/javax/imageio/ImageReader.java b/src/java.desktop/share/classes/javax/imageio/ImageReader.java index 3488f60b9b7..281e7298354 100644 --- a/src/java.desktop/share/classes/javax/imageio/ImageReader.java +++ b/src/java.desktop/share/classes/javax/imageio/ImageReader.java @@ -2501,8 +2501,7 @@ public abstract class ImageReader { /** * Allows any resources held by this object to be released. The - * result of calling any other method (other than - * {@code finalize}) subsequent to a call to this method + * result of calling any other method subsequent to a call to this method * is undefined. * *

    It is important for applications to call this method when they diff --git a/src/java.desktop/share/classes/javax/imageio/ImageWriter.java b/src/java.desktop/share/classes/javax/imageio/ImageWriter.java index a688d9aea91..fd8cc3a753d 100644 --- a/src/java.desktop/share/classes/javax/imageio/ImageWriter.java +++ b/src/java.desktop/share/classes/javax/imageio/ImageWriter.java @@ -2000,8 +2000,7 @@ public abstract class ImageWriter implements ImageTranscoder { /** * Allows any resources held by this object to be released. The - * result of calling any other method (other than - * {@code finalize}) subsequent to a call to this method + * result of calling any other method subsequent to a call to this method * is undefined. * *

    It is important for applications to call this method when they diff --git a/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageInputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageInputStream.java index d5c1ff18e5a..4f44ad1ab2a 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageInputStream.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageInputStream.java @@ -31,7 +31,6 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.nio.file.Files; import com.sun.imageio.stream.StreamCloser; -import com.sun.imageio.stream.StreamFinalizer; import sun.java2d.Disposer; import sun.java2d.DisposerRecord; @@ -58,7 +57,7 @@ public class FileCacheImageInputStream extends ImageInputStreamImpl { private boolean foundEOF = false; /** The referent to be registered with the Disposer. */ - private final Object disposerReferent; + private final Object disposerReferent = new Object(); /** The DisposerRecord that closes the underlying cache. */ private final DisposerRecord disposerRecord; @@ -109,12 +108,7 @@ public class FileCacheImageInputStream extends ImageInputStreamImpl { StreamCloser.addToQueue(closeAction); disposerRecord = new StreamDisposerRecord(cacheFile, cache); - if (getClass() == FileCacheImageInputStream.class) { - disposerReferent = new Object(); - Disposer.addRecord(disposerReferent, disposerRecord); - } else { - disposerReferent = new StreamFinalizer(this); - } + Disposer.addRecord(disposerReferent, disposerRecord); } /** @@ -258,22 +252,7 @@ public class FileCacheImageInputStream extends ImageInputStreamImpl { StreamCloser.removeFromQueue(closeAction); } - /** - * {@inheritDoc} - * - * @deprecated Finalization has been deprecated for removal. See - * {@link java.lang.Object#finalize} for background information and details - * about migration options. - */ - @Deprecated(since="9", forRemoval=true) - @SuppressWarnings("removal") - protected void finalize() throws Throwable { - // Empty finalizer: for performance reasons we instead use the - // Disposer mechanism for ensuring that the underlying - // RandomAccessFile is closed/deleted prior to garbage collection - } - - private static class StreamDisposerRecord implements DisposerRecord { + static class StreamDisposerRecord implements DisposerRecord { private File cacheFile; private RandomAccessFile cache; diff --git a/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java index 5886831d6e9..13269832ab5 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java @@ -30,7 +30,10 @@ import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.file.Files; +import javax.imageio.stream.FileCacheImageInputStream.StreamDisposerRecord; import com.sun.imageio.stream.StreamCloser; +import sun.java2d.Disposer; +import sun.java2d.DisposerRecord; /** * An implementation of {@code ImageOutputStream} that writes its @@ -46,6 +49,9 @@ public class FileCacheImageOutputStream extends ImageOutputStreamImpl { private RandomAccessFile cache; + private final Object disposerReferent = new Object(); + + private final StreamDisposerRecord disposerRecord; // Pos after last (rightmost) byte written private long maxStreamPos = 0L; @@ -91,6 +97,13 @@ public class FileCacheImageOutputStream extends ImageOutputStreamImpl { .toFile(); this.cache = new RandomAccessFile(cacheFile, "rw"); + // If this instance becomes unreachable the disposer will clean up resources + // used for caching. This can't flush any un-flushed cache. + this.disposerRecord = new StreamDisposerRecord(cacheFile, cache); + Disposer.addRecord(this.disposerReferent, this.disposerRecord); + // If the VM is exiting and this instance is still reachable, + // StreamCloser will call close() to flush the cache and clean up resources. + // However closing the java.io.OutputStream is the application's responsibility. this.closeAction = StreamCloser.createCloseAction(this); StreamCloser.addToQueue(closeAction); } @@ -231,9 +244,8 @@ public class FileCacheImageOutputStream extends ImageOutputStreamImpl { seek(maxStreamPos); flushBefore(maxStreamPos); super.close(); - cache.close(); + disposerRecord.dispose(); cache = null; - cacheFile.delete(); cacheFile = null; stream.flush(); stream = null; diff --git a/src/java.desktop/share/classes/javax/imageio/stream/FileImageInputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/FileImageInputStream.java index 98b444b3186..e5baee6e7c0 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/FileImageInputStream.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/FileImageInputStream.java @@ -30,7 +30,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import com.sun.imageio.stream.CloseableDisposerRecord; -import com.sun.imageio.stream.StreamFinalizer; import sun.java2d.Disposer; /** @@ -45,7 +44,7 @@ public class FileImageInputStream extends ImageInputStreamImpl { private RandomAccessFile raf; /** The referent to be registered with the Disposer. */ - private final Object disposerReferent; + private final Object disposerReferent = new Object(); /** The DisposerRecord that closes the underlying RandomAccessFile. */ private final CloseableDisposerRecord disposerRecord; @@ -95,12 +94,7 @@ public class FileImageInputStream extends ImageInputStreamImpl { } disposerRecord = new CloseableDisposerRecord(raf); - if (getClass() == FileImageInputStream.class) { - disposerReferent = new Object(); - Disposer.addRecord(disposerReferent, disposerRecord); - } else { - disposerReferent = new StreamFinalizer(this); - } + Disposer.addRecord(disposerReferent, disposerRecord); } public int read() throws IOException { @@ -154,19 +148,4 @@ public class FileImageInputStream extends ImageInputStreamImpl { disposerRecord.dispose(); // this closes the RandomAccessFile raf = null; } - - /** - * {@inheritDoc} - * - * @deprecated Finalization has been deprecated for removal. See - * {@link java.lang.Object#finalize} for background information and details - * about migration options. - */ - @Deprecated(since="9", forRemoval=true) - @SuppressWarnings("removal") - protected void finalize() throws Throwable { - // Empty finalizer: for performance reasons we instead use the - // Disposer mechanism for ensuring that the underlying - // RandomAccessFile is closed prior to garbage collection - } } diff --git a/src/java.desktop/share/classes/javax/imageio/stream/FileImageOutputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/FileImageOutputStream.java index 6597f137dcf..8a22afd5b12 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/FileImageOutputStream.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/FileImageOutputStream.java @@ -30,7 +30,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import com.sun.imageio.stream.CloseableDisposerRecord; -import com.sun.imageio.stream.StreamFinalizer; import sun.java2d.Disposer; /** @@ -44,7 +43,7 @@ public class FileImageOutputStream extends ImageOutputStreamImpl { private RandomAccessFile raf; /** The referent to be registered with the Disposer. */ - private final Object disposerReferent; + private final Object disposerReferent = new Object(); /** The DisposerRecord that closes the underlying RandomAccessFile. */ private final CloseableDisposerRecord disposerRecord; @@ -87,12 +86,7 @@ public class FileImageOutputStream extends ImageOutputStreamImpl { } disposerRecord = new CloseableDisposerRecord(raf); - if (getClass() == FileImageOutputStream.class) { - disposerReferent = new Object(); - Disposer.addRecord(disposerReferent, disposerRecord); - } else { - disposerReferent = new StreamFinalizer(this); - } + Disposer.addRecord(disposerReferent, disposerRecord); } public int read() throws IOException { @@ -162,19 +156,4 @@ public class FileImageOutputStream extends ImageOutputStreamImpl { disposerRecord.dispose(); // this closes the RandomAccessFile raf = null; } - - /** - * {@inheritDoc} - * - * @deprecated Finalization has been deprecated for removal. See - * {@link java.lang.Object#finalize} for background information and details - * about migration options. - */ - @Deprecated(since="9", forRemoval=true) - @SuppressWarnings("removal") - protected void finalize() throws Throwable { - // Empty finalizer: for performance reasons we instead use the - // Disposer mechanism for ensuring that the underlying - // RandomAccessFile is closed prior to garbage collection - } } diff --git a/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStreamImpl.java b/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStreamImpl.java index a6feaa45d47..f21f9ec5ade 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStreamImpl.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStreamImpl.java @@ -841,29 +841,4 @@ public abstract class ImageInputStreamImpl implements ImageInputStream { isClosed = true; } - - /** - * Finalizes this object prior to garbage collection. The - * {@code close} method is called to close any open input - * source. This method should not be called from application - * code. - * - * @throws Throwable if an error occurs during superclass - * finalization. - * - * @deprecated Finalization has been deprecated for removal. See - * {@link java.lang.Object#finalize} for background information and details - * about migration options. - */ - @Deprecated(since="9", forRemoval=true) - @SuppressWarnings("removal") - protected void finalize() throws Throwable { - if (!isClosed) { - try { - close(); - } catch (IOException e) { - } - } - super.finalize(); - } } diff --git a/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageInputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageInputStream.java index ecb30ddba52..1799254c59a 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageInputStream.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageInputStream.java @@ -27,7 +27,6 @@ package javax.imageio.stream; import java.io.InputStream; import java.io.IOException; -import com.sun.imageio.stream.StreamFinalizer; import sun.java2d.Disposer; import sun.java2d.DisposerRecord; @@ -50,7 +49,7 @@ public class MemoryCacheImageInputStream extends ImageInputStreamImpl { private MemoryCache cache = new MemoryCache(); /** The referent to be registered with the Disposer. */ - private final Object disposerReferent; + private final Object disposerReferent = new Object(); /** The DisposerRecord that resets the underlying MemoryCache. */ private final DisposerRecord disposerRecord; @@ -71,12 +70,7 @@ public class MemoryCacheImageInputStream extends ImageInputStreamImpl { this.stream = stream; disposerRecord = new StreamDisposerRecord(cache); - if (getClass() == MemoryCacheImageInputStream.class) { - disposerReferent = new Object(); - Disposer.addRecord(disposerReferent, disposerRecord); - } else { - disposerReferent = new StreamFinalizer(this); - } + Disposer.addRecord(disposerReferent, disposerRecord); } public int read() throws IOException { @@ -176,21 +170,6 @@ public class MemoryCacheImageInputStream extends ImageInputStreamImpl { cache = null; } - /** - * {@inheritDoc} - * - * @deprecated Finalization has been deprecated for removal. See - * {@link java.lang.Object#finalize} for background information and details - * about migration options. - */ - @Deprecated(since="9", forRemoval=true) - @SuppressWarnings("removal") - protected void finalize() throws Throwable { - // Empty finalizer: for performance reasons we instead use the - // Disposer mechanism for ensuring that the underlying - // MemoryCache is reset prior to garbage collection - } - private static class StreamDisposerRecord implements DisposerRecord { private MemoryCache cache; diff --git a/src/java.desktop/share/classes/javax/imageio/stream/package-info.java b/src/java.desktop/share/classes/javax/imageio/stream/package-info.java index b28caf0ef0d..56dccaa9823 100644 --- a/src/java.desktop/share/classes/javax/imageio/stream/package-info.java +++ b/src/java.desktop/share/classes/javax/imageio/stream/package-info.java @@ -44,7 +44,31 @@ *

    * The {@code IIOByteBuffer} class provides an alternative way to perform reads * of sequences of bytes that reduces the amount of internal data copying. - * + *

    + * An {@code ImageInputStream} or {@code ImageOutputStream} may internally allocate + * system resources, such as a temporary cache file. + * Clients are encouraged to use a try-with-resources statement to ensure the + * {@link ImageInputStream#close()} or {@link ImageOutputStream#close()} + * method is called which can promptly free those native resources. + * Otherwise there is the possibility they will leak and eventually cause the + * application to fail as well the possibility that not all data is flushed + * to the underlying output stream. A logical consequence of that is that + * this should be done before closing the destination {@link java.io.OutputStream}. + * A simple pattern would be + * {@snippet lang='java': + * try (FileOutputStream fos = new FileOutputStream("out.jpg"); + * ImageOutputStream ios = new FileCacheImageOutputStream(fos, null)) { + * ImageIO.write(img, "jpg", ios); + * } catch (IOException e) { + * } // implicit finally block closes the streams in the reverse order to opening + * } + *

    + * Sub-classers of these Image I/O API stream types can, to a limited extent, protect + * the application from the consequences of failures to close by adopting mechanisms + * such as {@link java.lang.ref.Cleaner} to free internal resources when it + * is no longer reachable. This is only necessary if there are any resources to release. + * However applications cannot rely on this, either for resource management, or + * for program correctness. * @since 1.4 */ package javax.imageio.stream; diff --git a/src/java.desktop/share/classes/sun/font/CompositeGlyphMapper.java b/src/java.desktop/share/classes/sun/font/CompositeGlyphMapper.java index 673e2eb0d9f..dcf0bab3ab3 100644 --- a/src/java.desktop/share/classes/sun/font/CompositeGlyphMapper.java +++ b/src/java.desktop/share/classes/sun/font/CompositeGlyphMapper.java @@ -25,7 +25,7 @@ package sun.font; -/* remember that the API requires a Font use a +/* Remember that the API requires a Font use a * consistent glyph id. for a code point, and this is a * problem if a particular strike uses native scaler sometimes * and the JDK scaler others. That needs to be dealt with somewhere, but @@ -36,9 +36,9 @@ package sun.font; * to the maximum surrogate pair code point. * This implementation will not cache as much, since the storage * requirements are not justifiable. Even so it still can use up - * to 216*256*4 bytes of storage per composite font. If an app + * to 2*216*256*4 bytes of storage per composite font. If an app * calls canDisplay on this range for all 20 composite fonts that's - * over 1Mb of cached data. May need to employ WeakReferences if + * over 2Mb of cached data. May need to employ WeakReferences if * this appears to cause problems. */ @@ -54,14 +54,20 @@ public class CompositeGlyphMapper extends CharToGlyphMapper { public static final int BLOCKSZ = 256; public static final int MAXUNICODE = NBLOCKS*BLOCKSZ; - CompositeFont font; - CharToGlyphMapper[] slotMappers; - int[][] glyphMaps; - private boolean hasExcludes; + private final CompositeFont font; + private final CharToGlyphMapper[] slotMappers; + private final int[][] glyphMapsRaw; + private final int[][] glyphMapsMod; + private final boolean hasExcludes; public CompositeGlyphMapper(CompositeFont compFont) { font = compFont; - initMapper(); + glyphMapsRaw = new int[NBLOCKS][]; + glyphMapsMod = new int[NBLOCKS][]; + slotMappers = new CharToGlyphMapper[font.numSlots]; + /* This requires that slot 0 is never empty. */ + missingGlyph = font.getSlotFont(0).getMissingGlyphCode(); + missingGlyph = compositeGlyphCode(0, missingGlyph); /* This is often false which saves the overhead of a * per-mapped char method call. */ @@ -73,34 +79,24 @@ public class CompositeGlyphMapper extends CharToGlyphMapper { return (slot << 24 | (glyphCode & GLYPHMASK)); } - private void initMapper() { - if (missingGlyph == CharToGlyphMapper.UNINITIALIZED_GLYPH) { - if (glyphMaps == null) { - glyphMaps = new int[NBLOCKS][]; - } - slotMappers = new CharToGlyphMapper[font.numSlots]; - /* This requires that slot 0 is never empty. */ - missingGlyph = font.getSlotFont(0).getMissingGlyphCode(); - missingGlyph = compositeGlyphCode(0, missingGlyph); - } - } - - private int getCachedGlyphCode(int unicode) { + private int getCachedGlyphCode(int unicode, boolean raw) { if (unicode >= MAXUNICODE) { return UNINITIALIZED_GLYPH; // don't cache surrogates } int[] gmap; + int[][] glyphMaps = raw ? glyphMapsRaw : glyphMapsMod; if ((gmap = glyphMaps[unicode >> 8]) == null) { return UNINITIALIZED_GLYPH; } return gmap[unicode & 0xff]; } - private void setCachedGlyphCode(int unicode, int glyphCode) { + private void setCachedGlyphCode(int unicode, int glyphCode, boolean raw) { if (unicode >= MAXUNICODE) { return; // don't cache surrogates } int index0 = unicode >> 8; + int[][] glyphMaps = raw ? glyphMapsRaw : glyphMapsMod; if (glyphMaps[index0] == null) { glyphMaps[index0] = new int[BLOCKSZ]; for (int i=0;i validSrcDataRef = new WeakReference<>(null); - private Reference validDstDataRef = new WeakReference<>(null); - private Reference validClipRef = new WeakReference<>(null); - private Reference validCompRef = new WeakReference<>(null); - private Reference validPaintRef = new WeakReference<>(null); + private Reference validSrcDataRef = null; + private Reference validDstDataRef = null; + private Reference validClipRef = null; + private Reference validCompRef = null; + private Reference validPaintRef = null; // renamed from isValidatedPaintAColor as part of a work around for 6764257 private boolean isValidatedPaintJustAColor; private int validatedRGB; @@ -213,20 +213,18 @@ public abstract class BufferedContext { updatePaint = true; isValidatedPaintJustAColor = true; } - } else if (validPaintRef.get() != paint) { + } else if (stateChanged(validPaintRef, paint)) { updatePaint = true; // this should be set when we are switching from paint to color // in which case this condition will be true isValidatedPaintJustAColor = false; } - final AccelSurface validatedSrcData = validSrcDataRef.get(); - final AccelSurface validatedDstData = validDstDataRef.get(); - if ((currentContext != this) || - (srcData != validatedSrcData) || - (dstData != validatedDstData)) + final boolean srcChanged = stateChanged(validSrcDataRef, srcData); + final boolean dstChanged = stateChanged(validDstDataRef, dstData); + if ((currentContext != this) || srcChanged || dstChanged) { - if (dstData != validatedDstData) { + if (dstChanged) { // the clip is dependent on the destination surface, so we // need to update it if we have a new destination surface updateClip = true; @@ -243,13 +241,13 @@ public abstract class BufferedContext { setSurfaces(srcData, dstData); currentContext = this; - validSrcDataRef = new WeakReference<>(srcData); - validDstDataRef = new WeakReference<>(dstData); + validSrcDataRef = wrapState(srcData); + validDstDataRef = wrapState(dstData); } // validate clip - final Region validatedClip = validClipRef.get(); - if ((clip != validatedClip) || updateClip) { + final Region validatedClip = validClipRef == null ? null : validClipRef.get(); + if (stateChanged(validClipRef, clip) || updateClip) { if (clip != null) { if (updateClip || validatedClip == null || @@ -264,13 +262,13 @@ public abstract class BufferedContext { } else { resetClip(); } - validClipRef = new WeakReference<>(clip); + validClipRef = wrapState(clip); } // validate composite (note that a change in the context flags // may require us to update the composite state, even if the // composite has not changed) - if ((comp != validCompRef.get()) || (flags != validatedFlags)) { + if (stateChanged(validCompRef, comp) || (flags != validatedFlags)) { if (comp != null) { setComposite(comp, flags); } else { @@ -279,7 +277,7 @@ public abstract class BufferedContext { // the paint state is dependent on the composite state, so make // sure we update the color below updatePaint = true; - validCompRef = new WeakReference<>(comp); + validCompRef = wrapState(comp); validatedFlags = flags; } @@ -313,7 +311,7 @@ public abstract class BufferedContext { } else { BufferedPaints.resetPaint(rq); } - validPaintRef = new WeakReference<>(paint); + validPaintRef = wrapState(paint); } // mark dstData dirty @@ -321,6 +319,18 @@ public abstract class BufferedContext { dstData.markDirty(); } + private static boolean stateChanged(Reference ref, T obj) { + // null ref means "true" null object + if (ref == null) return obj != null; + T old = ref.get(); + // null ref value means the object was GC'ed, return true in that case + return old == null || old != obj; + } + + private static Reference wrapState(T obj) { + return obj == null ? null : new WeakReference<>(obj); + } + private void setSurfaces(AccelSurface srcData, AccelSurface dstData) { @@ -434,11 +444,11 @@ public abstract class BufferedContext { resetComposite(); resetClip(); BufferedPaints.resetPaint(rq); - validSrcDataRef.clear(); - validDstDataRef.clear(); - validCompRef.clear(); - validClipRef.clear(); - validPaintRef.clear(); + validSrcDataRef = null; + validDstDataRef = null; + validCompRef = null; + validClipRef = null; + validPaintRef = null; isValidatedPaintJustAColor = false; xformInUse = false; } diff --git a/src/java.se/share/data/jdwp/jdwp.spec b/src/java.se/share/data/jdwp/jdwp.spec index 5206a0c6ae9..11e3160ec97 100644 --- a/src/java.se/share/data/jdwp/jdwp.spec +++ b/src/java.se/share/data/jdwp/jdwp.spec @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2148,12 +2148,8 @@ JDWP "Java(tm) Debug Wire Protocol" "the thread is not alive.") (Error INVALID_OBJECT "Thread or value is not a known ID.") (Error THREAD_NOT_SUSPENDED) - (Error OPAQUE_FRAME "Attempted to return early from a frame " - "corresponding to a native method, " - "the thread is a suspended virtual thread and the target " - "VM is unable to force its current frame to return, " - "or the implementation is unable to provide this " - "functionality on this frame.") + (Error OPAQUE_FRAME "Unable to force the current frame to return " + "(e.g. the current frame is executing a native method).") (Error NO_MORE_FRAMES) (Error NOT_IMPLEMENTED) (Error TYPE_MISMATCH "Value is not an appropriate type for the " @@ -2625,6 +2621,8 @@ JDWP "Java(tm) Debug Wire Protocol" (Error INVALID_OBJECT) (Error INVALID_FRAMEID) (Error INVALID_SLOT) + (Error OPAQUE_FRAME "Unable to get the value of local variables in the frame " + "(e.g. the frame is executing a native method).") (Error VM_DEAD) ) ) @@ -2662,9 +2660,9 @@ JDWP "Java(tm) Debug Wire Protocol" (Error INVALID_THREAD) (Error INVALID_OBJECT) (Error INVALID_FRAMEID) - (Error OPAQUE_FRAME "The thread is a suspended virtual thread and the target VM " - "does not support setting the value of local " - "variables in the frame.") + (Error INVALID_SLOT) + (Error OPAQUE_FRAME "Unable to set the value of local variables in the frame " + "(e.g. the frame is executing a native method).") (Error VM_DEAD) ) ) @@ -2713,10 +2711,9 @@ JDWP "Java(tm) Debug Wire Protocol" (Error INVALID_FRAMEID) (Error THREAD_NOT_SUSPENDED) (Error NO_MORE_FRAMES) - (Error OPAQUE_FRAME "If one or more of the frames to pop is a native " - "method or its caller is a native method, or the " - "thread is a suspended virtual thread and the implementation " - "was unable to pop the frames.") + (Error OPAQUE_FRAME "Unable to pop one or more of the frames " + "(e.g. one or more of the frames to pop is a native " + "method or its caller is a native method).") (Error NOT_IMPLEMENTED) (Error VM_DEAD) ) diff --git a/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template b/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template index 4fc0b2796ee..f3d03a9e9c7 100644 --- a/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template +++ b/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template @@ -309,7 +309,7 @@ public class EUC_JP try { if (enc0201.isASCIICompatible()) { - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); sp += n; dp += n; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java index 26f0d338a2e..5b59e47027e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java @@ -25,6 +25,8 @@ package com.sun.tools.javac.code; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.EnumSet; import java.util.Map; @@ -35,7 +37,6 @@ import java.util.stream.Collectors; import javax.lang.model.element.Modifier; import com.sun.tools.javac.util.Assert; -import com.sun.tools.javac.util.StringUtils; /** Access flags and other modifiers for Java classes and members. * @@ -51,7 +52,7 @@ public class Flags { public static String toString(long flags) { StringBuilder buf = new StringBuilder(); String sep = ""; - for (Flag flag : asFlagSet(flags)) { + for (FlagsEnum flag : asFlagSet(flags)) { buf.append(sep); buf.append(flag); sep = " "; @@ -59,12 +60,12 @@ public class Flags { return buf.toString(); } - public static EnumSet asFlagSet(long flags) { - EnumSet flagSet = EnumSet.noneOf(Flag.class); - for (Flag flag : Flag.values()) { - if ((flags & flag.value) != 0) { + public static EnumSet asFlagSet(long flags) { + EnumSet flagSet = EnumSet.noneOf(FlagsEnum.class); + for (FlagsEnum flag : FlagsEnum.values()) { + if ((flags & flag.value()) != 0) { flagSet.add(flag); - flags &= ~flag.value; + flags &= ~flag.value(); } } Assert.check(flags == 0); @@ -73,42 +74,67 @@ public class Flags { /* Standard Java flags. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int PUBLIC = 1; + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int PRIVATE = 1<<1; + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int PROTECTED = 1<<2; + @Use({FlagTarget.BLOCK, FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int STATIC = 1<<3; + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int FINAL = 1<<4; + @Use({FlagTarget.METHOD}) public static final int SYNCHRONIZED = 1<<5; + @Use({FlagTarget.VARIABLE}) public static final int VOLATILE = 1<<6; + @Use({FlagTarget.VARIABLE}) public static final int TRANSIENT = 1<<7; + @Use({FlagTarget.METHOD}) public static final int NATIVE = 1<<8; + @Use({FlagTarget.CLASS}) public static final int INTERFACE = 1<<9; + @Use({FlagTarget.CLASS, FlagTarget.METHOD}) public static final int ABSTRACT = 1<<10; + @Use({FlagTarget.CLASS, FlagTarget.METHOD}) public static final int STRICTFP = 1<<11; /* Flag that marks a symbol synthetic, added in classfile v49.0. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final int SYNTHETIC = 1<<12; /** Flag that marks attribute interfaces, added in classfile v49.0. */ + @Use({FlagTarget.CLASS}) public static final int ANNOTATION = 1<<13; /** An enumeration type or an enumeration constant, added in * classfile v49.0. */ + @Use({FlagTarget.CLASS, FlagTarget.VARIABLE}) public static final int ENUM = 1<<14; /** Added in SE8, represents constructs implicitly declared in source. */ + @Use({FlagTarget.MODULE, FlagTarget.VARIABLE}) public static final int MANDATED = 1<<15; + @NotFlag public static final int StandardFlags = 0x0fff; // Because the following access flags are overloaded with other // bit positions, we translate them when reading and writing class // files into unique bits positions: ACC_SYNTHETIC <-> SYNTHETIC, // for example. - public static final int ACC_SUPER = 0x0020; - public static final int ACC_BRIDGE = 0x0040; - public static final int ACC_VARARGS = 0x0080; - public static final int ACC_MODULE = 0x8000; + @Use({FlagTarget.CLASS}) + @NoToStringValue + public static final int ACC_SUPER = 1<<5; + @Use({FlagTarget.METHOD}) + @NoToStringValue + public static final int ACC_BRIDGE = 1<<6; + @Use({FlagTarget.METHOD}) + @NoToStringValue + public static final int ACC_VARARGS = 1<<7; + @Use({FlagTarget.CLASS}) + @NoToStringValue + public static final int ACC_MODULE = 1<<15; /* *************************************** * Internal compiler flags (no bits in the lower 16). @@ -116,25 +142,30 @@ public class Flags { /** Flag is set if symbol is deprecated. See also DEPRECATED_REMOVAL. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final int DEPRECATED = 1<<17; /** Flag is set for a variable symbol if the variable's definition * has an initializer part. */ + @Use({FlagTarget.VARIABLE}) public static final int HASINIT = 1<<18; /** Class is an implicitly declared top level class. */ + @Use({FlagTarget.CLASS}) public static final int IMPLICIT_CLASS = 1<<19; /** Flag is set for compiler-generated anonymous method symbols * that `own' an initializer block. */ + @Use({FlagTarget.METHOD}) public static final int BLOCK = 1<<20; /** Flag is set for ClassSymbols that are being compiled from source. */ - public static final int FROM_SOURCE = 1<<21; //ClassSymbols + @Use({FlagTarget.CLASS}) + public static final int FROM_SOURCE = 1<<21; /** Flag is set for nested classes that do not access instance members * or `this' of an outer class and therefore don't need to be passed @@ -143,25 +174,30 @@ public class Flags { * todo: use this value for optimizing away this$n parameters in * other cases. */ + @Use({FlagTarget.CLASS, FlagTarget.VARIABLE}) public static final int NOOUTERTHIS = 1<<22; /** Flag is set for package symbols if a package has a member or * directory and therefore exists. */ + @Use({FlagTarget.CLASS, FlagTarget.PACKAGE}) public static final int EXISTS = 1<<23; /** Flag is set for compiler-generated compound classes * representing multiple variable bounds */ + @Use({FlagTarget.CLASS}) public static final int COMPOUND = 1<<24; /** Flag is set for class symbols if a class file was found for this class. */ + @Use({FlagTarget.CLASS}) public static final int CLASS_SEEN = 1<<25; /** Flag is set for class symbols if a source file was found for this * class. */ + @Use({FlagTarget.CLASS}) public static final int SOURCE_SEEN = 1<<26; /* State flags (are reset during compilation). @@ -172,242 +208,291 @@ public class Flags { * relations. Similarly for constructor call cycle detection in * Attr. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD}) public static final int LOCKED = 1<<27; /** Flag for class symbols is set and later re-set to indicate that a class * has been entered but has not yet been attributed. */ + @Use({FlagTarget.CLASS}) public static final int UNATTRIBUTED = 1<<28; /** Flag for synthesized default constructors of anonymous classes. */ - public static final int ANONCONSTR = 1<<29; //non-class members + @Use({FlagTarget.METHOD}) + public static final int ANONCONSTR = 1<<29; /** * Flag to indicate the superclasses of this ClassSymbol has been attributed. */ - public static final int SUPER_OWNER_ATTRIBUTED = 1<<29; //ClassSymbols + @Use({FlagTarget.CLASS}) + public static final int SUPER_OWNER_ATTRIBUTED = 1<<29; /** Flag for class symbols to indicate it has been checked and found * acyclic. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.TYPE_VAR}) public static final int ACYCLIC = 1<<30; /** Flag that marks bridge methods. */ + @Use({FlagTarget.METHOD}) public static final long BRIDGE = 1L<<31; /** Flag that marks formal parameters. */ + @Use({FlagTarget.VARIABLE}) public static final long PARAMETER = 1L<<33; /** Flag that marks varargs methods. */ + @Use({FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final long VARARGS = 1L<<34; /** Flag for annotation type symbols to indicate it has been * checked and found acyclic. */ + @Use({FlagTarget.CLASS}) public static final long ACYCLIC_ANN = 1L<<35; /** Flag that marks a generated default constructor. */ + @Use({FlagTarget.METHOD}) public static final long GENERATEDCONSTR = 1L<<36; /** Flag that marks a hypothetical method that need not really be * generated in the binary, but is present in the symbol table to * simplify checking for erasure clashes - also used for 292 poly sig methods. */ + @Use({FlagTarget.METHOD}) public static final long HYPOTHETICAL = 1L<<37; /** * Flag that marks an internal proprietary class. */ + @Use({FlagTarget.CLASS}) public static final long PROPRIETARY = 1L<<38; /** * Flag that marks a multi-catch parameter. */ + @Use({FlagTarget.VARIABLE}) public static final long UNION = 1L<<39; /** * Flags an erroneous TypeSymbol as viable for recovery. * TypeSymbols only. */ + @Use({FlagTarget.CLASS, FlagTarget.TYPE_VAR}) public static final long RECOVERABLE = 1L<<40; /** * Flag that marks an 'effectively final' local variable. */ + @Use({FlagTarget.VARIABLE}) public static final long EFFECTIVELY_FINAL = 1L<<41; /** * Flag that marks non-override equivalent methods with the same signature, * or a conflicting match binding (BindingSymbol). */ + @Use({FlagTarget.METHOD, FlagTarget.VARIABLE}) public static final long CLASH = 1L<<42; /** * Flag that marks either a default method or an interface containing default methods. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD}) public static final long DEFAULT = 1L<<43; // part of ExtendedStandardFlags, cannot be reused /** * Flag that marks class as auxiliary, ie a non-public class following * the public class in a source file, that could block implicit compilation. */ + @Use({FlagTarget.CLASS}) public static final long AUXILIARY = 1L<<44; /** * Flag that marks that a symbol is not available in the current profile */ + @Use({FlagTarget.CLASS}) public static final long NOT_IN_PROFILE = 1L<<45; /** * Flag that indicates that an override error has been detected by Check. */ + @Use({FlagTarget.METHOD}) public static final long BAD_OVERRIDE = 1L<<45; /** * Flag that indicates a signature polymorphic method (292). */ + @Use({FlagTarget.METHOD}) public static final long SIGNATURE_POLYMORPHIC = 1L<<46; /** * Flag that indicates that an inference variable is used in a 'throws' clause. */ + @Use({FlagTarget.TYPE_VAR}) public static final long THROWS = 1L<<47; /** * Flag to indicate sealed class/interface declaration. */ + @Use({FlagTarget.CLASS}) public static final long SEALED = 1L<<48; // part of ExtendedStandardFlags, cannot be reused /** * Flag that marks a synthetic method body for a lambda expression */ - public static final long LAMBDA_METHOD = 1L<<49; //MethodSymbols only + @Use({FlagTarget.METHOD}) + public static final long LAMBDA_METHOD = 1L<<49; /** * Flag that marks a synthetic local capture field in a local/anon class */ - public static final long LOCAL_CAPTURE_FIELD = 1L<<49; //VarSymbols only + @Use({FlagTarget.VARIABLE}) + public static final long LOCAL_CAPTURE_FIELD = 1L<<49; /** * Flag to control recursion in TransTypes */ + @Use({FlagTarget.CLASS}) public static final long TYPE_TRANSLATED = 1L<<50; /** * Flag to indicate class symbol is for module-info */ + @Use({FlagTarget.CLASS}) public static final long MODULE = 1L<<51; /** * Flag to indicate the given ModuleSymbol is an automatic module. */ - public static final long AUTOMATIC_MODULE = 1L<<52; //ModuleSymbols only + @Use({FlagTarget.MODULE}) + public static final long AUTOMATIC_MODULE = 1L<<52; /** * Flag to indicate the given PackageSymbol contains any non-.java and non-.class resources. */ - public static final long HAS_RESOURCE = 1L<<52; //PackageSymbols only + @Use({FlagTarget.PACKAGE}) + public static final long HAS_RESOURCE = 1L<<52; /** * Flag to indicate the given ParamSymbol has a user-friendly name filled. */ - public static final long NAME_FILLED = 1L<<52; //ParamSymbols only + @Use({FlagTarget.VARIABLE}) //ParamSymbols only + public static final long NAME_FILLED = 1L<<52; /** * Flag to indicate the given ModuleSymbol is a system module. */ - public static final long SYSTEM_MODULE = 1L<<53; //ModuleSymbols only + @Use({FlagTarget.MODULE}) + public static final long SYSTEM_MODULE = 1L<<53; /** * Flag to indicate the given ClassSymbol is a value based. */ - public static final long VALUE_BASED = 1L<<53; //ClassSymbols only + @Use({FlagTarget.CLASS}) + public static final long VALUE_BASED = 1L<<53; /** * Flag to indicate the given symbol has a @Deprecated annotation. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final long DEPRECATED_ANNOTATION = 1L<<54; /** * Flag to indicate the given symbol has been deprecated and marked for removal. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final long DEPRECATED_REMOVAL = 1L<<55; /** * Flag to indicate the API element in question is for a preview API. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final long PREVIEW_API = 1L<<56; //any Symbol kind /** * Flag for synthesized default constructors of anonymous classes that have an enclosing expression. */ + @Use({FlagTarget.METHOD}) public static final long ANONCONSTR_BASED = 1L<<57; /** * Flag that marks finalize block as body-only, should not be copied into catch clauses. * Used to implement try-with-resources. */ - public static final long BODY_ONLY_FINALIZE = 1L<<17; //blocks only + @Use({FlagTarget.BLOCK}) + public static final long BODY_ONLY_FINALIZE = 1L<<17; /** * Flag to indicate the API element in question is for a preview API. */ + @Use({FlagTarget.CLASS, FlagTarget.METHOD, FlagTarget.MODULE, FlagTarget.PACKAGE, FlagTarget.TYPE_VAR, FlagTarget.VARIABLE}) public static final long PREVIEW_REFLECTIVE = 1L<<58; //any Symbol kind /** * Flag to indicate the given variable is a match binding variable. */ + @Use({FlagTarget.VARIABLE}) public static final long MATCH_BINDING = 1L<<59; /** * A flag to indicate a match binding variable whose scope extends after the current statement. */ + @Use({FlagTarget.VARIABLE}) public static final long MATCH_BINDING_TO_OUTER = 1L<<60; /** * Flag to indicate that a class is a record. The flag is also used to mark fields that are * part of the state vector of a record and to mark the canonical constructor */ - public static final long RECORD = 1L<<61; // ClassSymbols, MethodSymbols and VarSymbols + @Use({FlagTarget.CLASS, FlagTarget.VARIABLE, FlagTarget.METHOD}) + public static final long RECORD = 1L<<61; /** * Flag to mark a record constructor as a compact one */ - public static final long COMPACT_RECORD_CONSTRUCTOR = 1L<<51; // MethodSymbols only + @Use({FlagTarget.METHOD}) + public static final long COMPACT_RECORD_CONSTRUCTOR = 1L<<51; /** * Flag to mark a record field that was not initialized in the compact constructor */ - public static final long UNINITIALIZED_FIELD= 1L<<51; // VarSymbols only + @Use({FlagTarget.VARIABLE}) + public static final long UNINITIALIZED_FIELD= 1L<<51; /** Flag is set for compiler-generated record members, it could be applied to * accessors and fields */ - public static final int GENERATED_MEMBER = 1<<24; // MethodSymbols and VarSymbols + @Use({FlagTarget.METHOD, FlagTarget.VARIABLE}) + public static final int GENERATED_MEMBER = 1<<24; /** * Flag to indicate restricted method declaration. */ - public static final long RESTRICTED = 1L<<62; // MethodSymbols + @Use({FlagTarget.METHOD}) + public static final long RESTRICTED = 1L<<62; /** * Flag to indicate parameters that require identity. */ - public static final long REQUIRES_IDENTITY = 1L<<62; // VarSymbols (parameters) + @Use({FlagTarget.VARIABLE}) //ParamSymbols only + public static final long REQUIRES_IDENTITY = 1L<<62; /** * Flag to indicate type annotations have been queued for field initializers. */ - public static final long FIELD_INIT_TYPE_ANNOTATIONS_QUEUED = 1L<<53; // VarSymbols + @Use({FlagTarget.VARIABLE}) + public static final long FIELD_INIT_TYPE_ANNOTATIONS_QUEUED = 1L<<53; /** * Flag to indicate that the class/interface was declared with the non-sealed modifier. */ + @Use({FlagTarget.CLASS}) + @CustomToStringValue("non-sealed") public static final long NON_SEALED = 1L<<63; // part of ExtendedStandardFlags, cannot be reused /** @@ -422,6 +507,7 @@ public class Flags { /** Modifier masks. */ + @NotFlag public static final int AccessFlags = PUBLIC | PROTECTED | PRIVATE, LocalClassFlags = FINAL | ABSTRACT | STRICTFP | ENUM | SYNTHETIC, @@ -438,6 +524,7 @@ public class Flags { SYNCHRONIZED | FINAL | STRICTFP, RecordMethodFlags = AccessFlags | ABSTRACT | STATIC | SYNCHRONIZED | FINAL | STRICTFP; + @NotFlag public static final long //NOTE: flags in ExtendedStandardFlags cannot be overlayed across Symbol kinds: ExtendedStandardFlags = (long)StandardFlags | DEFAULT | SEALED | NON_SEALED, @@ -491,91 +578,45 @@ public class Flags { return symbol.getConstValue() != null; } - - public enum Flag { - PUBLIC(Flags.PUBLIC), - PRIVATE(Flags.PRIVATE), - PROTECTED(Flags.PROTECTED), - STATIC(Flags.STATIC), - FINAL(Flags.FINAL), - SYNCHRONIZED(Flags.SYNCHRONIZED), - VOLATILE(Flags.VOLATILE), - TRANSIENT(Flags.TRANSIENT), - NATIVE(Flags.NATIVE), - INTERFACE(Flags.INTERFACE), - ABSTRACT(Flags.ABSTRACT), - DEFAULT(Flags.DEFAULT), - STRICTFP(Flags.STRICTFP), - BRIDGE(Flags.BRIDGE), - SYNTHETIC(Flags.SYNTHETIC), - ANNOTATION(Flags.ANNOTATION), - DEPRECATED(Flags.DEPRECATED), - HASINIT(Flags.HASINIT), - IMPLICIT_CLASS(Flags.IMPLICIT_CLASS), - BLOCK(Flags.BLOCK), - FROM_SOURCE(Flags.FROM_SOURCE), - ENUM(Flags.ENUM), - MANDATED(Flags.MANDATED), - NOOUTERTHIS(Flags.NOOUTERTHIS), - EXISTS(Flags.EXISTS), - COMPOUND(Flags.COMPOUND), - CLASS_SEEN(Flags.CLASS_SEEN), - SOURCE_SEEN(Flags.SOURCE_SEEN), - LOCKED(Flags.LOCKED), - UNATTRIBUTED(Flags.UNATTRIBUTED), - ANONCONSTR(Flags.ANONCONSTR), - ACYCLIC(Flags.ACYCLIC), - PARAMETER(Flags.PARAMETER), - VARARGS(Flags.VARARGS), - ACYCLIC_ANN(Flags.ACYCLIC_ANN), - GENERATEDCONSTR(Flags.GENERATEDCONSTR), - HYPOTHETICAL(Flags.HYPOTHETICAL), - PROPRIETARY(Flags.PROPRIETARY), - UNION(Flags.UNION), - EFFECTIVELY_FINAL(Flags.EFFECTIVELY_FINAL), - CLASH(Flags.CLASH), - AUXILIARY(Flags.AUXILIARY), - NOT_IN_PROFILE(Flags.NOT_IN_PROFILE), - BAD_OVERRIDE(Flags.BAD_OVERRIDE), - SIGNATURE_POLYMORPHIC(Flags.SIGNATURE_POLYMORPHIC), - THROWS(Flags.THROWS), - LAMBDA_METHOD(Flags.LAMBDA_METHOD), - TYPE_TRANSLATED(Flags.TYPE_TRANSLATED), - MODULE(Flags.MODULE), - AUTOMATIC_MODULE(Flags.AUTOMATIC_MODULE), - SYSTEM_MODULE(Flags.SYSTEM_MODULE), - DEPRECATED_ANNOTATION(Flags.DEPRECATED_ANNOTATION), - DEPRECATED_REMOVAL(Flags.DEPRECATED_REMOVAL), - HAS_RESOURCE(Flags.HAS_RESOURCE), - SEALED(Flags.SEALED), - ANONCONSTR_BASED(Flags.ANONCONSTR_BASED), - NAME_FILLED(Flags.NAME_FILLED), - PREVIEW_API(Flags.PREVIEW_API), - PREVIEW_REFLECTIVE(Flags.PREVIEW_REFLECTIVE), - MATCH_BINDING(Flags.MATCH_BINDING), - MATCH_BINDING_TO_OUTER(Flags.MATCH_BINDING_TO_OUTER), - RECORD(Flags.RECORD), - RECOVERABLE(Flags.RECOVERABLE), - RESTRICTED(Flags.RESTRICTED), - NON_SEALED(Flags.NON_SEALED) { - @Override - public String toString() { - return "non-sealed"; - } - }; - - Flag(long flag) { - this.value = flag; - this.lowercaseName = StringUtils.toLowerCase(name()); - } - - @Override - public String toString() { - return lowercaseName; - } - - final long value; - final String lowercaseName; + public enum FlagTarget { + /** This flag can appear the JCBlock. + */ + BLOCK, + /** This flag can appear on ClassSymbols. + */ + CLASS, + /** This flag can appear on ModuleSymbols. + */ + MODULE, + /** This flag can appear on PackageSymbols. + */ + PACKAGE, + /** This flag can appear on TypeVarSymbols. + */ + TYPE_VAR, + /** This flag can appear on MethodSymbols. + */ + METHOD, + /** This flag can appear on VarSymbols, includes + * including ParamSymbol, and BindingSymbol. + */ + VARIABLE; } + @Retention(RetentionPolicy.RUNTIME) + public @interface Use { + public FlagTarget[] value(); + } + + @Retention(RetentionPolicy.RUNTIME) + public @interface NotFlag {} + + @Retention(RetentionPolicy.RUNTIME) + public @interface CustomToStringValue { + public String value(); + } + + @Retention(RetentionPolicy.RUNTIME) + public @interface NoToStringValue { + } } 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 72552fa76c0..8c78ab16520 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 @@ -581,13 +581,14 @@ public class Lint { // Given a @SuppressWarnings annotation, extract the recognized suppressions private EnumSet suppressionsFrom(Attribute.Compound suppressWarnings) { EnumSet result = LintCategory.newEmptySet(); - Attribute.Array values = (Attribute.Array)suppressWarnings.member(names.value); - for (Attribute value : values.values) { - Optional.of(value) - .filter(val -> val instanceof Attribute.Constant) - .map(val -> (String) ((Attribute.Constant) val).value) - .flatMap(LintCategory::get) - .ifPresent(result::add); + if (suppressWarnings.member(names.value) instanceof Attribute.Array values) { + for (Attribute value : values.values) { + Optional.of(value) + .filter(val -> val instanceof Attribute.Constant) + .map(val -> (String) ((Attribute.Constant) val).value) + .flatMap(LintCategory::get) + .ifPresent(result::add); + } } return result; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java index e9643420e55..39dae47b08b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java @@ -61,7 +61,7 @@ import com.sun.tools.javac.code.Directive.RequiresDirective; import com.sun.tools.javac.code.Directive.RequiresFlag; import com.sun.tools.javac.code.Directive.UsesDirective; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.code.Flags.Flag; +import com.sun.tools.javac.code.FlagsEnum; import com.sun.tools.javac.code.Kinds; import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Lint.LintCategory; @@ -816,7 +816,7 @@ public class Modules extends JCTree.Visitor { } if (tree.isStaticPhase) { if (msym == syms.java_base && source.compareTo(Source.JDK10) >= 0) { - log.error(tree.pos(), Errors.ModNotAllowedHere(EnumSet.of(Flag.STATIC))); + log.error(tree.pos(), Errors.ModNotAllowedHere(EnumSet.of(FlagsEnum.STATIC))); } else { flags.add(RequiresFlag.STATIC_PHASE); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java index 7cf419f3ca8..73219ca38ae 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java @@ -128,6 +128,7 @@ public class JavacElements implements Elements { @Override @DefinedBy(Api.LANGUAGE_MODEL) public Set getAllModuleElements() { + ensureEntered("getAllModuleElements"); if (allowModules) return Collections.unmodifiableSet(modules.allModules()); else diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c index f88477681b7..37404db1024 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_convert.c @@ -1461,8 +1461,7 @@ jobject ckAttributeValueToJObject(JNIEnv *env, const CK_ATTRIBUTE_PTR ckpAttribu case CKA_OWNER: case CKA_AC_ISSUER: case CKA_ATTR_TYPES: - case CKA_ECDSA_PARAMS: - /* CKA_EC_PARAMS is the same, these two are equivalent */ + case CKA_EC_PARAMS: case CKA_EC_POINT: case CKA_PRIVATE_EXPONENT: case CKA_PRIME_1: diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c index d522c4864ba..f2e1a46565d 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 2002 Graz University of Technology. All rights reserved. @@ -56,69 +56,78 @@ #ifdef P11_ENABLE_GETNATIVEKEYINFO -#define CK_ATTRIBUTES_TEMPLATE_LENGTH (CK_ULONG)61U +#define CK_ATTRIBUTES_TEMPLATE_LENGTH (CK_ULONG)60U +// Group attributes based on their value types; put attributes whose values +// requiring address alignments, e.g. CK_ULONG, first static CK_ATTRIBUTE ckpAttributesTemplate[CK_ATTRIBUTES_TEMPLATE_LENGTH] = { - {CKA_CLASS, 0, 0}, - {CKA_TOKEN, 0, 0}, - {CKA_PRIVATE, 0, 0}, - {CKA_LABEL, 0, 0}, - {CKA_APPLICATION, 0, 0}, - {CKA_VALUE, 0, 0}, - {CKA_OBJECT_ID, 0, 0}, + // CK_ULONG {CKA_CERTIFICATE_TYPE, 0, 0}, - {CKA_ISSUER, 0, 0}, - {CKA_SERIAL_NUMBER, 0, 0}, - {CKA_AC_ISSUER, 0, 0}, - {CKA_OWNER, 0, 0}, - {CKA_ATTR_TYPES, 0, 0}, - {CKA_TRUSTED, 0, 0}, + {CKA_CLASS, 0, 0}, + {CKA_HW_FEATURE_TYPE, 0, 0}, + {CKA_KEY_GEN_MECHANISM, 0, 0}, {CKA_KEY_TYPE, 0, 0}, - {CKA_SUBJECT, 0, 0}, - {CKA_ID, 0, 0}, - {CKA_SENSITIVE, 0, 0}, - {CKA_ENCRYPT, 0, 0}, - {CKA_DECRYPT, 0, 0}, - {CKA_WRAP, 0, 0}, - {CKA_UNWRAP, 0, 0}, - {CKA_SIGN, 0, 0}, - {CKA_SIGN_RECOVER, 0, 0}, - {CKA_VERIFY, 0, 0}, - {CKA_VERIFY_RECOVER, 0, 0}, - {CKA_DERIVE, 0, 0}, - {CKA_START_DATE, 0, 0}, - {CKA_END_DATE, 0, 0}, - {CKA_MODULUS, 0, 0}, {CKA_MODULUS_BITS, 0, 0}, - {CKA_PUBLIC_EXPONENT, 0, 0}, - {CKA_PRIVATE_EXPONENT, 0, 0}, - {CKA_PRIME_1, 0, 0}, - {CKA_PRIME_2, 0, 0}, - {CKA_EXPONENT_1, 0, 0}, - {CKA_EXPONENT_2, 0, 0}, - {CKA_COEFFICIENT, 0, 0}, - {CKA_PRIME, 0, 0}, - {CKA_SUBPRIME, 0, 0}, - {CKA_BASE, 0, 0}, {CKA_PRIME_BITS, 0, 0}, {CKA_SUB_PRIME_BITS, 0, 0}, {CKA_VALUE_BITS, 0, 0}, {CKA_VALUE_LEN, 0, 0}, - {CKA_EXTRACTABLE, 0, 0}, - {CKA_LOCAL, 0, 0}, - {CKA_NEVER_EXTRACTABLE, 0, 0}, + // CK_BBOOL {CKA_ALWAYS_SENSITIVE, 0, 0}, - {CKA_KEY_GEN_MECHANISM, 0, 0}, + {CKA_DECRYPT, 0, 0}, + {CKA_DERIVE, 0, 0}, + {CKA_ENCRYPT, 0, 0}, + {CKA_EXTRACTABLE, 0, 0}, + {CKA_HAS_RESET, 0, 0}, + {CKA_LOCAL, 0, 0}, {CKA_MODIFIABLE, 0, 0}, - {CKA_ECDSA_PARAMS, 0, 0}, + {CKA_NEVER_EXTRACTABLE, 0, 0}, + {CKA_PRIVATE, 0, 0}, + {CKA_RESET_ON_INIT, 0, 0}, + {CKA_SENSITIVE, 0, 0}, + {CKA_SIGN, 0, 0}, + {CKA_SIGN_RECOVER, 0, 0}, + {CKA_TOKEN, 0, 0}, + {CKA_TRUSTED, 0, 0}, + {CKA_UNWRAP, 0, 0}, + {CKA_VERIFY, 0, 0}, + {CKA_VERIFY_RECOVER, 0, 0}, + {CKA_WRAP, 0, 0}, + // PTR: byte[] + {CKA_AC_ISSUER, 0, 0}, + {CKA_ATTR_TYPES, 0, 0}, + {CKA_BASE, 0, 0}, + {CKA_COEFFICIENT, 0, 0}, {CKA_EC_PARAMS, 0, 0}, {CKA_EC_POINT, 0, 0}, + {CKA_EXPONENT_1, 0, 0}, + {CKA_EXPONENT_2, 0, 0}, + {CKA_ID, 0, 0}, + {CKA_ISSUER, 0, 0}, + {CKA_MODULUS, 0, 0}, + {CKA_OBJECT_ID, 0, 0}, + {CKA_OWNER, 0, 0}, + {CKA_PRIME, 0, 0}, + {CKA_PRIME_1, 0, 0}, + {CKA_PRIME_2, 0, 0}, + {CKA_PRIVATE_EXPONENT, 0, 0}, + {CKA_PUBLIC_EXPONENT, 0, 0}, + {CKA_SERIAL_NUMBER, 0, 0}, + {CKA_SUBJECT, 0, 0}, + {CKA_SUBPRIME, 0, 0}, + {CKA_VALUE, 0, 0}, + // PTR: CK_UTF8CHAR[] + {CKA_APPLICATION, 0, 0}, + {CKA_LABEL, 0, 0}, + // PTR: CK_DATE + {CKA_START_DATE, 0, 0}, + {CKA_END_DATE, 0, 0}, + // deprecated {CKA_SECONDARY_AUTH, 0, 0}, {CKA_AUTH_PIN_FLAGS, 0, 0}, - {CKA_HW_FEATURE_TYPE, 0, 0}, - {CKA_RESET_ON_INIT, 0, 0}, - {CKA_HAS_RESET, 0, 0}, + // misc {CKA_VENDOR_DEFINED, 0, 0}, + // keep this at the end to match the impl in getNativeKeyInfo(...) {CKA_NETSCAPE_DB, 0, 0}, }; diff --git a/src/jdk.editpad/share/classes/jdk/editpad/EditPad.java b/src/jdk.editpad/share/classes/jdk/editpad/EditPad.java index ee68f035dcf..42a6ca66cd6 100644 --- a/src/jdk.editpad/share/classes/jdk/editpad/EditPad.java +++ b/src/jdk.editpad/share/classes/jdk/editpad/EditPad.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -89,7 +89,6 @@ class EditPad implements Runnable { closer.run(); } }); - jframe.setLocationRelativeTo(null); jframe.setLayout(new BorderLayout()); JTextArea textArea = new JTextArea(initialText); textArea.setFont(new Font("monospaced", Font.PLAIN, 13)); @@ -97,6 +96,7 @@ class EditPad implements Runnable { jframe.add(buttons(closer, textArea), BorderLayout.SOUTH); jframe.setSize(800, 600); + jframe.setLocationRelativeTo(null); jframe.setVisible(true); } diff --git a/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java b/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java index 5dca2f2374f..d50af3a9bdd 100644 --- a/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java +++ b/src/jdk.internal.md/share/classes/jdk/internal/markdown/MarkdownTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -358,10 +358,20 @@ public class MarkdownTransformer implements JavacTrees.DocCommentTreeTransformer } private DCTree.DCSee transform(DCTree.DCSee tree) { - List ref2 = transform(tree.reference); - return (equal(ref2, tree.getReference())) + // Some extra work is required to accommodate various forms of @see tags, as a + // leading reference affects the position of the label following it (JDK-8356411), + var ref = tree.reference; + var hasReference = !ref.isEmpty() && ref.getFirst().getKind() == DocTree.Kind.REFERENCE; + List transformed = new ArrayList<>(); + if (hasReference) { + transformed.add(ref.getFirst()); + transformed.addAll(transform(ref.subList(1, ref.size()))); + } else { + transformed.addAll(transform(ref)); + } + return (equal(ref, transformed)) ? tree - : m.at(tree.pos).newSeeTree(ref2); + : m.at(tree.pos).newSeeTree(transformed); } private DCTree.DCSerial transform(DCTree.DCSerial tree) { diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIBackendFactory.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIBackendFactory.java index 319b9b9bc44..e0fc314de5b 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIBackendFactory.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIBackendFactory.java @@ -27,10 +27,10 @@ import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.function.LongFunction; import jdk.vm.ci.common.JVMCIError; import jdk.vm.ci.runtime.JVMCIBackend; -import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE; public interface HotSpotJVMCIBackendFactory { @@ -48,7 +48,8 @@ public interface HotSpotJVMCIBackendFactory { * @param enumType the class of {@code CPUFeatureType} * @param constants VM constants. Each entry whose key starts with {@code "VM_Version::CPU_"} * specifies a CPU feature and its value is a mask for a bit in {@code features} - * @param features bits specifying CPU features + * @param bitMaskSupplier supplier to get the bit mask for the corresponding VM constant + * @param featuresSupplier supplier to get the bits specifying CPU features * @param renaming maps from VM feature names to enum constant names where the two differ * @throws IllegalArgumentException if any VM CPU feature constant cannot be converted to an * enum value @@ -57,18 +58,19 @@ public interface HotSpotJVMCIBackendFactory { static > EnumSet convertFeatures( Class enumType, Map constants, - long features, + LongFunction bitMaskSupplier, + LongFunction featuresSupplier, Map renaming) { EnumSet outFeatures = EnumSet.noneOf(enumType); List missing = new ArrayList<>(); for (Entry e : constants.entrySet()) { - long bitMask = e.getValue(); + long bitMask = bitMaskSupplier.apply(e.getValue()); String key = e.getKey(); if (key.startsWith("VM_Version::CPU_")) { String name = key.substring("VM_Version::CPU_".length()); try { CPUFeatureType feature = Enum.valueOf(enumType, renaming.getOrDefault(name, name)); - if ((features & bitMask) != 0) { + if ((featuresSupplier.apply(e.getValue()) & bitMask) != 0) { outFeatures.add(feature); } } catch (IllegalArgumentException iae) { @@ -82,57 +84,4 @@ public interface HotSpotJVMCIBackendFactory { return outFeatures; } - /** - * Converts CPU features bit map into enum constants. - * - * @param CPU feature enum type - * @param enumType the class of {@code CPUFeatureType} - * @param constants VM constants. Each entry whose key starts with {@code "VM_Version::CPU_"} - * specifies a CPU feature and its value is a mask for a bit in {@code features} - * @param featuresBitMapAddress pointer to {@code VM_Features::_features_bitmap} field of {@code VM_Version::_features} - * @param featuresBitMapSize size of feature bit map in bytes - * @param renaming maps from VM feature names to enum constant names where the two differ - * @throws IllegalArgumentException if any VM CPU feature constant cannot be converted to an - * enum value - * @return the set of converted values - */ - static > EnumSet convertFeatures( - Class enumType, - Map constants, - long featuresBitMapAddress, - long featuresBitMapSize, - Map renaming) { - EnumSet outFeatures = EnumSet.noneOf(enumType); - List missing = new ArrayList<>(); - - for (Entry e : constants.entrySet()) { - String key = e.getKey(); - long bitIndex = e.getValue(); - if (key.startsWith("VM_Version::CPU_")) { - String name = key.substring("VM_Version::CPU_".length()); - try { - final long featuresElementShiftCount = 6; // log (# of bits per long) - final long featuresElementMask = (1L << featuresElementShiftCount) - 1; - - CPUFeatureType feature = Enum.valueOf(enumType, renaming.getOrDefault(name, name)); - - long featureIndex = bitIndex >>> featuresElementShiftCount; - long featureBitMask = 1L << (bitIndex & featuresElementMask); - assert featureIndex < featuresBitMapSize; - - long featuresElement = UNSAFE.getLong(featuresBitMapAddress + featureIndex * Long.BYTES); - - if ((featuresElement & featureBitMask) != 0) { - outFeatures.add(feature); - } - } catch (IllegalArgumentException iae) { - missing.add(name); - } - } - } - if (!missing.isEmpty()) { - throw new JVMCIError("Missing CPU feature constants: %s", missing); - } - return outFeatures; - } } diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java index 492f20f1b59..c35f216e624 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java @@ -186,7 +186,10 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime { // Can only do eager initialization of the JVMCI compiler // once the singleton instance is available. if (result.config.getFlag("EagerJVMCI", Boolean.class)) { - result.getCompiler(); + // EagerJVMCI only applies to JVMCI when used by the CompileBroker. + if (result.getCompilerToVM().isCompilerThread()) { + result.getCompiler(); + } } } // Ensures JVMCIRuntime::_HotSpotJVMCIRuntime_instance is diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotJVMCIBackendFactory.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotJVMCIBackendFactory.java index 19c161d6bde..1ee016b73a8 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotJVMCIBackendFactory.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/aarch64/AArch64HotSpotJVMCIBackendFactory.java @@ -49,7 +49,7 @@ public class AArch64HotSpotJVMCIBackendFactory implements HotSpotJVMCIBackendFac private static EnumSet computeFeatures(AArch64HotSpotVMConfig config) { // Configure the feature set using the HotSpot flag settings. Map constants = config.getStore().getConstants(); - return HotSpotJVMCIBackendFactory.convertFeatures(CPUFeature.class, constants, config.vmVersionFeatures, emptyMap()); + return HotSpotJVMCIBackendFactory.convertFeatures(CPUFeature.class, constants, idx -> 1L << idx, _ -> config.vmVersionFeatures, emptyMap()); } private static TargetDescription createTarget(AArch64HotSpotVMConfig config) { diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/amd64/AMD64HotSpotJVMCIBackendFactory.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/amd64/AMD64HotSpotJVMCIBackendFactory.java index 040f39e3b8a..cae6d18e71e 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/amd64/AMD64HotSpotJVMCIBackendFactory.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/amd64/AMD64HotSpotJVMCIBackendFactory.java @@ -26,6 +26,8 @@ import static jdk.vm.ci.common.InitTimer.timer; import java.util.EnumSet; import java.util.Map; + +import jdk.internal.misc.Unsafe; import jdk.internal.util.OperatingSystem; import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.amd64.AMD64.CPUFeature; @@ -50,11 +52,15 @@ public class AMD64HotSpotJVMCIBackendFactory implements HotSpotJVMCIBackendFacto Map constants = config.getStore().getConstants(); Map renaming = Map.of("3DNOW_PREFETCH", "AMD_3DNOW_PREFETCH"); long featuresBitMapAddress = config.vmVersionFeatures + config.vmFeaturesFeaturesOffset; - EnumSet features = HotSpotJVMCIBackendFactory.convertFeatures(CPUFeature.class, - constants, - featuresBitMapAddress, - config.vmFeaturesFeaturesSize, - renaming); + EnumSet features = HotSpotJVMCIBackendFactory.convertFeatures(CPUFeature.class, constants, idx -> { + final long featuresElementShiftCount = 6; // log (# of bits per long) + final long featuresElementMask = (1L << featuresElementShiftCount) - 1; + return 1L << (idx & featuresElementMask); + }, idx -> { + final long featuresElementShiftCount = 6; // log (# of bits per long) + long featureIndex = idx >>> featuresElementShiftCount; + return Unsafe.getUnsafe().getLong(featuresBitMapAddress + featureIndex * Long.BYTES); + }, renaming); assert features.contains(AMD64.CPUFeature.SSE) : "minimum config for x64"; assert features.contains(AMD64.CPUFeature.SSE2) : "minimum config for x64"; return features; diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/riscv64/RISCV64HotSpotJVMCIBackendFactory.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/riscv64/RISCV64HotSpotJVMCIBackendFactory.java index c0e31124500..f50fcf1dbbc 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/riscv64/RISCV64HotSpotJVMCIBackendFactory.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/riscv64/RISCV64HotSpotJVMCIBackendFactory.java @@ -49,7 +49,7 @@ public class RISCV64HotSpotJVMCIBackendFactory implements HotSpotJVMCIBackendFac private static EnumSet computeFeatures(RISCV64HotSpotVMConfig config) { // Configure the feature set using the HotSpot flag settings. Map constants = config.getStore().getConstants(); - return HotSpotJVMCIBackendFactory.convertFeatures(CPUFeature.class, constants, config.vmVersionFeatures, emptyMap()); + return HotSpotJVMCIBackendFactory.convertFeatures(CPUFeature.class, constants, mask -> mask, _ -> config.vmVersionFeatures, emptyMap()); } private static TargetDescription createTarget(RISCV64HotSpotVMConfig config) { diff --git a/src/jdk.jartool/share/classes/module-info.java b/src/jdk.jartool/share/classes/module-info.java index 880e516b702..b1dcfb78a52 100644 --- a/src/jdk.jartool/share/classes/module-info.java +++ b/src/jdk.jartool/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,6 @@ * questions. */ -import jdk.internal.javac.ParticipatesInPreview; - /** * Defines tools for manipulating Java Archive (JAR) files, * including the {@index jar jar tool} and @@ -49,7 +47,6 @@ import jdk.internal.javac.ParticipatesInPreview; * @moduleGraph * @since 9 */ -@ParticipatesInPreview module jdk.jartool { requires jdk.internal.opt; diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java index 015668fcbc4..f2c76058434 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java @@ -876,6 +876,11 @@ public class Main { sb.append('|'); } + // If the file exists, remove it from all entries in entriesInSF + for (var signed : entriesInSF.values()) { + signed.remove(name); + } + // When -certs provided, display info has extra empty // lines at the beginning and end. if (isSigned) { @@ -890,9 +895,6 @@ public class Main { sb.append('\n'); } } - for (var signed : entriesInSF.values()) { - signed.remove(name); - } } else if (showcerts && !verbose.equals("all")) { // Print no info for unsigned entries when -verbose:all, // to be consistent with old behavior. diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java index 60fb85baca8..38b0ba84c8a 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java @@ -420,7 +420,8 @@ public class Main { if (fname != null) { file = new File(fname); } else { - file = createTemporaryFile("tmpJar", ".jar"); + tmpFile = createTemporaryFile("tmpJar", ".jar"); + file = tmpFile; try (InputStream in = new FileInputStream(FileDescriptor.in); OutputStream os = Files.newOutputStream(file.toPath())) { in.transferTo(os); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java index 47b2322226e..d00d7b1becf 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java @@ -327,6 +327,8 @@ public class HtmlDoclet extends AbstractDoclet { copyResource(DocPaths.RIGHT_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.RIGHT_SVG), true); copyResource(DocPaths.CLIPBOARD_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.CLIPBOARD_SVG), true); copyResource(DocPaths.LINK_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.LINK_SVG), true); + copyResource(DocPaths.MOON_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.MOON_SVG), true); + copyResource(DocPaths.SUN_SVG, DocPaths.RESOURCE_FILES.resolve(DocPaths.SUN_SVG), true); if (options.createIndex()) { copyResource(DocPaths.SEARCH_JS_TEMPLATE, DocPaths.SCRIPT_FILES.resolve(DocPaths.SEARCH_JS), true); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java index 42d93c41e11..c257e1e153c 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlIds.java @@ -114,6 +114,11 @@ public class HtmlIds { static final HtmlId SEARCH_INPUT = HtmlId.of("search-input"); static final HtmlId SERVICES = HtmlId.of("services-summary"); static final HtmlId SKIP_NAVBAR_TOP = HtmlId.of("skip-navbar-top"); + static final HtmlId THEME_BUTTON = HtmlId.of("theme-button"); + static final HtmlId THEME_DARK = HtmlId.of("theme-dark"); + static final HtmlId THEME_LIGHT = HtmlId.of("theme-light"); + static final HtmlId THEME_OS = HtmlId.of("theme-os"); + static final HtmlId THEME_PANEL = HtmlId.of("theme-panel"); static final HtmlId UNNAMED_PACKAGE_ANCHOR = HtmlId.of("unnamed-package"); private static final String FIELDS_INHERITANCE = "fields-inherited-from-class-"; private static final String METHODS_INHERITANCE = "methods-inherited-from-class-"; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java index 61af26259af..1938a4838f5 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Navigation.java @@ -137,6 +137,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case MODULE: addOverviewLink(target); @@ -148,6 +149,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case PACKAGE: addOverviewLink(target); @@ -166,6 +168,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case CLASS: addOverviewLink(target); @@ -184,6 +187,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case USE: addOverviewLink(target); @@ -208,6 +212,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case TREE: addOverviewLink(target); @@ -226,6 +231,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case DEPRECATED: case INDEX: @@ -268,6 +274,7 @@ public class Navigation { } else { addHelpLink(target); } + addThemeSwitcher(target); break; case ALL_CLASSES: case ALL_PACKAGES: @@ -285,6 +292,7 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; case DOC_FILE: addOverviewLink(target); @@ -311,16 +319,13 @@ public class Navigation { addIndexLink(target); addSearchLink(target); addHelpLink(target); + addThemeSwitcher(target); break; default: break; } } - private void addContentToList(List listContents, Content source) { - listContents.add(HtmlTree.LI(source)); - } - private void addItemToList(Content list, Content item) { list.add(HtmlTree.LI(item)); } @@ -492,6 +497,17 @@ public class Navigation { } } + private void addThemeSwitcher(Content target) { + var selectTheme = contents.getContent("doclet.theme.select_theme"); + target.add(HtmlTree.LI(HtmlTree.BUTTON(HtmlIds.THEME_BUTTON) + .add(HtmlTree.IMG(pathToRoot.resolve(DocPaths.RESOURCE_FILES).resolve(DocPaths.SUN_SVG), + selectTheme.toString()).addStyle(HtmlIds.THEME_LIGHT.name())) + .add(HtmlTree.IMG(pathToRoot.resolve(DocPaths.RESOURCE_FILES).resolve(DocPaths.MOON_SVG), + selectTheme.toString()).addStyle(HtmlIds.THEME_DARK.name())) + .put(HtmlAttr.ARIA_LABEL, selectTheme.toString()) + .put(HtmlAttr.TITLE, selectTheme.toString()))); + } + private void addSearch(Content target) { var resources = configuration.getDocResources(); var inputText = HtmlTree.INPUT(HtmlAttr.InputType.TEXT, HtmlIds.SEARCH_INPUT) @@ -545,6 +561,7 @@ public class Navigation { navContent.add(aboutDiv); navigationBar.add(HtmlTree.DIV(HtmlStyles.topNav, navContent).setId(HtmlIds.NAVBAR_TOP)); + var subNavContent = HtmlTree.DIV(HtmlStyles.navContent); List subNavLinks = new ArrayList<>(); switch (documentedPage) { @@ -557,6 +574,22 @@ public class Navigation { breadcrumbNav.addAll(subNavLinks, HtmlTree::LI); subNavContent.addUnchecked(breadcrumbNav); + var selectTheme = contents.getContent("doclet.theme.select_theme"); + subNavContent.add(HtmlTree.DIV(HtmlIds.THEME_PANEL) + .add(HtmlTree.DIV(selectTheme)) + .add(HtmlTree.DIV(HtmlTree.LABEL(HtmlIds.THEME_LIGHT.name(), Text.EMPTY) + .add(HtmlTree.INPUT(HtmlAttr.InputType.RADIO, HtmlIds.THEME_LIGHT) + .put(HtmlAttr.NAME, "theme").put(HtmlAttr.VALUE, HtmlIds.THEME_LIGHT.name())) + .add(HtmlTree.SPAN(contents.getContent("doclet.theme.light")))) + .add(HtmlTree.LABEL(HtmlIds.THEME_DARK.name(), Text.EMPTY) + .add(HtmlTree.INPUT(HtmlAttr.InputType.RADIO, HtmlIds.THEME_DARK) + .put(HtmlAttr.NAME, "theme").put(HtmlAttr.VALUE, HtmlIds.THEME_DARK.name())) + .add(HtmlTree.SPAN(contents.getContent("doclet.theme.dark")))) + .add(HtmlTree.LABEL(HtmlIds.THEME_OS.name(), Text.EMPTY) + .add(HtmlTree.INPUT(HtmlAttr.InputType.RADIO, HtmlIds.THEME_OS) + .put(HtmlAttr.NAME, "theme").put(HtmlAttr.VALUE, HtmlIds.THEME_OS.name())) + .add(HtmlTree.SPAN(contents.getContent("doclet.theme.system")))))); + if (options.createIndex() && documentedPage != PageMode.SEARCH) { addSearch(subNavContent); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/glass.svg b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/glass.svg index ff7df85e8f6..c979c9f3d83 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/glass.svg +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/glass.svg @@ -8,6 +8,6 @@ --> - - - \ No newline at end of file + + + diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.css index 145c1c7f5d2..77c32543049 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.css +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/highlight.css @@ -65,3 +65,188 @@ /* ignored */ } +body.theme-dark { + .hljs-title.function_, + .hljs-template-variable { + color: #66bcce; + } + .hljs-code, + .hljs-comment, + .hljs-quote { + color:#9d9d9d; + font-style: italic; + } + .hljs-meta { + color: #836F00; + } + .hljs-symbol, + .hljs-template-tag, + .hljs-keyword, + .hljs-literal, + .hljs-name, + .hljs-built_in, + .hljs-char.escape_ { + color: #88aece; + } + .hljs-variable, + .hljs-property, + .hljs-attr, + .hljs-section { + color: #c59bc1; + } + .hljs-attribute { + color: #c59bc1; + } + .hljs-regexp, + .hljs-number { + color: #cfe374; + } + .hljs-link { + color: #b5bd68; + } + .hljs-string { + color: #b5bd68; + } + .hljs-doctag { + text-decoration: underline; + } + .hljs-emphasis { + font-style: italic; + } + .hljs-strong { + font-weight: bold; + } + .hljs-subst, + .hljs-title, + .hljs-params, + .hljs-bullet, + .hljs-formula, + .hljs-tag, + .hljs-type { + /* ignored */ + } +} + +@media (prefers-color-scheme: dark) { + .hljs-title.function_, + .hljs-template-variable { + color: #66bcce; + } + .hljs-code, + .hljs-comment, + .hljs-quote { + color:#9d9d9d; + font-style: italic; + } + .hljs-meta { + color: #836F00; + } + .hljs-symbol, + .hljs-template-tag, + .hljs-keyword, + .hljs-literal, + .hljs-name, + .hljs-built_in, + .hljs-char.escape_ { + color: #88aece; + } + .hljs-variable, + .hljs-property, + .hljs-attr, + .hljs-section { + color: #c59bc1; + } + .hljs-attribute { + color: #c59bc1; + } + .hljs-regexp, + .hljs-number { + color: #cfe374; + } + .hljs-link { + color: #b5bd68; + } + .hljs-string { + color: #b5bd68; + } + .hljs-doctag { + text-decoration: underline; + } + .hljs-emphasis { + font-style: italic; + } + .hljs-strong { + font-weight: bold; + } + .hljs-subst, + .hljs-title, + .hljs-params, + .hljs-bullet, + .hljs-formula, + .hljs-tag, + .hljs-type { + /* ignored */ + } + + body.theme-light { + .hljs-title.function_, + .hljs-template-variable { + color: #00738F; + } + .hljs-code, + .hljs-comment, + .hljs-quote { + color: #6e6e71; + font-style: italic; + } + .hljs-meta { + color: #836F00; + } + .hljs-symbol, + .hljs-template-tag, + .hljs-keyword, + .hljs-literal, + .hljs-name, + .hljs-built_in, + .hljs-char.escape_ { + color: #0C40C2; + } + .hljs-variable, + .hljs-property, + .hljs-attr, + .hljs-section { + color: #841191; + } + .hljs-attribute { + color: #164ad9; + } + .hljs-regexp, + .hljs-number { + color: #104BEB; + } + .hljs-link { + color: #47688a; + } + .hljs-string { + color: #008313; + } + .hljs-doctag { + text-decoration: underline; + } + .hljs-emphasis { + font-style: italic; + } + .hljs-strong { + font-weight: bold; + } + .hljs-subst, + .hljs-title, + .hljs-params, + .hljs-bullet, + .hljs-formula, + .hljs-tag, + .hljs-type { + /* ignored */ + } + } +} diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/moon.svg b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/moon.svg new file mode 100644 index 00000000000..856a8ffeeec --- /dev/null +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/moon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template index d3b59f4f40b..a32c44b8043 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/script.js.template @@ -328,11 +328,7 @@ document.addEventListener("readystatechange", (e) => { }); document.addEventListener("DOMContentLoaded", function(e) { setTopMargin(); - // Make sure current element is visible in breadcrumb navigation on small displays const subnav = document.querySelector("ol.sub-nav-list"); - if (subnav && subnav.lastElementChild) { - subnav.lastElementChild.scrollIntoView({ behavior: "instant", inline: "start", block: "nearest" }); - } const keymap = new Map(); const searchInput = document.getElementById("search-input") || document.getElementById("page-search-input"); @@ -351,6 +347,49 @@ document.addEventListener("DOMContentLoaded", function(e) { const mainnav = navbar.querySelector("ul.nav-list"); const toggleButton = document.querySelector("button#navbar-toggle-button"); const tocMenu = sidebar ? sidebar.cloneNode(true) : null; + const themeButton = document.querySelector("button#theme-button"); + const themePanel = document.querySelector("div#theme-panel"); + var themePanelVisible = false; + themeButton.addEventListener("click", e => { + if (!themePanelVisible) { + let {x, y} = themeButton.getBoundingClientRect(); + themePanel.style.display = "block"; + if (window.innerHeight - 85 < y) { + themePanel.style.top = ""; + themePanel.style.bottom = "4px"; + } else { + themePanel.style.top = y + (expanded ? 0 : 36) + "px"; + themePanel.style.bottom = ""; + } + themePanel.style.left = x + (expanded ? 36 : 0) + "px"; + themeButton.setAttribute("aria-expanded", "true"); + themePanelVisible = true; + e.stopPropagation(); + } + }); + function closeThemePanel(e) { + if (themePanelVisible && (!e || !themePanel.contains(e.target))) { + themePanel.style.removeProperty("display"); + themeButton.setAttribute("aria-expanded", "false"); + themePanelVisible = false; + } + } + themePanel.querySelectorAll("input").forEach(input => { + input.removeAttribute("disabled"); + input.addEventListener("change", e => { + setTheme(e.target.value); + }) + }); + const THEMES = ["theme-light", "theme-dark", "theme-os"]; + function setTheme(theme) { + THEMES.forEach(t => { + if (t !== theme) document.body.classList.remove(t); + }); + document.body.classList.add(theme); + localStorage.setItem("theme", theme); + document.getElementById(theme).checked = true; + } + setTheme(localStorage.getItem("theme") || THEMES[0]); makeFilterWidget(sidebar, updateToc); if (tocMenu) { navbar.appendChild(tocMenu); @@ -362,6 +401,7 @@ document.addEventListener("DOMContentLoaded", function(e) { return; } if (!isInput(e.target) && keymap.has(e.key)) { + closeThemePanel(); var elem = keymap.get(e.key); if (elem === filterInput && !elem.offsetParent) { elem = getVisibleFilterInput(true); @@ -370,6 +410,7 @@ document.addEventListener("DOMContentLoaded", function(e) { elem.select(); e.preventDefault(); } else if (e.key === "Escape") { + closeThemePanel(); if (expanded) { collapse(); e.preventDefault(); @@ -389,6 +430,7 @@ document.addEventListener("DOMContentLoaded", function(e) { var windowWidth; var bodyHeight; function collapse() { + closeThemePanel(); if (expanded) { mainnav.removeAttribute("style"); if (tocMenu) { @@ -403,6 +445,7 @@ document.addEventListener("DOMContentLoaded", function(e) { } } function expand() { + closeThemePanel(); expanded = true; mainnav.style.display = "block"; mainnav.style.removeProperty("height"); @@ -457,6 +500,7 @@ document.addEventListener("DOMContentLoaded", function(e) { }); } document.querySelector("main").addEventListener("click", collapse); + document.querySelector("body").addEventListener("click", closeThemePanel); document.querySelectorAll("h1, h2, h3, h4, h5, h6") .forEach((hdr, idx) => { // Create anchor links for headers with an associated id attribute diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/search.js.template b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/search.js.template index 5a36ee06819..16ae5a4d221 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/search.js.template +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/search.js.template @@ -423,7 +423,8 @@ $.widget("custom.catcomplete", $.ui.autocomplete, { var label = getResultLabel(item); var resultDesc = getResultDescription(item); return $("

  • ") - .append($("
    ") + .append($("") + .attr("href", item.indexItem ? pathtoroot + getURL(item.indexItem, item.category) : null) .append($("").addClass("search-result-label").html(label)) .append($("").addClass("search-result-desc").html(resultDesc))) .appendTo(ul); @@ -515,7 +516,7 @@ $(function() { this.menu.previousFilter = "_"; this.menu.filterTimer = this.menu._delay(function() { delete this.previousFilter; - }, 1000); + }, 500); return doSearch(request, response); }, response: function(event, ui) { @@ -531,6 +532,11 @@ $(function() { collision: "flip" }, select: function(event, ui) { + for (var e = event.originalEvent; e != null; e = e.originalEvent) { + if (e.type === "click") { + return; + } + } if (ui.item.indexItem) { var url = getURL(ui.item.indexItem, ui.item.category); window.location.href = pathtoroot + url; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties index f49174f6dcd..2281a469c1c 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties @@ -193,6 +193,10 @@ doclet.Window_Help_title=API Help doclet.references={0} references doclet.Window_Search_title=Search doclet.search.main_heading=Search +doclet.theme.select_theme=Select Theme +doclet.theme.light=Light +doclet.theme.dark=Dark +doclet.theme.system=System Setting # label for link/button element to show the information below doclet.search.show_more=Additional resources diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css index f7994c7f7ed..778ee5f03b3 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css @@ -28,13 +28,35 @@ /* Line height for continuous text blocks */ --block-line-height: 1.5; --code-line-height: 1.6; + /* Navigation bar and content area dimensions */ + --top-nav-height: 44px; + --sub-nav-height: 36px; + --nav-height: calc(var(--top-nav-height) + var(--sub-nav-height)); + --max-content-width: 1500px; + --content-margin: 0 auto; + /* Inline SVG icons for dark theme */ + --right-svg-dark: url('data:image/svg+xml; utf8, \ + \ + \ + '); + --glass-svg-dark: url('data:image/svg+xml; utf8, \ + \ + \ + '); + --x-svg-dark: url('data:image/svg+xml; utf8, \ + \ + \ + '); +} +body { /* Text colors for body and block elements */ --body-text-color: #181818; --block-text-color: #181818; /* Background colors for various elements */ --body-background-color: #ffffff; --section-background-color: var(--body-background-color); - --detail-background-color: #ffffff; + --detail-background-color: var(--body-background-color); --code-background-color: #f5f5f5; --mark-background-color: #f7f7f7; --detail-block-color: #f4f4f4; @@ -76,9 +98,10 @@ /* Search input colors */ --search-input-background-color: #ffffff; --search-input-text-color: #000000; - --search-input-placeholder-color: #909090; + --search-input-placeholder-color: #757575; /* Highlight color for active search tag target */ - --search-tag-highlight-color: #ffff66; + --search-tag-background-color: #ffff66; + --search-tag-text-color: var(--block-text-color); /* Copy button colors and filters */ --button-border-color: #b0b8c8; --button-active-filter: brightness(96%); @@ -86,12 +109,158 @@ /* Colors for invalid tag notifications */ --invalid-tag-background-color: #ffe6e6; --invalid-tag-text-color: #000000; - /* Navigation bar dimensions */ - --top-nav-height: 44px; - --sub-nav-height: 36px; - --nav-height: calc(var(--top-nav-height) + var(--sub-nav-height)); - --max-content-width: 1500px; - --content-margin: 0 auto; +} + +body.theme-dark { + --body-text-color: #e8e8e8; + --block-text-color: #e8e8e8; + --body-background-color: #222528; + --section-background-color: var(--body-background-color); + --detail-background-color: var(--body-background-color); + --code-background-color: #303940; + --mark-background-color: #313131; + --detail-block-color: #f4f4f4; + --navbar-background-color: #395A6F; + --navbar-text-color: #ffffff; + --subnav-background-color: #3d454d; + --subnav-link-color: #d8dcdf; + --member-heading-background-color: var(--subnav-background-color); + --selected-background-color: #f8981d; + --selected-text-color: #253441; + --selected-link-color: #4a698a; + --table-header-color: #38444d; + --even-row-color: #222528; + --odd-row-color: #2d3135; + --title-color: #fff; + --link-color: #94badb; + --link-color-active: #ffb45b; + --toc-background-color: #31363c; + --toc-highlight-color: var(--subnav-background-color); + --toc-hover-color: #3f4146; + --snippet-background-color: #2d363c; + --snippet-text-color: var(--block-text-color); + --snippet-highlight-color: #f7c590; + --pre-background-color: var(--snippet-background-color); + --pre-text-color: var(--snippet-text-color); + --border-color: #444444; + --table-border-color: #717171; + --tab-border-radius: 2px 2px 0 0; + --search-input-background-color: #303030; + --search-input-text-color: #d0d0d0; + --search-input-placeholder-color: #979797; + --search-tag-background-color: #c6c61e; + --search-tag-text-color: #282828; + --button-border-color: #909090; + --button-active-filter: brightness(96%); + --button-focus-filter: brightness(104%); + --invalid-tag-background-color: #ffe6e6; + --invalid-tag-text-color: #000000; + div.main-grid img { + filter: invert(100%) brightness(160%); + } +} + +/* + * Dark theme + */ +@media (prefers-color-scheme: dark) { + body { + --body-text-color: #e8e8e8; + --block-text-color: #e8e8e8; + --body-background-color: #222528; + --section-background-color: var(--body-background-color); + --detail-background-color: var(--body-background-color); + --code-background-color: #303940; + --mark-background-color: #313131; + --detail-block-color: #f4f4f4; + --navbar-background-color: #395A6F; + --navbar-text-color: #ffffff; + --subnav-background-color: #3d454d; + --subnav-link-color: #d8dcdf; + --member-heading-background-color: var(--subnav-background-color); + --selected-background-color: #f8981d; + --selected-text-color: #253441; + --selected-link-color: #4a698a; + --table-header-color: #38444d; + --even-row-color: #222528; + --odd-row-color: #2d3135; + --title-color: #fff; + --link-color: #94badb; + --link-color-active: #ffb45b; + --toc-background-color: #31363c; + --toc-highlight-color: var(--subnav-background-color); + --toc-hover-color: #3f4146; + --snippet-background-color: #2d363c; + --snippet-text-color: var(--block-text-color); + --snippet-highlight-color: #f7c590; + --pre-background-color: var(--snippet-background-color); + --pre-text-color: var(--snippet-text-color); + --border-color: #444444; + --table-border-color: #717171; + --tab-border-radius: 2px 2px 0 0; + --search-input-background-color: #303030; + --search-input-text-color: #d0d0d0; + --search-input-placeholder-color: #979797; + --search-tag-background-color: #c6c61e; + --search-tag-text-color: #282828; + --button-border-color: #909090; + --button-active-filter: brightness(96%); + --button-focus-filter: brightness(104%); + --invalid-tag-background-color: #ffe6e6; + --invalid-tag-text-color: #000000; + div.main-grid img { + filter: invert(100%) brightness(160%); + } + } + + body.theme-light { + --body-text-color: #282828; + --block-text-color: #282828; + --body-background-color: #ffffff; + --section-background-color: var(--body-background-color); + --detail-background-color: var(--body-background-color); + --code-background-color: #f5f5f5; + --mark-background-color: #f7f7f7; + --detail-block-color: #f4f4f4; + --navbar-background-color: #4D7A97; + --navbar-text-color: #ffffff; + --subnav-background-color: #dee3e9; + --subnav-link-color: #47688a; + --member-heading-background-color: var(--subnav-background-color); + --selected-background-color: #f8981d; + --selected-text-color: #253441; + --selected-link-color: #4a698a; + --table-header-color: #ebeff4; + --even-row-color: #ffffff; + --odd-row-color: #f0f0f2; + --title-color: #2c4557; + --link-color: #437291; + --link-color-active: #bb7a2a; + --toc-background-color: #f8f8f8; + --toc-highlight-color: var(--subnav-background-color); + --toc-hover-color: #e9ecf0; + --snippet-background-color: #f2f2f4; + --snippet-text-color: var(--block-text-color); + --snippet-highlight-color: #f7c590; + --pre-background-color: var(--snippet-background-color); + --pre-text-color: var(--snippet-text-color); + --border-color: #e6e6e6; + --table-border-color: #000000; + --tab-border-radius: 2px 2px 0 0; + --search-input-background-color: #ffffff; + --search-input-text-color: #000000; + --search-input-placeholder-color: #757575; + --search-tag-background-color: #ffff66; + --search-tag-text-color: var(--block-text-color); + --button-border-color: #b0b8c8; + --button-active-filter: brightness(96%); + --button-focus-filter: brightness(104%); + --invalid-tag-background-color: #ffe6e6; + --invalid-tag-text-color: #000000; + div.main-grid img { + filter: none; + } + } } /* * Styles for individual HTML elements. @@ -186,7 +355,7 @@ hr { */ .about-language { flex: 0 0 auto; - padding:0 20px; + padding:0 20px 0 10px; margin:0; font-size:0.915em; max-width: 50%; @@ -215,6 +384,7 @@ hr { height: 100%; max-width: var(--max-content-width); margin: var(--content-margin); + position: relative; } .top-nav { background-color:var(--navbar-background-color); @@ -236,6 +406,68 @@ hr { button#navbar-toggle-button { display:none; } +button#theme-button { + position: relative; + top: -9.5px; + width: 32px; + height: 32px; + padding: 6px; + margin-left: -6px; + background-color: transparent; + border: 1px solid transparent; + border-radius: 6px; + cursor: pointer; +} +button#theme-button:hover, +button#theme-button:focus-visible { + border: 1px solid var(--button-border-color); +} +button#theme-button img { + display: none; + width: 18px; +} +body.theme-light button#theme-button img.theme-light { + display: inline; +} +body.theme-dark button#theme-button img.theme-dark { + display: inline; +} +@media (prefers-color-scheme: dark) { + body.theme-os button#theme-button img.theme-dark { + display: inline; + } +} +@media (prefers-color-scheme: light) { + body.theme-os button#theme-button img.theme-light { + display: inline; + } +} +div#theme-panel { + display: none; + position: fixed; + z-index: 8; + background-color: var(--toc-background-color); + color: var(--body-text-color); + padding: 4px; + white-space: nowrap; + border: 1px solid var(--border-color); + border-radius: 4px; + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); +} +div#theme-panel div:first-child { + font-weight: bold; + font-size: 16px; +} +div#theme-panel div { + padding: 8px; +} +div#theme-panel label { + margin-right: 8px; + cursor: pointer; +} +div#theme-panel input { + margin-right: 4px; +} ul.nav-list { display:inline-flex; margin:0; @@ -252,28 +484,25 @@ ul.nav-list li { div.sub-nav { background-color:var(--subnav-background-color); width:100%; - overflow:hidden; font-size:var(--nav-font-size); - height: var(--sub-nav-height); + min-height: var(--sub-nav-height); } ol.sub-nav-list { flex: 1 1 90%; - line-height: 1.8; - display: inline-flex; + line-height: 1.7; overflow: auto; - scroll-snap-type: x mandatory; - scroll-padding-left: 13px; - scrollbar-width: none; padding-left:6px; - white-space: nowrap; - margin:0; + margin: 4px 0; + min-height: 28px; } ol.sub-nav-list::-webkit-scrollbar { display: none; } ol.sub-nav-list li { list-style:none; - scroll-snap-align: start; + display: inline-block; + padding: 2px 0; + margin-right: -4px; } ol.sub-nav-list li:not(:first-child) { background: url("right.svg") no-repeat 3px; @@ -281,6 +510,17 @@ ol.sub-nav-list li:not(:first-child) { padding-left: 17px; list-style: none; } +body.theme-dark ol.sub-nav-list li:not(:first-child) { + background-image: var(--right-svg-dark); +} +@media (prefers-color-scheme: dark) { + ol.sub-nav-list li:not(:first-child) { + background-image: var(--right-svg-dark); + } + body.theme-light ol.sub-nav-list li:not(:first-child) { + background-image: url("right.svg"); + } +} ol.sub-nav-list a { padding: 3px; } @@ -290,7 +530,8 @@ ol.sub-nav-list a.current-selection { } .sub-nav .nav-list-search { flex: 1 1 10%; - margin: 0 15px; + margin: 4.5px 15px auto 5px; + padding: 1px 0; position:relative; white-space: nowrap; } @@ -362,7 +603,7 @@ body.class-declaration-page section.detail > h3:target { } body.class-declaration-page section.detail:target > h3 > a.anchor-link > img, body.class-declaration-page section.detail > h3:target > a.anchor-link > img { - filter: invert(100%) sepia(4%) saturate(98%) hue-rotate(212deg) brightness(160%) contrast(160%); + filter: invert(100%) brightness(160%); } h1 > sup { font-size: small; @@ -994,11 +1235,11 @@ li.ui-static-link a, li.ui-static-link a:visited { grid-template-columns: auto auto; } .ui-autocomplete > li, -.ui-autocomplete > li > div { +.ui-autocomplete > li > a { grid-column: 1 / 3; } .ui-autocomplete > li.result-item, -.ui-autocomplete > li.result-item > div { +.ui-autocomplete > li.result-item > a { display: grid; grid-template-columns: subgrid; } @@ -1009,21 +1250,23 @@ li.ui-static-link a, li.ui-static-link a:visited { } .ui-autocomplete .search-result-label { padding: 1px 4px; + color: var(--block-text-color); overflow: hidden; text-overflow: ellipsis; } .ui-autocomplete .search-result-desc { font-size: var(--nav-font-size); padding: 2px 4px; - color: #404040; + color: var(--block-text-color); overflow: hidden; text-overflow: ellipsis; } .ui-autocomplete .result-highlight { font-weight:bold; } +.ui-menu .ui-state-active .search-result-label, .ui-menu .ui-state-active .search-result-desc { - color: #383838; + color: var(--selected-text-color); } .ui-menu .ui-menu-item-wrapper { padding: 3px 4px; @@ -1042,6 +1285,17 @@ input[type="text"] { font-size: var(--nav-font-size); height: 19px; } +body.theme-dark input[type="text"] { + background-image: var(--glass-svg-dark); +} +@media (prefers-color-scheme: dark) { + input[type="text"] { + background-image: var(--glass-svg-dark); + } + body.theme-light input[type="text"] { + background-image: url('glass.svg'); + } +} input#page-search-input { width: calc(180px + 10vw); margin: 10px 0; @@ -1069,6 +1323,21 @@ input#reset-search, input.reset-filter, input#page-search-reset { font-size:0; visibility:hidden; } +body.theme-dark input#reset-search, +body.theme-dark input.reset-filter, +body.theme-dark input#page-search-reset { + background-image: var(--x-svg-dark); +} +@media (prefers-color-scheme: dark) { + input#reset-search, input.reset-filter, input#page-search-reset { + background-image: var(--x-svg-dark); + } + body.theme-light input#reset-search, + body.theme-light input.reset-filter, + body.theme-light input#page-search-reset { + background-image: url('x.svg'); + } +} input#reset-search { position:absolute; right:5px; @@ -1101,6 +1370,7 @@ select#search-modules { } kbd { background-color: #eeeeee; + color: #282828; border: 1px solid #b0b0b0; border-radius: 3px; padding: 0 4px; @@ -1109,11 +1379,13 @@ kbd { font-weight: bold; } .search-tag-result:target { - background-color:var(--search-tag-highlight-color); + background-color:var(--search-tag-background-color); + color:var(--search-tag-text-color); } dd > span:target, h1 > span:target { - background-color: var(--search-tag-highlight-color); + background-color: var(--search-tag-background-color); + color:var(--search-tag-text-color); } section.class-description dd > span:target, section.class-description h1 > span:target { @@ -1160,6 +1432,7 @@ div#result-container { #result-container div.result-table > a.search-result-link:focus-visible, #result-container div.result-table > a.search-result-link.selected { background-color: var(--selected-background-color); + color: var(--selected-text-color); outline: none; } #result-container div.result-table > a.search-result-link .search-result-label { @@ -1168,7 +1441,6 @@ div#result-container { } #result-container div.result-table > a.search-result-link .search-result-desc { font-size: var(--nav-font-size); - color: #404040; overflow: hidden; text-overflow: ellipsis; } @@ -1502,17 +1774,6 @@ table.striped > tbody > tr > th { .top-nav nav.toc ol.toc-list li { font-size: 1.04em; } - nav.toc a:link, nav.toc a:visited { - text-decoration:none; - color:var(--link-color); - } - nav.toc a[href]:hover, nav.toc a[href]:focus { - text-decoration:none; - color:var(--link-color-active); - } - :root { - scroll-behavior: auto; - } header { max-height: 100vh; overflow-y: visible; @@ -1584,9 +1845,9 @@ table.striped > tbody > tr > th { grid-template-columns: none; } .ui-autocomplete > li, - .ui-autocomplete > li > div, + .ui-autocomplete > li > a, .ui-autocomplete > li.result-item, - .ui-autocomplete > li.result-item > div { + .ui-autocomplete > li.result-item > a { grid-column: unset; display: block; grid-template-columns: none; @@ -1603,7 +1864,6 @@ table.striped > tbody > tr > th { } @media screen and (max-width: 800px) { .about-language { - padding: 0 16px; max-width: 90%; } ul.nav-list li { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/sun.svg b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/sun.svg new file mode 100644 index 00000000000..e8fa03499ac --- /dev/null +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/sun.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/x.svg b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/x.svg index 1efb4109241..34a942ffff1 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/x.svg +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/x.svg @@ -8,6 +8,6 @@ --> - - - \ No newline at end of file + + + diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java index 21f12673619..7bc4da60419 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java @@ -115,6 +115,12 @@ public class DocPaths { /** The name of the right pointing angle icon. */ public static final DocPath RIGHT_SVG = DocPath.create("right.svg"); + /** The name of the moon icon for the dark theme. */ + public static final DocPath MOON_SVG = DocPath.create("moon.svg"); + + /** The name of the sun icon for the light theme. */ + public static final DocPath SUN_SVG = DocPath.create("sun.svg"); + /** The name of the syntax highlighting style sheet. */ public static final DocPath HIGHLIGHT_CSS = DocPath.create("highlight.css"); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlAttr.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlAttr.java index 10aa7c0c57b..2ef3ae9db2a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlAttr.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlAttr.java @@ -203,6 +203,7 @@ public enum HtmlAttr { public enum InputType { CHECKBOX, + RADIO, RESET, TEXT; diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTree.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTree.java index 2d81a2914e6..f53a71a6999 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTree.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/html/HtmlTree.java @@ -586,8 +586,7 @@ public class HtmlTree extends Content { * @return the element */ public static HtmlTree FOOTER() { - return new HtmlTree(HtmlTag.FOOTER) - .setRole(HtmlAttr.Role.CONTENTINFO); + return new HtmlTree(HtmlTag.FOOTER); } /** diff --git a/src/jdk.jdeps/share/classes/module-info.java b/src/jdk.jdeps/share/classes/module-info.java index 3c15c3330c7..e996f66b7d4 100644 --- a/src/jdk.jdeps/share/classes/module-info.java +++ b/src/jdk.jdeps/share/classes/module-info.java @@ -23,8 +23,6 @@ * questions. */ -import jdk.internal.javac.ParticipatesInPreview; - /** * Defines tools for analysing dependencies in Java libraries and programs, * including the {@index jdeps jdeps tool}, @@ -62,7 +60,6 @@ import jdk.internal.javac.ParticipatesInPreview; * @moduleGraph * @since 9 */ -@ParticipatesInPreview module jdk.jdeps { requires java.compiler; requires jdk.compiler; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/AnnotationElement.java b/src/jdk.jfr/share/classes/jdk/jfr/AnnotationElement.java index 046709fa92e..155e9098d91 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/AnnotationElement.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/AnnotationElement.java @@ -69,7 +69,7 @@ public final class AnnotationElement { } StringJoiner values = new StringJoiner(",", "[", "]"); for (Object object : objects) { - descriptors.add(String.valueOf(object)); + values.add(String.valueOf(object)); } throw new IllegalArgumentException("Annotation " + descriptors + " for " + type.getName() + " doesn't match number of values " + values); } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/Configuration.java b/src/jdk.jfr/share/classes/jdk/jfr/Configuration.java index 097f069c895..a423af114a9 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/Configuration.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,7 +67,7 @@ public final class Configuration { } /** - * Returns the settings that specifies how a recording is configured. + * Returns the settings that specify how a recording is configured. *

    * Modifying the returned {@code Map} object doesn't change the * configuration. diff --git a/src/jdk.jfr/share/classes/jdk/jfr/Enabled.java b/src/jdk.jfr/share/classes/jdk/jfr/Enabled.java index 328826356a1..3f3270d7fc0 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/Enabled.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/Enabled.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -64,7 +64,7 @@ public @interface Enabled { /** * Returns {@code true} if by default the event should be enabled, {@code false} otherwise. * - * @return {@code true} if by default the event should be enabled by default, {@code false} otherwise + * @return {@code true} if by default the event should be enabled, {@code false} otherwise */ boolean value() default true; } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/EventType.java b/src/jdk.jfr/share/classes/jdk/jfr/EventType.java index d9b4e50fca8..0f2057a7981 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/EventType.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/EventType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -125,7 +125,7 @@ public final class EventType { */ public String getLabel() { if (label == UNKNOWN) { - label = platformEventType.getLabel();; + label = platformEventType.getLabel(); } return label; } @@ -226,7 +226,7 @@ public final class EventType { } /** - * Returns the list of human-readable names that makes up the categories for + * Returns the list of human-readable names that make up the categories for * this event type (for example, {@code "Java Application"}, {@code "Statistics"}). * * @return an immutable list of category names, or a list with the name diff --git a/src/jdk.jfr/share/classes/jdk/jfr/FlightRecorderListener.java b/src/jdk.jfr/share/classes/jdk/jfr/FlightRecorderListener.java index 55798f89de4..e3a172ca3fd 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/FlightRecorderListener.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/FlightRecorderListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ public interface FlightRecorderListener { /** * Receives notification when Flight Recorder is initialized. *

    - * This method is also be invoked when a listener is added to an already + * This method is also invoked when a listener is added to an already * initialized Flight Recorder. *

    * This method allows clients to implement their own initialization mechanism diff --git a/src/jdk.jfr/share/classes/jdk/jfr/Recording.java b/src/jdk.jfr/share/classes/jdk/jfr/Recording.java index 333645f1731..089a5ed37d8 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/Recording.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/Recording.java @@ -44,7 +44,7 @@ import jdk.jfr.internal.WriteablePath; /** * Provides means to configure, start, stop and dump recording data to disk. *

    - * The following example shows how configure, start, stop and dump recording data to disk. + * The following example shows how to configure, start, stop and dump recording data to disk. * * {@snippet class="Snippets" region="RecordingOverview"} * @@ -138,7 +138,7 @@ public final class Recording implements Closeable { * The newly created recording is in the {@link RecordingState#NEW} state. To * start the recording, invoke the {@link Recording#start()} method. * - * @param configuration configuration that contains the settings to be use, not + * @param configuration configuration that contains the settings to be used, not * {@code null} * * @throws IllegalStateException if Flight Recorder can't be created (for @@ -339,7 +339,7 @@ public final class Recording implements Closeable { *

    * Clones are useful for dumping data without stopping the recording. After * a clone is created, the amount of data to copy is constrained - * with the {@link #setMaxAge(Duration)} method and the {@link #setMaxSize(long)}method. + * with the {@link #setMaxAge(Duration)} method and the {@link #setMaxSize(long)} method. * * @param stop {@code true} if the newly created copy should be stopped * immediately, {@code false} otherwise @@ -615,9 +615,9 @@ public final class Recording implements Closeable { /** * Disables event with the specified name. *

    - * If multiple events with same name (for example, the same class is loaded + * If multiple events with the same name (for example, the same class is loaded * in different class loaders), then all events that match the - * name is disabled. To disable a specific class, use the + * name are disabled. To disable a specific class, use the * {@link #disable(Class)} method or a {@code String} representation of the event * type ID. * @@ -653,7 +653,7 @@ public final class Recording implements Closeable { /** * Disables event. * - * @param eventClass the event to enable, not {@code null} + * @param eventClass the event to disable, not {@code null} * * @throws IllegalArgumentException if {@code eventClass} is an abstract * class or not a subclass of {@link Event} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java index ac325f19f22..d7de775ea3c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java @@ -295,7 +295,7 @@ public interface EventStream extends AutoCloseable { void setReuse(boolean reuse); /** - * Specifies that events arrives in chronological order, sorted by the time + * Specifies that events arrive in chronological order, sorted by the time * they were committed to the stream. * * @param ordered if event objects arrive in chronological order to diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/MetadataEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/MetadataEvent.java index 153d4f343f7..df1e1edc236 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/MetadataEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/MetadataEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -82,7 +82,7 @@ public final class MetadataEvent { * The delta will be from the last metadata event. If no metadata event has been * emitted earlier, the list will be empty. * - * @return an immutable list of added event types, not {@code null} + * @return an immutable list of removed event types, not {@code null} */ public final List getRemovedEventTypes() { if (removed == null) { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java index 6c8f23aa02a..7f0dbbf7318 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,7 @@ public final class RecordedMethod extends RecordedObject { } /** - * Returns the class this method belongs to, if it belong to a Java frame. + * Returns the class this method belongs to, if it belongs to a Java frame. *

    * To ensure this is a Java frame, use the {@link RecordedFrame#isJavaFrame()} * method. @@ -99,7 +99,7 @@ public final class RecordedMethod extends RecordedObject { /** * Returns whether this method is hidden (for example, wrapper code in a lambda - * expressions). + * expression). * * @return {@code true} if method is hidden, {@code false} otherwise */ diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java index 8d4a69a92d7..a229709ecaa 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordedObject.java @@ -183,7 +183,7 @@ public sealed class RecordedObject return defaultValue; } T object = getValue(name); - if (object == null || object.getClass().isAssignableFrom(clazz)) { + if (object == null || object.getClass() == clazz) { return object; } else { return defaultValue; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java index a3880da33fa..2e839e93068 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java @@ -51,7 +51,7 @@ import jdk.jfr.internal.consumer.filter.ChunkWriter.RemovedEvents; /** * A recording file. *

    - * The following example shows how read and print all events in a recording file. + * The following example shows how to read and print all events in a recording file. * * {@snippet class="Snippets" region="RecordingFileOverview"} * diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java index 3d6c8de3015..5ec794bdf7c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java @@ -232,8 +232,8 @@ public final class RecordingStream implements AutoCloseable, EventStream { * that is older than the specified length of time is removed by the Java * Virtual Machine (JVM). *

    - * If neither maximum limit or the maximum age is set, the size of the - * recording may grow indefinitely if events are on + * If neither the maximum limit nor the maximum age is set, the size of the + * recording may grow indefinitely if events are not consumed. * * @param maxAge the length of time that data is kept, or {@code null} if * infinite diff --git a/src/jdk.jfr/share/classes/jdk/jfr/consumer/package-info.java b/src/jdk.jfr/share/classes/jdk/jfr/consumer/package-info.java index 81f7977deba..56f47b4e483 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/package-info.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ * Typically this is expressed as {@code "not null"}. If a {@code null} * parameter is used where it is not allowed, a * {@code java.lang.NullPointerException} is thrown. If a {@code null} - * parameters is passed to a method that throws other exceptions, such as + * parameter is passed to a method that throws other exceptions, such as * {@code java.io.IOException}, the {@code java.lang.NullPointerException} takes * precedence, unless the Javadoc for the method explicitly states how * {@code null} is handled, i.e. by throwing {@code java.lang.IllegalArgumentException}. 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 7a8d490d6e5..621c879f712 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java @@ -97,7 +97,7 @@ public final class EventControl { } if (eventType.hasThrottle()) { addControl(Throttle.NAME, defineThrottle(eventType)); - eventType.setThrottler(new Throttler(eventType)); + eventType.setThrottler(new Throttler()); } if (eventType.hasLevel()) { addControl(Level.NAME, defineLevel(eventType)); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/LongMap.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/LongMap.java index dfb549b5778..3f02e71acdd 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/LongMap.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/LongMap.java @@ -273,7 +273,7 @@ public final class LongMap { for (int i = 0; i < keys.length; i++) { T o = objects[i]; if (o != null) { - list.add(o); + list.add(o == NULL_OBJECT ? null : o); } } return list; 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 dfe87509e14..c4f0a8e9a0e 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PlatformEventType.java @@ -30,7 +30,6 @@ import java.util.List; import java.util.Objects; import jdk.jfr.SettingDescriptor; -import jdk.jfr.events.ActiveSettingEvent; import jdk.jfr.internal.periodic.PeriodicEvents; import jdk.jfr.internal.util.ImplicitFields; import jdk.jfr.internal.util.TimespanRate; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/PrivateAccess.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/PrivateAccess.java index 95dbd0986dc..fe5d5aea327 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/PrivateAccess.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/PrivateAccess.java @@ -32,7 +32,6 @@ import jdk.jfr.AnnotationElement; import jdk.jfr.Configuration; import jdk.jfr.EventSettings; import jdk.jfr.EventType; -import jdk.jfr.FlightRecorderPermission; import jdk.jfr.Recording; import jdk.jfr.SettingDescriptor; import jdk.jfr.ValueDescriptor; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/management/ChunkFilename.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/management/ChunkFilename.java index 0d8290b206e..47e8de74037 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/management/ChunkFilename.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/management/ChunkFilename.java @@ -24,7 +24,6 @@ */ package jdk.jfr.internal.management; -import java.nio.file.Paths; import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDateTime; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/periodic/FlushTask.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/periodic/FlushTask.java index 739f32bf634..bed92a2f283 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/periodic/FlushTask.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/periodic/FlushTask.java @@ -25,7 +25,6 @@ package jdk.jfr.internal.periodic; import jdk.jfr.internal.JVM; -import jdk.jfr.internal.MetadataRepository; import jdk.jfr.internal.PlatformRecorder; import jdk.jfr.internal.PrivateAccess; import jdk.jfr.internal.util.Utils; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Histogram.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Histogram.java index 0eca381cca4..d3d596dc4b9 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Histogram.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/Histogram.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,7 +50,7 @@ final class Histogram { private static final class LookupKey { private Object keys; - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({ "unchecked"}) public void add(Object o) { // One key, fast path if (keys == null) { @@ -58,8 +58,8 @@ final class Histogram { return; } // Three or more keys - if (keys instanceof Set set) { - set.add(o); + if (keys instanceof Set set) { + ((Set) set).add(o); return; } // Two keys diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryParser.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryParser.java index 51cb2cb03e7..3c7d1e58b35 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryParser.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/QueryParser.java @@ -28,7 +28,6 @@ import java.text.ParseException; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.function.BiConsumer; import java.util.function.Consumer; import jdk.jfr.internal.query.Configuration.Truncate; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini index 495c5659949..1ab9840b28e 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/query/view.ini @@ -40,9 +40,9 @@ table = "COLUMN 'Event Type', 'Enabled', 'Threshold', 'Stack Trace','Period','Cutoff', 'Throttle' FORMAT none, missing:whitespace, missing:whitespace, missing:whitespace, missing:whitespace, missing:whitespace, missing:whitespace - SELECT E.id, LAST_BATCH(E.value), LAST_BATCH(T.value), - LAST_BATCH(S.value), LAST_BATCH(P.value), - LAST_BATCH(C.value), LAST_BATCH(U.value) + SELECT E.id, LAST(E.value), LAST(T.value), + LAST(S.value), LAST(P.value), + LAST(C.value), LAST(U.value) FROM ActiveSetting AS E, ActiveSetting AS T, diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java index 997f4a84949..944827f6d6f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CPUThrottleSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Datadog, Inc. All rights reserved. * Copyright (c) 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -27,9 +27,6 @@ package jdk.jfr.internal.settings; -import static jdk.jfr.internal.util.TimespanUnit.SECONDS; -import static jdk.jfr.internal.util.TimespanUnit.MILLISECONDS; - import java.util.Objects; import java.util.Set; @@ -41,7 +38,6 @@ import jdk.jfr.Name; import jdk.jfr.internal.PlatformEventType; import jdk.jfr.internal.Type; import jdk.jfr.internal.util.TimespanRate; -import jdk.jfr.internal.util.Utils; @MetadataDefinition @Label("CPUThrottleSetting") diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/MethodSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/MethodSetting.java index 0f2751b6ac7..b369172d7b6 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/MethodSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/MethodSetting.java @@ -42,7 +42,7 @@ import jdk.jfr.internal.tracing.PlatformTracer; @Name(Type.SETTINGS_PREFIX + "Filter") public final class MethodSetting extends FilterSetting { private final Modification modification; - private volatile static boolean initialized; + private static volatile boolean initialized; public MethodSetting(PlatformEventType eventType, Modification modification, String defaultValue) { super(eventType, defaultValue); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/Throttler.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/Throttler.java index df8f5a58700..e5093de781b 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/Throttler.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/Throttler.java @@ -55,8 +55,6 @@ public final class Throttler { private boolean disabled; private boolean update = true; - public Throttler(PlatformEventType t) { - } // Not synchronized in fast path, but uses volatile reads. public boolean sample(long ticks) { if (disabled) { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Filters.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Filters.java index e63fd34f8b8..ed983467dc6 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Filters.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Filters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -91,7 +91,7 @@ public class Filters { return !types.stream().anyMatch(f); } - public static Predicate matchAny(List> filters) { + public static Predicate matchAll(List> filters) { if (filters.isEmpty()) { return t -> true; } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java index e456b14362f..2d6f55dd4b3 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Metadata.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -203,7 +203,7 @@ final class Metadata extends Command { try (PrintWriter pw = new PrintWriter(System.out, false, UTF_8)) { MetadataWriter metadataWriter = new MetadataWriter(pw, showIds); if (!filters.isEmpty()) { - filter = Filters.matchAny(filters); + filter = Filters.matchAll(filters); } List types = findTypes(file); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/PrettyWriter.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/PrettyWriter.java index 10f50095247..44aecf6413c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/PrettyWriter.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/PrettyWriter.java @@ -27,6 +27,7 @@ package jdk.jfr.internal.tool; import java.io.IOException; import java.io.PrintWriter; +import java.lang.reflect.Array; import java.nio.file.Path; import java.time.Duration; import java.time.Instant; @@ -291,7 +292,7 @@ public final class PrettyWriter extends EventPrintWriter { RecordedFrame frame = frames.get(i); if (frame.isJavaFrame() && !frame.getMethod().isHidden()) { printIndent(); - printValue(frame, null, ""); + printJavaFrame(frame, ""); println(); depth++; } @@ -323,12 +324,12 @@ public final class PrettyWriter extends EventPrintWriter { printValue(getValue(struct, v), v, ""); } - private void printArray(Object[] array) { + private void printArray(Object[] array, ValueDescriptor field) { println("["); indent(); for (int i = 0; i < array.length; i++) { printIndent(); - printValue(array[i], null, i + 1 < array.length ? ", " : ""); + printValue(array[i], field, i + 1 < array.length ? ", " : ""); } retract(); printIndent(); @@ -336,81 +337,38 @@ public final class PrettyWriter extends EventPrintWriter { } private void printValue(Object value, ValueDescriptor field, String postFix) { - if (value == null) { - println("N/A" + postFix); - return; - } - if (value instanceof RecordedObject) { - if (value instanceof RecordedThread rt) { - printThread(rt, postFix); - return; - } - if (value instanceof RecordedClass rc) { - printClass(rc, postFix); - return; - } - if (value instanceof RecordedClassLoader rcl) { - printClassLoader(rcl, postFix); - return; - } - if (value instanceof RecordedFrame frame) { - if (frame.isJavaFrame()) { - printJavaFrame((RecordedFrame) value, postFix); - return; - } - } - if (value instanceof RecordedMethod rm) { - println(formatMethod(rm)); - return; - } - if (field.getTypeName().equals(TYPE_OLD_OBJECT)) { - printOldObject((RecordedObject) value); - return; - } - print((RecordedObject) value, postFix); - return; - } - if (value.getClass().isArray()) { - printArray((Object[]) value); - return; + switch (value) { + case null -> println("N/A" + postFix); + case RecordedObject object -> printRecordedObject(object, field, postFix); + case Number number -> printNumber(number, field); + case String text -> println("\"" + text + "\""); + case Duration duration -> printDuration(duration); + case OffsetDateTime dateTime -> printOffsetDateTime(dateTime); + case Object[] array -> printArray(array, field); + default -> println(value); } + } - if (value instanceof Double d) { - if (Double.isNaN(d) || d == Double.NEGATIVE_INFINITY) { - println("N/A"); - return; - } - } - if (value instanceof Float f) { - if (Float.isNaN(f) || f == Float.NEGATIVE_INFINITY) { - println("N/A"); - return; - } - } - if (value instanceof Long l) { - if (l == Long.MIN_VALUE) { - println("N/A"); - return; - } - } - if (value instanceof Integer i) { - if (i == Integer.MIN_VALUE) { - println("N/A"); - return; - } + private void printRecordedObject(RecordedObject struct, ValueDescriptor field, String postFix) { + switch (struct) { + case RecordedThread rt -> printThread(rt, postFix); + case RecordedClass rc -> printClass(rc, postFix); + case RecordedClassLoader rcl -> printClassLoader(rcl, postFix); + case RecordedFrame rf when rf.isJavaFrame() -> printJavaFrame(rf, postFix); + case RecordedMethod rm -> println(formatMethod(rm)); + case RecordedObject ro when field.getTypeName().equals(TYPE_OLD_OBJECT) -> printOldObject(ro); + default -> print(struct, postFix); } + } - if (field.getContentType() != null) { - if (printFormatted(field, value)) { - return; - } + private void printNumber(Number number, ValueDescriptor field) { + switch (number) { + case Double d when Double.isNaN(d) || d == Double.NEGATIVE_INFINITY -> println("N/A"); + case Float f when Float.isNaN(f) || f == Float.NEGATIVE_INFINITY -> println("N/A"); + case Long l when l == Long.MIN_VALUE -> println("N/A"); + case Integer i when i == Integer.MIN_VALUE -> println("N/A"); + default -> printFormatted(field, number); } - - String text = String.valueOf(value); - if (value instanceof String) { - text = "\"" + text + "\""; - } - println(text); } private void printOldObject(RecordedObject object) { @@ -546,78 +504,73 @@ public final class PrettyWriter extends EventPrintWriter { } } - private boolean printFormatted(ValueDescriptor field, Object value) { - if (value instanceof Duration d) { - if (d.getSeconds() == Long.MIN_VALUE && d.getNano() == 0) { - println("N/A"); - return true; - } - if (d.equals(ChronoUnit.FOREVER.getDuration())) { - println("Forever"); - return true; - } - if (showExact) { - println(String.format("%.9f s", (double) d.toNanos() / 1_000_000_000)); - } else { - println(ValueFormatter.formatDuration(d)); - } - return true; + private void printOffsetDateTime(OffsetDateTime dateTime) { + if (dateTime.equals(OffsetDateTime.MIN)) { + println("N/A"); + return; } - if (value instanceof OffsetDateTime odt) { - if (odt.equals(OffsetDateTime.MIN)) { - println("N/A"); - return true; - } - if (showExact) { - println(TIME_FORMAT_EXACT.format(odt)); - } else { - println(TIME_FORMAT.format(odt)); - } - return true; + if (showExact) { + println(TIME_FORMAT_EXACT.format(dateTime)); + } else { + println(TIME_FORMAT.format(dateTime)); } - Percentage percentage = field.getAnnotation(Percentage.class); - if (percentage != null) { - if (value instanceof Number n) { - double p = 100 * n.doubleValue(); + } + + private void printDuration(Duration duration) { + if (duration.getSeconds() == Long.MIN_VALUE && duration.getNano() == 0) { + println("N/A"); + return; + } + if (duration.equals(ChronoUnit.FOREVER.getDuration())) { + println("Forever"); + return; + } + if (showExact) { + println(String.format("%.9f s", (double) duration.toNanos() / 1_000_000_000)); + } else { + println(ValueFormatter.formatDuration(duration)); + } + } + + private void printFormatted(ValueDescriptor field, Number number) { + if (field.getContentType() != null) { + Percentage percentage = field.getAnnotation(Percentage.class); + if (percentage != null) { + double p = 100 * number.doubleValue(); if (showExact) { println(String.format("%.9f%%", p)); } else { println(String.format("%.2f%%", p)); } - return true; + return; } - } - DataAmount dataAmount = field.getAnnotation(DataAmount.class); - if (dataAmount != null && value instanceof Number number) { - boolean frequency = field.getAnnotation(Frequency.class) != null; - String unit = dataAmount.value(); - boolean bits = unit.equals(DataAmount.BITS); - boolean bytes = unit.equals(DataAmount.BYTES); - if (bits || bytes) { - formatMemory(number.longValue(), bytes, frequency); - return true; + DataAmount dataAmount = field.getAnnotation(DataAmount.class); + if (dataAmount != null) { + boolean frequency = field.getAnnotation(Frequency.class) != null; + String unit = dataAmount.value(); + boolean bits = unit.equals(DataAmount.BITS); + boolean bytes = unit.equals(DataAmount.BYTES); + if (bits || bytes) { + printMemory(number.longValue(), bytes, frequency); + return; + } } - } - MemoryAddress memoryAddress = field.getAnnotation(MemoryAddress.class); - if (memoryAddress != null) { - if (value instanceof Number n) { - long d = n.longValue(); + MemoryAddress memoryAddress = field.getAnnotation(MemoryAddress.class); + if (memoryAddress != null) { + long d = number.longValue(); println(String.format("0x%08X", d)); - return true; + return; + } + Frequency frequency = field.getAnnotation(Frequency.class); + if (frequency != null) { + println(number + " Hz"); + return; } } - Frequency frequency = field.getAnnotation(Frequency.class); - if (frequency != null) { - if (value instanceof Number) { - println(value + " Hz"); - return true; - } - } - - return false; + println(number); } - private void formatMemory(long value, boolean bytesUnit, boolean frequency) { + private void printMemory(long value, boolean bytesUnit, boolean frequency) { if (showExact) { StringBuilder sb = new StringBuilder(); sb.append(value); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Print.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Print.java index 79dd04c379f..0bc6ceeb18f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Print.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Print.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -164,7 +164,7 @@ final class Print extends Command { } eventWriter.setStackDepth(stackDepth); if (!eventFilters.isEmpty()) { - eventWriter.setEventFilter(Filters.matchAny(eventFilters)); + eventWriter.setEventFilter(Filters.matchAll(eventFilters)); } try { eventWriter.print(file); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Scrub.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Scrub.java index 8a02ded1125..6043e841b50 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Scrub.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/Scrub.java @@ -214,7 +214,7 @@ final class Scrub extends Command { } optionCount = options.size(); } - return Filters.matchAny(filters); + return Filters.matchAll(filters); } private void ensureUsableOutput(Path input, Path output) throws UserSyntaxException, UserDataException { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/StructuredWriter.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/StructuredWriter.java index 8e16e5fb0bf..1eabbb49d9b 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/StructuredWriter.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/StructuredWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -113,6 +113,10 @@ abstract class StructuredWriter { println(); } + public final void println(Object object) { + println(String.valueOf(object)); + } + private void updateIndent() { if (indent > indention.length()) { indention = " ".repeat(2 * indent); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/PlatformTracer.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/PlatformTracer.java index 2d40e496dc1..a77123b30f3 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/PlatformTracer.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/PlatformTracer.java @@ -35,7 +35,6 @@ import jdk.jfr.internal.LogTag; import jdk.jfr.internal.Logger; import jdk.jfr.internal.MetadataRepository; import jdk.jfr.internal.SecuritySupport; -import jdk.jfr.internal.Type; import jdk.jfr.internal.util.Bytecode; import jdk.jfr.tracing.MethodTracer; @@ -264,18 +263,16 @@ public final class PlatformTracer { // provided a user has specified a non-empty filter for the MethodTrace or // MethodTiming event. public static void initialize() { - try { - Logger.log(LogTag.JFR_METHODTRACE, LogLevel.DEBUG, "Method tracer initialization started."); - Thread current = Thread.currentThread(); - JVM.exclude(current); - long methodId = 16384126; - long classId = methodId >> 16; - ClassLoader cl = null; - String className = " java/lang/String"; - Module m = String.class.getModule(); - var is = ClassLoader.getSystemClassLoader().getResourceAsStream("java/lang/String.class"); + Logger.log(LogTag.JFR_METHODTRACE, LogLevel.DEBUG, "Method tracer initialization started."); + Thread current = Thread.currentThread(); + JVM.exclude(current); + long methodId = 16384126; + long classId = methodId >> 16; + ClassLoader cl = null; + String className = "java/lang/String"; + Module m = String.class.getModule(); + try (var is = ClassLoader.getSystemClassLoader().getResourceAsStream("java/lang/String.class")) { byte[] oldBytecode = is.readAllBytes(); - is.close(); long[] ids = { methodId }; String[] names = { "" }; String[] signatures = { "()V" }; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java index c35432d5d6b..e4606059822 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanRate.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2025 SAP SE. All rights reserved. - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,6 @@ */ package jdk.jfr.internal.util; -import jdk.jfr.internal.settings.CPUThrottleSetting; - /** * A rate or fixed period, see {@link jdk.jfr.internal.Rate} */ diff --git a/src/jdk.jfr/share/classes/jdk/jfr/package-info.java b/src/jdk.jfr/share/classes/jdk/jfr/package-info.java index 54866ec508f..812525e0ff4 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/package-info.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/package-info.java @@ -33,7 +33,7 @@ * running application up to the failure or crash. *

    * To define a Flight Recorder event, extend {@link jdk.jfr.Event} and add - * fields that matches the data types of the payload. Metadata about fields, + * fields that match the data types of the payload. Metadata about fields, * such as labels, descriptions and units, can be added by using the annotations * available in the {@code jdk.jfr} package, or by using a user-defined * annotation that has the {@link jdk.jfr.MetadataDefinition} annotation. @@ -232,7 +232,7 @@ * Typically this is expressed as {@code "not null"}. If a {@code null} * parameter is used where it is not allowed, a * {@code java.lang.NullPointerException} is thrown. If a {@code null} - * parameters is passed to a method that throws other exceptions, such as + * parameters are passed to a method that throws other exceptions, such as * {@code java.io.IOException}, the {@code java.lang.NullPointerException} takes * precedence, unless the Javadoc for the method explicitly states how * {@code null} is handled, i.e. by throwing diff --git a/src/jdk.jfr/share/classes/module-info.java b/src/jdk.jfr/share/classes/module-info.java index 8b2a5af7e80..211c1f1e2a1 100644 --- a/src/jdk.jfr/share/classes/module-info.java +++ b/src/jdk.jfr/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,15 +23,12 @@ * questions. */ -import jdk.internal.javac.ParticipatesInPreview; - /** * Defines the API for JDK Flight Recorder. * * @moduleGraph * @since 9 */ -@ParticipatesInPreview module jdk.jfr { exports jdk.jfr; exports jdk.jfr.consumer; diff --git a/src/jdk.jfr/share/conf/jfr/default.jfc b/src/jdk.jfr/share/conf/jfr/default.jfc index 20051864895..eb3b8626722 100644 --- a/src/jdk.jfr/share/conf/jfr/default.jfc +++ b/src/jdk.jfr/share/conf/jfr/default.jfc @@ -33,12 +33,12 @@ - true + false 10 s - true + false 10 s @@ -762,7 +762,7 @@ - true + false endChunk diff --git a/src/jdk.jlink/share/classes/module-info.java b/src/jdk.jlink/share/classes/module-info.java index a4fa40dc790..ba66da53604 100644 --- a/src/jdk.jlink/share/classes/module-info.java +++ b/src/jdk.jlink/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,6 @@ * questions. */ -import jdk.internal.javac.ParticipatesInPreview; - /** * Defines the {@index jlink jlink tool} tool for creating run-time * images, the {@index jmod jmod tool} tool for creating and manipulating @@ -53,7 +51,6 @@ import jdk.internal.javac.ParticipatesInPreview; * @moduleGraph * @since 9 */ -@ParticipatesInPreview module jdk.jlink { requires jdk.internal.opt; requires jdk.jdeps; 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 476ca3201ce..2d0c78c2474 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java @@ -46,6 +46,7 @@ import javax.imageio.ImageIO; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import jdk.jpackage.internal.model.FileAssociation; +import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LinuxLauncher; import jdk.jpackage.internal.model.LinuxPackage; import jdk.jpackage.internal.model.Package; @@ -237,6 +238,23 @@ final class DesktopIntegration extends ShellCustomAction { data.put("DEPLOY_BUNDLE_CATEGORY", pkg.menuGroupName()); data.put("APPLICATION_LAUNCHER", Enquoter.forPropertyValues().applyTo( installedLayout.launchersDirectory().resolve(launcher.executableNameWithSuffix()).toString())); + data.put("STARTUP_DIRECTORY", launcher.shortcut() + .flatMap(LauncherShortcut::startupDirectory) + .map(startupDirectory -> { + switch (startupDirectory) { + case DEFAULT -> { + return (Path)null; + } + case APP_DIR -> { + return installedLayout.appDirectory(); + } + default -> { + throw new AssertionError(); + } + } + }).map(str -> { + return "Path=" + str; + }).orElse(null)); return data; } @@ -337,7 +355,7 @@ final class DesktopIntegration extends ShellCustomAction { * - installPath(): path where it should be installed by package manager; */ private InstallableFile createDesktopFile(String fileName) { - var srcPath = pkg.asPackageApplicationLayout().orElseThrow().resolveAt(env.appImageDir()).desktopIntegrationDirectory().resolve(fileName); + var srcPath = env.asApplicationLayout().orElseThrow().desktopIntegrationDirectory().resolve(fileName); var installPath = pkg.asInstalledPackageApplicationLayout().orElseThrow().desktopIntegrationDirectory().resolve(fileName); return new InstallableFile(srcPath, installPath); } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayout.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayout.java index ea87363a9bb..677ee199a84 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayout.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayout.java @@ -24,9 +24,10 @@ */ package jdk.jpackage.internal; -import static jdk.jpackage.internal.util.PathUtils.resolveNullablePath; +import static jdk.jpackage.internal.util.PathUtils.mapNullablePath; import java.nio.file.Path; +import java.util.function.UnaryOperator; import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.util.CompositeProxy; @@ -40,7 +41,25 @@ interface LinuxApplicationLayout extends ApplicationLayout, LinuxApplicationLayo @Override default LinuxApplicationLayout resolveAt(Path root) { - return create(ApplicationLayout.super.resolveAt(root), - resolveNullablePath(root, libAppLauncher())); + return (LinuxApplicationLayout)ApplicationLayout.super.resolveAt(root); + } + + @Override + default LinuxApplicationLayout unresolve() { + return (LinuxApplicationLayout)ApplicationLayout.super.unresolve(); + } + + @Override + default LinuxApplicationLayout resetRootDirectory() { + if (isResolved()) { + return create(ApplicationLayout.super.resetRootDirectory(), libAppLauncher()); + } else { + return this; + } + } + + @Override + default LinuxApplicationLayout map(UnaryOperator mapper) { + return create(ApplicationLayout.super.map(mapper), mapNullablePath(mapper, libAppLauncher())); } } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayoutMixin.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayoutMixin.java index a88dbc9e16e..21606153669 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayoutMixin.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxApplicationLayoutMixin.java @@ -26,7 +26,7 @@ package jdk.jpackage.internal; import java.nio.file.Path; -// Must be publc to allow access from AppImageLayout.toPathGroup() +// Must be public to allow access from AppImageLayout.toPathGroup() public interface LinuxApplicationLayoutMixin { /** diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java index 1915fd00c0e..d5b369ba23a 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java @@ -351,8 +351,7 @@ public class LinuxDebBundler extends LinuxPackageBundler { data.put("APPLICATION_LICENSE_TEXT", licenseText); data.put("APPLICATION_ARCH", pkg.arch()); data.put("APPLICATION_INSTALLED_SIZE", Long.toString( - AppImageLayout.toPathGroup(pkg.packageLayout().resolveAt( - env.appImageDir())).sizeInBytes() >> 10)); + AppImageLayout.toPathGroup(env.appImageLayout()).sizeInBytes() >> 10)); data.put("APPLICATION_HOMEPAGE", pkg.aboutURL().map( value -> "Homepage: " + value).orElse("")); data.put("APPLICATION_VERSION_WITH_RELEASE", ((LinuxDebPackage) pkg).versionWithRelease()); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java index 6967dea111e..ced77b1aa68 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java @@ -109,12 +109,8 @@ final class LinuxFromParams { static final BundlerParamInfo DEB_PACKAGE = createPackageBundlerParam( LinuxFromParams::createLinuxDebPackage); - private static final BundlerParamInfo LINUX_SHORTCUT_HINT = new BundlerParamInfo<>( - Arguments.CLIOptions.LINUX_SHORTCUT_HINT.getId(), - Boolean.class, - params -> false, - (s, p) -> (s == null || "null".equalsIgnoreCase(s)) ? false : Boolean.valueOf(s) - ); + private static final BundlerParamInfo LINUX_SHORTCUT_HINT = createStringBundlerParam( + Arguments.CLIOptions.LINUX_SHORTCUT_HINT.getId()); private static final BundlerParamInfo LINUX_CATEGORY = createStringBundlerParam( Arguments.CLIOptions.LINUX_CATEGORY.getId()); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java index 4ed84909958..cc00d7816f5 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java @@ -33,7 +33,9 @@ import java.util.regex.Pattern; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.LinuxApplication; import jdk.jpackage.internal.model.LinuxPackage; +import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.model.LinuxPackageMixin; import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.model.StandardPackageType; @@ -52,24 +54,35 @@ final class LinuxPackageBuilder { pkgBuilder.name(pkgBuilder.create().packageName().toLowerCase().replaceAll("[ _]", "-")); } - final var pkg = pkgBuilder.create(); + final var tmpPkg = pkgBuilder.create(); - final var stdPkgType = pkg.asStandardPackageType(); + final var stdPkgType = tmpPkg.asStandardPackageType(); if (stdPkgType.isPresent()) { - validatePackageName(pkg.packageName(), stdPkgType.orElseThrow()); + validatePackageName(tmpPkg.packageName(), stdPkgType.orElseThrow()); } - var reply = create(pkg, pkg.packageLayout()); - if (reply.isInstallDirInUsrTree()) { - reply = create(pkg, usrTreePackageLayout(pkg.relativeInstallDir(), pkg.packageName())); + final AppImageLayout relativeInstalledLayout; + if (create(tmpPkg).isInstallDirInUsrTree()) { + final var usrTreeLayout = usrTreePackageLayout(tmpPkg.relativeInstallDir(), tmpPkg.packageName()); + if (tmpPkg.isRuntimeInstaller()) { + relativeInstalledLayout = RuntimeLayout.create(usrTreeLayout.runtimeDirectory()); + } else { + relativeInstalledLayout = usrTreeLayout; + } + } else { + relativeInstalledLayout = tmpPkg.appImageLayout().resolveAt(tmpPkg.relativeInstallDir()).resetRootDirectory(); } - return reply; + final var app = ApplicationBuilder.overrideAppImageLayout(pkgBuilder.app(), relativeInstalledLayout); + + return create(pkgBuilder + .app(LinuxApplication.create(app)) + .installedPackageLayout(relativeInstalledLayout.resolveAt(Path.of("/")).resetRootDirectory()) + .create()); } - private LinuxPackage create(Package pkg, AppImageLayout pkgLayout) throws ConfigException { + private LinuxPackage create(Package pkg) throws ConfigException { return LinuxPackage.create(pkg, new LinuxPackageMixin.Stub( - pkgLayout, Optional.ofNullable(menuGroupName).orElseGet(DEFAULTS::menuGroupName), Optional.ofNullable(category), Optional.ofNullable(additionalDependencies), diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackage.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackage.java index aef1c5eb700..28d47fc46a8 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackage.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackage.java @@ -36,9 +36,6 @@ public interface LinuxPackage extends Package, LinuxPackageMixin { LinuxApplication app(); - @Override - AppImageLayout packageLayout(); - @Override default String packageFileName() { String packageFileNameTemlate = asStandardPackageType().map(stdType -> { diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackageMixin.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackageMixin.java index f890aa6ff39..5bcf57194f6 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackageMixin.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/model/LinuxPackageMixin.java @@ -31,11 +31,6 @@ import java.util.Optional; */ public interface LinuxPackageMixin { - /** - * Overrides {@link Package#packageLayout()}. - */ - AppImageLayout packageLayout(); - /** * Gets the name of the start menu group where to create shortcuts for * application launchers of this package. @@ -88,7 +83,7 @@ public interface LinuxPackageMixin { /** * Default implementation of {@link LinuxPackageMixin} interface. */ - record Stub(AppImageLayout packageLayout, String menuGroupName, + record Stub(String menuGroupName, Optional category, Optional additionalDependencies, Optional release, String arch) implements LinuxPackageMixin { } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop index bd645b77669..de7df568454 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/template.desktop @@ -2,6 +2,7 @@ Name=APPLICATION_NAME Comment=APPLICATION_DESCRIPTION Exec=APPLICATION_LAUNCHER +STARTUP_DIRECTORY Icon=APPLICATION_ICON Terminal=false Type=Application diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java index d7b2431c1d3..7563dfa0ed3 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java @@ -25,6 +25,8 @@ package jdk.jpackage.internal; import static java.util.stream.Collectors.joining; +import static jdk.jpackage.internal.MacPackagingPipeline.APPLICATION_LAYOUT; +import static jdk.jpackage.internal.model.MacPackage.RUNTIME_BUNDLE_LAYOUT; import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; import java.io.IOException; @@ -44,13 +46,14 @@ import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.MacApplication; +import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.function.ExceptionBox; final class AppImageSigner { - static Consumer createSigner(MacApplication app, CodesignConfig signingCfg) { + static Consumer createSigner(MacApplication app, CodesignConfig signingCfg) { return toConsumer(appImage -> { try { new AppImageSigner(Codesigners.create(signingCfg)).sign(app, appImage); @@ -67,12 +70,12 @@ final class AppImageSigner { private static final class SignFilter implements Predicate { - SignFilter(Application app, Path appImage) { + SignFilter(Application app, MacBundle appImage) { Objects.requireNonNull(appImage); // Don't explicitly sign main launcher. It will be implicitly signed when the bundle is signed. otherExcludePaths = app.asApplicationLayout().map(appLayout -> { - return appLayout.resolveAt(appImage); + return appLayout.resolveAt(appImage.root()); }).map(ApplicationLayout::launchersDirectory).flatMap(launchersDir -> { return app.mainLauncher().map(Launcher::executableNameWithSuffix).map(launchersDir::resolve); }).map(Set::of).orElseGet(Set::of); @@ -98,11 +101,16 @@ final class AppImageSigner { private final Set otherExcludePaths; } - private void sign(MacApplication app, Path appImage) throws CodesignException, IOException { + private void sign(MacApplication app, MacBundle appImage) throws CodesignException, IOException { + if (!appImage.isValid()) { + throw new IllegalArgumentException(); + } + + app = normalizeAppImageLayout(app); final var fileFilter = new SignFilter(app, appImage); - try (var content = Files.walk(appImage)) { + try (var content = Files.walk(appImage.root())) { content.filter(fileFilter).forEach(toConsumer(path -> { final var origPerms = ensureCanWrite(path); try { @@ -118,10 +126,10 @@ final class AppImageSigner { // Sign runtime root directory if present app.asApplicationLayout().map(appLayout -> { - return appLayout.resolveAt(appImage); + return appLayout.resolveAt(appImage.root()); }).map(MacApplicationLayout.class::cast).map(MacApplicationLayout::runtimeRootDirectory).ifPresent(codesigners); - final var frameworkPath = appImage.resolve("Contents/Frameworks"); + final var frameworkPath = appImage.contentsDir().resolve("Frameworks"); if (Files.isDirectory(frameworkPath)) { try (var content = Files.list(frameworkPath)) { content.forEach(toConsumer(path -> { @@ -131,7 +139,7 @@ final class AppImageSigner { } // Sign the app image itself - codesigners.accept(appImage); + codesigners.accept(appImage.root()); } private static Set ensureCanWrite(Path path) { @@ -235,5 +243,19 @@ final class AppImageSigner { } } + private static MacApplication normalizeAppImageLayout(MacApplication app) { + switch (app.imageLayout()) { + case MacApplicationLayout macLayout -> { + return MacApplicationBuilder.overrideAppImageLayout(app, APPLICATION_LAYOUT); + } + case RuntimeLayout macLayout -> { + return MacApplicationBuilder.overrideAppImageLayout(app, RUNTIME_BUNDLE_LAYOUT); + } + default -> { + throw new IllegalArgumentException(); + } + } + } + private final Codesigners codesigners; } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java index 28d91156059..cce35ece117 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacAppBundler.java @@ -42,11 +42,11 @@ public class MacAppBundler extends AppImageBundler { final BuildEnv env; if (StandardBundlerParam.hasPredefinedAppImage(params)) { - env = BuildEnvFromParams.BUILD_ENV.fetchFrom(params); + env = MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params); final var pkg = MacPackagingPipeline.createSignAppImagePackage(app, env); MacPackagingPipeline.build(Optional.of(pkg)).create().execute(env, pkg, output); } else { - env = BuildEnv.withAppImageDir(BuildEnvFromParams.BUILD_ENV.fetchFrom(params), output); + env = BuildEnv.withAppImageDir(MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params), output); MacPackagingPipeline.build(Optional.empty()) .excludeDirFromCopying(output.getParent()) .excludeDirFromCopying(OUTPUT_DIR.fetchFrom(params)).create().execute(env, app); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java index ca05be519ff..fc1dd97d9ab 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java @@ -34,6 +34,7 @@ import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.MacApplication; import jdk.jpackage.internal.model.MacApplicationMixin; +import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.AppImageSigningConfig; final class MacApplicationBuilder { @@ -95,12 +96,28 @@ final class MacApplicationBuilder { validateAppVersion(app); - final var mixin = new MacApplicationMixin.Stub(validatedIcon(), validatedBundleName(), - validatedBundleIdentifier(), validatedCategory(), appStore, createSigningConfig()); + final var mixin = new MacApplicationMixin.Stub( + validatedIcon(), + validatedBundleName(), + validatedBundleIdentifier(), + validatedCategory(), + appStore, + createSigningConfig()); return MacApplication.create(app, mixin); } + static MacApplication overrideAppImageLayout(MacApplication app, AppImageLayout appImageLayout) { + final var mixin = new MacApplicationMixin.Stub( + app.icon(), + app.bundleName(), + app.bundleIdentifier(), + app.category(), + app.appStore(), + app.signingConfig()); + return MacApplication.create(ApplicationBuilder.overrideAppImageLayout(app, appImageLayout), mixin); + } + static boolean isValidBundleIdentifier(String id) { for (int i = 0; i < id.length(); i++) { char a = id.charAt(i); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayout.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayout.java index 971548ea4f5..7c90134e288 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayout.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayout.java @@ -24,9 +24,10 @@ */ package jdk.jpackage.internal; -import static jdk.jpackage.internal.util.PathUtils.resolveNullablePath; +import static jdk.jpackage.internal.util.PathUtils.mapNullablePath; import java.nio.file.Path; +import java.util.function.UnaryOperator; import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.util.CompositeProxy; @@ -35,12 +36,32 @@ interface MacApplicationLayout extends ApplicationLayout, MacApplicationLayoutMi static MacApplicationLayout create(ApplicationLayout layout, Path runtimeRootDir) { return CompositeProxy.build() .invokeTunnel(CompositeProxyTunnel.INSTANCE) - .create(MacApplicationLayout.class, layout, new MacApplicationLayoutMixin.Stub(runtimeRootDir)); + .create(MacApplicationLayout.class, layout, + new MacApplicationLayoutMixin.Stub(runtimeRootDir)); } @Override default MacApplicationLayout resolveAt(Path root) { - return create(ApplicationLayout.super.resolveAt(root), - resolveNullablePath(root, runtimeRootDirectory())); + return (MacApplicationLayout)ApplicationLayout.super.resolveAt(root); } + + @Override + default MacApplicationLayout unresolve() { + return (MacApplicationLayout)ApplicationLayout.super.unresolve(); + } + + @Override + default MacApplicationLayout resetRootDirectory() { + if (isResolved()) { + return create(ApplicationLayout.super.resetRootDirectory(), runtimeRootDirectory()); + } else { + return this; + } + } + + @Override + default MacApplicationLayout map(UnaryOperator mapper) { + return create(ApplicationLayout.super.map(mapper), mapNullablePath(mapper, runtimeRootDirectory())); + } + } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayoutMixin.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayoutMixin.java index b6342b173dc..0acc853f58b 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayoutMixin.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationLayoutMixin.java @@ -26,12 +26,15 @@ package jdk.jpackage.internal; import java.nio.file.Path; -// Must be publc to allow access from AppImageLayout.toPathGroup() +// Must be public to allow access from AppImageLayout.toPathGroup() public interface MacApplicationLayoutMixin { /** - * Path to the root Java runtime directory in the application image. + * Returns path to the root Java runtime directory in the application image. + *

    * The root Java runtime directory should have "Contents/Home" subdirectory. + * + * @return the path to the root Java runtime directory in the application image */ Path runtimeRootDirectory(); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageDesc.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBuildEnvFromParams.java similarity index 59% rename from src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageDesc.java rename to src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBuildEnvFromParams.java index fafcb7777a6..31759c8c529 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageDesc.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBuildEnvFromParams.java @@ -22,35 +22,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package jdk.jpackage.internal; -import java.nio.file.Path; -import java.util.Objects; -import java.util.Optional; -import jdk.jpackage.internal.model.AppImageLayout; -import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.MacPackage; -record AppImageDesc(AppImageLayout appImageLayout, Path path) { +final class MacBuildEnvFromParams { - AppImageDesc { - Objects.requireNonNull(appImageLayout); - Objects.requireNonNull(path); - } - - AppImageLayout resolvedAppImagelayout() { - return appImageLayout.resolveAt(path); - } - - Optional asResolvedApplicationLayout() { - return asApplicationLayout().map(v -> v.resolveAt(path)); - } - - Optional asApplicationLayout() { - if (appImageLayout instanceof ApplicationLayout layout) { - return Optional.of(layout); - } else { - return Optional.empty(); - } - } + static final BundlerParamInfo BUILD_ENV = BundlerParamInfo.createBundlerParam(BuildEnv.class, params -> { + return BuildEnvFromParams.create(params, MacPackagingPipeline.APPLICATION_LAYOUT::resolveAt, MacPackage::guessRuntimeLayout); + }); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java index af07a1145dc..f485653cf80 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java @@ -28,6 +28,7 @@ package jdk.jpackage.internal; import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; +import java.util.Optional; import jdk.jpackage.internal.model.AppImageLayout; /** @@ -73,7 +74,19 @@ record MacBundle(Path root) { return new MacBundle(dir).isValid(); } - static MacBundle fromAppImageLayout(AppImageLayout layout) { - return new MacBundle(layout.rootDirectory()); + static Optional fromAppImageLayout(AppImageLayout layout) { + final var root = layout.rootDirectory(); + final var bundleSubdir = root.relativize(layout.runtimeDirectory()); + final var contentsDirname = Path.of("Contents"); + var bundleRoot = root; + for (int i = 0; i != bundleSubdir.getNameCount(); i++) { + var nameComponent = bundleSubdir.getName(i); + if (contentsDirname.equals(nameComponent)) { + return Optional.of(new MacBundle(bundleRoot)); + } else { + bundleRoot = bundleRoot.resolve(nameComponent); + } + } + return Optional.empty(); } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java index 56aacf6e44a..d2c72765d7a 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgBundler.java @@ -71,7 +71,7 @@ public class MacDmgBundler extends MacBaseInstallerBundler { Path outputParentDir) throws PackagerException { final var pkg = MacFromParams.DMG_PACKAGE.fetchFrom(params); - var env = BuildEnvFromParams.BUILD_ENV.fetchFrom(params); + var env = MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params); final var packager = MacDmgPackager.build().outputDir(outputParentDir).pkg(pkg).env(env); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java index d453bb59184..c2b9c25f327 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java @@ -53,7 +53,6 @@ final class MacDmgPackageBuilder { } MacDmgPackage create() throws ConfigException { - final var superPkgBuilder = pkgBuilder.pkgBuilder(); final var pkg = pkgBuilder.create(); return MacDmgPackage.create(pkg, new MacDmgPackageMixin.Stub( diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java index 754d09a7156..4be2f9859c8 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java @@ -39,13 +39,12 @@ import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE_FI import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE; import static jdk.jpackage.internal.StandardBundlerParam.SIGN_BUNDLE; import static jdk.jpackage.internal.StandardBundlerParam.hasPredefinedAppImage; -import static jdk.jpackage.internal.model.MacPackage.RUNTIME_PACKAGE_LAYOUT; +import static jdk.jpackage.internal.model.MacPackage.RUNTIME_BUNDLE_LAYOUT; import static jdk.jpackage.internal.model.StandardPackageType.MAC_DMG; import static jdk.jpackage.internal.model.StandardPackageType.MAC_PKG; import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.List; @@ -64,6 +63,7 @@ import jdk.jpackage.internal.model.MacApplication; import jdk.jpackage.internal.model.MacDmgPackage; import jdk.jpackage.internal.model.MacFileAssociation; import jdk.jpackage.internal.model.MacLauncher; +import jdk.jpackage.internal.model.MacPackage; import jdk.jpackage.internal.model.MacPkgPackage; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.RuntimeLayout; @@ -75,20 +75,16 @@ final class MacFromParams { private static MacApplication createMacApplication( Map params) throws ConfigException, IOException { - final var predefinedRuntimeLayout = PREDEFINED_RUNTIME_IMAGE.findIn(params).map(predefinedRuntimeImage -> { - if (Files.isDirectory(RUNTIME_PACKAGE_LAYOUT.resolveAt(predefinedRuntimeImage).runtimeDirectory())) { - return RUNTIME_PACKAGE_LAYOUT; - } else { - return RuntimeLayout.DEFAULT; - } - }); + final var predefinedRuntimeLayout = PREDEFINED_RUNTIME_IMAGE.findIn(params) + .map(MacPackage::guessRuntimeLayout) + .map(RuntimeLayout::unresolve); final var launcherFromParams = new LauncherFromParams(Optional.of(MacFromParams::createMacFa)); final var superAppBuilder = createApplicationBuilder(params, toFunction(launcherParams -> { var launcher = launcherFromParams.create(launcherParams); return MacLauncher.create(launcher); - }), APPLICATION_LAYOUT, predefinedRuntimeLayout); + }), APPLICATION_LAYOUT, RUNTIME_BUNDLE_LAYOUT, predefinedRuntimeLayout); if (hasPredefinedAppImage(params)) { // Set the main launcher start up info. diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java index a16194a6260..cf5c6a934f7 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java @@ -24,8 +24,11 @@ */ package jdk.jpackage.internal; +import static jdk.jpackage.internal.MacPackagingPipeline.LayoutUtils.packagerLayout; + import java.util.Objects; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.MacApplication; import jdk.jpackage.internal.model.MacPackage; import jdk.jpackage.internal.model.MacPackageMixin; @@ -45,7 +48,15 @@ final class MacPackageBuilder { } MacPackage create() throws ConfigException { - final var pkg = pkgBuilder.create(); + + final var app = (MacApplication)pkgBuilder.app(); + + var pkg = pkgBuilder.create(); + + pkgBuilder.app(MacApplicationBuilder.overrideAppImageLayout(app, packagerLayout(pkg))) + .installedPackageLayout(pkg.installedPackageLayout()); + + pkg = pkgBuilder.create(); return MacPackage.create(pkg, new MacPackageMixin.Stub(pkg.predefinedAppImage().map(v -> predefinedAppImageSigned))); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java index 556efdd0fd3..a6ed164d9f7 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java @@ -39,6 +39,7 @@ import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import java.io.IOException; import java.io.StringWriter; +import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.LinkOption; @@ -49,10 +50,12 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.function.UnaryOperator; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import jdk.jpackage.internal.PackagingPipeline.AppImageBuildEnv; +import jdk.jpackage.internal.PackagingPipeline.AppImageTaskAction; import jdk.jpackage.internal.PackagingPipeline.ApplicationImageTaskAction; import jdk.jpackage.internal.PackagingPipeline.BuildApplicationTaskID; import jdk.jpackage.internal.PackagingPipeline.CopyAppImageTaskID; @@ -98,21 +101,10 @@ final class MacPackagingPipeline { COPY_SIGN } - static AppImageLayout packagingLayout(Package pkg) { - return pkg.appImageLayout().resolveAt(pkg.relativeInstallDir().getFileName()); - } - static PackagingPipeline.Builder build(Optional pkg) { final var builder = PackagingPipeline.buildStandard() - .appContextMapper(appContext -> { - return new TaskContextProxy(appContext, true, false); - }) - .pkgContextMapper(appContext -> { - final var isRuntimeInstaller = pkg.map(Package::isRuntimeInstaller).orElse(false); - final var withPredefinedAppImage = pkg.flatMap(Package::predefinedAppImage).isPresent(); - return new TaskContextProxy(appContext, false, isRuntimeInstaller || withPredefinedAppImage); - }) - .appImageLayoutForPackaging(MacPackagingPipeline::packagingLayout) + .contextMapper(pkg.map(MacPackagingPipeline::mapPackageTaskContext) + .orElseGet(MacPackagingPipeline::mapAppTaskContext)) .task(PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT) .packageAction(MacPackagingPipeline::runPostAppImageUserScript).add() .task(CopyAppImageTaskID.COPY) @@ -156,13 +148,13 @@ final class MacPackagingPipeline { .addDependent(BuildApplicationTaskID.CONTENT).add(); builder.task(MacBuildApplicationTaskID.SIGN) - .appImageAction(MacPackagingPipeline::sign) + .appImageAction(LayoutUtils.withBundleLayout(MacPackagingPipeline::sign)) .addDependencies(builder.taskGraphSnapshot().getAllTailsOf(PrimaryTaskID.BUILD_APPLICATION_IMAGE)) .addDependent(PrimaryTaskID.BUILD_APPLICATION_IMAGE) .add(); builder.task(MacCopyAppImageTaskID.COPY_SIGN) - .appImageAction(MacPackagingPipeline::sign) + .appImageAction(LayoutUtils.withBundleLayout(MacPackagingPipeline::sign)) .addDependencies(builder.taskGraphSnapshot().getAllTailsOf(PrimaryTaskID.COPY_APP_IMAGE)) .addDependent(PrimaryTaskID.COPY_APP_IMAGE) .add(); @@ -182,7 +174,6 @@ final class MacPackagingPipeline { disabledTasks.add(PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT); builder.task(MacCopyAppImageTaskID.REPLACE_APP_IMAGE_FILE) .applicationAction(createWriteAppImageFileAction()).add(); - builder.appImageLayoutForPackaging(Package::appImageLayout); } else if (p.isRuntimeInstaller()) { builder.task(MacCopyAppImageTaskID.COPY_RUNTIME_JLILIB) @@ -236,23 +227,83 @@ final class MacPackagingPipeline { }).get(); } - private static void copyAppImage(MacPackage pkg, AppImageDesc srcAppImage, - AppImageDesc dstAppImage) throws IOException { + static final class LayoutUtils { + /** + * Returns unresolved app image layout for the specified package for use with + * the signing function defined in {@link MacPackagingPipeline} class and + * {@link MacPkgPackager} and {@link MacDmgPackager} packagers. + *

    + * Paths of the result app image layout will start with the bundle name. E.g.: + * for a package with relative installation directory set to + * {@code "Applications/Acme/MyApp.app"} and the "launchers" directory of an + * application layout set to {@code "Contents/MacOS"}, the result application + * layout object will be such that the value of its "launchers" directory will + * be {@code "MyApp.app/Contents/MacOS"}. The root directory of the result app + * image layout will be an empty path ({@link Path.of("")}), i.e. the app image + * layout will be unresolved. + * + * @param pkg the package + * @return the unresolved app image layout for the specified package suitable + * for the use with macosx packaging pipeline and packagers + */ + static AppImageLayout packagerLayout(Package pkg) { + return pkg.appImageLayout().resolveAt(pkg.relativeInstallDir().getFileName()).resetRootDirectory(); + } + + static AppImageBuildEnv fromPackagerLayout(AppImageBuildEnv cfg) { + + var bundleDirectoryName = cfg.envLayout().runtimeDirectory().getName(0); + var bundleLayout = cfg.envLayout().map(bundleDirectoryName::relativize).resetRootDirectory(); + var bundleRoot = cfg.env().appImageDir().resolve(bundleDirectoryName); + var app = MacApplicationBuilder.overrideAppImageLayout(cfg.app(), bundleLayout); + var env = BuildEnv.withAppImageLayout(cfg.env(), bundleLayout.resolveAt(bundleRoot)); + + return new AppImageBuildEnv<>(env, app); + } + + static AppImageTaskAction withBundleLayout(AppImageTaskAction action) { + return new AppImageTaskAction<>() { + @Override + public void execute(AppImageBuildEnv env) throws IOException, PackagerException { + if (!env.envLayout().runtimeDirectory().getName(0).equals(Path.of("Contents"))) { + env = LayoutUtils.fromPackagerLayout(env); + } + action.execute(env); + } + }; + } + } + + private static void copyAppImage(MacPackage pkg, AppImageLayout srcAppImage, + AppImageLayout dstAppImage) throws IOException { boolean predefinedAppImageSigned = pkg.predefinedAppImageSigned().orElse(false); - var inputRootDirectory = srcAppImage.resolvedAppImagelayout().rootDirectory(); + final Optional srcMacBundle; + if (pkg.isRuntimeInstaller()) { + srcMacBundle = MacBundle.fromAppImageLayout(srcAppImage); + } else { + srcMacBundle = Optional.empty(); + } - if (pkg.isRuntimeInstaller() && MacBundle.isDirectoryMacBundle(inputRootDirectory)) { + srcMacBundle.ifPresentOrElse(inputBundle -> { // Building runtime package from the input runtime bundle. // Copy the input bundle verbatim. - FileUtils.copyRecursive( - inputRootDirectory, - dstAppImage.resolvedAppImagelayout().rootDirectory(), - LinkOption.NOFOLLOW_LINKS); - } else { - PackagingPipeline.copyAppImage(srcAppImage, dstAppImage, !predefinedAppImageSigned); - } + try { + FileUtils.copyRecursive( + inputBundle.root(), + MacBundle.fromAppImageLayout(dstAppImage).orElseThrow().root(), + LinkOption.NOFOLLOW_LINKS); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }, () -> { + try { + PackagingPipeline.copyAppImage(srcAppImage, dstAppImage, !predefinedAppImageSigned); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); } private static void copyJliLib( @@ -272,10 +323,23 @@ final class MacPackagingPipeline { } } - private static void runPostAppImageUserScript(PackageBuildEnv env) throws IOException { - PackagingPipeline.runPostAppImageUserScript(new PackageBuildEnv<>( - BuildEnv.withAppImageDir(env.env(), env.env().appImageDir().resolve(env.envLayout().rootDirectory())), - env.pkg(), env.pkg().appImageLayout(), env.outputDir())); + private static void runPostAppImageUserScript(PackageBuildEnv cfg) throws IOException { + var appCfg = LayoutUtils.fromPackagerLayout( + new AppImageBuildEnv<>(cfg.env(), (MacApplication)cfg.pkg().app())); + + var pkg = cfg.pkg(); pkg = new Package.Stub( + appCfg.app(), + pkg.type(), + pkg.packageName(), + pkg.description(), + pkg.version(), + pkg.aboutURL(), + pkg.licenseFile(), + pkg.predefinedAppImage(), + pkg.installedPackageLayout(), + pkg.relativeInstallDir()); + + PackagingPipeline.runPostAppImageUserScript(new PackageBuildEnv<>(appCfg.env(), pkg, cfg.outputDir())); } private static void writePackageFile(PackageBuildEnv env) throws IOException { @@ -331,7 +395,7 @@ final class MacPackagingPipeline { final var app = env.app(); - final var infoPlistFile = MacBundle.fromAppImageLayout(env.resolvedLayout()).infoPlistFile(); + final var infoPlistFile = MacBundle.fromAppImageLayout(env.resolvedLayout()).orElseThrow().infoPlistFile(); Log.verbose(I18N.format("message.preparing-info-plist", PathUtils.normalizedAbsolutePathString(infoPlistFile))); @@ -384,8 +448,7 @@ final class MacPackagingPipeline { } final Runnable signAction = () -> { - final var appImageDir = env.resolvedLayout().rootDirectory(); - AppImageSigner.createSigner(app, codesignConfigBuilder.create()).accept(appImageDir); + AppImageSigner.createSigner(app, codesignConfigBuilder.create()).accept(MacBundle.fromAppImageLayout(env.resolvedLayout()).orElseThrow()); }; app.signingConfig().flatMap(AppImageSigningConfig::keychain).map(Keychain::new).ifPresentOrElse(keychain -> { @@ -467,7 +530,7 @@ final class MacPackagingPipeline { private static MacBundle runtimeBundle(AppImageBuildEnv env) { if (env.app().isRuntime()) { - return new MacBundle(env.resolvedLayout().rootDirectory()); + return MacBundle.fromAppImageLayout(env.resolvedLayout()).orElseThrow(); } else { return new MacBundle(((MacApplicationLayout)env.resolvedLayout()).runtimeRootDirectory()); } @@ -498,6 +561,21 @@ final class MacPackagingPipeline { } } + private static UnaryOperator mapAppTaskContext() { + return ctx -> { + return new TaskContextProxy(ctx, true, false); + }; + } + + private static UnaryOperator mapPackageTaskContext(Package pkg) { + return ctx -> { + final var isRuntimeInstaller = pkg.isRuntimeInstaller(); + final var withPredefinedAppImage = pkg.predefinedAppImage().isPresent(); + return new TaskContextProxy(ctx, false, isRuntimeInstaller || withPredefinedAppImage); + }; + } + + private record TaskContextProxy(TaskContext delegate, boolean forApp, boolean copyAppImage) implements TaskContext { @Override diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java index f785cc49f67..bc19e7c4a1a 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgBundler.java @@ -73,7 +73,7 @@ public class MacPkgBundler extends MacBaseInstallerBundler { Path outputParentDir) throws PackagerException { final var pkg = MacFromParams.PKG_PACKAGE.fetchFrom(params); - var env = BuildEnvFromParams.BUILD_ENV.fetchFrom(params); + var env = MacBuildEnvFromParams.BUILD_ENV.fetchFrom(params); final var packager = MacPkgPackager.build().outputDir(outputParentDir).pkg(pkg).env(env); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacPackage.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacPackage.java index 083fc225581..4d38cf4ed70 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacPackage.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacPackage.java @@ -24,21 +24,14 @@ */ package jdk.jpackage.internal.model; +import java.nio.file.Files; import java.nio.file.Path; import jdk.jpackage.internal.util.CompositeProxy; public interface MacPackage extends Package, MacPackageMixin { - MacApplication app(); - @Override - default AppImageLayout appImageLayout() { - if (isRuntimeInstaller()) { - return RUNTIME_PACKAGE_LAYOUT; - } else { - return Package.super.appImageLayout(); - } - } + MacApplication app(); default Path installDir() { return Path.of("/").resolve(relativeInstallDir()); @@ -48,5 +41,19 @@ public interface MacPackage extends Package, MacPackageMixin { return CompositeProxy.create(MacPackage.class, pkg, mixin); } - public static final RuntimeLayout RUNTIME_PACKAGE_LAYOUT = RuntimeLayout.create(Path.of("Contents/Home")); + /** + * Guesses layout of a runtime image at the given path. + * + * @param path the path to a runtime image + * @return the runtime image layout resolved at the given path + */ + public static RuntimeLayout guessRuntimeLayout(Path path) { + if (Files.isDirectory(RUNTIME_BUNDLE_LAYOUT.resolveAt(path).runtimeDirectory())) { + return RUNTIME_BUNDLE_LAYOUT.resolveAt(path); + } else { + return RuntimeLayout.DEFAULT.resolveAt(path); + } + } + + public static final RuntimeLayout RUNTIME_BUNDLE_LAYOUT = RuntimeLayout.create(Path.of("Contents/Home")); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java index 09bf04372ce..141a1b5155f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java @@ -153,9 +153,30 @@ final class ApplicationBuilder { } static Launcher overrideLauncherStartupInfo(Launcher launcher, LauncherStartupInfo startupInfo) { - return new Launcher.Stub(launcher.name(), Optional.of(startupInfo), - launcher.fileAssociations(), launcher.isService(), launcher.description(), - launcher.icon(), launcher.defaultIconResourceName(), launcher.extraAppImageFileData()); + return new Launcher.Stub( + launcher.name(), + Optional.of(startupInfo), + launcher.fileAssociations(), + launcher.isService(), + launcher.description(), + launcher.icon(), + launcher.defaultIconResourceName(), + launcher.extraAppImageFileData()); + } + + static Application overrideAppImageLayout(Application app, AppImageLayout appImageLayout) { + return new Application.Stub( + app.name(), + app.description(), + app.version(), + app.vendor(), + app.copyright(), + app.srcDir(), + app.contentDirs(), + Objects.requireNonNull(appImageLayout), + app.runtimeBuilder(), + app.launchers(), + app.extraAppImageFileData()); } record MainLauncherStartupInfo(String qualifiedClassName) implements LauncherStartupInfo { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java index e2686c17128..7d3250ff7b9 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java @@ -84,7 +84,7 @@ final class ApplicationImageUtils { static ApplicationImageTaskAction createWriteRuntimeAction() { return env -> { - env.app().runtimeBuilder().orElseThrow().createRuntime(env.resolvedLayout()); + env.app().runtimeBuilder().orElseThrow().create(env.resolvedLayout()); }; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java index 4700231a162..f9a5429a8bf 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java @@ -37,6 +37,7 @@ import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.ResourceBundle; @@ -348,16 +349,13 @@ public class Arguments { WIN_UPDATE_URL ("win-update-url", OptionCategories.PLATFORM_WIN), - WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN, () -> { - setOptionValue("win-menu", true); - }), + WIN_MENU_HINT ("win-menu", OptionCategories.PLATFORM_WIN, + createArgumentWithOptionalValueAction("win-menu")), WIN_MENU_GROUP ("win-menu-group", OptionCategories.PLATFORM_WIN), - WIN_SHORTCUT_HINT ("win-shortcut", - OptionCategories.PLATFORM_WIN, () -> { - setOptionValue("win-shortcut", true); - }), + WIN_SHORTCUT_HINT ("win-shortcut", OptionCategories.PLATFORM_WIN, + createArgumentWithOptionalValueAction("win-shortcut")), WIN_SHORTCUT_PROMPT ("win-shortcut-prompt", OptionCategories.PLATFORM_WIN, () -> { @@ -396,10 +394,8 @@ public class Arguments { LINUX_PACKAGE_DEPENDENCIES ("linux-package-deps", OptionCategories.PLATFORM_LINUX), - LINUX_SHORTCUT_HINT ("linux-shortcut", - OptionCategories.PLATFORM_LINUX, () -> { - setOptionValue("linux-shortcut", true); - }), + LINUX_SHORTCUT_HINT ("linux-shortcut", OptionCategories.PLATFORM_LINUX, + createArgumentWithOptionalValueAction("linux-shortcut")), LINUX_MENU_GROUP ("linux-menu-group", OptionCategories.PLATFORM_LINUX); @@ -478,9 +474,32 @@ public class Arguments { context().pos++; } + private static void prevArg() { + Objects.checkIndex(context().pos, context().argList.size()); + context().pos--; + } + private static boolean hasNextArg() { return context().pos < context().argList.size(); } + + private static Runnable createArgumentWithOptionalValueAction(String option) { + Objects.requireNonNull(option); + return () -> { + nextArg(); + if (hasNextArg()) { + var value = getArg(); + if (value.startsWith("-")) { + prevArg(); + setOptionValue(option, true); + } else { + setOptionValue(option, value); + } + } else { + setOptionValue(option, true); + } + }; + } } enum OptionCategories { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnv.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnv.java index 1d2a43bf5d6..527f747b229 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnv.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnv.java @@ -27,57 +27,111 @@ package jdk.jpackage.internal; import java.nio.file.Path; import java.util.Objects; import java.util.Optional; +import jdk.jpackage.internal.model.AppImageLayout; +import jdk.jpackage.internal.model.ApplicationLayout; +/** + * Build environment. + */ interface BuildEnv { + /** + * Returns root directory for intermediate build files. + * + * @return the root directory for intermediate build files + */ Path buildRoot(); + /** + * Returns true if the build should be verbose output. + * + * @return true if the build should be verbose output + */ boolean verbose(); + /** + * Returns the path of the resource directory or an empty {@link Optional} + * instance if none is configured with the build. + * + * @return the path of the resource directory or an empty {@link Optional} + * instance if non is configured with the build + */ Optional resourceDir(); /** - * Returns path to application image directory. + * Returns the path of the app image directory of this build. * - * The return value is supposed to be used as a parameter for - * ApplicationLayout#resolveAt function. + * @return the path of the app image directory of this build */ default Path appImageDir() { - return buildRoot().resolve("image"); + return appImageLayout().rootDirectory(); } + /** + * Returns resolved app image layout of the app image directory. The return + * layout is resolved at {@link #appImageDir()} path. + * + * @return the resolved app image layout of the app image directory + */ + AppImageLayout appImageLayout(); + + default Optional asApplicationLayout() { + if (appImageLayout() instanceof ApplicationLayout layout) { + return Optional.of(layout); + } else { + return Optional.empty(); + } + } + + /** + * Returns a path to a directory for intermediate configuration files. + * @return the path to the directory for intermediate configuration files + */ default Path configDir() { return buildRoot().resolve("config"); } + /** + * Creates an {@link OverridableResource} instance for the given resource name. + * + * @param defaultName the resource name + * @return the {@link OverridableResource} instance wrapping a resource with the + * given name + */ OverridableResource createResource(String defaultName); static BuildEnv withAppImageDir(BuildEnv env, Path appImageDir) { return ((Internal.DefaultBuildEnv)env).copyWithAppImageDir(appImageDir); } - static BuildEnv create(Path buildRoot, Optional resourceDir, boolean verbose, Class resourceLocator) { - return new Internal.DefaultBuildEnv(buildRoot, resourceDir, verbose, resourceLocator, Optional.empty()); + static BuildEnv withAppImageLayout(BuildEnv env, AppImageLayout appImageLayout) { + return ((Internal.DefaultBuildEnv)env).copyWithAppImageLayout(appImageLayout); + } + + static BuildEnv create(Path buildRoot, Optional resourceDir, boolean verbose, + Class resourceLocator, AppImageLayout appImageLayout) { + return new Internal.DefaultBuildEnv(buildRoot, resourceDir, verbose, + resourceLocator, appImageLayout); } static final class Internal { - private static record DefaultBuildEnv(Path buildRoot, Optional resourceDir, - boolean verbose, Class resourceLocator, Optional optAppImageDir) implements BuildEnv { + private record DefaultBuildEnv(Path buildRoot, Optional resourceDir, + boolean verbose, Class resourceLocator, + AppImageLayout appImageLayout) implements BuildEnv { DefaultBuildEnv { Objects.requireNonNull(buildRoot); Objects.requireNonNull(resourceDir); Objects.requireNonNull(resourceLocator); - Objects.requireNonNull(optAppImageDir); + Objects.requireNonNull(appImageLayout); } - DefaultBuildEnv copyWithAppImageDir(Path appImageDir) { - return new DefaultBuildEnv(buildRoot, resourceDir, verbose, resourceLocator, Optional.of(appImageDir)); + DefaultBuildEnv copyWithAppImageDir(Path v) { + return copyWithAppImageLayout(appImageLayout.unresolve().resolveAt(v)); } - @Override - public Path appImageDir() { - return optAppImageDir.orElseGet(BuildEnv.super::appImageDir); + DefaultBuildEnv copyWithAppImageLayout(AppImageLayout v) { + return new DefaultBuildEnv(buildRoot, resourceDir, verbose, resourceLocator, v); } @Override diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvBuilder.java index 9974e407ca7..ba70913eca9 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvBuilder.java @@ -29,8 +29,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; import java.util.Optional; +import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.resources.ResourceLocator; final class BuildEnvBuilder { @@ -40,8 +42,6 @@ final class BuildEnvBuilder { } BuildEnv create() throws ConfigException { - Objects.requireNonNull(appImageDir); - var exceptionBuilder = I18N.buildConfigException("ERR_BuildRootInvalid", root); if (Files.isDirectory(root)) { try (var rootDirContents = Files.list(root)) { @@ -57,8 +57,8 @@ final class BuildEnvBuilder { throw exceptionBuilder.create(); } - return BuildEnv.withAppImageDir(BuildEnv.create(root, Optional.ofNullable(resourceDir), - verbose, ResourceLocator.class), appImageDir); + return BuildEnv.create(root, Optional.ofNullable(resourceDir), verbose, + ResourceLocator.class, resolvedAppImageLayout()); } BuildEnvBuilder verbose(boolean v) { @@ -76,21 +76,34 @@ final class BuildEnvBuilder { return this; } - BuildEnvBuilder appImageDirFor(Application app) { - appImageDir = defaultAppImageDir(root).resolve(app.appImageDirName()); + BuildEnvBuilder appImageLayout(AppImageLayout v) { + appImageLayout = v; return this; } - BuildEnvBuilder appImageDirForPackage() { - appImageDir = defaultAppImageDir(root); + BuildEnvBuilder appImageDirFor(Application app) { + appImageDir = defaultAppImageDir(root).resolve(app.appImageDirName()); + appImageLayout = app.imageLayout(); return this; } + BuildEnvBuilder appImageDirFor(Package pkg) { + appImageDir = defaultAppImageDir(root); + appImageLayout = pkg.appImageLayout(); + return this; + } + + private AppImageLayout resolvedAppImageLayout() { + Objects.requireNonNull(appImageLayout); + return Optional.ofNullable(appImageDir).map(appImageLayout.unresolve()::resolveAt).orElse(appImageLayout); + } + private static Path defaultAppImageDir(Path root) { return root.resolve("image"); } private Path appImageDir; + private AppImageLayout appImageLayout; private Path resourceDir; private boolean verbose; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java index 24ae249297e..6fb1a342fbf 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/BuildEnvFromParams.java @@ -24,18 +24,25 @@ */ package jdk.jpackage.internal; -import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE; +import static jdk.jpackage.internal.ApplicationLayoutUtils.PLATFORM_APPLICATION_LAYOUT; import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_APP_IMAGE; +import static jdk.jpackage.internal.StandardBundlerParam.PREDEFINED_RUNTIME_IMAGE; import static jdk.jpackage.internal.StandardBundlerParam.RESOURCE_DIR; import static jdk.jpackage.internal.StandardBundlerParam.TEMP_ROOT; import static jdk.jpackage.internal.StandardBundlerParam.VERBOSE; +import java.nio.file.Path; import java.util.Map; +import java.util.function.Function; +import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.RuntimeLayout; final class BuildEnvFromParams { - static BuildEnv create(Map params) throws ConfigException { + static BuildEnv create(Map params, + Function predefinedAppImageLayoutProvider, + Function predefinedRuntimeImageLayoutProvider) throws ConfigException { final var builder = new BuildEnvBuilder(TEMP_ROOT.fetchFrom(params)); @@ -47,11 +54,13 @@ final class BuildEnvFromParams { final var pkg = FromParams.getCurrentPackage(params); if (app.isRuntime()) { - PREDEFINED_RUNTIME_IMAGE.copyInto(params, builder::appImageDir); + var layout = predefinedRuntimeImageLayoutProvider.apply(PREDEFINED_RUNTIME_IMAGE.findIn(params).orElseThrow()); + builder.appImageLayout(layout); } else if (StandardBundlerParam.hasPredefinedAppImage(params)) { - PREDEFINED_APP_IMAGE.copyInto(params, builder::appImageDir); + var layout = predefinedAppImageLayoutProvider.apply(PREDEFINED_APP_IMAGE.findIn(params).orElseThrow()); + builder.appImageLayout(layout); } else if (pkg.isPresent()) { - builder.appImageDirForPackage(); + builder.appImageDirFor(pkg.orElseThrow()); } else { builder.appImageDirFor(app); } @@ -59,6 +68,7 @@ final class BuildEnvFromParams { return builder.create(); } - static final BundlerParamInfo BUILD_ENV = BundlerParamInfo.createBundlerParam( - BuildEnv.class, BuildEnvFromParams::create); + static final BundlerParamInfo BUILD_ENV = BundlerParamInfo.createBundlerParam(BuildEnv.class, params -> { + return create(params, PLATFORM_APPLICATION_LAYOUT::resolveAt, RuntimeLayout.DEFAULT::resolveAt); + }); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java index 34818fafc94..ec9718c5d24 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromParams.java @@ -52,6 +52,7 @@ import static jdk.jpackage.internal.StandardBundlerParam.VENDOR; import static jdk.jpackage.internal.StandardBundlerParam.VERSION; import static jdk.jpackage.internal.StandardBundlerParam.hasPredefinedAppImage; import static jdk.jpackage.internal.StandardBundlerParam.isRuntimeInstaller; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import java.io.IOException; import java.nio.file.Path; @@ -69,6 +70,7 @@ import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.model.ParseUtils; import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.util.function.ThrowingFunction; @@ -77,12 +79,13 @@ final class FromParams { static ApplicationBuilder createApplicationBuilder(Map params, Function, Launcher> launcherMapper, ApplicationLayout appLayout) throws ConfigException, IOException { - return createApplicationBuilder(params, launcherMapper, appLayout, Optional.of(RuntimeLayout.DEFAULT)); + return createApplicationBuilder(params, launcherMapper, appLayout, RuntimeLayout.DEFAULT, Optional.of(RuntimeLayout.DEFAULT)); } static ApplicationBuilder createApplicationBuilder(Map params, Function, Launcher> launcherMapper, - ApplicationLayout appLayout, Optional predefinedRuntimeLayout) throws ConfigException, IOException { + ApplicationLayout appLayout, RuntimeLayout runtimeLayout, + Optional predefinedRuntimeLayout) throws ConfigException, IOException { final var appBuilder = new ApplicationBuilder(); @@ -102,7 +105,7 @@ final class FromParams { layout -> predefinedRuntimeImage.map(layout::resolveAt)).map(RuntimeLayout::runtimeDirectory); if (isRuntimeInstaller) { - appBuilder.appImageLayout(predefinedRuntimeLayout.orElseThrow()); + appBuilder.appImageLayout(runtimeLayout); } else { appBuilder.appImageLayout(appLayout); @@ -171,11 +174,11 @@ final class FromParams { } static Optional findLauncherShortcut( - BundlerParamInfo shortcutParam, + BundlerParamInfo shortcutParam, Map mainParams, Map launcherParams) { - Optional launcherValue; + Optional launcherValue; if (launcherParams == mainParams) { // The main launcher launcherValue = Optional.empty(); @@ -183,17 +186,19 @@ final class FromParams { launcherValue = shortcutParam.findIn(launcherParams); } - return launcherValue.map(withShortcut -> { - if (withShortcut) { - return Optional.of(LauncherShortcutStartupDirectory.DEFAULT); - } else { - return Optional.empty(); - } - }).or(() -> { - return shortcutParam.findIn(mainParams).map(_ -> { - return Optional.of(LauncherShortcutStartupDirectory.DEFAULT); - }); - }).map(LauncherShortcut::new); + return launcherValue.map(ParseUtils::parseLauncherShortcutForAddLauncher).or(() -> { + return Optional.ofNullable(mainParams.get(shortcutParam.getID())).map(toFunction(value -> { + if (value instanceof Boolean) { + return new LauncherShortcut(LauncherShortcutStartupDirectory.DEFAULT); + } else { + try { + return ParseUtils.parseLauncherShortcutForMainLauncher((String)value); + } catch (IllegalArgumentException ex) { + throw I18N.buildConfigException("error.invalid-option-value", value, "--" + shortcutParam.getID()).create(); + } + } + })); + }); } private static ApplicationLaunchers createLaunchers( diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java index c09c1ae4b1f..bd82b34d897 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java @@ -61,7 +61,7 @@ final class JLinkRuntimeBuilder implements RuntimeBuilder { } @Override - public void createRuntime(AppImageLayout appImageLayout) throws PackagerException { + public void create(AppImageLayout appImageLayout) throws PackagerException { var args = new ArrayList(); args.add("--output"); args.add(appImageLayout.runtimeDirectory().toString()); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java index 221e0dcac07..f93cd0eab14 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackageBuilder.java @@ -29,6 +29,7 @@ import static jdk.jpackage.internal.I18N.buildConfigException; import java.nio.file.Path; import java.util.Objects; import java.util.Optional; +import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.Package; @@ -86,9 +87,19 @@ final class PackageBuilder { Optional.ofNullable(aboutURL), Optional.ofNullable(licenseFile), Optional.ofNullable(predefinedAppImage), + validatedInstalledPackageLayout(relativeInstallDir), relativeInstallDir); } + PackageBuilder app(Application v) { + app = v; + return this; + } + + Application app() { + return app; + } + PackageBuilder name(String v) { name = v; return this; @@ -169,10 +180,34 @@ final class PackageBuilder { } } + PackageBuilder installedPackageLayout(AppImageLayout v) { + installedPackageLayout = v; + return this; + } + + Optional installedPackageLayout() { + return Optional.ofNullable(installedPackageLayout); + } + private String validatedName() { return name().orElseGet(app::name); } + private AppImageLayout validatedInstalledPackageLayout(Path relativeInstallDir) { + return installedPackageLayout().orElseGet(() -> { + var theInstallDir = relativeInstallDir; + if (type instanceof StandardPackageType stdType) { + switch (stdType) { + case LINUX_DEB, LINUX_RPM, MAC_DMG, MAC_PKG -> { + theInstallDir = Path.of("/").resolve(theInstallDir); + } + default -> {} + } + } + return app.imageLayout().resolveAt(theInstallDir).resetRootDirectory(); + }); + } + private static Path mapInstallDir(Path installDir, PackageType pkgType) throws ConfigException { var ex = buildConfigException("error.invalid-install-dir", installDir).create(); @@ -235,6 +270,7 @@ final class PackageBuilder { } } + private Application app; private String name; private Path fileName; private String description; @@ -243,7 +279,7 @@ final class PackageBuilder { private Path licenseFile; private Path predefinedAppImage; private Path installDir; + private AppImageLayout installedPackageLayout; private final PackageType type; - private final Application app; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java index 276bed4f31c..64a37878bd8 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java @@ -26,6 +26,7 @@ package jdk.jpackage.internal; import static java.util.stream.Collectors.toMap; +import static jdk.jpackage.internal.model.AppImageLayout.toPathGroup; import java.io.IOException; import java.nio.file.LinkOption; @@ -38,9 +39,9 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.Callable; -import java.util.function.Function; import java.util.function.Predicate; import java.util.function.UnaryOperator; +import java.util.stream.Stream; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; @@ -62,7 +63,7 @@ final class PackagingPipeline { * @param app the application */ void execute(BuildEnv env, Application app) throws PackagerException { - execute(appContextMapper.apply(createTaskContext(env, app))); + execute(contextMapper.apply(createTaskContext(env, app))); } /** @@ -81,8 +82,7 @@ final class PackagingPipeline { * @param outputDir the output directory for the package file */ void execute(BuildEnv env, Package pkg, Path outputDir) throws PackagerException { - execute((StartupParameters)createPackagingTaskContext(env, pkg, outputDir, - taskConfig, appImageLayoutForPackaging.apply(pkg))); + execute((StartupParameters)createPackagingTaskContext(env, pkg, outputDir, taskConfig)); } /** @@ -92,7 +92,7 @@ final class PackagingPipeline { * @param startupParameters the pipeline startup parameters */ void execute(StartupParameters startupParameters) throws PackagerException { - execute(pkgContextMapper.apply(createTaskContext((PackagingTaskContext)startupParameters))); + execute(contextMapper.apply(createTaskContext((PackagingTaskContext)startupParameters))); } /** @@ -135,21 +135,27 @@ final class PackagingPipeline { void execute(TaskAction taskAction) throws IOException, PackagerException; } - record AppImageBuildEnv(BuildEnv env, T app, U envLayout) { + record AppImageBuildEnv(BuildEnv env, T app) { + @SuppressWarnings("unchecked") + U envLayout() { + return (U)app.imageLayout(); + } + @SuppressWarnings("unchecked") U resolvedLayout() { - return (U)envLayout.resolveAt(env.appImageDir()); + return (U)env.appImageLayout(); } } - record PackageBuildEnv(BuildEnv env, T pkg, U envLayout, Path outputDir) { + record PackageBuildEnv(BuildEnv env, T pkg, Path outputDir) { @SuppressWarnings("unchecked") - U resolvedLayout() { - return (U)envLayout.resolveAt(env.appImageDir()); + U envLayout() { + return (U)pkg.appImageLayout(); } - AppImageBuildEnv appImageBuildEnv() { - return new AppImageBuildEnv<>(env, pkg.app(), envLayout); + @SuppressWarnings("unchecked") + U resolvedLayout() { + return (U)env.appImageLayout(); } } @@ -165,7 +171,7 @@ final class PackagingPipeline { @FunctionalInterface interface CopyAppImageTaskAction extends TaskAction { - void execute(T pkg, AppImageDesc srcAppImage, AppImageDesc dstAppImage) throws IOException, PackagerException; + void execute(T pkg, AppImageLayout srcAppImage, AppImageLayout dstAppImage) throws IOException, PackagerException; } @FunctionalInterface @@ -195,6 +201,11 @@ final class PackagingPipeline { super(id); } + private TaskBuilder(TaskID id, TaskConfig config) { + this(id); + config.action().ifPresent(this::setAction); + } + private TaskBuilder setAction(TaskAction v) { action = v; return this; @@ -225,6 +236,10 @@ final class PackagingPipeline { return setAction(action); } + boolean hasAction() { + return action != null; + } + @Override public TaskBuilder addDependent(TaskID v) { super.addDependent(v); @@ -283,6 +298,12 @@ final class PackagingPipeline { return new TaskBuilder(id); } + Stream configuredTasks() { + return taskConfig.entrySet().stream().map(e -> { + return new TaskBuilder(e.getKey(), e.getValue()); + }); + } + Builder excludeDirFromCopying(Path path) { Objects.requireNonNull(path); excludeCopyDirs.add(path); @@ -290,23 +311,7 @@ final class PackagingPipeline { } Builder contextMapper(UnaryOperator v) { - appContextMapper(v); - pkgContextMapper(v); - return this; - } - - Builder appContextMapper(UnaryOperator v) { - appContextMapper = v; - return this; - } - - Builder pkgContextMapper(UnaryOperator v) { - pkgContextMapper = v; - return this; - } - - Builder appImageLayoutForPackaging(Function v) { - appImageLayoutForPackaging = v; + contextMapper = v; return this; } @@ -318,27 +323,18 @@ final class PackagingPipeline { } StartupParameters createStartupParameters(BuildEnv env, Package pkg, Path outputDir) { - return createPackagingTaskContext(env, pkg, outputDir, taskConfig, - validatedAppImageLayoutForPackaging().apply(pkg)); - } - - private Function validatedAppImageLayoutForPackaging() { - return Optional.ofNullable(appImageLayoutForPackaging).orElse(Package::packageLayout); + return createPackagingTaskContext(env, pkg, outputDir, taskConfig); } PackagingPipeline create() { return new PackagingPipeline(taskGraphSnapshot(), taskConfig, - Optional.ofNullable(appContextMapper).orElse(UnaryOperator.identity()), - Optional.ofNullable(pkgContextMapper).orElse(UnaryOperator.identity()), - validatedAppImageLayoutForPackaging()); + Optional.ofNullable(contextMapper).orElse(UnaryOperator.identity())); } private final FixedDAG.Builder taskGraphBuilder = FixedDAG.build(); private final List excludeCopyDirs = new ArrayList<>(); private final Map taskConfig = new HashMap<>(); - private UnaryOperator appContextMapper; - private UnaryOperator pkgContextMapper; - private Function appImageLayoutForPackaging; + private UnaryOperator contextMapper; private FixedDAG taskGraphSnapshot; } @@ -404,21 +400,20 @@ final class PackagingPipeline { return builder; } - static void copyAppImage(Package pkg, AppImageDesc srcAppImage, AppImageDesc dstAppImage) throws IOException { + static void copyAppImage(Package pkg, AppImageLayout srcAppImage, AppImageLayout dstAppImage) throws IOException { copyAppImage(srcAppImage, dstAppImage, true); } - static void copyAppImage(AppImageDesc srcAppImage, AppImageDesc dstAppImage, + static void copyAppImage(AppImageLayout srcAppImage, AppImageLayout dstAppImage, boolean removeAppImageFile) throws IOException { - final var srcLayout = srcAppImage.resolvedAppImagelayout(); - final var srcLayoutPathGroup = AppImageLayout.toPathGroup(srcLayout); + final var srcLayoutPathGroup = toPathGroup(srcAppImage); - if (removeAppImageFile && srcLayout instanceof ApplicationLayout appLayout) { + if (removeAppImageFile && srcAppImage instanceof ApplicationLayout appLayout) { // Copy app layout omitting application image info file. srcLayoutPathGroup.ghostPath(AppImageFile.getPathInAppImage(appLayout)); } - srcLayoutPathGroup.copy(AppImageLayout.toPathGroup(dstAppImage.resolvedAppImagelayout()), LinkOption.NOFOLLOW_LINKS); + srcLayoutPathGroup.copy(toPathGroup(dstAppImage), LinkOption.NOFOLLOW_LINKS); } static void runPostAppImageUserScript(PackageBuildEnv env) throws IOException { @@ -432,57 +427,66 @@ final class PackagingPipeline { } private PackagingPipeline(FixedDAG taskGraph, Map taskConfig, - UnaryOperator appContextMapper, UnaryOperator pkgContextMapper, - Function appImageLayoutForPackaging) { + UnaryOperator contextMapper) { this.taskGraph = Objects.requireNonNull(taskGraph); this.taskConfig = Objects.requireNonNull(taskConfig); - this.appContextMapper = Objects.requireNonNull(appContextMapper); - this.pkgContextMapper = Objects.requireNonNull(pkgContextMapper); - this.appImageLayoutForPackaging = Objects.requireNonNull(appImageLayoutForPackaging); + this.contextMapper = Objects.requireNonNull(contextMapper); } private TaskContext createTaskContext(BuildEnv env, Application app) { - return new DefaultTaskContext(taskGraph, env, app, app.asApplicationLayout(), Optional.empty()); + return new DefaultTaskContext(taskGraph, env, app, Optional.empty()); } private TaskContext createTaskContext(PackagingTaskContext packagingContext) { - final var pkgEnv = BuildEnv.withAppImageDir(packagingContext.env.env(), packagingContext.srcAppImage.path()); - return new DefaultTaskContext(taskGraph, pkgEnv, packagingContext.env.pkg.app(), - packagingContext.srcAppImage.asApplicationLayout(), Optional.of(packagingContext)); + return new DefaultTaskContext(taskGraph, packagingContext.env(), + packagingContext.pkg().app(), Optional.of(packagingContext)); } private static PackagingTaskContext createPackagingTaskContext(BuildEnv env, Package pkg, - Path outputDir, Map taskConfig, AppImageLayout appImageLayoutForPackaging) { + Path outputDir, Map taskConfig) { Objects.requireNonNull(env); Objects.requireNonNull(outputDir); Objects.requireNonNull(taskConfig); - Objects.requireNonNull(appImageLayoutForPackaging); + if (pkg.appImageLayout().isResolved()) { + throw new IllegalArgumentException(); + } - final AppImageDesc srcAppImageDesc; - final AppImageDesc dstAppImageDesc; + final AppImageLayout srcLayout; + final AppImageLayout dstLayout; if (pkg.app().runtimeBuilder().isPresent()) { // Runtime builder is present, will build application image. - // appImageDir() should point to a directory where the application image will be created. - srcAppImageDesc = new AppImageDesc(appImageLayoutForPackaging, env.appImageDir()); - dstAppImageDesc = srcAppImageDesc; + srcLayout = pkg.appImageLayout().resolveAt(env.appImageDir()); + dstLayout = srcLayout; } else { - srcAppImageDesc = new AppImageDesc(pkg.app().imageLayout(), - pkg.predefinedAppImage().orElseThrow(UnsupportedOperationException::new)); + srcLayout = pkg.predefinedAppImage().map(predefinedAppImage -> { + // Will create a package from the predefined app image. + if (predefinedAppImage.equals(env.appImageDir())) { + return env.appImageLayout(); + } else { + return pkg.appImageLayout().resolveAt(predefinedAppImage); + } + }).orElseGet(() -> { + // No predefined app image and no runtime builder. + // This should be runtime packaging. + if (pkg.isRuntimeInstaller()) { + return env.appImageLayout(); + } else { + // Can't create app image without runtime builder. + throw new UnsupportedOperationException(); + } + }); if (taskConfig.get(CopyAppImageTaskID.COPY).action().isEmpty()) { - // "copy app image" task action is undefined indicating - // the package will use provided app image as-is. - dstAppImageDesc = srcAppImageDesc; + // "copy app image" task action is empty indicating + // the package will use provided app image in place. + dstLayout = srcLayout; } else { - dstAppImageDesc = new AppImageDesc(appImageLayoutForPackaging, env.buildRoot().resolve("image")); + dstLayout = pkg.appImageLayout().resolveAt(env.buildRoot().resolve("image")); } } - final var pkgEnv = new PackageBuildEnv<>( - BuildEnv.withAppImageDir(env, dstAppImageDesc.path()), pkg, dstAppImageDesc.appImageLayout(), outputDir); - - return new PackagingTaskContext(pkgEnv, srcAppImageDesc); + return new PackagingTaskContext(BuildEnv.withAppImageLayout(env, dstLayout), pkg, outputDir, srcLayout); } private void execute(TaskContext context) throws PackagerException { @@ -500,28 +504,30 @@ final class PackagingPipeline { try { builder.create().call(); + } catch (ExceptionBox ex) { + throw new PackagerException(ex.getCause()); + } catch (RuntimeException ex) { + throw ex; + } catch (PackagerException ex) { + throw ex; } catch (Exception ex) { - if (ex instanceof PackagerException pex) { - throw pex; - } else if (ex instanceof ExceptionBox bex) { - throw new PackagerException(bex.getCause()); - } else { - throw new PackagerException(ex); - } + throw new PackagerException(ex); } } - private record PackagingTaskContext(PackageBuildEnv env, - AppImageDesc srcAppImage) implements TaskContext, StartupParameters { + private record PackagingTaskContext(BuildEnv env, Package pkg, Path outputDir, + AppImageLayout srcAppImage) implements TaskContext, StartupParameters { PackagingTaskContext { Objects.requireNonNull(env); + Objects.requireNonNull(pkg); + Objects.requireNonNull(outputDir); Objects.requireNonNull(srcAppImage); } @Override public BuildEnv packagingEnv() { - return env.env; + return env; } @Override @@ -538,28 +544,31 @@ final class PackagingPipeline { @Override public void execute(TaskAction taskAction) throws IOException, PackagerException { if (taskAction instanceof PackageTaskAction) { - ((PackageTaskAction)taskAction).execute(env); + ((PackageTaskAction)taskAction).execute(pkgBuildEnv()); } else if (taskAction instanceof CopyAppImageTaskAction) { - ((CopyAppImageTaskAction)taskAction).execute(env.pkg(), - srcAppImage, new AppImageDesc(env.envLayout(), env.env().appImageDir())); + ((CopyAppImageTaskAction)taskAction).execute(pkg(), + srcAppImage, env.appImageLayout()); } else { throw new IllegalArgumentException(); } } AppImageBuildEnv appImageBuildEnv() { - return env.appImageBuildEnv(); + return new AppImageBuildEnv<>(env, pkg.app()); + } + + PackageBuildEnv pkgBuildEnv() { + return new PackageBuildEnv<>(env, pkg, outputDir); } } private record DefaultTaskContext(FixedDAG taskGraph, BuildEnv env, Application app, - Optional appLayout, Optional pkg) implements TaskContext { + Optional pkg) implements TaskContext { DefaultTaskContext { Objects.requireNonNull(taskGraph); Objects.requireNonNull(env); Objects.requireNonNull(app); - Objects.requireNonNull(appLayout); Objects.requireNonNull(pkg); } @@ -571,11 +580,11 @@ final class PackagingPipeline { if (pkg.isPresent() && !pkg.orElseThrow().test(taskID)) { return false; - } else if (pkg.isEmpty() && isPackageTask) { - // Building application image, skip packaging tasks. + } else if (pkg.isEmpty() && (isPackageTask || isCopyAppImageTask)) { + // Building application image, skip packaging and copying app image tasks. return false; - } else if (app.runtimeBuilder().isEmpty() && isBuildApplicationImageTask && !isCopyAppImageTask) { - // Runtime builder is not present, skip building application image tasks. + } else if (pkg.isPresent() && app.runtimeBuilder().isEmpty() && isBuildApplicationImageTask && !isCopyAppImageTask) { + // Building a package, runtime builder is not present, skip building application image tasks. return false; } else if (app.runtimeBuilder().isPresent() && isCopyAppImageTask && !isBuildApplicationImageTask) { // Runtime builder is present, skip copying app image tasks. @@ -596,7 +605,7 @@ final class PackagingPipeline { } else if (taskAction instanceof NoArgTaskAction noArgAction) { noArgAction.execute(); } else { - pkg.orElseThrow().execute(taskAction); + pkg.orElseThrow(UnsupportedOperationException::new).execute(taskAction); } } @@ -612,7 +621,7 @@ final class PackagingPipeline { @SuppressWarnings("unchecked") private AppImageBuildEnv appBuildEnv() { - return new AppImageBuildEnv<>(env, app, (T)appLayout.orElseThrow()); + return new AppImageBuildEnv<>(env, app); } } @@ -622,11 +631,7 @@ final class PackagingPipeline { Objects.requireNonNull(config); return () -> { if (config.action.isPresent() && context.test(id)) { - try { - context.execute(config.action.orElseThrow()); - } catch (ExceptionBox ex) { - throw ExceptionBox.rethrowUnchecked(ex); - } + context.execute(config.action.orElseThrow()); } return null; }; @@ -634,7 +639,5 @@ final class PackagingPipeline { private final FixedDAG taskGraph; private final Map taskConfig; - private final Function appImageLayoutForPackaging; - private final UnaryOperator appContextMapper; - private final UnaryOperator pkgContextMapper; + private final UnaryOperator contextMapper; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageLayout.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageLayout.java index 1cd0e0f6c2e..a64946c06d0 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageLayout.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageLayout.java @@ -25,15 +25,18 @@ package jdk.jpackage.internal.model; +import static jdk.jpackage.internal.util.PathUtils.mapNullablePath; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; + import java.lang.reflect.Modifier; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.function.UnaryOperator; import java.util.stream.Stream; import jdk.jpackage.internal.util.PathGroup; -import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; -import static jdk.jpackage.internal.util.PathUtils.resolveNullablePath; /** @@ -41,9 +44,14 @@ import static jdk.jpackage.internal.util.PathUtils.resolveNullablePath; * * App image layout is a collection of files and directories with specific roles * (executables, configuration files, etc.) sharing the same root directory. - * - * The layout is "unresolved" if the root directory is an empty string and - * "resolved" otherwise. + *

    + * The layout is "unresolved" if the root directory is an empty path + * ({@code Path.of("")}) and "resolved" otherwise. + *

    + * The return value of the {@link #runtimeDirectory()} method call is always a + * path starting with the path returned by the {@link #rootDirectory()} method + * call. Public methods without parameters and with the return type {@link Path} + * in the derived interfaces must comply to this constrain. */ public interface AppImageLayout { @@ -56,29 +64,94 @@ public interface AppImageLayout { Path runtimeDirectory(); /** - * Root directory of this app image. + * Root directory of this app image layout. * It should normally be equal to Path.of("") for unresolved layout. * - * @return the root directory of this app image + * @return the root directory of this app image layout */ Path rootDirectory(); /** - * Creates a copy of this app image resolved at the given root directory. + * Returns a copy of this app image layout with the root directory set to an empty + * path ({@code Path.of("")}) or this instance if its root directory is already + * an empty path. + * + * @return an app image layout with the root directory set to an empty path + */ + AppImageLayout resetRootDirectory(); + + /** + * Returns true if the root directory of this app image layout is + * not an empty path, i.e, if it is not equal to Path.of(""). + * + * @return true if the root directory of this app image layout is + * not an empty path + */ + default boolean isResolved() { + return !rootDirectory().equals(Path.of("")); + } + + /** + * Creates a copy of this app image layout resolved at the given root directory. * * @param root path to a directory at which to resolve the layout - * @return a copy of this app image resolved at the given root directory + * @return a copy of this app image layout resolved at the given root directory */ - AppImageLayout resolveAt(Path root); + default AppImageLayout resolveAt(Path root) { + return map(root::resolve); + } + + /** + * Returns a copy of this app image layout resolved such that its root directory + * is set to an empty path ({@code Path.of("")}) or this instance if its root + * directory is already an empty path. + * + * @return an app image layout resolved at {@code Path.of("")} path + */ + default AppImageLayout unresolve() { + if (isResolved()) { + final var root = rootDirectory(); + return map(root::relativize); + } else { + return this; + } + } + + /** + * Returns a copy of this app image layout with the specified mapper applied to + * every path. + * + * @param mapper the mapper to use with every path in this app image layout. + * @return the copy of this app image layout with the specified mapper applied + * to every path + */ + AppImageLayout map(UnaryOperator mapper); /** * Default implementation of {@link AppImageLayout} interface. - */ + */ record Stub(Path rootDirectory, Path runtimeDirectory) implements AppImageLayout { + public Stub { + Objects.requireNonNull(rootDirectory); + } + + public Stub(Path runtimeDirectory) { + this(Path.of(""), runtimeDirectory); + } + @Override - public AppImageLayout resolveAt(Path base) { - return new Stub(resolveNullablePath(base, rootDirectory), resolveNullablePath(base, runtimeDirectory)); + public AppImageLayout resetRootDirectory() { + if (isResolved()) { + return new Stub(runtimeDirectory); + } else { + return this; + } + } + + @Override + public AppImageLayout map(UnaryOperator mapper) { + return new Stub(mapNullablePath(mapper, rootDirectory), mapNullablePath(mapper, runtimeDirectory)); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ApplicationLayout.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ApplicationLayout.java index b8b2eda4437..013d2fc78cd 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ApplicationLayout.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ApplicationLayout.java @@ -24,10 +24,11 @@ */ package jdk.jpackage.internal.model; -import static jdk.jpackage.internal.util.PathUtils.resolveNullablePath; +import static jdk.jpackage.internal.util.PathUtils.mapNullablePath; import java.nio.file.Path; import java.util.Objects; +import java.util.function.UnaryOperator; import jdk.jpackage.internal.util.CompositeProxy; /** @@ -42,7 +43,26 @@ public interface ApplicationLayout extends AppImageLayout, ApplicationLayoutMixi @Override default ApplicationLayout resolveAt(Path root) { - return buildFrom(this).resolveAt(root).create(); + return (ApplicationLayout)AppImageLayout.super.resolveAt(root); + } + + @Override + default ApplicationLayout unresolve() { + return (ApplicationLayout)AppImageLayout.super.unresolve(); + } + + @Override + default ApplicationLayout resetRootDirectory() { + if (isResolved()) { + return buildFrom(this).rootDirectory("").create(); + } else { + return this; + } + } + + @Override + default ApplicationLayout map(UnaryOperator mapper) { + return buildFrom(this).mutate(mapper).create(); } /** @@ -113,14 +133,14 @@ public interface ApplicationLayout extends AppImageLayout, ApplicationLayoutMixi return this; } - public Builder resolveAt(Path base) { - rootDirectory(resolveNullablePath(base, rootDirectory)); - launchersDirectory(resolveNullablePath(base, launchersDirectory)); - appDirectory(resolveNullablePath(base, appDirectory)); - runtimeDirectory(resolveNullablePath(base, runtimeDirectory)); - appModsDirectory(resolveNullablePath(base, appModsDirectory)); - desktopIntegrationDirectory(resolveNullablePath(base, desktopIntegrationDirectory)); - contentDirectory(resolveNullablePath(base, contentDirectory)); + public Builder mutate(UnaryOperator mapper) { + rootDirectory(mapNullablePath(mapper, rootDirectory)); + launchersDirectory(mapNullablePath(mapper, launchersDirectory)); + appDirectory(mapNullablePath(mapper, appDirectory)); + runtimeDirectory(mapNullablePath(mapper, runtimeDirectory)); + appModsDirectory(mapNullablePath(mapper, appModsDirectory)); + desktopIntegrationDirectory(mapNullablePath(mapper, desktopIntegrationDirectory)); + contentDirectory(mapNullablePath(mapper, contentDirectory)); return this; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcutStartupDirectory.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcutStartupDirectory.java index c604b00c3e2..c6ceb1af40d 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcutStartupDirectory.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/LauncherShortcutStartupDirectory.java @@ -41,7 +41,14 @@ public enum LauncherShortcutStartupDirectory { * On Linux, it indicates that a shortcut doesn't have the startup directory * configured explicitly. */ - DEFAULT("true"); + DEFAULT("true"), + + /** + * The 'app' directory in the installed application app image. This is the + * directory that is referenced with {@link ApplicationLayout#appDirectory()} + * method. + */ + APP_DIR("app-dir"); LauncherShortcutStartupDirectory(String stringValue) { this.stringValue = Objects.requireNonNull(stringValue); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Package.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Package.java index 6d2fdaf0bbb..33c707b776f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Package.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Package.java @@ -30,19 +30,11 @@ import java.util.Optional; /** * Native application package. * - * The interface specifies the source app image layout with two transformations: - * package app image layout and installed app image layout. + * The interface specifies the source app image and the installed app image layouts. *

    * Use {@link #appImageLayout()} or {@link #asApplicationLayout()} to get the * unresolved source app image layout. *

    - * Package app image layout is the source app image layout resolved at the - * relative installation directory of the package. Additionally, to resolve the - * source layout, some packages may transform the source layout. - *

    - * Use {@link #packageLayout()} or {@link #asPackageApplicationLayout()} to get - * the package app image layout. - *

    * Installed app image layout is the layout of the installed app image. *

    * Use {@link #installedPackageLayout()} or @@ -55,25 +47,21 @@ import java.util.Optional; * * * Source app image layout - * Package app image layout * Installed app image layout * * * Windows * bin/foo.exe app/foo.jar * Duke/bin/foo.exe Duke/app/foo.jar - * Duke/bin/foo.exe Duke/app/foo.jar * * * Linux * bin/foo lib/app/foo.jar - * opt/duke/bin/foo opt/duke/lib/app/foo.jar * /opt/duke/bin/foo /opt/duke/lib/app/foo.jar * * * OSX * Contents/MacOS/foo Contents/app/foo.jar - * Applications/Duke.app/Contents/MacOS/foo Applications/Duke.app/Contents/app/foo.jar * /Applications/Duke.app/Contents/MacOS/foo /Applications/Duke.app/Contents/app/foo.jar * * @@ -169,7 +157,6 @@ public interface Package extends BundleSpec { * * @return the unresolved app image layout of the application of this package * - * @see #packageLayout * @see #installedPackageLayout */ default AppImageLayout appImageLayout() { @@ -193,68 +180,14 @@ public interface Package extends BundleSpec { return app().asApplicationLayout(); } - /** - * Gets the layout of the installed app image of the application resolved at the - * relative installation directory of this package. - * - * @return the layout of the installed app image of the application resolved at - * the relative installation directory of this package - * - * @see #relativeInstallDir - * @see #appImageLayout - * @see #installedPackageLayout - */ - default AppImageLayout packageLayout() { - return appImageLayout().resolveAt(relativeInstallDir()); - } - - /** - * Returns the layout of the installed app image of the application resolved at - * the relative installation directory of this package as - * {@link ApplicationLayout} type or an empty {@link Optional} instance if the - * layout object is of incompatible type. - *

    - * Returns an empty {@link Optional} instance if {@link #isRuntimeInstaller()} - * returns true. - * - * @return the layout of the installed app image of the application resolved at - * the relative installation directory of this package as - * {@link ApplicationLayout} type - * - * @see #packageLayout - */ - default Optional asPackageApplicationLayout() { - if (packageLayout() instanceof ApplicationLayout layout) { - return Optional.of(layout); - } else { - return Optional.empty(); - } - } - /** * Gets the layout of the installed app image of this package. * * @return the layout of the installed app image of this package * * @see #appImageLayout - * @see #packageLayout */ - default AppImageLayout installedPackageLayout() { - return asStandardPackageType().map(stdType -> { - switch (stdType) { - case LINUX_DEB, LINUX_RPM, MAC_DMG, MAC_PKG -> { - return packageLayout().resolveAt(Path.of("/")); - } - case WIN_EXE, WIN_MSI -> { - return packageLayout(); - } - default -> { - // Should never get here - throw new IllegalStateException(); - } - } - }).orElseThrow(UnsupportedOperationException::new); - } + AppImageLayout installedPackageLayout(); /** * Returns the layout of the installed app image of this package as @@ -334,6 +267,6 @@ public interface Package extends BundleSpec { */ record Stub(Application app, PackageType type, String packageName, String description, String version, Optional aboutURL, Optional licenseFile, Optional predefinedAppImage, - Path relativeInstallDir) implements Package { + AppImageLayout installedPackageLayout, Path relativeInstallDir) implements Package { } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ParseUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ParseUtils.java new file mode 100644 index 00000000000..411e487d9e0 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ParseUtils.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.model; + +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * Collection of functions to create instances of types defined in this package from strings. + */ +public final class ParseUtils { + + private ParseUtils() { + } + + public static LauncherShortcut parseLauncherShortcutForMainLauncher(String str) { + return parse(str, LauncherShortcutStartupDirectory.APP_DIR).map(LauncherShortcut::new).orElseThrow(IllegalArgumentException::new); + } + + public static LauncherShortcut parseLauncherShortcutForAddLauncher(String str) { + return parse(str, LauncherShortcutStartupDirectory.values()).map(LauncherShortcut::new).orElseGet(() -> { + if (Boolean.valueOf(str)) { + return new LauncherShortcut(LauncherShortcutStartupDirectory.DEFAULT); + } else { + return new LauncherShortcut(); + } + }); + } + + private static Optional parse(String str, LauncherShortcutStartupDirectory... recognizedValues) { + Objects.requireNonNull(str); + return Stream.of(recognizedValues).filter(v -> { + return str.equals(v.asStringValue()); + }).findFirst(); + } +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java index bce3b417b7c..c989bcc8915 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeBuilder.java @@ -43,7 +43,7 @@ public interface RuntimeBuilder { * @param appImageLayout the app image where to create Java runtime. * @throws PackagerException if packaging error occurs */ - void createRuntime(AppImageLayout appImageLayout) throws PackagerException; + void create(AppImageLayout appImageLayout) throws PackagerException; /** * Gets the default set of paths where to find Java modules. diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeLayout.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeLayout.java index 733fc7a74c3..d995774af8f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeLayout.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/RuntimeLayout.java @@ -24,9 +24,10 @@ */ package jdk.jpackage.internal.model; -import static jdk.jpackage.internal.util.PathUtils.resolveNullablePath; +import static jdk.jpackage.internal.util.PathUtils.mapNullablePath; import java.nio.file.Path; +import java.util.function.UnaryOperator; import jdk.jpackage.internal.util.CompositeProxy; /** @@ -39,8 +40,27 @@ public interface RuntimeLayout extends AppImageLayout { @Override default RuntimeLayout resolveAt(Path root) { - return create(new AppImageLayout.Stub(resolveNullablePath(root, rootDirectory()), - resolveNullablePath(root, runtimeDirectory()))); + return (RuntimeLayout)AppImageLayout.super.resolveAt(root); + } + + @Override + default RuntimeLayout resetRootDirectory() { + if (isResolved()) { + return create(runtimeDirectory()); + } else { + return this; + } + } + + @Override + default RuntimeLayout unresolve() { + return (RuntimeLayout)AppImageLayout.super.unresolve(); + } + + @Override + default RuntimeLayout map(UnaryOperator mapper) { + return create(new RuntimeLayout.Stub(mapNullablePath(mapper, rootDirectory()), + mapNullablePath(mapper, runtimeDirectory()))); } /** diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties index ae225e15ea2..684a97bc1bd 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties @@ -82,6 +82,8 @@ error.invalid-app-image=Error: app-image dir "{0}" generated by another jpackage error.invalid-install-dir=Invalid installation directory "{0}" +error.invalid-option-value=Invalid value "{0}" of option {1} + MSG_BundlerFailed=Error: Bundler "{1}" ({0}) failed to produce a package MSG_BundlerConfigException=Bundler {0} skipped because of a configuration problem: {1} \n\ Advice to fix: {2} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java index dfb5da7f829..93d317b0128 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/ResourceLocator.java @@ -32,7 +32,7 @@ package jdk.jpackage.internal.resources; * to call getResourceAsStream() to get those resources. */ -public class ResourceLocator { - public ResourceLocator() { +public final class ResourceLocator { + private ResourceLocator() { } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathGroup.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathGroup.java index 08a91175c1e..55144826efb 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathGroup.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathGroup.java @@ -470,5 +470,22 @@ public final class PathGroup { } } + @Override + public int hashCode() { + return Objects.hash(entries); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PathGroup other = (PathGroup) obj; + return Objects.equals(entries, other.entries); + } + private final Map entries; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathUtils.java index 31019832b73..a8944a67ae0 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PathUtils.java @@ -25,7 +25,9 @@ package jdk.jpackage.internal.util; import java.nio.file.Path; +import java.util.Objects; import java.util.Optional; +import java.util.function.UnaryOperator; public final class PathUtils { @@ -47,8 +49,13 @@ public final class PathUtils { return parent != null ? parent.resolve(filename) : Path.of(filename); } - public static Path resolveNullablePath(Path base, Path path) { - return Optional.ofNullable(path).map(base::resolve).orElse(null); + public static Path mapNullablePath(UnaryOperator mapper, Path path) { + Objects.requireNonNull(mapper); + if (path != null) { + return mapper.apply(path); + } else { + return null; + } } public static Path normalizedAbsolutePath(Path path) { diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java index 15d8d2f83b0..e2259535058 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromParams.java @@ -105,10 +105,10 @@ final class WinFromParams { static final BundlerParamInfo MSI_PACKAGE = createPackageBundlerParam( WinFromParams::createWinMsiPackage); - private static final BundlerParamInfo WIN_MENU_HINT = createBooleanBundlerParam( + private static final BundlerParamInfo WIN_MENU_HINT = createStringBundlerParam( Arguments.CLIOptions.WIN_MENU_HINT.getId()); - private static final BundlerParamInfo WIN_SHORTCUT_HINT = createBooleanBundlerParam( + private static final BundlerParamInfo WIN_SHORTCUT_HINT = createStringBundlerParam( Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId()); public static final BundlerParamInfo CONSOLE_HINT = createBooleanBundlerParam( diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinPackagingPipeline.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinPackagingPipeline.java index be529bdc19e..2fa7dd895c3 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinPackagingPipeline.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinPackagingPipeline.java @@ -34,7 +34,6 @@ import jdk.jpackage.internal.PackagingPipeline.CopyAppImageTaskID; import jdk.jpackage.internal.PackagingPipeline.PrimaryTaskID; import jdk.jpackage.internal.PackagingPipeline.TaskID; import jdk.jpackage.internal.model.ApplicationLayout; -import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.model.PackagerException; import jdk.jpackage.internal.model.WinApplication; import jdk.jpackage.internal.model.WinLauncher; @@ -47,7 +46,6 @@ final class WinPackagingPipeline { static PackagingPipeline.Builder build() { return PackagingPipeline.buildStandard() - .appImageLayoutForPackaging(Package::appImageLayout) .task(CopyAppImageTaskID.COPY).noaction().add() .task(WinAppImageTaskID.REBRAND_LAUNCHERS) .addDependency(BuildApplicationTaskID.LAUNCHERS) diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java index ea4d9eee19a..63be18a5ee8 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixAppImageFragmentBuilder.java @@ -474,6 +474,9 @@ final class WixAppImageFragmentBuilder extends WixFragmentBuilder { case DEFAULT -> { return INSTALLDIR; } + case APP_DIR -> { + return installedAppImage.appDirectory(); + } default -> { throw new AssertionError(); } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinExePackage.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinExePackage.java index 6244090b71f..f7715c5ac3d 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinExePackage.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinExePackage.java @@ -46,6 +46,7 @@ public interface WinExePackage extends Package, WinExePackageMixin { pkg.aboutURL(), pkg.licenseFile(), pkg.predefinedAppImage(), + pkg.installedPackageLayout(), pkg.relativeInstallDir()); } } diff --git a/src/jdk.jshell/share/classes/jdk/jshell/JShellConsole.java b/src/jdk.jshell/share/classes/jdk/jshell/JShellConsole.java index d0792f7bdd0..845ec20c041 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/JShellConsole.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/JShellConsole.java @@ -28,7 +28,6 @@ import java.io.IOError; import java.io.PrintWriter; import java.io.Reader; import java.nio.charset.Charset; -import jdk.internal.javac.PreviewFeature; /** * An interface providing functionality for {@link java.io.Console} in the user's snippet. diff --git a/src/jdk.jshell/share/classes/module-info.java b/src/jdk.jshell/share/classes/module-info.java index b1e4270692d..938310c1dd4 100644 --- a/src/jdk.jshell/share/classes/module-info.java +++ b/src/jdk.jshell/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,6 @@ * questions. */ -import jdk.internal.javac.ParticipatesInPreview; - /** * Provides the {@index jshell jshell tool} tool for evaluating * snippets of Java code, and defines a JDK-specific API for modeling and @@ -65,7 +63,6 @@ import jdk.internal.javac.ParticipatesInPreview; * @moduleGraph * @since 9 */ -@ParticipatesInPreview module jdk.jshell { requires java.logging; requires jdk.compiler; diff --git a/src/jdk.management.jfr/share/classes/jdk/management/jfr/EventTypeInfo.java b/src/jdk.management.jfr/share/classes/jdk/management/jfr/EventTypeInfo.java index 15739829ee4..9be2ad90f62 100644 --- a/src/jdk.management.jfr/share/classes/jdk/management/jfr/EventTypeInfo.java +++ b/src/jdk.management.jfr/share/classes/jdk/management/jfr/EventTypeInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -163,7 +163,7 @@ public final class EventTypeInfo { /** * Returns a short sentence or two describing the event type associated with * this {@code EventTypeInfo}, for example - * {@code "Garbage collection performed by the JVM""}. + * {@code "Garbage collection performed by the JVM"}. * * @return the description, or {@code null} if no description exists * diff --git a/src/jdk.management.jfr/share/classes/jdk/management/jfr/FlightRecorderMXBean.java b/src/jdk.management.jfr/share/classes/jdk/management/jfr/FlightRecorderMXBean.java index a0648a0b1a6..9f61cc3f358 100644 --- a/src/jdk.management.jfr/share/classes/jdk/management/jfr/FlightRecorderMXBean.java +++ b/src/jdk.management.jfr/share/classes/jdk/management/jfr/FlightRecorderMXBean.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ package jdk.management.jfr; import java.io.IOException; import java.lang.management.PlatformManagedObject; +import java.nio.file.Paths; import java.time.Instant; import java.util.List; import java.util.Map; @@ -47,7 +48,7 @@ import jdk.jfr.Recording; * Specify how long a recording should last, and where and when data * should be dumped. *

  • Settings
    - * Specify which events should be enabled and what kind information each + * Specify which events should be enabled and what kind of information each * event should capture.
  • *
  • Configurations
    * Predefined sets of settings, typically derived from a settings file, @@ -59,7 +60,7 @@ import jdk.jfr.Recording; * *

    Recording options

    *

    - * The following table shows the options names to use with {@link #setRecordingOptions(long, Map)} + * The following table shows the option names to use with {@link #setRecordingOptions(long, Map)} * and {@link #getRecordingOptions(long)}. * * @@ -67,7 +68,7 @@ import jdk.jfr.Recording; * * * - * + * * * * @@ -107,7 +108,7 @@ import jdk.jfr.Recording; * * * - * * @@ -131,8 +132,8 @@ import jdk.jfr.Recording; * - * + * * * @@ -145,7 +146,7 @@ import jdk.jfr.Recording; * {@code "false"} * * - * + * * * * * - * + * * * * @@ -337,7 +338,7 @@ public interface FlightRecorderMXBean extends PlatformManagedObject { * * + * To be able to read from a running recording the value must be set to * * @@ -355,7 +356,7 @@ public interface FlightRecorderMXBean extends PlatformManagedObject { * * @return a unique ID for the stream. * - * @throws IllegalArgumentException if a recording with the iD doesn't + * @throws IllegalArgumentException if a recording with the ID doesn't * exist, or if {@code options} contains invalid values * * @throws IOException if the recording is closed, an I/O error occurs, or @@ -425,7 +426,7 @@ public interface FlightRecorderMXBean extends PlatformManagedObject { * is a textual representation of the settings value (for example, * {@code "60 s"}). * - * @param recordingId the ID of the recordings to get settings for + * @param recordingId the ID of the recording to get settings for * * @return a map that describes the recording settings, not {@code null} * @@ -522,7 +523,7 @@ public interface FlightRecorderMXBean extends PlatformManagedObject { void setRecordingOptions(long recordingId, Map options) throws IllegalArgumentException; /** - * Returns the list of the available recordings, not necessarily running. + * Returns the list of available recordings, not necessarily running. *

    * MBeanServer access:
    * The mapped type of {@code RecordingInfo} is {@code CompositeData} with diff --git a/src/jdk.management.jfr/share/classes/jdk/management/jfr/SettingDescriptorInfo.java b/src/jdk.management.jfr/share/classes/jdk/management/jfr/SettingDescriptorInfo.java index 6c13dd24a64..bece634dcca 100644 --- a/src/jdk.management.jfr/share/classes/jdk/management/jfr/SettingDescriptorInfo.java +++ b/src/jdk.management.jfr/share/classes/jdk/management/jfr/SettingDescriptorInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,7 +108,7 @@ public final class SettingDescriptorInfo { /** * Returns the description of the setting associated this * {@code SettingDescriptorInfo} (for example, - * {@code "The duration an event must exceed to be be recorded"}). + * {@code "The duration an event must exceed to be recorded"}). * * @return the description of this setting, not null */ diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahSimpleBitMap.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahSimpleBitMap.cpp index 45d6cf47fe0..0d81320befc 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahSimpleBitMap.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahSimpleBitMap.cpp @@ -443,7 +443,7 @@ public: }; -TEST(BasicShenandoahSimpleBitMapTest, minimum_test) { +TEST_F(ShenandoahSimpleBitMapTest, minimum_test) { bool result = ShenandoahSimpleBitMapTest::run_test(); ASSERT_EQ(result, true); diff --git a/test/hotspot/gtest/gc/z/test_zArray.cpp b/test/hotspot/gtest/gc/z/test_zArray.cpp index acc7688cb10..79d4b29b1f5 100644 --- a/test/hotspot/gtest/gc/z/test_zArray.cpp +++ b/test/hotspot/gtest/gc/z/test_zArray.cpp @@ -127,9 +127,11 @@ TEST_F(ZArrayTest, slice) { const auto check_reversed = [](ZArraySlice original, ZArraySlice reversed) { ASSERT_EQ(original.length(), reversed.length()); + int ri = reversed.length(); for (int e : original) { - ASSERT_EQ(e, reversed.pop()); + ASSERT_EQ(e, reversed.at(--ri)); } + ASSERT_EQ(ri, 0); }; ZArraySlice a_reversed = reverse(a, reverse); diff --git a/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp b/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp index 83d4bd6235b..da357285148 100644 --- a/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp @@ -42,8 +42,8 @@ static bool compare_limits(const malloclimit* a, const malloclimit* b) { static bool compare_sets(const MallocLimitSet* a, const MallocLimitSet* b) { if (compare_limits(a->global_limit(), b->global_limit())) { for (int i = 0; i < mt_number_of_tags; i++) { - if (!compare_limits(a->category_limit(NMTUtil::index_to_tag(i)), - b->category_limit(NMTUtil::index_to_tag(i)))) { + if (!compare_limits(a->mem_tag_limit(NMTUtil::index_to_tag(i)), + b->mem_tag_limit(NMTUtil::index_to_tag(i)))) { return false; } } @@ -92,7 +92,7 @@ TEST(NMT, MallocLimitPerCategory) { test("metaspace:1m,compiler:2m:oom,thread:3m:oom,threadstack:4m:oom,class:5m,classshared:6m", expected); } -TEST(NMT, MallocLimitCategoryEnumNames) { +TEST(NMT, MallocLimitMemTagEnumNames) { MallocLimitSet expected; stringStream option; for (int i = 0; i < mt_number_of_tags; i++) { diff --git a/test/hotspot/gtest/nmt/test_nmt_totals.cpp b/test/hotspot/gtest/nmt/test_nmt_totals.cpp index 61c591fa0bb..690751ede54 100644 --- a/test/hotspot/gtest/nmt/test_nmt_totals.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_totals.cpp @@ -88,8 +88,8 @@ TEST_VM(NMTNumbers, totals) { void* p[NUM_ALLOCS]; for (int i = 0; i < NUM_ALLOCS; i ++) { // spread over categories - int category = i % (mt_number_of_tags - 1); - p[i] = NEW_C_HEAP_ARRAY(char, ALLOC_SIZE, (MemTag)category); + int mtag = i % (mt_number_of_tags - 1); + p[i] = NEW_C_HEAP_ARRAY(char, ALLOC_SIZE, (MemTag)mtag); } const totals_t t2 = get_totals(); diff --git a/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp b/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp index cd47e3a4e17..caf685f587b 100644 --- a/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp +++ b/test/hotspot/gtest/runtime/test_committed_virtualmemory.cpp @@ -43,10 +43,16 @@ public: // snapshot current stack usage VirtualMemoryTracker::Instance::snapshot_thread_stacks(); - ReservedMemoryRegion rmr_found = VirtualMemoryTracker::Instance::tree()->find_reserved_region(stack_end); + ReservedMemoryRegion rmr_found; + { + MemTracker::NmtVirtualMemoryLocker vml; + rmr_found = VirtualMemoryTracker::Instance::tree()->find_reserved_region(stack_end); + } + ASSERT_TRUE(rmr_found.is_valid()); ASSERT_EQ(rmr_found.base(), stack_end); + int i = 0; address i_addr = (address)&i; bool found_i_addr = false; @@ -54,18 +60,20 @@ public: // stack grows downward address stack_top = stack_end + stack_size; bool found_stack_top = false; - VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rmr_found, [&](const CommittedMemoryRegion& cmr) { - if (cmr.base() + cmr.size() == stack_top) { - EXPECT_TRUE(cmr.size() <= stack_size); - found_stack_top = true; - } - if(i_addr < stack_top && i_addr >= cmr.base()) { - found_i_addr = true; - } - i++; - return true; - }); - + { + MemTracker::NmtVirtualMemoryLocker vml; + VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rmr_found, [&](const CommittedMemoryRegion& cmr) { + if (cmr.base() + cmr.size() == stack_top) { + EXPECT_TRUE(cmr.size() <= stack_size); + found_stack_top = true; + } + if (i_addr < stack_top && i_addr >= cmr.base()) { + found_i_addr = true; + } + i++; + return true; + }); + } // stack and guard pages may be contiguous as one region ASSERT_TRUE(i >= 1); diff --git a/test/hotspot/gtest/runtime/test_os_reserve_between.cpp b/test/hotspot/gtest/runtime/test_os_reserve_between.cpp index ee91ff49ded..8e68be22749 100644 --- a/test/hotspot/gtest/runtime/test_os_reserve_between.cpp +++ b/test/hotspot/gtest/runtime/test_os_reserve_between.cpp @@ -27,8 +27,8 @@ #include "runtime/os.hpp" #include "utilities/align.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/hashTable.hpp" #include "utilities/macros.hpp" -#include "utilities/resourceHash.hpp" // #define LOG_PLEASE #include "testutils.hpp" @@ -206,7 +206,7 @@ static void test_attempt_reserve_memory_between_random_distribution(unsigned num // Allocate n times within that hole (with subsequent deletions) and remember unique addresses returned. constexpr unsigned num_tries_per_attach_point = 100; ResourceMark rm; - ResourceHashtable ht; + HashTable ht; const unsigned num_tries = expect_failure ? 3 : (num_possible_attach_points * num_tries_per_attach_point); unsigned num_uniq = 0; // Number of uniq addresses returned diff --git a/test/hotspot/gtest/utilities/test_resourceHash.cpp b/test/hotspot/gtest/utilities/test_hashtable.cpp similarity index 82% rename from test/hotspot/gtest/utilities/test_resourceHash.cpp rename to test/hotspot/gtest/utilities/test_hashtable.cpp index 7f89f6c6b91..4fa4bcfa8fc 100644 --- a/test/hotspot/gtest/utilities/test_resourceHash.cpp +++ b/test/hotspot/gtest/utilities/test_hashtable.cpp @@ -29,9 +29,9 @@ #include "unittest.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/resourceHash.hpp" +#include "utilities/hashTable.hpp" -class CommonResourceHashtableTest : public ::testing::Test { +class CommonHashTableTest : public ::testing::Test { protected: typedef void* K; typedef uintx V; @@ -79,7 +79,7 @@ class CommonResourceHashtableTest : public ::testing::Test { }; -class SmallResourceHashtableTest : public CommonResourceHashtableTest { +class SmallHashTableTest : public CommonHashTableTest { protected: template< @@ -93,7 +93,7 @@ class SmallResourceHashtableTest : public CommonResourceHashtableTest { static void test(V step) { EqualityTestIter et; - ResourceHashtable rh; + HashTable rh; ASSERT_FALSE(rh.contains(as_K(step))); @@ -157,61 +157,61 @@ class SmallResourceHashtableTest : public CommonResourceHashtableTest { }; }; -TEST_VM_F(SmallResourceHashtableTest, default) { +TEST_VM_F(SmallHashTableTest, default) { ResourceMark rm; Runner<>::test(0x1); } -TEST_VM_F(SmallResourceHashtableTest, default_shifted) { +TEST_VM_F(SmallHashTableTest, default_shifted) { ResourceMark rm; Runner<>::test(0x10); } -TEST_VM_F(SmallResourceHashtableTest, bad_hash) { +TEST_VM_F(SmallHashTableTest, bad_hash) { ResourceMark rm; Runner::test(0x1); } -TEST_VM_F(SmallResourceHashtableTest, bad_hash_shifted) { +TEST_VM_F(SmallHashTableTest, bad_hash_shifted) { ResourceMark rm; Runner::test(0x10); } -TEST_VM_F(SmallResourceHashtableTest, identity_hash) { +TEST_VM_F(SmallHashTableTest, identity_hash) { ResourceMark rm; Runner::test(0x1); } -TEST_VM_F(SmallResourceHashtableTest, identity_hash_shifted) { +TEST_VM_F(SmallHashTableTest, identity_hash_shifted) { ResourceMark rm; Runner::test(0x10); } -TEST_VM_F(SmallResourceHashtableTest, primitive_hash_no_rm) { +TEST_VM_F(SmallHashTableTest, primitive_hash_no_rm) { Runner, primitive_equals, 512, AnyObj::C_HEAP>::test(0x1); } -TEST_VM_F(SmallResourceHashtableTest, primitive_hash_no_rm_shifted) { +TEST_VM_F(SmallHashTableTest, primitive_hash_no_rm_shifted) { Runner, primitive_equals, 512, AnyObj::C_HEAP>::test(0x10); } -TEST_VM_F(SmallResourceHashtableTest, bad_hash_no_rm) { +TEST_VM_F(SmallHashTableTest, bad_hash_no_rm) { Runner, 512, AnyObj::C_HEAP>::test(0x1); } -TEST_VM_F(SmallResourceHashtableTest, bad_hash_no_rm_shifted) { +TEST_VM_F(SmallHashTableTest, bad_hash_no_rm_shifted) { Runner, 512, AnyObj::C_HEAP>::test(0x10); } -TEST_VM_F(SmallResourceHashtableTest, identity_hash_no_rm) { +TEST_VM_F(SmallHashTableTest, identity_hash_no_rm) { Runner, 1, AnyObj::C_HEAP>::test(0x1); } -TEST_VM_F(SmallResourceHashtableTest, identity_hash_no_rm_shifted) { +TEST_VM_F(SmallHashTableTest, identity_hash_no_rm_shifted) { Runner, 1, AnyObj::C_HEAP>::test(0x10); } -class GenericResourceHashtableTest : public CommonResourceHashtableTest { +class GenericHashTableTest : public CommonHashTableTest { protected: template< @@ -225,7 +225,7 @@ class GenericResourceHashtableTest : public CommonResourceHashtableTest { static void test(unsigned num_elements = SIZE) { EqualityTestIter et; - ResourceHashtable rh; + HashTable rh; for (uintptr_t i = 0; i < num_elements; ++i) { ASSERT_TRUE(rh.put(as_K(i), i)); @@ -263,39 +263,39 @@ class GenericResourceHashtableTest : public CommonResourceHashtableTest { }; }; -TEST_VM_F(GenericResourceHashtableTest, default) { +TEST_VM_F(GenericHashTableTest, default) { ResourceMark rm; Runner<>::test(); } -TEST_VM_F(GenericResourceHashtableTest, bad_hash) { +TEST_VM_F(GenericHashTableTest, bad_hash) { ResourceMark rm; Runner::test(); } -TEST_VM_F(GenericResourceHashtableTest, identity_hash) { +TEST_VM_F(GenericHashTableTest, identity_hash) { ResourceMark rm; Runner::test(); } -TEST_VM_F(GenericResourceHashtableTest, primitive_hash_no_rm) { +TEST_VM_F(GenericHashTableTest, primitive_hash_no_rm) { Runner, primitive_equals, 512, AnyObj::C_HEAP>::test(); } -TEST_VM_F(GenericResourceHashtableTest, bad_hash_no_rm) { +TEST_VM_F(GenericHashTableTest, bad_hash_no_rm) { Runner, 512, AnyObj::C_HEAP>::test(); } -TEST_VM_F(GenericResourceHashtableTest, identity_hash_no_rm) { +TEST_VM_F(GenericHashTableTest, identity_hash_no_rm) { Runner, 1, AnyObj::C_HEAP>::test(512); } -// Simple ResourceHashtable whose key is a SymbolHandle and value is an int +// Simple HashTable whose key is a SymbolHandle and value is an int // This test is to show that the SymbolHandle will correctly handle the refcounting // in the table. -class SimpleResourceHashtableDeleteTest : public ::testing::Test { +class SimpleHashTableDeleteTest : public ::testing::Test { public: - ResourceHashtable _simple_test_table; + HashTable _simple_test_table; class SimpleDeleter : public StackObj { public: @@ -305,7 +305,7 @@ class SimpleResourceHashtableDeleteTest : public ::testing::Test { }; }; -TEST_VM_F(SimpleResourceHashtableDeleteTest, simple_remove) { +TEST_VM_F(SimpleHashTableDeleteTest, simple_remove) { TempNewSymbol t = SymbolTable::new_symbol("abcdefg_simple"); Symbol* s = t; int s_orig_count = s->refcount(); @@ -317,7 +317,7 @@ TEST_VM_F(SimpleResourceHashtableDeleteTest, simple_remove) { ASSERT_EQ(s->refcount(), s_orig_count) << "refcount should be same as start"; } -TEST_VM_F(SimpleResourceHashtableDeleteTest, simple_delete) { +TEST_VM_F(SimpleHashTableDeleteTest, simple_delete) { TempNewSymbol t = SymbolTable::new_symbol("abcdefg_simple"); Symbol* s = t; int s_orig_count = s->refcount(); @@ -330,10 +330,10 @@ TEST_VM_F(SimpleResourceHashtableDeleteTest, simple_delete) { ASSERT_EQ(s->refcount(), s_orig_count) << "refcount should be same as start"; } -// More complicated ResourceHashtable with SymbolHandle in the key. Since the *same* Symbol is part +// More complicated HashTable with SymbolHandle in the key. Since the *same* Symbol is part // of the value, it's not necessary to manipulate the refcount of the key, but you must in the value. // Luckily SymbolHandle does this. -class ResourceHashtableDeleteTest : public ::testing::Test { +class HashTableDeleteTest : public ::testing::Test { public: class TestValue : public CHeapObj { SymbolHandle _s; @@ -348,8 +348,8 @@ class ResourceHashtableDeleteTest : public ::testing::Test { // Symbol* s() const { return _s; } // needed for conversion from TempNewSymbol to SymbolHandle member }; - // ResourceHashtable whose value is a *copy* of TestValue. - ResourceHashtable _test_table; + // HashTable whose value is a *copy* of TestValue. + HashTable _test_table; class Deleter : public StackObj { public: @@ -362,8 +362,8 @@ class ResourceHashtableDeleteTest : public ::testing::Test { } }; - // ResourceHashtable whose value is a pointer to TestValue. - ResourceHashtable _ptr_test_table; + // HashTable whose value is a pointer to TestValue. + HashTable _ptr_test_table; class PtrDeleter : public StackObj { public: @@ -378,7 +378,7 @@ class ResourceHashtableDeleteTest : public ::testing::Test { }; -TEST_VM_F(ResourceHashtableDeleteTest, value_remove) { +TEST_VM_F(HashTableDeleteTest, value_remove) { TempNewSymbol s = SymbolTable::new_symbol("abcdefg"); int s_orig_count = s->refcount(); { @@ -396,7 +396,7 @@ TEST_VM_F(ResourceHashtableDeleteTest, value_remove) { ASSERT_EQ(s->refcount(), s_orig_count) << "refcount should be as we started"; } -TEST_VM_F(ResourceHashtableDeleteTest, value_delete) { +TEST_VM_F(HashTableDeleteTest, value_delete) { TempNewSymbol d = SymbolTable::new_symbol("defghijklmnop"); int d_orig_count = d->refcount(); { @@ -412,7 +412,7 @@ TEST_VM_F(ResourceHashtableDeleteTest, value_delete) { ASSERT_EQ(d->refcount(), d_orig_count) << "refcount should be as we started"; } -TEST_VM_F(ResourceHashtableDeleteTest, check_delete_ptr) { +TEST_VM_F(HashTableDeleteTest, check_delete_ptr) { TempNewSymbol s = SymbolTable::new_symbol("abcdefg_ptr"); int s_orig_count = s->refcount(); { @@ -432,7 +432,7 @@ TEST_VM_F(ResourceHashtableDeleteTest, check_delete_ptr) { ASSERT_EQ(s->refcount(), s_orig_count) << "refcount should be as we started"; } -class ResourceHashtablePrintTest : public ::testing::Test { +class HashTablePrintTest : public ::testing::Test { public: class TestValue { int _i; @@ -441,7 +441,7 @@ class ResourceHashtablePrintTest : public ::testing::Test { public: TestValue(int i) : _i(i), _j(i+1), _k(i+2) {} }; - ResourceHashtable _test_table; + HashTable _test_table; class TableDeleter { public: @@ -452,7 +452,7 @@ class ResourceHashtablePrintTest : public ::testing::Test { }; }; -TEST_VM_F(ResourceHashtablePrintTest, print_test) { +TEST_VM_F(HashTablePrintTest, print_test) { for (int i = 0; i < 300; i++) { TestValue* tv = new TestValue(i); _test_table.put(i, tv); // all the entries can be the same. diff --git a/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt b/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt new file mode 100644 index 00000000000..636d02cb8b0 --- /dev/null +++ b/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt @@ -0,0 +1,101 @@ +# +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +############################################################################# +# +# List of quarantined tests failing with jvmti stress agent in any mode. +# +############################################################################# + + +compiler/macronodes/TestTopInMacroElimination.java 8362832 generic-all + +gc/stringdedup/TestStringDeduplicationAgeThreshold.java 8362562 generic-all +gc/stringdedup/TestStringDeduplicationInterned.java 8362562 generic-all +gc/stringdedup/TestStringDeduplicationPrintOptions.java 8362562 generic-all + + +serviceability/jvmti/events/SingleStep/singlestep02/singlestep02.java 8362350 generic-all +vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t007/TestDescription.java 8362350 generic-all + +# Incompatbile tests + +# IR +compiler/loopopts/superword/TestDependencyOffsets.java#avx1-v016-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx1-v016-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx1-v032-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx1-v032-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx2-v016-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx2-v016-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx2-v032-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#avx2-v032-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#sse4-v004-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#sse4-v004-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#sse4-v008-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#sse4-v008-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#sse4-v016-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#sse4-v016-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v004-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v004-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v008-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v008-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v016-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v016-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v032-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v032-U 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v064-A 0000000 generic-all +compiler/loopopts/superword/TestDependencyOffsets.java#vec-v064-U 0000000 generic-all + +# Requires solo jvmti capabilities + +compiler/jvmci/events/JvmciShutdownEventTest.java 0000000 generic-all + +# jdwp +runtime/6294277/SourceDebugExtension.java 0000000 generic-all + +# heap stats +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorArrayAllSampledTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorEventOnOffTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCParallelTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCSerialTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorGCTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorIllegalArgumentTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInitialAllocationTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterArrayTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorInterpreterObjectTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorMultiArrayTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorNoCapabilityTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorRecursiveTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatArrayCorrectnessTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatIntervalTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatObjectCorrectnessTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorStatSimpleTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadDisabledTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadOnOffTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTwoAgentsTest.java 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorVMEventsTest.java#id0 0000000 generic-all +serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorVMEventsTest.java#id1 0000000 generic-all diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 7026b6f79d6..37986c67dd8 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -97,6 +97,7 @@ gc/shenandoah/TestEvilSyncBug.java#generational 8345501 generic-all # :hotspot_runtime +runtime/cds/DeterministicDump.java 8363986 macosx-x64,macosx-aarch64 runtime/jni/terminatedThread/TestTerminatedThread.java 8317789 aix-ppc64 runtime/Monitor/SyncOnValueBasedClassTest.java 8340995 linux-s390x runtime/os/TestTracePageSizes.java#no-options 8267460 linux-aarch64 diff --git a/test/hotspot/jtreg/compiler/arguments/TestProfileCasts.java b/test/hotspot/jtreg/compiler/arguments/TestProfileCasts.java new file mode 100644 index 00000000000..1a6bcf37919 --- /dev/null +++ b/test/hotspot/jtreg/compiler/arguments/TestProfileCasts.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8358781 + * @summary Regression test for -XX:-TypeProfileCasts crash + * @requires vm.debug + * @run main/othervm -XX:-TypeProfileCasts -XX:CompileThresholdScaling=0.01 + * compiler.arguments.TestProfileCasts + */ +package compiler.arguments; + +public class TestProfileCasts { + static class Foo { + } + + private static void test(Object o) { + if (o instanceof Foo) { + } + } + + public static void main(String[] args) { + for (int i = 0; i < 100; i++) { + test(new Foo()); + test(null); + } + } +} \ No newline at end of file diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestStack2RegSlotMismatch.java b/test/hotspot/jtreg/compiler/intrinsics/TestStack2RegSlotMismatch.java new file mode 100644 index 00000000000..d2942f061db --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/TestStack2RegSlotMismatch.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8359235 + * @summary Test C1 stack2reg after fixing incorrect use of T_LONG in intrinsic + * @requires vm.debug == true & vm.compiler1.enabled + * @run main/othervm -XX:TieredStopAtLevel=1 + * -XX:C1MaxInlineSize=200 + * -XX:CompileThreshold=10 + * -XX:CompileCommand=compileonly,java.lang.invoke.LambdaFormEditor::putInCache + * compiler.intrinsics.TestStack2RegSlotMismatch + */ +package compiler.intrinsics; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.List; + +public class TestStack2RegSlotMismatch { + public static int target(int x, int y) { + return x + y; + } + + public static void main(String[] args) throws Throwable { + MethodHandle mh = MethodHandles.lookup().findStatic( + TestStack2RegSlotMismatch.class, + "target", + MethodType.methodType(int.class, int.class, int.class) + ); + List argsList = new ArrayList<>(); + int j = 0; + + for (int i = 0; i < 50; i++) { + mh = MethodHandles.dropArguments(mh, 0, int.class); + argsList.add(0); + argsList.add(1); + argsList.add(2); + Object result = mh.invokeWithArguments(argsList); + j += (int) result; + argsList.remove(argsList.size() - 1); + argsList.remove(argsList.size() - 1); + if (i % 5 == 0) { + Thread.sleep(1000); + } + } + + if (j == 150) { + System.out.println("passed"); + } else { + throw new Exception("TestStack2RegSlotMismatch Error"); + } + } +} \ No newline at end of file diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestVerifyIntrinsicChecks.java b/test/hotspot/jtreg/compiler/intrinsics/TestVerifyIntrinsicChecks.java new file mode 100644 index 00000000000..392ca35e2fd --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/TestVerifyIntrinsicChecks.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8361842 + * @summary Verify the effectiveness of the `VerifyIntrinsicChecks` VM flag + * through (bypassing `StringCoding::encodeAsciiArray`, and) feeding + * invalid input to an intrinsified `StringCoding::encodeAsciiArray0` + * (note the `0` suffix!). + * @library /compiler/patches + * @library /test/lib + * @build java.base/java.lang.Helper + * @comment `vm.debug == true` is required since `VerifyIntrinsicChecks` is a + * development flag + * @requires vm.debug == true & vm.flavor == "server" & !vm.graal.enabled + * @run main/othervm compiler.intrinsics.TestVerifyIntrinsicChecks verify + */ + +package compiler.intrinsics; + +import java.lang.Helper; +import java.time.Instant; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +public final class TestVerifyIntrinsicChecks { + + public static void main(String[] args) throws Exception { + switch (args[0]) { + case "verify" -> { + log("Starting JVM in a separate process to verify the crash"); + OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava( + "-Xcomp", + "-XX:-TieredCompilation", + "-XX:CompileCommand=inline,java.lang.StringCoding::encodeAsciiArray0", + "-XX:+VerifyIntrinsicChecks", + "--patch-module", "java.base=%s/java.base".formatted(System.getProperty("test.patch.path")), + "compiler.intrinsics.TestVerifyIntrinsicChecks", + "crash"); + outputAnalyzer.shouldContain("unexpected null in intrinsic"); + outputAnalyzer.shouldNotHaveExitValue(0); + } + case "crash" -> { + log("Triggering the crash"); + warmUpIntrinsicMethod(); + violateIntrinsicMethodContract(); + } + default -> throw new IllegalArgumentException(); + } + } + + private static void warmUpIntrinsicMethod() { + log("Warming up the intrinsic method"); + char[] sa = createAsciiChars(8192); + byte[] sp = new byte[4096]; + for (int i = 0; i < 1_000; i++) { + Helper.StringCodingEncodeAsciiArray0(sa, i, sp, 0, sp.length - i); + } + } + + private static char[] createAsciiChars(int length) { + char[] buffer = new char[length]; + for (int i = 0; i < length; i++) { + buffer[i] = (char) (i % '\u0080'); + } + return buffer; + } + + private static void violateIntrinsicMethodContract() { + log("Violating the intrinsic method contract (sa=null)"); + Helper.StringCodingEncodeAsciiArray0(null, 1, null, 1, 1); + } + + private synchronized static void log(String format, Object... args) { + Object[] extendedArgs = new Object[2 + args.length]; + extendedArgs[0] = Instant.now(); + extendedArgs[1] = Thread.currentThread().getName(); + System.arraycopy(args, 0, extendedArgs, extendedArgs.length - args.length, args.length); + String extendedFormat = "%%s [%%s] %s%%n".formatted(format); + System.out.printf(extendedFormat, extendedArgs); + } + +} diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java index 76ef4766159..1c20a49d281 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ * @build java.base/java.lang.Helper * @run main compiler.intrinsics.string.TestCountPositives */ + /* * @test * @bug 8281146 8318509 @@ -46,17 +47,38 @@ * @run main/othervm/timeout=1200 -XX:UseAVX=3 compiler.intrinsics.string.TestCountPositives * @run main/othervm/timeout=1200 -XX:UseAVX=3 -XX:+UnlockDiagnosticVMOptions -XX:AVX3Threshold=0 compiler.intrinsics.string.TestCountPositives */ -/** - * This test was derived from compiler.intrinsics.string.TestHasNegatives + +/* + * @test + * @bug 8281146 + * @summary Verify `StringCoding::countPositives` intrinsic Java wrapper checks + * by enabling the ones in the VM intrinsic using + * `-XX:+VerifyIntrinsicChecks` + * @comment This does not check out-of-range conditions. The + * `-XX:+VerifyIntrinsicChecks` version of this test simply ensures + * that the VM intrinsic will produce no spurious errors. + * @key randomness + * @library /compiler/patches + * @library /test/lib + * @comment `vm.debug == true` is required since `VerifyIntrinsicChecks` is a + * development flag + * @requires vm.debug == true + * @build java.base/java.lang.Helper + * @run main/othervm + * -XX:+VerifyIntrinsicChecks + * compiler.intrinsics.string.TestCountPositives */ + package compiler.intrinsics.string; import java.lang.Helper; import java.util.Random; -import java.util.stream.IntStream; import jdk.test.lib.Utils; +/** + * This test was derived from {@link TestHasNegatives}. + */ public class TestCountPositives { private static byte[] bytes = new byte[4096 + 32]; diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java index 38a516e7521..bb343b246ff 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,26 @@ * @run main/othervm/timeout=1200 --add-opens=java.base/sun.nio.cs=ALL-UNNAMED -Xbatch -Xmx256m compiler.intrinsics.string.TestEncodeIntrinsics */ +/* + * @test + * @bug 6896617 8274242 + * @summary Verify `sun.nio.cs.ISO_8859_1.Encoder::encodeISOArray` intrinsic + * Java wrapper checks by enabling the ones in the VM intrinsic using + * `-XX:+VerifyIntrinsicChecks` + * @comment This does not check out-of-range conditions. The + * `-XX:+VerifyIntrinsicChecks` version of this test simply ensures + * that the VM intrinsic will produce no spurious errors. + * @key randomness + * @library /test/lib + * @comment `vm.debug == true` is required since `VerifyIntrinsicChecks` is a + * development flag + * @requires vm.debug == true + * @run main/othervm/timeout=1200 + * -XX:+VerifyIntrinsicChecks + * --add-opens=java.base/sun.nio.cs=ALL-UNNAMED -Xbatch -Xmx256m + * compiler.intrinsics.string.TestEncodeIntrinsics + */ + package compiler.intrinsics.string; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java index 6edf2dc2e56..a15f6aade2e 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ * @build java.base/java.lang.Helper * @run main compiler.intrinsics.string.TestHasNegatives */ + /* * @test * @bug 8054307 8318509 @@ -46,11 +47,31 @@ * @run main/othervm/timeout=1200 -XX:UseAVX=3 -XX:+UnlockDiagnosticVMOptions -XX:AVX3Threshold=0 compiler.intrinsics.string.TestHasNegatives */ +/* + * @test + * @bug 8054307 + * @summary Verify `StringCoding::hasNegatives` intrinsic Java wrapper checks + * by enabling the ones in the VM intrinsic using + * `-XX:+VerifyIntrinsicChecks` + * @comment This does not check out-of-range conditions. The + * `-XX:+VerifyIntrinsicChecks` version of this test simply ensures + * that the VM intrinsic will produce no spurious errors. + * @key randomness + * @library /compiler/patches + * @library /test/lib + * @comment `vm.debug == true` is required since `VerifyIntrinsicChecks` is a + * development flag + * @requires vm.debug == true + * @build java.base/java.lang.Helper + * @run main/othervm + * -XX:+VerifyIntrinsicChecks + * compiler.intrinsics.string.TestHasNegatives + */ + package compiler.intrinsics.string; import java.lang.Helper; import java.util.Random; -import java.util.stream.IntStream; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index 6782bd68fed..8e509c66c9a 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java @@ -36,6 +36,7 @@ import jdk.test.lib.Platform; import jdk.test.lib.Utils; import jdk.test.lib.helpers.ClassFileInstaller; import jdk.test.whitebox.WhiteBox; +import jtreg.SkippedException; import java.io.PrintWriter; import java.io.StringWriter; @@ -349,6 +350,7 @@ public class TestFramework { if (shouldInstallWhiteBox()) { installWhiteBox(); } + checkCompatibleFlags(); checkIRRuleCompilePhasesFormat(); disableIRVerificationIfNotFeasible(); @@ -775,12 +777,23 @@ public class TestFramework { return Arrays.stream(testClass.getDeclaredMethods()).anyMatch(m -> m.getAnnotationsByType(IR.class).length > 0); } + private void checkCompatibleFlags() { + for (String flag : Utils.getTestJavaOpts()) { + if (flag.contains("-agentpath")) { + throw new SkippedException("Can't run test with agent."); + } + } + } + private List anyNonWhitelistedJTregVMAndJavaOptsFlags() { List flags = Arrays.stream(Utils.getTestJavaOpts()) .map(s -> s.replaceFirst("-XX:[+|-]?|-(?=[^D|^e])", "")) .collect(Collectors.toList()); List nonWhiteListedFlags = new ArrayList(); for (String flag : flags) { + if (flag.contains("agentpath")) { + throw new SkippedException("Can't run test with -javaagent"); + } // Property flags (prefix -D), -ea and -esa are whitelisted. if (!flag.startsWith("-D") && !flag.startsWith("-e") && JTREG_WHITELIST_FLAGS.stream().noneMatch(flag::contains)) { // Found VM flag that is not whitelisted diff --git a/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateAddSub.java b/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateAddSub.java index 4e8daf3d163..11f37c1f7e7 100644 --- a/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateAddSub.java +++ b/test/hotspot/jtreg/compiler/loopopts/InvariantCodeMotionReassociateAddSub.java @@ -345,7 +345,7 @@ public class InvariantCodeMotionReassociateAddSub { @Test @Arguments(setup = "setup") - @IR(counts = {IRNode.ADD_I, "3"}) + @IR(counts = {IRNode.ADD_I, "2"}) @IR(counts = {IRNode.SUB_I, "1"}) public int addSubInt(int inv1, int inv2, int size) { int result = -1; @@ -381,7 +381,7 @@ public class InvariantCodeMotionReassociateAddSub { @Test @Arguments(setup = "setup") - @IR(counts = {IRNode.ADD_I, "3"}) + @IR(counts = {IRNode.ADD_I, "2"}) @IR(counts = {IRNode.SUB_I, "1"}) public int addSubInt2(int inv1, int inv2, int size) { int result = -1; @@ -417,7 +417,7 @@ public class InvariantCodeMotionReassociateAddSub { @Test @Arguments(setup = "setup") - @IR(counts = {IRNode.ADD_I, "3"}) + @IR(counts = {IRNode.ADD_I, "2"}) @IR(counts = {IRNode.SUB_I, "1"}) public int addSubInt3(int inv1, int inv2, int size) { int result = -1; @@ -453,7 +453,7 @@ public class InvariantCodeMotionReassociateAddSub { @Test @Arguments(setup = "setup") - @IR(counts = {IRNode.ADD_I, "2"}) + @IR(counts = {IRNode.ADD_I, "1"}) @IR(counts = {IRNode.SUB_I, "2"}) public int subSubInt(int inv1, int inv2, int size) { int result = -1; @@ -489,7 +489,7 @@ public class InvariantCodeMotionReassociateAddSub { @Test @Arguments(setup = "setup") - @IR(counts = {IRNode.ADD_I, "2"}) + @IR(counts = {IRNode.ADD_I, "1"}) @IR(counts = {IRNode.SUB_I, "2"}) public int subSubInt2(int inv1, int inv2, int size) { int result = -1; diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java index 97a3c4ed037..322c36c39e1 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestAlignVector.java @@ -1063,8 +1063,16 @@ public class TestAlignVector { IRNode.ADD_VL, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", IRNode.STORE_VECTOR, "> 0"}, applyIfPlatform = {"64-bit", "true"}, - applyIfCPUFeatureOr = {"avx2", "true", "rvv", "true"}) + applyIfCPUFeature = {"avx2", "true"}) // require avx to ensure vectors are larger than what unrolling produces + @IR(counts = {IRNode.LOAD_VECTOR_I, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.LOAD_VECTOR_L, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.ADD_VI, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.ADD_VL, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfPlatform = {"riscv64", "true"}, + applyIfCPUFeature = {"rvv", "true"}, + applyIf = {"MaxVectorSize", ">=32"}) static Object[] test13aIL(int[] a, long[] b) { for (int i = 0; i < RANGE; i++) { a[i]++; @@ -1175,8 +1183,16 @@ public class TestAlignVector { IRNode.ADD_VL, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", IRNode.STORE_VECTOR, "> 0"}, applyIfPlatform = {"64-bit", "true"}, - applyIfCPUFeatureOr = {"avx2", "true", "rvv", "true"}) + applyIfCPUFeature = {"avx2", "true"}) // require avx to ensure vectors are larger than what unrolling produces + @IR(counts = {IRNode.LOAD_VECTOR_I, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.LOAD_VECTOR_L, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.ADD_VI, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.ADD_VL, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfPlatform = {"riscv64", "true"}, + applyIfCPUFeature = {"rvv", "true"}, + applyIf = {"MaxVectorSize", ">=32"}) static Object[] test13bIL(int[] a, long[] b) { for (int i = 1; i < RANGE; i++) { a[i]++; diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestGeneralizedReductions.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestGeneralizedReductions.java index bda0979a70b..6c2f0d825b9 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestGeneralizedReductions.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestGeneralizedReductions.java @@ -160,13 +160,13 @@ public class TestGeneralizedReductions { @Test @IR(applyIfCPUFeatureOr = {"avx2", "true"}, - applyIfAnd = {"SuperWordReductions", "true","UsePopCountInstruction", "true"}, + applyIfAnd = {"SuperWordReductions", "true", "UsePopCountInstruction", "true"}, applyIfPlatform = {"64-bit", "true"}, counts = {IRNode.ADD_REDUCTION_VI, ">= 1", IRNode.POPCOUNT_VL, ">= 1"}) @IR(applyIfPlatform = {"riscv64", "true"}, applyIfCPUFeatureOr = {"zvbb", "true"}, - applyIfAnd = {"SuperWordReductions", "true","UsePopCountInstruction", "true"}, + applyIfAnd = {"SuperWordReductions", "true", "UsePopCountInstruction", "true", "MaxVectorSize", ">=32"}, counts = {IRNode.ADD_REDUCTION_VI, ">= 1", IRNode.POPCOUNT_VL, ">= 1"}) private static long testMapReductionOnGlobalAccumulator(long[] array) { diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment.java index 7ecc14e8980..502fad5be96 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment.java @@ -706,9 +706,11 @@ class TestMemorySegmentImpl { } @Test - // FAILS: invariants are sorted differently, because of differently inserted Cast. - // See: JDK-8331659 - // Interestingly, it now vectorizes for native, but not for arrays. + @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", + IRNode.ADD_VB, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) static Object[] testLongLoop_longIndex_intInvar_byte(MemorySegment a, int invar) { for (long i = 0; i < a.byteSize(); i++) { long adr1 = (long)(i) + (long)(invar); @@ -720,9 +722,11 @@ class TestMemorySegmentImpl { } @Test - // FAILS: invariants are sorted differently, because of differently inserted Cast. - // See: JDK-8331659 - // Interestingly, it now vectorizes for native, but not for arrays. + @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", + IRNode.ADD_VB, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) static Object[] testLongLoop_longIndex_longInvar_byte(MemorySegment a, long invar) { for (long i = 0; i < a.byteSize(); i++) { long adr1 = (long)(i) + (long)(invar); @@ -798,20 +802,12 @@ class TestMemorySegmentImpl { } @Test - @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0", - IRNode.ADD_VI, "= 0", - IRNode.STORE_VECTOR, "= 0"}, - applyIfAnd = { "ShortRunningLongLoop", "false", "AlignVector", "false" }, - applyIfPlatform = {"64-bit", "true"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.ADD_VI, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfAnd = { "ShortRunningLongLoop", "true", "AlignVector", "false" }, + applyIf = { "AlignVector", "false" }, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) - // FAILS: invariants are sorted differently, because of differently inserted Cast. - // See: JDK-8331659 static Object[] testLongLoop_longIndex_intInvar_int(MemorySegment a, int invar) { for (long i = 0; i < a.byteSize()/4; i++) { long adr1 = 4L * (long)(i) + 4L * (long)(invar); @@ -823,20 +819,12 @@ class TestMemorySegmentImpl { } @Test - @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0", - IRNode.ADD_VI, "= 0", - IRNode.STORE_VECTOR, "= 0"}, - applyIfAnd = { "ShortRunningLongLoop", "false", "AlignVector", "false" }, - applyIfPlatform = {"64-bit", "true"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", IRNode.ADD_VI, "> 0", IRNode.STORE_VECTOR, "> 0"}, - applyIfAnd = { "ShortRunningLongLoop", "true", "AlignVector", "false" }, + applyIf = { "AlignVector", "false" }, applyIfPlatform = {"64-bit", "true"}, applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) - // FAILS: invariants are sorted differently, because of differently inserted Cast. - // See: JDK-8331659 static Object[] testLongLoop_longIndex_longInvar_int(MemorySegment a, long invar) { for (long i = 0; i < a.byteSize()/4; i++) { long adr1 = 4L * (long)(i) + 4L * (long)(invar); diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentByteSizeLongLoopLimit.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentByteSizeLongLoopLimit.java new file mode 100644 index 00000000000..4a449d94ca9 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentByteSizeLongLoopLimit.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.loopopts.superword; + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; + +import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.IRNode; +import compiler.lib.ir_framework.Scenario; +import compiler.lib.ir_framework.Test; +import compiler.lib.ir_framework.TestFramework; + +/* + * @test + * @bug 8356176 + * @summary Test vectorization of loops over MemorySegment with unaligned access + * @library /test/lib / + * @run driver compiler.loopopts.superword.TestMemorySegmentByteSizeLongLoopLimit + */ + + +public class TestMemorySegmentByteSizeLongLoopLimit { + public static int SIZE = 10_000; + + public static int[] a = new int[SIZE]; + public static long[] b = new long[SIZE]; + + public static MemorySegment msA = MemorySegment.ofArray(a); + public static MemorySegment msB = MemorySegment.ofArray(b); + + public static void main(String[] args) { + TestFramework f = new TestFramework(); + f.addFlags("-XX:+IgnoreUnrecognizedVMOptions"); + f.addScenarios(new Scenario(0, "-XX:-AlignVector", "-XX:-ShortRunningLongLoop"), + new Scenario(1, "-XX:+AlignVector", "-XX:-ShortRunningLongLoop"), + new Scenario(2, "-XX:-AlignVector", "-XX:+ShortRunningLongLoop"), + new Scenario(3, "-XX:+AlignVector", "-XX:+ShortRunningLongLoop")); + f.start(); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.ADD_VI, IRNode.VECTOR_SIZE + "min(max_int, max_long)", "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfPlatform = {"64-bit", "true"}, + applyIf = {"AlignVector", "false"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + public static void test() { + for (long i = 0; i < msA.byteSize() / 8L; i++) { + int v = msA.get(ValueLayout.JAVA_INT_UNALIGNED, i * 4L); + msB.set(ValueLayout.JAVA_LONG_UNALIGNED, i * 8L, v + 1); + } + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentField.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentField.java new file mode 100644 index 00000000000..6f83b93d055 --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegmentField.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.loopopts.superword; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Utils; + +import java.util.Random; +import java.lang.foreign.*; + +/* + * @test + * @bug 8356176 + * @summary Test vectorization of loops over MemorySegment stored in field + * @library /test/lib / + * @run driver compiler.loopopts.superword.TestMemorySegmentField + */ + +public class TestMemorySegmentField { + static int SIZE = 10_000; + static MemorySegment MS = Arena.ofAuto().allocate(SIZE * 4); + + public static void main(String[] args) { + TestFramework f = new TestFramework(); + f.addFlags("-XX:+IgnoreUnrecognizedVMOptions"); + f.addScenarios(new Scenario(0, "-XX:-AlignVector", "-XX:-ShortRunningLongLoop"), + new Scenario(1, "-XX:+AlignVector", "-XX:-ShortRunningLongLoop"), + new Scenario(2, "-XX:-AlignVector", "-XX:+ShortRunningLongLoop"), + new Scenario(3, "-XX:+AlignVector", "-XX:+ShortRunningLongLoop")); + f.start(); + } + + static int zeroInvarI = 0; + @Test + @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", + IRNode.ADD_VB, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true", "rvv", "true"}) + public static void testFields() { + int invar = zeroInvarI; + for (long i = 0; i < MS.byteSize(); i++) { + long adr = (long)(i) + (long)(invar); + byte v = MS.get(ValueLayout.JAVA_BYTE, adr); + MS.set(ValueLayout.JAVA_BYTE, adr, (byte)(v + 1)); + } + } +} diff --git a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java index e6c8b68fc6f..3985ad8ea90 100644 --- a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java +++ b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java @@ -39,6 +39,11 @@ public class Helper { return StringCoding.countPositives(ba, off, len); } + @jdk.internal.vm.annotation.ForceInline + public static int StringCodingEncodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) { + return StringCoding.encodeAsciiArray0(sa, sp, da, dp, len); + } + @jdk.internal.vm.annotation.ForceInline public static byte[] compressByte(byte[] src, int srcOff, int dstSize, int dstOff, int len) { byte[] dst = new byte[dstSize]; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java index 16334103614..8d0ba2be589 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -106,7 +106,7 @@ public class ArrayIndexFillTest extends VectorizationTestRunner { @Test @IR(applyIfCPUFeatureOr = {"sve", "true", "avx2", "true", "rvv", "true"}, counts = {IRNode.POPULATE_INDEX, "=0"}) - // The ConvI2L can be split through the AddI, creating a mix of + // The ConvI2L can be pushed through the AddI, creating a mix of // ConvI2L(AddI) and AddL(ConvI2L) cases, which do not vectorize. // See: JDK-8332878 public long[] fillLongArray() { @@ -132,8 +132,7 @@ public class ArrayIndexFillTest extends VectorizationTestRunner { @Test @IR(applyIfCPUFeatureOr = {"sve", "true", "avx2", "true", "rvv", "true"}, - counts = {IRNode.POPULATE_INDEX, "=0"}) - // See: JDK-8332878 + counts = {IRNode.POPULATE_INDEX, ">0"}) public float[] fillFloatArray() { float[] res = new float[SIZE]; for (int i = 0; i < SIZE; i++) { @@ -155,8 +154,7 @@ public class ArrayIndexFillTest extends VectorizationTestRunner { @Test @IR(applyIfCPUFeatureOr = {"sve", "true", "avx2", "true", "rvv", "true"}, - counts = {IRNode.POPULATE_INDEX, "=0"}) - // See: JDK-8332878 + counts = {IRNode.POPULATE_INDEX, ">0"}) public double[] fillDoubleArray() { double[] res = new double[SIZE]; for (int i = 0; i < SIZE; i++) { diff --git a/test/hotspot/jtreg/runtime/CompressedOops/UseCompressedOops.java b/test/hotspot/jtreg/runtime/CompressedOops/UseCompressedOops.java index 3d118bf73b1..e5e2a23c565 100644 --- a/test/hotspot/jtreg/runtime/CompressedOops/UseCompressedOops.java +++ b/test/hotspot/jtreg/runtime/CompressedOops/UseCompressedOops.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,8 @@ * @modules java.base/jdk.internal.misc * java.management * @build jdk.test.whitebox.WhiteBox + * @comment Asan changes memory layout and we get a different coops mode + * @requires !vm.asan * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=480 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. UseCompressedOops */ diff --git a/test/hotspot/jtreg/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java b/test/hotspot/jtreg/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java index e28dd7f4d2a..b46a5deb70d 100644 --- a/test/hotspot/jtreg/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java +++ b/test/hotspot/jtreg/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java @@ -88,7 +88,7 @@ public class TestAbortVMOnSafepointTimeout { } else { output.shouldContain("SIGILL"); if (Platform.isLinux()) { - output.shouldContain("(sent by safepoint timeout handler"); + output.shouldContain("(sent by safepoint timeout handler)"); } } output.shouldNotHaveExitValue(0); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java index 0d03313bec2..6db81351e6b 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java @@ -257,6 +257,16 @@ public class AOTFlags { out.shouldContain("AOTCache creation is complete: hello.aot"); out.shouldMatch("Picked up JAVA_TOOL_OPTIONS:.* -Dmy.prop=My' 'string' '-Xshare:off' 'here"); out.shouldHaveExitValue(0); + + // Training run with -XX:+PrintTieredEvents (see JDK-8362530). + printTestCase("Training run with -XX:+PrintTieredEvents"); + pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:AOTMode=record", + "-XX:+PrintTieredEvents", + "-XX:AOTConfiguration=" + aotConfigFile, + "-cp", appJar, helloClass); + out = CDSTestUtils.executeAndLog(pb, "train-with-tiered-events"); + out.shouldHaveExitValue(0); } static void negativeTests() throws Exception { diff --git a/test/hotspot/jtreg/sources/TestIncludesAreSorted.java b/test/hotspot/jtreg/sources/TestIncludesAreSorted.java index 072784cda9d..7460a160ee6 100644 --- a/test/hotspot/jtreg/sources/TestIncludesAreSorted.java +++ b/test/hotspot/jtreg/sources/TestIncludesAreSorted.java @@ -50,6 +50,7 @@ public class TestIncludesAreSorted { "share/classfile", "share/code", "share/compiler", + "share/interpreter", "share/jvmci", "share/libadt", "share/metaprogramming", diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/Allocate/alloc001/alloc001.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/Allocate/alloc001/alloc001.java index eafbbf8b05f..e9f4ee7c4f1 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/Allocate/alloc001/alloc001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/Allocate/alloc001/alloc001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,6 +43,8 @@ * * @comment Not run on AIX as it does not support ulimit -v * @requires os.family != "aix" + * @comment Do not run with asan enabled because asan has issues with ulimit + * @requires !vm.asan * @run main/native nsk.jvmti.Allocate.alloc001.alloc001 */ diff --git a/test/jdk/ProblemList-jvmti-stress-agent.txt b/test/jdk/ProblemList-jvmti-stress-agent.txt new file mode 100644 index 00000000000..4a6e80a9402 --- /dev/null +++ b/test/jdk/ProblemList-jvmti-stress-agent.txt @@ -0,0 +1,47 @@ +# +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +############################################################################# +# +# List of quarantined tests failing with jvmti stress agent in any mode. +# +############################################################################# + + +sun/security/ssl/SSLEngineImpl/SSLEngineKeyLimit.java 8362658 generic-all +sun/security/ssl/SSLSessionImpl/MultiNSTClient.java 8362658 generic-all +sun/security/ssl/SSLSessionImpl/MultiNSTNoSessionCreation.java 8362658 generic-all +sun/security/ssl/SSLSessionImpl/MultiNSTParallel.java 8362658 generic-all +sun/security/ssl/SSLSessionImpl/MultiNSTSequence.java 8362658 generic-all +sun/security/ssl/SSLSessionImpl/ResumptionUpdateBoundValues.java 8362658 generic-all +sun/security/ssl/SSLSocketImpl/SSLSocketKeyLimit.java 8362658 generic-all + + +# List of tests incompatible with jvmti stress agent or requiring more investigation + +com/sun/jdi/EATests.java#id0 0000000 generic-all +com/sun/jdi/ThreadMemoryLeakTest.java 0000000 generic-all + +# weak referenced are not cleared +java/lang/WeakPairMap/Driver.java 0000000 generic-all +java/lang/ref/ReachabilityFenceTest.java 0000000 generic-all diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 84555a6edfb..02eef5888e0 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -615,8 +615,6 @@ java/rmi/server/Unreferenced/finiteGCLatency/FiniteGCLatency.java 7140992 generi java/rmi/transport/rapidExportUnexport/RapidExportUnexport.java 7146541 linux-all -java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java 7191877 generic-all - java/rmi/registry/readTest/CodebaseTest.java 8173324 windows-all java/rmi/registry/multipleRegistries/MultipleRegistries.java 8268182 macosx-all diff --git a/test/jdk/java/awt/ColorClass/WeakColorTest.java b/test/jdk/java/awt/ColorClass/WeakColorTest.java new file mode 100644 index 00000000000..d4adbc3f111 --- /dev/null +++ b/test/jdk/java/awt/ColorClass/WeakColorTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.image.BufferedImage; +import java.awt.image.VolatileImage; +import java.lang.ref.WeakReference; + +import jtreg.SkippedException; + +/* + * @test + * @key headful + * @bug 8364434 + * @summary Check that garbage-collecting Color before accelerated painting is complete does not cause artifacts. + * @requires (os.family != "linux") + * @library /test/lib + * @run main/othervm -Xms16m -Xmx16m WeakColorTest + */ + +public class WeakColorTest { + public static void main(String[] args) throws Exception { + BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); // This image is full-black. + VolatileImage image = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration().createCompatibleVolatileImage(100, 100); + Graphics2D g = image.createGraphics(); + + // Create a new Color - we want it to be collected later. + g.setColor(new Color(255, 0, 0)); + WeakReference color = new WeakReference<>(g.getColor()); + + g.fillRect(0, 0, 100, 100); + + // Change color to prevent Graphics from keeping our Color alive. + g.setColor(Color.BLACK); + + // Force Color to be GC'ed. + final int MAX_ITERATIONS = 1000, ARRAY_SIZE = 1000000; + WeakReference array = null; + for (int i = 0;; i++) { + System.gc(); + if (color.get() == null) { + System.out.println("Color collected at: " + i); + break; + } else if (i >= MAX_ITERATIONS) { + throw new SkippedException("Color was not collected after " + MAX_ITERATIONS + " iterations"); + } + Object[] a = new Object[ARRAY_SIZE]; + a[0] = array; + array = new WeakReference<>(a); + } + + // Do a blit. If it succeeds, the resulting image will be full-black. + g.drawImage(bi, 0, 0, null); + g.dispose(); + + // We expect black. If it's red, then the blit must have failed. + int actualColor = image.getSnapshot().getRGB(50, 50); + if ((actualColor & 0xFFFFFF) != 0) throw new Error("Wrong color: 0x" + Integer.toHexString(actualColor)); + } +} diff --git a/test/jdk/java/awt/color/CheckDefaultProperties.java b/test/jdk/java/awt/color/CheckDefaultProperties.java index 583c46da7e1..fd44a7e669b 100644 --- a/test/jdk/java/awt/color/CheckDefaultProperties.java +++ b/test/jdk/java/awt/color/CheckDefaultProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,8 +34,9 @@ import static java.awt.color.ICC_Profile.CLASS_DISPLAY; /** * @test - * @bug 8256321 - * @summary Verifies profile properties are the same before/after activation + * @bug 8256321 8359380 + * @summary Verifies built-in profile properties are the same before and after + * activation and in copies of built-in profiles */ public final class CheckDefaultProperties { @@ -46,21 +47,26 @@ public final class CheckDefaultProperties { ICC_Profile lrgb = ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB); ICC_Profile pycc = ICC_Profile.getInstance(ColorSpace.CS_PYCC); - // check default values, before profile activation - test(srgb, TYPE_RGB, 3, CLASS_DISPLAY); - test(gray, TYPE_GRAY, 1, CLASS_DISPLAY); - test(xyz, TYPE_XYZ, 3, CLASS_ABSTRACT); - test(lrgb, TYPE_RGB, 3, CLASS_DISPLAY); - test(pycc, TYPE_3CLR, 3, CLASS_COLORSPACECONVERSION); + // checks default values before built-in profiles are activated + test(srgb, gray, xyz, lrgb, pycc); - // activate profiles - srgb.getData(); - gray.getData(); - xyz.getData(); - lrgb.getData(); - pycc.getData(); + // activates built-in profiles and creates copies + ICC_Profile srgbCopy = ICC_Profile.getInstance(srgb.getData()); + ICC_Profile grayCopy = ICC_Profile.getInstance(gray.getData()); + ICC_Profile xyzCopy = ICC_Profile.getInstance(xyz.getData()); + ICC_Profile lrgbCopy = ICC_Profile.getInstance(lrgb.getData()); + ICC_Profile pyccCopy = ICC_Profile.getInstance(pycc.getData()); - // check default values, after profile activation + // checks default values after profile activation + test(srgb, gray, xyz, lrgb, pycc); + + // checks default values in copies of the built-in profiles + test(srgbCopy, grayCopy, xyzCopy, lrgbCopy, pyccCopy); + } + + private static void test(ICC_Profile srgb, ICC_Profile gray, + ICC_Profile xyz, ICC_Profile lrgb, + ICC_Profile pycc) { test(srgb, TYPE_RGB, 3, CLASS_DISPLAY); test(gray, TYPE_GRAY, 1, CLASS_DISPLAY); test(xyz, TYPE_XYZ, 3, CLASS_ABSTRACT); diff --git a/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/BuiltInProfileCheck.java b/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/BuiltInProfileCheck.java index 8f00ab10747..6cc10c47550 100644 --- a/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/BuiltInProfileCheck.java +++ b/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/BuiltInProfileCheck.java @@ -39,7 +39,7 @@ import java.util.Map; public class BuiltInProfileCheck { private static final int HEADER_TAG = ICC_Profile.icSigHead; private static final int INDEX = ICC_Profile.icHdrDeviceClass; - private static final String EXCEPTION_MSG = "Built-in profile cannot be modified"; + private static final String EXCEPTION_MSG = "Can't modify built-in profile"; /** * {@link #prepareTestProfile(String, boolean, int)} * stores the profile to test in testProfile. diff --git a/test/jdk/java/awt/color/ICC_Profile/GetProfileDataAfterTransform.java b/test/jdk/java/awt/color/ICC_Profile/GetProfileDataAfterTransform.java new file mode 100644 index 00000000000..e6a436ba81e --- /dev/null +++ b/test/jdk/java/awt/color/ICC_Profile/GetProfileDataAfterTransform.java @@ -0,0 +1,131 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.color.ColorSpace; +import java.awt.color.ICC_ColorSpace; +import java.awt.color.ICC_Profile; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ColorConvertOp; +import java.util.ArrayList; +import java.util.List; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +import static java.awt.image.BufferedImage.TYPE_INT_RGB; + +/** + * @test + * @bug 8272860 8359380 + * @summary Verifies that ICC_Profile methods work correctly after a + * ColorConvertOp transformation + * @library /test/lib + */ +public final class GetProfileDataAfterTransform { + + private static final int[] CSS = { + ColorSpace.CS_CIEXYZ, ColorSpace.CS_GRAY, + ColorSpace.CS_LINEAR_RGB, ColorSpace.CS_PYCC, ColorSpace.CS_sRGB + }; + + /** + * The main process records expected get*() values before conversion and + * passes them to a subprocess, which verifies it after the transform. + * + * @param args If empty, the main process runs all color space pairs and + * spawns subprocesses. If not empty, args[0] and args[1] are source + * and target color space constants, followed by expected profile + * values to validate in the subprocess. + */ + public static void main(String[] args) throws Exception { + if (args.length == 0) { + for (int csFrom : CSS) { + for (int csTo : CSS) { + var from = (ICC_ColorSpace) ColorSpace.getInstance(csFrom); + var to = (ICC_ColorSpace) ColorSpace.getInstance(csTo); + + ICC_Profile profileFrom = from.getProfile(); + ICC_Profile profileTo = to.getProfile(); + + List cmd = new ArrayList<>(); + cmd.add(GetProfileDataAfterTransform.class.getSimpleName()); + + cmd.add(String.valueOf(csFrom)); + cmd.add(String.valueOf(csTo)); + + for (ICC_Profile p : List.of(profileFrom, profileTo)) { + cmd.add(String.valueOf(p.getPCSType())); + cmd.add(String.valueOf(p.getProfileClass())); + cmd.add(String.valueOf(p.getMinorVersion())); + cmd.add(String.valueOf(p.getMajorVersion())); + cmd.add(String.valueOf(p.getColorSpaceType())); + cmd.add(String.valueOf(p.getNumComponents())); + } + + OutputAnalyzer output = ProcessTools.executeTestJava(cmd); + output.shouldHaveExitValue(0).stdoutShouldBeEmpty() + .stderrShouldBeEmpty(); + } + } + } else { + int csFrom = Integer.parseInt(args[0]); + int csTo = Integer.parseInt(args[1]); + var from = (ICC_ColorSpace) ColorSpace.getInstance(csFrom); + var to = (ICC_ColorSpace) ColorSpace.getInstance(csTo); + + BufferedImageOp op = new ColorConvertOp(from, to, null); + // Note from.getProfile() and to.getProfile() are not loaded yet! + op.filter(new BufferedImage(10, 10, TYPE_INT_RGB), + new BufferedImage(10, 10, TYPE_INT_RGB)); + + test(from.getProfile(), args, 2); + test(to.getProfile(), args, 8); + } + } + + private static void test(ICC_Profile profile, String[] args, int offset) { + // Uncomment when JDK-8272860 is fixed + // if (profile.getData() == null) { + // throw new RuntimeException("Profile data is null"); + // } + if (profile.getPCSType() != Integer.parseInt(args[offset++])) { + throw new RuntimeException("Wrong PCStype"); + } + if (profile.getProfileClass() != Integer.parseInt(args[offset++])) { + throw new RuntimeException("Wrong ProfileClass"); + } + if (profile.getMinorVersion() != Integer.parseInt(args[offset++])) { + throw new RuntimeException("Wrong MinorVersion"); + } + if (profile.getMajorVersion() != Integer.parseInt(args[offset++])) { + throw new RuntimeException("Wrong MajorVersion"); + } + if (profile.getColorSpaceType() != Integer.parseInt(args[offset++])) { + throw new RuntimeException("Wrong ColorSpaceType"); + } + if (profile.getNumComponents() != Integer.parseInt(args[offset])) { + throw new RuntimeException("Wrong NumComponents"); + } + } +} diff --git a/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/exePrintSignalDisposition.c b/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/exePrintSignalDisposition.c index 5a96c2556fc..8f18a32a0f9 100644 --- a/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/exePrintSignalDisposition.c +++ b/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/exePrintSignalDisposition.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -72,7 +72,7 @@ int main(int argc, char** argv) { } else { printf("%p ", handler); } - printf("%X %X\n", act.sa_flags, act.sa_mask); + printf("%X\n", act.sa_flags); } return 0; diff --git a/test/jdk/java/lang/StrictMath/ExpTests.java b/test/jdk/java/lang/StrictMath/ExpTests.java index 9f7d2d1ffd0..c36314a48fa 100644 --- a/test/jdk/java/lang/StrictMath/ExpTests.java +++ b/test/jdk/java/lang/StrictMath/ExpTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8139688 + * @bug 8139688 8362376 * @key randomness * @library /test/lib * @build jdk.test.lib.RandomFactory @@ -87,6 +87,10 @@ public class ExpTests { {-0x1.49f33ad2c1c58p+9, 0x1.f3ccc815431b6p-953}, {+0x1.fce66609f7428p+5, 0x1.b59724cb0bc4cp91}, {-0x1.49f33ad2c1c58p+9, 0x1.f3ccc815431b6p-953}, + + // Test values near 0.5*ln*(2) and 1.5*ln(2) to check refactoring + {0.34657359027997275, 1.4142135623730951}, + {1.0397207708399183, 2.828427124746191}, }; for(double[] testCase: testCases) diff --git a/test/jdk/java/lang/StrictMath/PowTests.java b/test/jdk/java/lang/StrictMath/PowTests.java index 2936dfdf247..4b68c5bdcd6 100644 --- a/test/jdk/java/lang/StrictMath/PowTests.java +++ b/test/jdk/java/lang/StrictMath/PowTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8136874 + * @bug 8136874 8362376 * @summary Tests for StrictMath.pow */ @@ -283,6 +283,21 @@ public class PowTests { 0x1.ffffffffffd9fp1023, // 1.7976931348621944E308 }, + // Check refactoring, abs(y) < 2^31, x < sqrt(3/2), x < sqrt(3) + {1.2, // x < sqrt(3/2) + 5.0, + 2.4883199999999994 + }, + + {1.4142135623730951, // sqrt(3/2) < x < sqrt(3) + 5.0, + 5.656854249492382 + }, + + {2.23606797749979, // x > sqrt(3) + 5.0, + 55.901699437494756 + }, }; for (double[] testCase: testCases) diff --git a/test/jdk/java/net/CookieHandler/B6644726.java b/test/jdk/java/net/CookieHandler/B6644726.java index b1caa182d73..cc31f64c1b0 100644 --- a/test/jdk/java/net/CookieHandler/B6644726.java +++ b/test/jdk/java/net/CookieHandler/B6644726.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,8 +46,8 @@ public class B6644726 { // Let's test the default path lst.add("myCookie1=foo"); // Then some alternate expires format - lst.add("myCookie2=bar; path=/dir; expires=Tue, 19 Aug 2025 16:00:00 GMT"); - lst.add("myCookie3=test; path=/dir; expires=Tue Aug 19 2025 16:00:00 GMT-0100"); + lst.add("myCookie2=bar; path=/dir; expires=Fri, 19 Aug 4242 16:00:00 GMT"); + lst.add("myCookie3=test; path=/dir; expires=Fri Aug 19 4242 16:00:00 GMT-0100"); // Then Netscape draft cookies and domains lst.add("myCookie4=test; domain=.sun.com; path=/dir/foo"); HashMap> map = new HashMap>(); @@ -64,7 +64,8 @@ public class B6644726 { List cookies = cs.getCookies(); // There should be 5 cookies if all dates parsed correctly if (cookies.size() != 5) { - fail("Should have 5 cookies. Got only "+ cookies.size() + ", expires probably didn't parse correctly"); + fail("unexpected cookies: " + cookies + ", should have 5 cookies. Got only " + + cookies.size() + ", expires probably didn't parse correctly"); } // Check Path for first Cookie for (HttpCookie c : cookies) { diff --git a/test/jdk/java/net/httpclient/ProxyServer.java b/test/jdk/java/net/httpclient/ProxyServer.java index 747a20772d1..9ec84fe428f 100644 --- a/test/jdk/java/net/httpclient/ProxyServer.java +++ b/test/jdk/java/net/httpclient/ProxyServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,7 +40,7 @@ import static java.util.stream.Collectors.toList; * Two threads are created per client connection. So, it's not * intended for large numbers of parallel connections. */ -public class ProxyServer extends Thread implements Closeable { +public final class ProxyServer implements Closeable { // could use the test library here - Platform.isWindows(), // but it would force all tests that use ProxyServer to @@ -97,9 +97,7 @@ public class ProxyServer extends Thread implements Closeable { this(port, debug, null); } - public ProxyServer(Integer port, - Boolean debug, - Credentials credentials) + private ProxyServer(Integer port, Boolean debug, Credentials credentials) throws IOException { this.debug = debug; @@ -108,15 +106,8 @@ public class ProxyServer extends Thread implements Closeable { listener.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), port)); this.port = ((InetSocketAddress)listener.getLocalAddress()).getPort(); this.credentials = credentials; - setName("ProxyListener"); - setDaemon(true); - connections = new CopyOnWriteArrayList(); - start(); - } - - public ProxyServer(String s) { - credentials = null; connections = new CopyOnWriteArrayList(); + Thread.ofPlatform().name("ProxyListener").daemon().start(this::run); } /** @@ -148,7 +139,7 @@ public class ProxyServer extends Thread implements Closeable { volatile boolean done; - public void run() { + private void run() { int id = 0; try { while (!done) { @@ -656,10 +647,11 @@ public class ProxyServer extends Thread implements Closeable { int port = Integer.parseInt(args[0]); boolean debug = args.length > 1 && args[1].equals("-debug"); System.out.println("Debugging : " + debug); - ProxyServer ps = new ProxyServer(port, debug); - System.out.println("Proxy server listening on port " + ps.getPort()); - while (true) { - Thread.sleep(5000); + try (ProxyServer ps = new ProxyServer(port, debug)) { + System.out.println("Proxy server listening on port " + ps.getPort()); + while (true) { + Thread.sleep(5000); + } } } } diff --git a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java index 6199596b30c..d5d4b1904ea 100644 --- a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java +++ b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java @@ -24,7 +24,7 @@ /* @test * @bug 4313887 6838333 8343020 8357425 * @summary Unit test for java.nio.file.SecureDirectoryStream - * @requires (os.family == "linux" | os.family == "mac") + * @requires (os.family == "linux" | os.family == "mac" | os.family == "aix") * @library .. /test/lib * @build jdk.test.lib.Platform * @run main SecureDS diff --git a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java index 2370dcd35c4..4de6598a0f4 100644 --- a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java +++ b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/CheckLeaseLeak.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,12 +54,11 @@ import java.rmi.*; import java.rmi.server.*; -import sun.rmi.transport.*; -import sun.rmi.*; import java.util.Map; import java.io.*; import java.lang.reflect.*; import java.rmi.registry.*; +import sun.rmi.transport.*; public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak { public CheckLeaseLeak() throws RemoteException { } @@ -102,8 +101,6 @@ public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak { System.err.println("Created client: " + i); JavaVM jvm = new JavaVM("LeaseLeakClient", - " -Djava.security.policy=" + - TestParams.defaultPolicy + " -Drmi.registry.port=" + registryPort, ""); @@ -128,8 +125,8 @@ public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak { } } - /* numLeft should be 2 - if 11 there is a problem. */ - if (numLeft > 2) { + /* numLeft should be 4 - if 11 there is a problem. */ + if (numLeft > 4) { TestLibrary.bomb("Too many objects in DGCImpl.leaseTable: "+ numLeft); } else { @@ -156,57 +153,51 @@ public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak { Field f; try { - f = (Field) java.security.AccessController.doPrivileged - (new java.security.PrivilegedExceptionAction() { - public Object run() throws Exception { + ObjID dgcID = new ObjID(DGC_ID); + /* + * Construct an ObjectEndpoint containing DGC's + * ObjID. + */ + Class oeClass = + Class.forName("sun.rmi.transport.ObjectEndpoint"); + Class[] constrParams = + new Class[]{ ObjID.class, Transport.class }; + Constructor oeConstructor = + oeClass.getDeclaredConstructor(constrParams); + oeConstructor.setAccessible(true); + Object oe = + oeConstructor.newInstance( + new Object[]{ dgcID, null }); - ObjID dgcID = new ObjID(DGC_ID); + /* + * Get Target that contains DGCImpl in ObjectTable + */ + Class objTableClass = + Class.forName("sun.rmi.transport.ObjectTable"); + Class getTargetParams[] = new Class[] { oeClass }; + Method objTableGetTarget = + objTableClass.getDeclaredMethod("getTarget", + getTargetParams); + objTableGetTarget.setAccessible(true); + Target dgcTarget = (Target) + objTableGetTarget.invoke(null, new Object[]{ oe }); - /* - * Construct an ObjectEndpoint containing DGC's - * ObjID. - */ - Class oeClass = - Class.forName("sun.rmi.transport.ObjectEndpoint"); - Class[] constrParams = - new Class[]{ ObjID.class, Transport.class }; - Constructor oeConstructor = - oeClass.getDeclaredConstructor(constrParams); - oeConstructor.setAccessible(true); - Object oe = - oeConstructor.newInstance( - new Object[]{ dgcID, null }); - - /* - * Get Target that contains DGCImpl in ObjectTable - */ - Class objTableClass = - Class.forName("sun.rmi.transport.ObjectTable"); - Class getTargetParams[] = new Class[] { oeClass }; - Method objTableGetTarget = - objTableClass.getDeclaredMethod("getTarget", - getTargetParams); - objTableGetTarget.setAccessible(true); - Target dgcTarget = (Target) - objTableGetTarget.invoke(null, new Object[]{ oe }); - - /* get the DGCImpl from its Target */ - Method targetGetImpl = - dgcTarget.getClass().getDeclaredMethod + /* get the DGCImpl from its Target */ + Method targetGetImpl = + dgcTarget.getClass().getDeclaredMethod ("getImpl", null); - targetGetImpl.setAccessible(true); - dgcImpl[0] = - (Remote) targetGetImpl.invoke(dgcTarget, null); + targetGetImpl.setAccessible(true); + dgcImpl[0] = + (Remote) targetGetImpl.invoke(dgcTarget, null); - /* Get the lease table from the DGCImpl. */ - Field reflectedLeaseTable = - dgcImpl[0].getClass().getDeclaredField + /* Get the lease table from the DGCImpl. */ + Field reflectedLeaseTable = + dgcImpl[0].getClass().getDeclaredField ("leaseTable"); - reflectedLeaseTable.setAccessible(true); + reflectedLeaseTable.setAccessible(true); + + f = reflectedLeaseTable; - return reflectedLeaseTable; - } - }); /** * This is the leaseTable that will fill up with LeaseInfo @@ -217,9 +208,6 @@ public class CheckLeaseLeak extends UnicastRemoteObject implements LeaseLeak { numLeaseInfosLeft = leaseTable.size(); } catch(Exception e) { - if (e instanceof java.security.PrivilegedActionException) - e = ((java.security.PrivilegedActionException) e). - getException(); TestLibrary.bomb(e); } diff --git a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/LeaseLeakClient.java b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/LeaseLeakClient.java index d9cf9fa9e96..dd24b819146 100644 --- a/test/jdk/java/rmi/transport/checkLeaseInfoLeak/LeaseLeakClient.java +++ b/test/jdk/java/rmi/transport/checkLeaseInfoLeak/LeaseLeakClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ import java.rmi.registry.*; public class LeaseLeakClient { public static void main(String args[]) { - TestLibrary.suggestSecurityManager("java.rmi.RMISecurityManager"); try { LeaseLeak leaseLeak = null; diff --git a/test/jdk/java/text/Format/DecimalFormat/DoubleFormattingTest.java b/test/jdk/java/text/Format/DecimalFormat/DoubleFormattingTest.java new file mode 100644 index 00000000000..5a20965c67f --- /dev/null +++ b/test/jdk/java/text/Format/DecimalFormat/DoubleFormattingTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact 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 8362448 + * @summary Verify DecimalFormat::format on doubles. + * @run junit DoubleFormattingTest + * @run junit/othervm -Djdk.compat.DecimalFormat=true DoubleFormattingTest + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Formatter; +import java.util.Locale; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +public class DoubleFormattingTest { + + private static final boolean COMPAT = Boolean.getBoolean("jdk.compat.DecimalFormat"); + + static Stream testFormat() { + return Stream.of( + Arguments.of(4.8726570057E288, 0), + Arguments.of(7.3879E20, 0), + Arguments.of(1.9400994884341945E25, 0), + Arguments.of(6.3E-322, 324) + ); + } + + @ParameterizedTest + @MethodSource + void testFormat(double v, int minFractionDigits) { + DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.ROOT); + df.setGroupingUsed(false); + df.setMinimumFractionDigits(minFractionDigits); + String actual = df.format(v); + Formatter fmt = new Formatter(Locale.ROOT); + fmt.format("%." + minFractionDigits + "f", v); + String expected = fmt.toString(); + if (COMPAT) { + assertNotEquals(expected, actual); + } else { + assertEquals(expected, actual); + } + } + +} diff --git a/test/jdk/jdk/classfile/BuilderTryCatchTest.java b/test/jdk/jdk/classfile/BuilderTryCatchTest.java index 3de12559163..0a966e2a655 100644 --- a/test/jdk/jdk/classfile/BuilderTryCatchTest.java +++ b/test/jdk/jdk/classfile/BuilderTryCatchTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* * @test + * @bug 8361638 * @summary Testing ClassFile builder blocks. * @run junit BuilderTryCatchTest */ @@ -37,6 +38,7 @@ import java.lang.classfile.instruction.ExceptionCatch; import static java.lang.classfile.ClassFile.ACC_PUBLIC; import static java.lang.classfile.ClassFile.ACC_STATIC; +import static java.lang.constant.ConstantDescs.*; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; @@ -47,20 +49,40 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; -import static java.lang.constant.ConstantDescs.CD_Double; -import static java.lang.constant.ConstantDescs.CD_Integer; -import static java.lang.constant.ConstantDescs.CD_Object; -import static java.lang.constant.ConstantDescs.CD_String; - class BuilderTryCatchTest { static final ClassDesc CD_IOOBE = IndexOutOfBoundsException.class.describeConstable().get(); static final ClassDesc CD_NPE = NullPointerException.class.describeConstable().get(); static final MethodTypeDesc MTD_String = MethodType.methodType(String.class).describeConstable().get(); + @Test + void testExceptionalContracts() throws Throwable { + generateTryCatchMethod(catchBuilder -> { + Consumer handler = tb -> tb.pop().aconst_null().areturn(); + assertThrows(NullPointerException.class, () -> catchBuilder.catching(CD_NPE, null)); + assertThrows(NullPointerException.class, () -> catchBuilder.catchingMulti(null, handler)); + assertThrows(NullPointerException.class, () -> catchBuilder.catchingMulti(List.of(), null)); + assertThrows(NullPointerException.class, () -> catchBuilder.catchingMulti(Collections.singletonList(null), null)); + assertThrows(NullPointerException.class, () -> catchBuilder.catchingAll(null)); + catchBuilder.catchingMulti(List.of(CD_IOOBE, CD_NPE), tb -> { + tb.invokevirtual(CD_Object, "toString", MTD_String); + tb.astore(1); + }); + catchBuilder.catchingAll(tb -> tb.pop().loadConstant("all").areturn()); + assertThrows(IllegalArgumentException.class, () -> catchBuilder.catching(CD_int, handler)); + assertDoesNotThrow(() -> catchBuilder.catching(CD_NPE, handler)); + assertDoesNotThrow(() -> catchBuilder.catching(null, handler)); + assertDoesNotThrow(() -> catchBuilder.catchingMulti(List.of(), handler)); + assertDoesNotThrow(() -> catchBuilder.catchingMulti(List.of(CD_Exception, CD_IOOBE), handler)); + assertThrows(IllegalArgumentException.class, () -> catchBuilder.catchingMulti(List.of(CD_long, CD_Throwable), handler)); + assertDoesNotThrow(() -> catchBuilder.catchingAll(handler)); + }); + } + @Test void testTryCatchCatchAll() throws Throwable { byte[] bytes = generateTryCatchMethod(catchBuilder -> { diff --git a/test/jdk/jdk/internal/jimage/ImageReaderTest.java b/test/jdk/jdk/internal/jimage/ImageReaderTest.java new file mode 100644 index 00000000000..8db9a3768d4 --- /dev/null +++ b/test/jdk/jdk/internal/jimage/ImageReaderTest.java @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.internal.jimage.ImageReader; +import jdk.internal.jimage.ImageReader.Node; +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.util.JarBuilder; +import jdk.tools.jlink.internal.LinkableRuntimeImage; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import tests.Helper; +import tests.JImageGenerator; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; + +/* + * @test + * @summary Tests for ImageReader. + * @modules java.base/jdk.internal.jimage + * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jimage + * @library /test/jdk/tools/lib + * /test/lib + * @build tests.* + * @run junit/othervm ImageReaderTest + */ + +/// Using PER_CLASS lifecycle means the (expensive) image file is only build once. +/// There is no mutable test instance state to worry about. +@TestInstance(PER_CLASS) +public class ImageReaderTest { + + private static final Map> IMAGE_ENTRIES = Map.of( + "modfoo", Arrays.asList( + "com.foo.Alpha", + "com.foo.Beta", + "com.foo.bar.Gamma"), + "modbar", Arrays.asList( + "com.bar.One", + "com.bar.Two")); + private final Path image = buildJImage(IMAGE_ENTRIES); + + @ParameterizedTest + @ValueSource(strings = { + "/", + "/modules", + "/modules/modfoo", + "/modules/modbar", + "/modules/modfoo/com", + "/modules/modfoo/com/foo", + "/modules/modfoo/com/foo/bar"}) + public void testModuleDirectories_expected(String name) throws IOException { + try (ImageReader reader = ImageReader.open(image)) { + assertDir(reader, name); + } + } + + @ParameterizedTest + @ValueSource(strings = { + "", + "//", + "/modules/", + "/modules/unknown", + "/modules/modbar/", + "/modules/modfoo//com", + "/modules/modfoo/com/"}) + public void testModuleNodes_absent(String name) throws IOException { + try (ImageReader reader = ImageReader.open(image)) { + assertAbsent(reader, name); + } + } + + @Test + public void testModuleResources() throws IOException { + try (ImageReader reader = ImageReader.open(image)) { + assertNode(reader, "/modules/modfoo/com/foo/Alpha.class"); + assertNode(reader, "/modules/modbar/com/bar/One.class"); + + ImageClassLoader loader = new ImageClassLoader(reader, IMAGE_ENTRIES.keySet()); + assertEquals("Class: com.foo.Alpha", loader.loadAndGetToString("modfoo", "com.foo.Alpha")); + assertEquals("Class: com.foo.Beta", loader.loadAndGetToString("modfoo", "com.foo.Beta")); + assertEquals("Class: com.foo.bar.Gamma", loader.loadAndGetToString("modfoo", "com.foo.bar.Gamma")); + assertEquals("Class: com.bar.One", loader.loadAndGetToString("modbar", "com.bar.One")); + } + } + + @Test + public void testPackageDirectories() throws IOException { + try (ImageReader reader = ImageReader.open(image)) { + Node root = assertDir(reader, "/packages"); + Set pkgNames = root.getChildNames().collect(Collectors.toSet()); + assertTrue(pkgNames.contains("/packages/com")); + assertTrue(pkgNames.contains("/packages/com.foo")); + assertTrue(pkgNames.contains("/packages/com.bar")); + + // Even though no classes exist directly in the "com" package, it still + // creates a directory with links back to all the modules which contain it. + Set comLinks = assertDir(reader, "/packages/com").getChildNames().collect(Collectors.toSet()); + assertTrue(comLinks.contains("/packages/com/modfoo")); + assertTrue(comLinks.contains("/packages/com/modbar")); + } + } + + @Test + public void testPackageLinks() throws IOException { + try (ImageReader reader = ImageReader.open(image)) { + Node moduleFoo = assertDir(reader, "/modules/modfoo"); + Node moduleBar = assertDir(reader, "/modules/modbar"); + assertSame(assertLink(reader, "/packages/com.foo/modfoo").resolveLink(), moduleFoo); + assertSame(assertLink(reader, "/packages/com.bar/modbar").resolveLink(), moduleBar); + } + } + + private static ImageReader.Node assertNode(ImageReader reader, String name) throws IOException { + ImageReader.Node node = reader.findNode(name); + assertNotNull(node, "Could not find node: " + name); + return node; + } + + private static ImageReader.Node assertDir(ImageReader reader, String name) throws IOException { + ImageReader.Node dir = assertNode(reader, name); + assertTrue(dir.isDirectory(), "Node was not a directory: " + name); + return dir; + } + + private static ImageReader.Node assertLink(ImageReader reader, String name) throws IOException { + ImageReader.Node link = assertNode(reader, name); + assertTrue(link.isLink(), "Node was not a symbolic link: " + name); + return link; + } + + private static void assertAbsent(ImageReader reader, String name) throws IOException { + assertNull(reader.findNode(name), "Should not be able to find node: " + name); + } + + /// Builds a jimage file with the specified class entries. The classes in the built + /// image can be loaded and executed to return their names via `toString()` to confirm + /// the correct bytes were returned. + public static Path buildJImage(Map> entries) { + Helper helper = getHelper(); + Path outDir = helper.createNewImageDir("test"); + JImageGenerator.JLinkTask jlink = JImageGenerator.getJLinkTask() + .modulePath(helper.defaultModulePath()) + .output(outDir); + + Path jarDir = helper.getJarDir(); + entries.forEach((module, classes) -> { + JarBuilder jar = new JarBuilder(jarDir.resolve(module + ".jar").toString()); + String moduleInfo = "module " + module + " {}"; + jar.addEntry("module-info.class", InMemoryJavaCompiler.compile("module-info", moduleInfo)); + + classes.forEach(fqn -> { + int lastDot = fqn.lastIndexOf('.'); + String pkg = fqn.substring(0, lastDot); + String cls = fqn.substring(lastDot + 1); + + String path = fqn.replace('.', '/') + ".class"; + String source = String.format( + """ + package %s; + public class %s { + public String toString() { + return "Class: %s"; + } + } + """, pkg, cls, fqn); + jar.addEntry(path, InMemoryJavaCompiler.compile(fqn, source)); + }); + try { + jar.build(); + } catch (IOException e) { + throw new RuntimeException(e); + } + jlink.addMods(module); + }); + return jlink.call().assertSuccess().resolve("lib", "modules"); + } + + /// Returns the helper for building JAR and jimage files. + private static Helper getHelper() { + Helper helper; + try { + boolean isLinkableRuntime = LinkableRuntimeImage.isLinkableRuntime(); + helper = Helper.newHelper(isLinkableRuntime); + } catch (IOException e) { + throw new RuntimeException(e); + } + Assumptions.assumeTrue(helper != null, "Cannot create test helper, skipping test!"); + return helper; + } + + /// Loads and performs actions on classes stored in a given `ImageReader`. + private static class ImageClassLoader extends ClassLoader { + private final ImageReader reader; + private final Set testModules; + + private ImageClassLoader(ImageReader reader, Set testModules) { + this.reader = reader; + this.testModules = testModules; + } + + @FunctionalInterface + public interface ClassAction { + R call(Class cls) throws T; + } + + String loadAndGetToString(String module, String fqn) { + return loadAndCall(module, fqn, c -> c.getDeclaredConstructor().newInstance().toString()); + } + + R loadAndCall(String module, String fqn, ClassAction action) { + Class cls = findClass(module, fqn); + assertNotNull(cls, "Could not load class: " + module + "/" + fqn); + try { + return action.call(cls); + } catch (Exception e) { + fail("Class loading failed", e); + return null; + } + } + + @Override + protected Class findClass(String module, String fqn) { + assumeTrue(testModules.contains(module), "Can only load classes in modules: " + testModules); + String name = "/modules/" + module + "/" + fqn.replace('.', '/') + ".class"; + Class cls = findLoadedClass(fqn); + if (cls == null) { + try { + ImageReader.Node node = reader.findNode(name); + if (node != null && node.isResource()) { + byte[] classBytes = reader.getResource(node); + cls = defineClass(fqn, classBytes, 0, classBytes.length); + resolveClass(cls); + return cls; + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return null; + } + } +} diff --git a/test/jdk/jdk/internal/jimage/JImageReadTest.java b/test/jdk/jdk/internal/jimage/JImageReadTest.java index ea700d03a4f..35fb2adb687 100644 --- a/test/jdk/jdk/internal/jimage/JImageReadTest.java +++ b/test/jdk/jdk/internal/jimage/JImageReadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,6 +49,9 @@ import org.testng.annotations.Test; import org.testng.Assert; import org.testng.TestNG; +import static java.nio.ByteOrder.BIG_ENDIAN; +import static java.nio.ByteOrder.LITTLE_ENDIAN; + @Test public class JImageReadTest { @@ -333,32 +336,21 @@ public class JImageReadTest { */ @Test static void test5_imageReaderEndianness() throws IOException { - ImageReader nativeReader = ImageReader.open(imageFile); - Assert.assertEquals(nativeReader.getByteOrder(), ByteOrder.nativeOrder()); - - try { - ImageReader leReader = ImageReader.open(imageFile, ByteOrder.LITTLE_ENDIAN); - Assert.assertEquals(leReader.getByteOrder(), ByteOrder.LITTLE_ENDIAN); - leReader.close(); - } catch (IOException io) { - // IOException expected if LITTLE_ENDIAN not the nativeOrder() - Assert.assertNotEquals(ByteOrder.nativeOrder(), ByteOrder.LITTLE_ENDIAN); + // Will be opened with native byte order. + try (ImageReader nativeReader = ImageReader.open(imageFile)) { + // Just ensure something works as expected. + Assert.assertNotNull(nativeReader.findNode("/")); + } catch (IOException expected) { + Assert.fail("Reader should be openable with native byte order."); } - try { - ImageReader beReader = ImageReader.open(imageFile, ByteOrder.BIG_ENDIAN); - Assert.assertEquals(beReader.getByteOrder(), ByteOrder.BIG_ENDIAN); - beReader.close(); - } catch (IOException io) { - // IOException expected if LITTLE_ENDIAN not the nativeOrder() - Assert.assertNotEquals(ByteOrder.nativeOrder(), ByteOrder.BIG_ENDIAN); - } - - nativeReader.close(); + // Reader should not be openable with the wrong byte order. + ByteOrder otherOrder = ByteOrder.nativeOrder() == BIG_ENDIAN ? LITTLE_ENDIAN : BIG_ENDIAN; + Assert.assertThrows(IOException.class, () -> ImageReader.open(imageFile, otherOrder)); } - // main method to run standalone from jtreg - @Test(enabled=false) + // main method to run standalone from jtreg + @Test(enabled = false) @Parameters({"x"}) @SuppressWarnings("raw_types") public static void main(@Optional String[] args) { diff --git a/test/jdk/jdk/jfr/api/consumer/streaming/TestFilledChunks.java b/test/jdk/jdk/jfr/api/consumer/streaming/TestFilledChunks.java index 423e3969af8..f435fe9747f 100644 --- a/test/jdk/jdk/jfr/api/consumer/streaming/TestFilledChunks.java +++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestFilledChunks.java @@ -26,6 +26,7 @@ package jdk.jfr.api.consumer.streaming; import java.util.Random; import jdk.jfr.Event; +import jdk.jfr.StackTrace; import jdk.jfr.consumer.RecordingStream; /** @@ -38,6 +39,7 @@ import jdk.jfr.consumer.RecordingStream; */ public class TestFilledChunks { + @StackTrace(false) static class FillEvent extends Event { String message; int value; diff --git a/test/jdk/jdk/jfr/api/consumer/streaming/TestStartMultiChunk.java b/test/jdk/jdk/jfr/api/consumer/streaming/TestStartMultiChunk.java index fbc27b051b0..71113631521 100644 --- a/test/jdk/jdk/jfr/api/consumer/streaming/TestStartMultiChunk.java +++ b/test/jdk/jdk/jfr/api/consumer/streaming/TestStartMultiChunk.java @@ -44,7 +44,7 @@ import jdk.jfr.consumer.RecordingStream; */ public class TestStartMultiChunk { - @Period("10 s") + @Period("2 s") @Name("Zebra") static class ZebraEvent extends Event { } @@ -65,7 +65,7 @@ public class TestStartMultiChunk { CountDownLatch dogLatch = new CountDownLatch(1); CountDownLatch catLatch = new CountDownLatch(1); CountDownLatch mouseLatch = new CountDownLatch(1); - CountDownLatch zebraLatch = new CountDownLatch(3); + CountDownLatch zebraLatch = new CountDownLatch(2); FlightRecorder.addPeriodicEvent(ZebraEvent.class, () -> { ZebraEvent ze = new ZebraEvent(); diff --git a/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWith32BitOops.java b/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWith32BitOops.java index 9ef90afb063..c98f055aa72 100644 --- a/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWith32BitOops.java +++ b/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWith32BitOops.java @@ -35,6 +35,8 @@ import jdk.test.whitebox.WhiteBox; * @requires vm.gc == "Parallel" | vm.gc == null * @requires os.family == "linux" | os.family == "windows" * @requires sun.arch.data.model == "64" + * @comment Asan changes memory layout and we get a different coops mode + * @requires !vm.asan * @library /test/lib /test/jdk * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox diff --git a/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWithZeroBasedOops.java b/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWithZeroBasedOops.java index 62d858eef98..f69d192ca5f 100644 --- a/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWithZeroBasedOops.java +++ b/test/jdk/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWithZeroBasedOops.java @@ -33,6 +33,8 @@ import jdk.test.lib.jfr.EventVerifier; * @requires vm.gc == "Parallel" | vm.gc == null * @requires os.family == "linux" | os.family == "windows" * @requires sun.arch.data.model == "64" + * @comment Asan changes memory layout and we get a different coops mode + * @requires !vm.asan * @library /test/lib /test/jdk * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:+UseCompressedOops -Xmx4g jdk.jfr.event.gc.configuration.TestGCHeapConfigurationEventWithZeroBasedOops */ diff --git a/test/jdk/jdk/jfr/event/runtime/StressJavaMonitorEvents.java b/test/jdk/jdk/jfr/event/runtime/StressJavaMonitorEvents.java index ed3b5454163..fa9bb0c18a2 100644 --- a/test/jdk/jdk/jfr/event/runtime/StressJavaMonitorEvents.java +++ b/test/jdk/jdk/jfr/event/runtime/StressJavaMonitorEvents.java @@ -52,7 +52,7 @@ import jdk.test.lib.thread.XRun; */ public class StressJavaMonitorEvents { - static final int RUN_TIME_MS = 10000; + static final int RUN_TIME_MS = 5000; static final int GC_EVERY_MS = 500; static final int THREADS = 4; static final int NUM_LOCKS = 1024; diff --git a/test/jdk/jdk/jfr/event/runtime/TestFlush.java b/test/jdk/jdk/jfr/event/runtime/TestFlush.java index 5c450eab05f..fa570406f2a 100644 --- a/test/jdk/jdk/jfr/event/runtime/TestFlush.java +++ b/test/jdk/jdk/jfr/event/runtime/TestFlush.java @@ -143,8 +143,6 @@ public class TestFlush { printFlushEvent(re); Asserts.assertTrue(re.getEventType().getName().contains("Flush"), "invalid Event type"); Asserts.assertGT((long) re.getValue("flushId"), 0L, "Invalid flush ID"); - Asserts.assertGT((long) re.getValue("elements"), 0L, "No elements"); - Asserts.assertGT((long) re.getValue("size"), 0L, "Empty size"); } private static void acknowledgeFlushEvent() { diff --git a/test/jdk/jdk/jfr/jmx/streaming/TestMaxSize.java b/test/jdk/jdk/jfr/jmx/streaming/TestMaxSize.java index b79fbc2bcf0..5f490594370 100644 --- a/test/jdk/jdk/jfr/jmx/streaming/TestMaxSize.java +++ b/test/jdk/jdk/jfr/jmx/streaming/TestMaxSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicInteger; import javax.management.MBeanServerConnection; import jdk.jfr.Event; +import jdk.jfr.StackTrace; import jdk.management.jfr.RemoteRecordingStream; /** @@ -45,6 +46,7 @@ import jdk.management.jfr.RemoteRecordingStream; */ public class TestMaxSize { + @StackTrace(false) static class Monkey extends Event { } @@ -92,7 +94,7 @@ public class TestMaxSize { m.commit(); } System.out.println("Emitted " + count + " events"); - Thread.sleep(1000); + Thread.sleep(100); } private static int fileCount(Path dir) throws IOException { diff --git a/test/jdk/jdk/jfr/jmx/streaming/TestRemoteDump.java b/test/jdk/jdk/jfr/jmx/streaming/TestRemoteDump.java index 9390857c09e..6731f3c44cf 100644 --- a/test/jdk/jdk/jfr/jmx/streaming/TestRemoteDump.java +++ b/test/jdk/jdk/jfr/jmx/streaming/TestRemoteDump.java @@ -97,7 +97,7 @@ public class TestRemoteDump { } } - private static List recordWithPolicy(String filename, Consumer policy) throws Exception { + private static List recordWithPolicy(String filename, boolean awaitEvents, Consumer policy) throws Exception { CountDownLatch latch1 = new CountDownLatch(1); CountDownLatch latch2 = new CountDownLatch(2); CountDownLatch latch3 = new CountDownLatch(3); @@ -111,14 +111,18 @@ public class TestRemoteDump { rs.startAsync(); DumpEvent e1 = new DumpEvent(); e1.commit(); - latch1.await(); + if (awaitEvents) { + latch1.await(); + } // Force chunk rotation try (Recording r = new Recording()) { r.start(); DumpEvent e2 = new DumpEvent(); e2.commit(); } - latch2.await(); + if (awaitEvents) { + latch2.await(); + } DumpEvent e3 = new DumpEvent(); e3.commit(); latch3.await(); @@ -129,7 +133,7 @@ public class TestRemoteDump { } private static void testSetMaxSize() throws Exception { - var events = recordWithPolicy("max-size.jfr", rs -> { + var events = recordWithPolicy("max-size.jfr", false, rs -> { // keeps all events for the dump rs.setMaxSize(100_000_000); }); @@ -140,7 +144,7 @@ public class TestRemoteDump { } private static void testSetMaxAge() throws Exception { - var events = recordWithPolicy("max-age.jfr", rs -> { + var events = recordWithPolicy("max-age.jfr", false, rs -> { // keeps all events for the dump rs.setMaxAge(Duration.ofDays(1)); }); @@ -151,7 +155,7 @@ public class TestRemoteDump { } private static void testSetNoPolicy() throws Exception { - var events = recordWithPolicy("no-policy.jfr", rs -> { + var events = recordWithPolicy("no-policy.jfr", true, rs -> { // use default policy, remove after consumption }); // Since latch3 have been triggered at least two events/chunks diff --git a/test/jdk/jdk/jfr/jvm/TestWaste.java b/test/jdk/jdk/jfr/jvm/TestWaste.java index c755ca4c3d0..4de14fc2461 100644 --- a/test/jdk/jdk/jfr/jvm/TestWaste.java +++ b/test/jdk/jdk/jfr/jvm/TestWaste.java @@ -61,7 +61,7 @@ public class TestWaste { try (Recording r = new Recording(c)) { // Old objects that are cleared out should not create waste r.enable("jdk.OldObjectSample") - .with("cutoff", "infinity") + .with("cutoff", "2 s") .withStackTrace(); // No stack trace waste from allocation sample r.enable("jdk.ObjectAllocationSample") diff --git a/test/jdk/jdk/jfr/startupargs/TestMultipleStartupRecordings.java b/test/jdk/jdk/jfr/startupargs/TestMultipleStartupRecordings.java index c70f9810568..bfa02fd8a74 100644 --- a/test/jdk/jdk/jfr/startupargs/TestMultipleStartupRecordings.java +++ b/test/jdk/jdk/jfr/startupargs/TestMultipleStartupRecordings.java @@ -74,13 +74,6 @@ public class TestMultipleStartupRecordings { test(pb, "Started recording 1", "Started recording 2", "Started recording 3"); } - private static void testDefault() throws Exception { - System.out.println("testDefault"); - launchUnary(null); - launchBinary(null, null); - launchTernary(null, null, null); - } - private static void testColonDelimited() throws Exception { launchBinary(":name=myrecording1,filename=myrecording1.jfr", ":filename=myrecording2.jfr,name=myrecording2"); } @@ -99,7 +92,6 @@ public class TestMultipleStartupRecordings { } public static void main(String[] args) throws Exception { - testDefault(); testColonDelimited(); testMixed(); testWithFlightRecorderOptions(); diff --git a/test/jdk/jdk/jfr/tool/TestPrintContextual.java b/test/jdk/jdk/jfr/tool/TestPrintContextual.java index efecc31cb16..15486148b9c 100644 --- a/test/jdk/jdk/jfr/tool/TestPrintContextual.java +++ b/test/jdk/jdk/jfr/tool/TestPrintContextual.java @@ -410,6 +410,7 @@ public class TestPrintContextual { private static List readPrintedLines(Path file, String... options) throws Exception { JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jfr"); launcher.addToolArg("print"); + launcher.addToolArg("--exact"); for (String option : options) { launcher.addToolArg(option); } diff --git a/test/jdk/sun/security/krb5/config/IncludeRandom.java b/test/jdk/sun/security/krb5/config/IncludeRandom.java index 11f93f17830..fe05917fd4c 100644 --- a/test/jdk/sun/security/krb5/config/IncludeRandom.java +++ b/test/jdk/sun/security/krb5/config/IncludeRandom.java @@ -53,7 +53,7 @@ public class IncludeRandom { public static void main(String[] args) throws Exception { System.setProperty("java.security.krb5.conf", "f"); - for (var i = 0; i < 10_000; i++) { + for (var i = 0; i < 1000; i++) { test(); } } diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/SupportedDHKeys.java b/test/jdk/sun/security/pkcs11/KeyAgreement/SupportedDHKeys.java index c4f8e189937..d044934fb59 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/SupportedDHKeys.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/SupportedDHKeys.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,8 @@ * @run main/othervm SupportedDHKeys */ +import jtreg.SkippedException; + import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -62,8 +64,7 @@ public class SupportedDHKeys extends PKCS11Test { @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyPairGenerator", "DiffieHellman") == null) { - System.out.println("No support of DH KeyPairGenerator, skipping"); - return; + throw new SkippedException("No support of DH KeyPairGenerator, skipping"); } for (SupportedKeySize keySize : SupportedKeySize.values()) { diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/TestDH.java b/test/jdk/sun/security/pkcs11/KeyAgreement/TestDH.java index 560459c2b09..1140ba75e12 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/TestDH.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/TestDH.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,14 +38,14 @@ import java.util.Arrays; import javax.crypto.KeyAgreement; import javax.crypto.SecretKey; import jdk.test.lib.security.SecurityUtils; +import jtreg.SkippedException; public class TestDH extends PKCS11Test { @Override public void main(Provider p) throws Exception { if (p.getService("KeyAgreement", "DH") == null) { - System.out.println("DH not supported, skipping"); - return; + throw new SkippedException("DH not supported, skipping"); } String kpgAlgorithm = "DH"; KeyPairGenerator kpg = KeyPairGenerator.getInstance(kpgAlgorithm, p); diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/TestInterop.java b/test/jdk/sun/security/pkcs11/KeyAgreement/TestInterop.java index 810814ebd5d..da15c41132a 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/TestInterop.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/TestInterop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,6 +40,7 @@ import javax.crypto.spec.DHPrivateKeySpec; import javax.crypto.spec.DHPublicKeySpec; import jdk.test.lib.security.DiffieHellmanGroup; import jdk.test.lib.security.SecurityUtils; +import jtreg.SkippedException; public class TestInterop extends PKCS11Test { @@ -76,8 +77,7 @@ public class TestInterop extends PKCS11Test { @Override public void main(Provider prov) throws Exception { if (prov.getService("KeyAgreement", "DH") == null) { - System.out.println("DH not supported, skipping"); - return; + throw new SkippedException("DH not supported, skipping"); } try { System.out.println("testing generateSecret()"); diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/TestShort.java b/test/jdk/sun/security/pkcs11/KeyAgreement/TestShort.java index 27e6bcbc8a3..d0e03c21f36 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/TestShort.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/TestShort.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,8 @@ * @run main/othervm TestShort */ +import jtreg.SkippedException; + import java.math.BigInteger; import java.security.KeyFactory; import java.security.PrivateKey; @@ -90,12 +92,10 @@ public class TestShort extends PKCS11Test { @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyAgreement", "DH") == null) { - System.out.println("DH not supported, skipping"); - return; + throw new SkippedException("DH not supported, skipping"); } + try { - DHPublicKeySpec publicSpec; - DHPrivateKeySpec privateSpec; KeyFactory kf = KeyFactory.getInstance("DH", provider); KeyAgreement ka = KeyAgreement.getInstance("DH", provider); @@ -106,7 +106,7 @@ public class TestShort extends PKCS11Test { ka.init(pr1); ka.doPhase(pu2, true); byte[] n2 = ka.generateSecret(); - if (Arrays.equals(s2, n2) == false) { + if (!Arrays.equals(s2, n2)) { throw new Exception("mismatch 2"); } System.out.println("short ok"); @@ -114,7 +114,7 @@ public class TestShort extends PKCS11Test { ka.init(pr1); ka.doPhase(pu3, true); byte[] n3 = ka.generateSecret(); - if (Arrays.equals(s3, n3) == false) { + if (!Arrays.equals(s3, n3)) { throw new Exception("mismatch 3"); } System.out.println("normal ok"); @@ -123,27 +123,6 @@ public class TestShort extends PKCS11Test { ex.printStackTrace(); throw ex; } - -/* - KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH", provider); - kpg.initialize(512); -// KeyPair kp1 = kpg.generateKeyPair(); -// System.out.println(kp1.getPublic()); -// System.out.println(kp1.getPrivate()); - while (true) { - KeyAgreement ka = KeyAgreement.getInstance("DH", provider); - ka.init(pr1); - KeyPair kp2 = kpg.generateKeyPair(); - ka.doPhase(kp2.getPublic(), true); - byte[] sec = ka.generateSecret(); - if (sec.length == 64) { - System.out.println(kp2.getPrivate()); - System.out.println(kp2.getPublic()); - System.out.println(toString(sec)); - break; - } - } -/**/ } public static void main(String[] args) throws Exception { diff --git a/test/jdk/sun/security/pkcs11/KeyAgreement/UnsupportedDHKeys.java b/test/jdk/sun/security/pkcs11/KeyAgreement/UnsupportedDHKeys.java index b2a2c29e8af..1cd224c2362 100644 --- a/test/jdk/sun/security/pkcs11/KeyAgreement/UnsupportedDHKeys.java +++ b/test/jdk/sun/security/pkcs11/KeyAgreement/UnsupportedDHKeys.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,8 @@ * @run main/othervm UnsupportedDHKeys */ +import jtreg.SkippedException; + import java.security.InvalidParameterException; import java.security.KeyPairGenerator; import java.security.Provider; @@ -59,8 +61,7 @@ public class UnsupportedDHKeys extends PKCS11Test { @Override public void main(Provider provider) throws Exception { if (provider.getService("KeyPairGenerator", "DiffieHellman") == null) { - System.out.println("No supported of DH KeyPairGenerator, skipping"); - return; + throw new SkippedException("DH (DiffieHellman) is not supported in KeyPairGenerator, skipping"); } for (UnsupportedKeySize keySize : UnsupportedKeySize.values()) { diff --git a/test/jdk/sun/security/tools/jarsigner/RemovedFiles.java b/test/jdk/sun/security/tools/jarsigner/RemovedFiles.java index 598e25a9a70..766e0a11a2a 100644 --- a/test/jdk/sun/security/tools/jarsigner/RemovedFiles.java +++ b/test/jdk/sun/security/tools/jarsigner/RemovedFiles.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8309841 + * @bug 8309841 8365559 * @summary Jarsigner should print a warning if an entry is removed * @library /test/lib */ @@ -40,8 +40,15 @@ public class RemovedFiles { private static final String NONEXISTENT_ENTRIES_FOUND = "This jar contains signed entries for files that do not exist. See the -verbose output for more details."; + private static final String WEAK_UNSIGNED + = "The jar will be treated as unsigned, because it is signed with a weak algorithm that is now disabled"; public static void main(String[] args) throws Exception { + t8309841(); + t8365559(); + } + + static void t8309841() throws Exception { JarUtils.createJarFile( Path.of("a.jar"), Path.of("."), @@ -89,6 +96,26 @@ public class RemovedFiles { SecurityTools.jarsigner("-verbose -verify b.jar") .shouldContain("Warning: nonexistent signed entries: [Hello]") .shouldContain(NONEXISTENT_ENTRIES_FOUND); + } + static void t8365559() throws Exception { + JarUtils.createJarFile( + Path.of("c.jar"), + Path.of("."), + Files.writeString(Path.of("c"), "c")); + SecurityTools.keytool("-genkeypair -storepass changeit -keystore ks -alias w -dname CN=w -keyalg ec"); + + // Sign the JAR using an already disabled signature algorithm SHA1withECDSA. + // The file can still be signed but verification will treat it as unsigned. + SecurityTools.jarsigner("-storepass changeit -keystore ks c.jar w -sigalg SHA1withECDSA") + .shouldContain("the -sigalg option is considered a security risk and is disabled."); + + SecurityTools.jarsigner("-verify c.jar") + .shouldContain(WEAK_UNSIGNED) + .shouldNotContain(NONEXISTENT_ENTRIES_FOUND); + SecurityTools.jarsigner("-verify -verbose c.jar") + .shouldContain(WEAK_UNSIGNED) + .shouldNotContain(NONEXISTENT_ENTRIES_FOUND) + .shouldNotContain("Warning: nonexistent signed entries:"); } } diff --git a/test/jdk/sun/tools/jhsdb/JShellHeapDumpTest.java b/test/jdk/sun/tools/jhsdb/JShellHeapDumpTest.java index 9e9a6c79c7b..6fbc96362a9 100644 --- a/test/jdk/sun/tools/jhsdb/JShellHeapDumpTest.java +++ b/test/jdk/sun/tools/jhsdb/JShellHeapDumpTest.java @@ -162,12 +162,7 @@ public class JShellHeapDumpTest { long startTime = System.currentTimeMillis(); try { JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jshell"); - if (doSleep) { - launcher.addVMArgs(Utils.getTestJavaOpts()); - } else { - // Don't allow use of SerialGC. See JDK-8313655. - launcher.addVMArgs(Utils.getFilteredTestJavaOpts("-XX:\\+UseSerialGC")); - } + launcher.addVMArgs(Utils.getTestJavaOpts()); ProcessBuilder pb = new ProcessBuilder(launcher.getCommand()); // Needed so we can properly parse the "Welcome to JShell" output. pb.command().add("-J-Duser.language=en"); diff --git a/test/jdk/tools/jimage/ImageReaderDuplicateChildNodesTest.java b/test/jdk/tools/jimage/ImageReaderDuplicateChildNodesTest.java index bec32bee0f8..8656a4a3d00 100644 --- a/test/jdk/tools/jimage/ImageReaderDuplicateChildNodesTest.java +++ b/test/jdk/tools/jimage/ImageReaderDuplicateChildNodesTest.java @@ -68,17 +68,17 @@ public class ImageReaderDuplicateChildNodesTest { + " in " + imagePath); } // now verify that the parent node which is a directory, doesn't have duplicate children - final List children = parent.getChildren(); - if (children == null || children.isEmpty()) { + final List childNames = parent.getChildNames().toList(); + if (childNames.isEmpty()) { throw new RuntimeException("ImageReader did not return any child resources under " + integersParentResource + " in " + imagePath); } final Set uniqueChildren = new HashSet<>(); - for (final ImageReader.Node child : children) { - final boolean unique = uniqueChildren.add(child); + for (final String childName : childNames) { + final boolean unique = uniqueChildren.add(reader.findNode(childName)); if (!unique) { throw new RuntimeException("ImageReader returned duplicate child resource " - + child + " under " + parent + " from image " + imagePath); + + childName + " under " + parent + " from image " + imagePath); } } } diff --git a/test/jdk/tools/jpackage/helpers-test/TEST.properties b/test/jdk/tools/jpackage/helpers-test/TEST.properties index 1830ea05443..ff5e0eef9de 100644 --- a/test/jdk/tools/jpackage/helpers-test/TEST.properties +++ b/test/jdk/tools/jpackage/helpers-test/TEST.properties @@ -1,6 +1,6 @@ JUnit.dirs = . -lib.dirs = /test/jdk/tools/jpackage/helpers /test/jdk/tools/jpackage/helpers-test +lib.dirs = /test/jdk/tools/jpackage/helpers /test/jdk/tools/jpackage/junit/tools modules=jdk.jpackage/jdk.jpackage.internal:+open \ jdk.jpackage/jdk.jpackage.internal.util:+open \ diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JavaAppDescTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JavaAppDescTest.java index 50f1992f796..08d0ba76d1c 100644 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JavaAppDescTest.java +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JavaAppDescTest.java @@ -42,11 +42,10 @@ public class JavaAppDescTest extends JUnitAdapter { @Parameter({"Foo", "Foo.class"}) @Parameter({"com.bar.A", "com/bar/A.class"}) @Parameter({"module/com.bar.A", "com/bar/A.class"}) - public static void testClassFilePath(String... args) { - var appDesc = args[0]; - var expectedClassFilePath = Path.of(args[1]); - TKit.assertEquals(expectedClassFilePath.toString(), JavaAppDesc.parse( - appDesc).classFilePath().toString(), null); + public static void testClassFilePath(String appDesc, String expectedClassFile) { + var expectedClassFilePath = Path.of(expectedClassFile); + TKit.assertEquals(expectedClassFilePath.toString(), + JavaAppDesc.parse(appDesc).classFilePath().toString(), null); } public static List input() { @@ -55,6 +54,12 @@ public class JavaAppDescTest extends JUnitAdapter { createTestCase("foo.jar*", "foo.jar*hello.jar:Hello"), createTestCase("Bye", "hello.jar:Bye"), createTestCase("bye.jar:", "bye.jar:Hello"), + createTestCase("bye.jar:!", appDesc -> { + return appDesc + .setBundleFileName("bye.jar") + .setClassName("Hello") + .setWithMainClass(true); + }), createTestCase("duke.jar:com.other/com.other.foo.bar.Buz!@3.7", appDesc -> { return appDesc .setBundleFileName("duke.jar") 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 07c8e06856f..50222d89ceb 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AdditionalLauncher.java @@ -54,6 +54,10 @@ public final class AdditionalLauncher { setPersistenceHandler(null); } + public String name() { + return name; + } + public AdditionalLauncher withVerifyActions(Action... actions) { verifyActions.addAll(List.of(actions)); return this; 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 3a89ba28d26..3a423fd71ca 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -1174,10 +1174,15 @@ public class JPackageCommand extends CommandArguments { "Check for unexpected value of property in app image file"); } - TKit.assertStringListEquals( - addLauncherNames().stream().sorted().toList(), - aif.addLaunchers().keySet().stream().sorted().toList(), - "Check additional launcher names"); + // Don't compare the add launchers configured on the command line with the + // add launchers listed in the `.jpackage.xml` file if the latter comes from + // a predefined app image. + if (!hasArgument("--app-image")) { + TKit.assertStringListEquals( + addLauncherNames().stream().sorted().toList(), + aif.addLaunchers().keySet().stream().sorted().toList(), + "Check additional launcher names"); + } } } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherShortcut.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherShortcut.java index 15bb3ea0333..1ee3b8d47b0 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherShortcut.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherShortcut.java @@ -44,6 +44,7 @@ public enum LauncherShortcut { public enum StartupDirectory { DEFAULT("true"), + APP_DIR("app-dir"), ; StartupDirectory(String stringValue) { @@ -79,7 +80,7 @@ public enum LauncherShortcut { private final String stringValue; - private final static Map VALUE_MAP = + private static final Map VALUE_MAP = Stream.of(values()).collect(toMap(StartupDirectory::asStringValue, x -> x)); } @@ -147,7 +148,14 @@ public enum LauncherShortcut { private Optional findMainLauncherShortcut(JPackageCommand cmd) { if (cmd.hasArgument(optionName())) { - return Optional.of(StartupDirectory.DEFAULT); + var value = Optional.ofNullable(cmd.getArgumentValue(optionName())).filter(optionValue -> { + return !optionValue.startsWith("-"); + }); + if (value.isPresent()) { + return value.flatMap(StartupDirectory::parse); + } else { + return Optional.of(StartupDirectory.DEFAULT); + } } else { return Optional.empty(); } 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 e8f6273b18b..caec0e315c4 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java @@ -514,6 +514,9 @@ public final class LinuxHelper { case DEFAULT -> { return (Path)null; } + case APP_DIR -> { + return cmd.pathToPackageFile(appLayout.appDirectory()); + } default -> { throw new AssertionError(); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WinShortcutVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WinShortcutVerifier.java index cca904e017e..27e94366e69 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WinShortcutVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WinShortcutVerifier.java @@ -213,7 +213,17 @@ public final class WinShortcutVerifier { final var installDir = Path.of(installRoot.getMsiPropertyName()).resolve(getInstallationSubDirectory(cmd)); final Function workDir = startupDirectory -> { - return installDir; + switch (startupDirectory) { + case DEFAULT -> { + return installDir; + } + case APP_DIR -> { + return ApplicationLayout.windowsAppImage().resolveAt(installDir).appDirectory(); + } + default -> { + throw new IllegalArgumentException(); + } + } }; if (winMenu.isPresent()) { diff --git a/test/jdk/tools/jpackage/junit/TEST.properties b/test/jdk/tools/jpackage/junit/TEST.properties index 34d66c3b8d7..c540eb76869 100644 --- a/test/jdk/tools/jpackage/junit/TEST.properties +++ b/test/jdk/tools/jpackage/junit/TEST.properties @@ -1,5 +1,7 @@ JUnit.dirs = share +lib.dirs = /test/jdk/tools/jpackage/helpers /test/jdk/tools/jpackage/junit/tools + modules=jdk.jpackage/jdk.jpackage.internal:+open \ jdk.jpackage/jdk.jpackage.internal.model:+open \ jdk.jpackage/jdk.jpackage.internal.pipeline:+open \ diff --git a/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxApplicationLayoutTest.java b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxApplicationLayoutTest.java new file mode 100644 index 00000000000..0bb0fe5b73e --- /dev/null +++ b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxApplicationLayoutTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal; + +import java.nio.file.Path; +import jdk.jpackage.internal.model.AppImageLayoutTest; +import jdk.jpackage.internal.model.ApplicationLayoutTest; +import org.junit.jupiter.api.Test; + +public class LinuxApplicationLayoutTest { + + @Test + public void testResolveAt() { + AppImageLayoutTest.testResolveAt(createLayout()); + } + + @Test + public void testResolveAtRepeat() { + AppImageLayoutTest.testResolveAtRepeat(createLayout()); + } + + @Test + public void testUnresolve() { + AppImageLayoutTest.testUnresolve(createLayout()); + } + + @Test + public void testEmptyRootDirectory() { + AppImageLayoutTest.testEmptyRootDirectory(createLayout()); + } + + public static LinuxApplicationLayout createLayout() { + return LinuxApplicationLayout.create(ApplicationLayoutTest.createLayout(), + Path.of("libapplauncher.so")); + } +} diff --git a/test/jdk/tools/jpackage/junit/linux/junit.java b/test/jdk/tools/jpackage/junit/linux/junit.java new file mode 100644 index 00000000000..214812e951e --- /dev/null +++ b/test/jdk/tools/jpackage/junit/linux/junit.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @summary Test LinuxApplicationLayout + * @requires (os.family == "linux") + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/LinuxApplicationLayoutTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/model/AppImageLayoutTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.LinuxApplicationLayoutTest + */ diff --git a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacApplicationLayoutTest.java b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacApplicationLayoutTest.java new file mode 100644 index 00000000000..7ee3b41b7f2 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacApplicationLayoutTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal; + +import java.nio.file.Path; +import jdk.jpackage.internal.model.AppImageLayoutTest; +import jdk.jpackage.internal.model.ApplicationLayoutTest; +import org.junit.jupiter.api.Test; + +public class MacApplicationLayoutTest { + + @Test + public void testResolveAt() { + AppImageLayoutTest.testResolveAt(createLayout()); + } + + @Test + public void testResolveAtRepeat() { + AppImageLayoutTest.testResolveAtRepeat(createLayout()); + } + + @Test + public void testUnresolve() { + AppImageLayoutTest.testUnresolve(createLayout()); + } + + @Test + public void testEmptyRootDirectory() { + AppImageLayoutTest.testEmptyRootDirectory(createLayout()); + } + + public static MacApplicationLayout createLayout() { + return MacApplicationLayout.create(ApplicationLayoutTest.createLayout(), + Path.of("Contents/runtime")); + } +} diff --git a/test/jdk/tools/jpackage/junit/macosx/junit.java b/test/jdk/tools/jpackage/junit/macosx/junit.java new file mode 100644 index 00000000000..1549aaa6cd7 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/macosx/junit.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @summary Test MacApplicationLayoutTest + * @requires (os.family == "mac") + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/MacApplicationLayoutTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/model/AppImageLayoutTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.MacApplicationLayoutTest + */ diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/BuildEnvTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/BuildEnvTest.java new file mode 100644 index 00000000000..c71ef307f0e --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/BuildEnvTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal; + +import static jdk.jpackage.internal.model.AppImageLayout.toPathGroup; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.file.Path; +import java.util.Optional; +import jdk.jpackage.internal.model.AppImageLayout; +import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.RuntimeLayout; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + + +public class BuildEnvTest { + + @ParameterizedTest + @ValueSource(strings = {"", "image"}) + public void testUnresolvedAppImageLayout(Path appImageDir) { + final var rootDir = Path.of(""); + + final var env = BuildEnv.create(rootDir, Optional.empty(), true, + BuildEnvTest.class, RuntimeLayout.DEFAULT.resolveAt(appImageDir).resetRootDirectory()); + + assertEquals(env.appImageDir(), env.appImageLayout().rootDirectory()); + + assertEquals(Path.of(""), env.appImageDir()); + assertEquals(toPathGroup(RuntimeLayout.DEFAULT.resolveAt(appImageDir)), toPathGroup(env.appImageLayout())); + assertEquals(rootDir, env.buildRoot()); + assertEquals(rootDir.resolve("config"), env.configDir()); + assertEquals(Optional.empty(), env.resourceDir()); + assertTrue(env.verbose()); + } + + @Test + public void testResolvedAppImageLayout() { + final var rootDir = Path.of("/oof"); + final var appImageDir = Path.of("/foo/bar"); + + final var layout = RuntimeLayout.DEFAULT.resolveAt(appImageDir); + final var env = BuildEnv.create(rootDir, Optional.empty(), true, BuildEnvTest.class, layout); + + assertSame(layout, env.appImageLayout()); + assertEquals(env.appImageDir(), env.appImageLayout().rootDirectory()); + + assertEquals(Path.of("/foo/bar"), env.appImageDir()); + assertEquals(toPathGroup(RuntimeLayout.DEFAULT.resolveAt(appImageDir)), toPathGroup(env.appImageLayout())); + assertEquals(rootDir, env.buildRoot()); + assertEquals(rootDir.resolve("config"), env.configDir()); + assertEquals(Optional.empty(), env.resourceDir()); + assertTrue(env.verbose()); + } + + @ParameterizedTest + @ValueSource(strings = {"", "/foo/bar"}) + public void test_withAppImageDir(Path appImageDir) { + final var rootDir = Path.of("/oof"); + + final var layout = RuntimeLayout.DEFAULT; + final var env = BuildEnv.withAppImageDir(BuildEnv.create(rootDir, + Optional.empty(), false, BuildEnvTest.class, layout), appImageDir); + + assertNotSame(layout, env.appImageLayout()); + assertEquals(env.appImageDir(), env.appImageLayout().rootDirectory()); + + assertEquals(appImageDir, env.appImageDir()); + assertEquals(toPathGroup(RuntimeLayout.DEFAULT.resolveAt(appImageDir)), toPathGroup(env.appImageLayout())); + assertEquals(rootDir, env.buildRoot()); + assertEquals(rootDir.resolve("config"), env.configDir()); + assertEquals(Optional.empty(), env.resourceDir()); + assertFalse(env.verbose()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_withAppImageLayout(boolean resolved) { + final var rootDir = Path.of("/oof"); + + final var appImageDir = Path.of("/foo/bar"); + + final AppImageLayout layout; + if (resolved) { + layout = RuntimeLayout.DEFAULT.resolveAt(appImageDir); + } else { + layout = RuntimeLayout.DEFAULT.resolveAt(appImageDir).resetRootDirectory(); + } + + final var env = BuildEnv.withAppImageLayout(BuildEnv.create(rootDir, + Optional.empty(), false, BuildEnvTest.class, RuntimeLayout.DEFAULT), layout); + + assertSame(layout, env.appImageLayout()); + assertEquals(env.appImageDir(), env.appImageLayout().rootDirectory()); + + assertEquals(toPathGroup(RuntimeLayout.DEFAULT.resolveAt(appImageDir)), toPathGroup(env.appImageLayout())); + assertEquals(rootDir, env.buildRoot()); + assertEquals(rootDir.resolve("config"), env.configDir()); + assertEquals(Optional.empty(), env.resourceDir()); + assertFalse(env.verbose()); + } + + @Test + public void test_asApplicationLayout() { + final var rootDir = Path.of("r"); + + assertTrue(BuildEnv.create(rootDir, Optional.empty(), false, + BuildEnvTest.class, RuntimeLayout.DEFAULT).asApplicationLayout().isEmpty()); + + var layout = ApplicationLayout.build().setAll("foo").create(); + assertSame(layout, BuildEnv.create(rootDir, Optional.empty(), false, + BuildEnvTest.class, layout).asApplicationLayout().orElseThrow()); + } +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java new file mode 100644 index 00000000000..24c1d62c34f --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java @@ -0,0 +1,872 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal; + +import static jdk.jpackage.internal.util.function.ExceptionBox.rethrowUnchecked; +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import jdk.jpackage.internal.PackagingPipeline.BuildApplicationTaskID; +import jdk.jpackage.internal.PackagingPipeline.CopyAppImageTaskID; +import jdk.jpackage.internal.PackagingPipeline.NoArgTaskAction; +import jdk.jpackage.internal.PackagingPipeline.PackageTaskID; +import jdk.jpackage.internal.PackagingPipeline.PrimaryTaskID; +import jdk.jpackage.internal.PackagingPipeline.TaskAction; +import jdk.jpackage.internal.PackagingPipeline.TaskContext; +import jdk.jpackage.internal.PackagingPipeline.TaskID; +import jdk.jpackage.internal.model.AppImageLayout; +import jdk.jpackage.internal.model.Application; +import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.Package; +import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.model.PackagerException; +import jdk.jpackage.internal.model.RuntimeBuilder; +import jdk.jpackage.internal.model.RuntimeLayout; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; + + +public class PackagingPipelineTest { + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testBuildApplication(boolean withRuntimeBuilder, @TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final var app = createApp(TEST_LAYOUT_1, withRuntimeBuilder ? Optional.of(TestRuntimeBuilder.INSTANCE) : Optional.empty()); + final var env = buildEnv(workDir.resolve("build")).appImageDirFor(app).create(); + + // Build application image in `env.appImageDir()` directory. + final var builder = buildPipeline(); + if (app.runtimeBuilder().isEmpty()) { + builder.task(BuildApplicationTaskID.RUNTIME).noaction().add(); + } + + builder.create().execute(env, app); + + assertEquals(app.appImageDirName(), env.appImageDir().getFileName()); + + var executedTaskActions = dryRun(builder, toConsumer(_ -> { + builder.create().execute(env, app); + })); + + List expectedActions = new ArrayList<>(); + if (app.runtimeBuilder().isPresent()) { + expectedActions.add(BuildApplicationTaskID.RUNTIME); + } + expectedActions.addAll(List.of(BuildApplicationTaskID.LAUNCHERS, BuildApplicationTaskID.CONTENT)); + + assertEquals(expectedActions, executedTaskActions); + + final ExpectedAppImage expectedAppImage; + if (withRuntimeBuilder) { + expectedAppImage = ExpectedAppImage.build().dir("") + .file("launchers/my-launcher", TestLauncher.CONTENT) + .file("runtime/my-runtime", TestRuntimeBuilder.CONTENT); + } else { + expectedAppImage = ExpectedAppImage.build().dir("") + .file("launchers/my-launcher", TestLauncher.CONTENT); + } + + assertEquals(expectedAppImage, ExpectedAppImage.load(env.appImageDir())); + } + + @Test + void testCopyApplication(@TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final var srcApp = createApp(TEST_LAYOUT_1, TestRuntimeBuilder.INSTANCE); + + final var srcEnv = buildEnv(workDir.resolve("build")).appImageDirFor(srcApp).create(); + + // Build application image in `srcEnv.appImageDir()` directory. + buildPipeline().create().execute(srcEnv, srcApp); + + final var dstApp = createApp(TEST_LAYOUT_2, TestRuntimeBuilder.INSTANCE); + + final var dstEnv = buildEnv(workDir.resolve("build-2")) + .appImageLayout(dstApp.imageLayout().resolveAt(workDir.resolve("a/b/c"))) + .create(); + + // Copy application image from `srcEnv.appImageDir()` into `dstEnv.appImageDir()` + // with layout transformation. + // This test exercises flexibility of the packaging pipeline. + final var builder = buildPipeline() + .task(PrimaryTaskID.BUILD_APPLICATION_IMAGE).applicationAction(cfg -> { + assertSame(dstApp, cfg.app()); + assertEquals(dstEnv.appImageDir(), cfg.env().appImageLayout().rootDirectory()); + assertFalse(Files.exists(dstEnv.appImageDir())); + PackagingPipeline.copyAppImage(srcEnv.appImageLayout(), cfg.env().appImageLayout(), false); + }).add(); + + // Disable the default "build application image" actions of the tasks which + // are the dependencies of `PrimaryTaskID.BUILD_APPLICATION_IMAGE` task as + // their output will be overwritten in the custom action of this task. + builder.taskGraphSnapshot().getAllTailsOf(PrimaryTaskID.BUILD_APPLICATION_IMAGE).forEach(taskId -> { + builder.task(taskId).noaction().add(); + }); + + builder.create().execute(dstEnv, dstApp); + + AppImageLayout.toPathGroup(dstEnv.appImageLayout()).paths().forEach(path -> { + assertTrue(Files.exists(path)); + }); + + assertEquals(Path.of("c"), dstEnv.appImageDir().getFileName()); + + var executedTaskActions = dryRun(builder, toConsumer(_ -> { + builder.create().execute(dstEnv, dstApp); + })); + + assertEquals(List.of(PrimaryTaskID.BUILD_APPLICATION_IMAGE), executedTaskActions); + + final ExpectedAppImage expectedAppImage = ExpectedAppImage.build().dir("") + .file("q/launchers/my-launcher", TestLauncher.CONTENT) + .file("qqq/runtime/my-runtime", TestRuntimeBuilder.CONTENT); + + assertEquals(expectedAppImage, ExpectedAppImage.load(dstEnv.appImageDir())); + } + + @Test + void testCreatePackage(@TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final var outputDir = workDir.resolve("bundles"); + final var pkg = buildPackage(createApp(TEST_LAYOUT_1_WITH_INSTALL_DIR, TestRuntimeBuilder.INSTANCE)).create(); + final var env = buildEnv(workDir.resolve("build")).appImageDirFor(pkg).create(); + + final var builder = buildPipeline(); + + // Will create an app image in `env.appImageDir()` directory with `pkg.appImageLayout()` layout. + // Will convert the created app image into a package. + builder.create().execute(env, pkg, outputDir); + + final var expected = createTestPackageFileContents(env.appImageLayout()); + final var actual = Files.readString(outputDir.resolve(pkg.packageFileNameWithSuffix())); + + assertEquals(expected, actual); + System.out.println(String.format("testCreatePackage:\n---\n%s\n---", actual)); + + var executedTaskActions = dryRun(builder, toConsumer(_ -> { + builder.create().execute(env, pkg, outputDir); + })); + + assertEquals(List.of( + BuildApplicationTaskID.RUNTIME, + BuildApplicationTaskID.LAUNCHERS, + BuildApplicationTaskID.CONTENT, + PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT, + PrimaryTaskID.PACKAGE + ), executedTaskActions); + + final var expectedAppImage = ExpectedAppImage.build().dir("") + .file(TEST_INSTALL_DIR.resolve("launchers/my-launcher"), TestLauncher.CONTENT) + .file(TEST_INSTALL_DIR.resolve("runtime/my-runtime"), TestRuntimeBuilder.CONTENT); + + assertEquals(expectedAppImage, ExpectedAppImage.load(env.appImageDir())); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testCreateRuntimeInstaller(boolean transformLayout, @TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final AppImageLayout srcLayout; + if (transformLayout) { + // Create an application layout such that the runtime directory doesn't + // have a common parent with other directories otherwise the runtime directory + // will be skipped when copying the app image layout as path groups because + // the destination app image layout is of type RuntimeLayout and have only + // the runtime directory. + srcLayout = ApplicationLayout.build() + .launchersDirectory("launchers") + .appDirectory("lib") + .runtimeDirectory("runtime") + .appModsDirectory("lib") + .contentDirectory("lib") + .desktopIntegrationDirectory("lib") + .create(); + } else { + srcLayout = RuntimeLayout.DEFAULT; + } + + // Create a runtime image in `env.appImageDir()` directory. + final var env = buildEnv(workDir.resolve("build")) + .appImageLayout(srcLayout) + .appImageDir(workDir.resolve("rt")) + .create(); + TestRuntimeBuilder.INSTANCE.create(env.appImageLayout()); + + final var pipeline = buildPackage(createApp( + RuntimeLayout.DEFAULT.resolveAt(TEST_INSTALL_DIR).resetRootDirectory())).create(); + + final var expectedAppImage = ExpectedAppImage.build().dir("") + .file(TEST_INSTALL_DIR.resolve("my-runtime"), TestRuntimeBuilder.CONTENT); + + createAndVerifyPackage(buildPipeline(), pipeline, env, workDir.resolve("bundles"), + String.format("testCreateRuntimeInstaller(%s)", transformLayout), expectedAppImage, + CopyAppImageTaskID.COPY, + PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT, + PrimaryTaskID.PACKAGE); + } + + private enum ExternalAppImageMode { + + // Copy predefined app image from `BuildEnv.appImageDir()`. + // Layout of the predefined app image is `BuildEnv.appImageLayout()` and + // its unresolved variant equals to `Package.appImageLayout()`. + COPY_FROM_BUILD_ENV, + + // Copy predefined app image from some directory. + // Layout of the predefined app image is `Package.appImageLayout()`. + COPY, + + // Copy predefined app image from `BuildEnv.appImageDir()`. + // Layout of the predefined app image is `BuildEnv.appImageLayout()` and + // its unresolved variant is NOT equal to `Package.appImageLayout()`. + TRANSFORM_FROM_BUILD_ENV, + ; + + static final Set FROM_BUILD_ENV = Set.of( + COPY_FROM_BUILD_ENV, TRANSFORM_FROM_BUILD_ENV); + } + + @ParameterizedTest + @EnumSource(ExternalAppImageMode.class) + void testCreatePackageFromExternalAppImage(ExternalAppImageMode mode, @TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final ApplicationLayout appLayout; + final ExpectedAppImage expectedAppImage; + if (ExternalAppImageMode.TRANSFORM_FROM_BUILD_ENV == mode) { + appLayout = TEST_LAYOUT_2_WITH_INSTALL_DIR; + expectedAppImage = ExpectedAppImage.build().dir("") + .file(TEST_INSTALL_DIR.resolve("q/launchers/my-launcher"), TestLauncher.CONTENT) + .file(TEST_INSTALL_DIR.resolve("qqq/runtime/my-runtime"), TestRuntimeBuilder.CONTENT); + } else { + appLayout = TEST_LAYOUT_1_WITH_INSTALL_DIR; + expectedAppImage = ExpectedAppImage.build().dir("") + .file(TEST_INSTALL_DIR.resolve("launchers/my-launcher"), TestLauncher.CONTENT) + .file(TEST_INSTALL_DIR.resolve("runtime/my-runtime"), TestRuntimeBuilder.CONTENT); + } + + final BuildEnv env; + final Path predefinedAppImage; + if (ExternalAppImageMode.FROM_BUILD_ENV.contains(mode)) { + // External app image is stored in the build env app image directory. + env = setupBuildEnvForExternalAppImage(workDir); + predefinedAppImage = env.appImageDir(); + } else { + // External app image is stored outside of the build env app image directory + // and should have the same layout as the app's app image layout. + env = buildEnv(workDir.resolve("build")) + .appImageDir(workDir) + // Always need some app image layout. + .appImageLayout(new AppImageLayout.Stub(Path.of(""))) + .create(); + final var externalAppImageLayout = appLayout.resolveAt(workDir.resolve("app-image")); + TestRuntimeBuilder.INSTANCE.create(externalAppImageLayout); + TestLauncher.INSTANCE.create(externalAppImageLayout); + predefinedAppImage = externalAppImageLayout.rootDirectory(); + } + + final var pkg = buildPackage(createApp(appLayout)) + .predefinedAppImage(predefinedAppImage) + .create(); + + createAndVerifyPackage(buildPipeline(), pkg, env, workDir.resolve("bundles"), + String.format("testCreatePackageFromExternalAppImage(%s)", mode), expectedAppImage, + CopyAppImageTaskID.COPY, + PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT, + PrimaryTaskID.PACKAGE); + } + + @ParameterizedTest + @EnumSource(names={"COPY", "COPY_FROM_BUILD_ENV"}) + void testCreatePackageFromExternalAppImageNoCopyAction(ExternalAppImageMode mode, @TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final ApplicationLayout appLayout = TEST_LAYOUT_1_WITH_INSTALL_DIR; + + final BuildEnv env; + final ApplicationLayout predefinedAppImageLayout; + if (ExternalAppImageMode.FROM_BUILD_ENV.contains(mode)) { + // External app image is stored in the build env app image directory. + env = setupBuildEnvForExternalAppImage(workDir); + predefinedAppImageLayout = env.asApplicationLayout().orElseThrow(); + } else { + // External app image is stored outside of the build env app image directory + // and should have the same layout as the app's app image layout. + env = buildEnv(workDir.resolve("build")) + .appImageDir(workDir) + // Always need some app image layout. + .appImageLayout(new AppImageLayout.Stub(Path.of(""))) + .create(); + predefinedAppImageLayout = appLayout.resolveAt(workDir.resolve("app-image")); + TestRuntimeBuilder.INSTANCE.create(predefinedAppImageLayout); + TestLauncher.INSTANCE.create(predefinedAppImageLayout); + } + + final var pkg = buildPackage(createApp(appLayout)) + .predefinedAppImage(predefinedAppImageLayout.rootDirectory()) + .create(); + + final var outputDir = workDir.resolve("bundles"); + + final var builder = buildPipeline().configuredTasks().filter(task -> { + return CopyAppImageTaskID.COPY.equals(task.task()); + }).findFirst().orElseThrow().noaction().add(); + + final var startupParameters = builder.createStartupParameters(env, pkg, outputDir); + + builder.create().execute(startupParameters); + + final var expected = createTestPackageFileContents(predefinedAppImageLayout); + final var actual = Files.readString(outputDir.resolve(pkg.packageFileNameWithSuffix())); + assertEquals(expected, actual); + System.out.println(String.format("%s:\n---\n%s\n---", + String.format("testCreatePackageFromExternalAppImage(%s)", mode), actual)); + + final ExpectedAppImage expectedAppImage = ExpectedAppImage.build().dir("") + .file(predefinedAppImageLayout.unresolve().launchersDirectory().resolve("my-launcher"), TestLauncher.CONTENT) + .file(predefinedAppImageLayout.unresolve().runtimeDirectory().resolve("my-runtime"), TestRuntimeBuilder.CONTENT); + assertEquals(expectedAppImage, ExpectedAppImage.load(pkg.predefinedAppImage().orElseThrow())); + + var actualExecutedTaskActions = dryRun(builder, toConsumer(_ -> { + builder.create().execute(startupParameters); + })); + assertEquals(List.of( + PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT, + PrimaryTaskID.PACKAGE), actualExecutedTaskActions); + } + + @Test + void testCreatePackageFromExternalAppImageWithoutExternalAppImageError(@TempDir Path workDir) throws ConfigException, PackagerException, IOException { + + final var env = setupBuildEnvForExternalAppImage(workDir); + final var pkg = buildPackage(createApp(TEST_LAYOUT_1_WITH_INSTALL_DIR)).create(); + final var pipeline = buildPipeline().create(); + + assertThrowsExactly(UnsupportedOperationException.class, () -> pipeline.execute(env, pkg, workDir)); + } + + @Test + void testExceptionRethrow_RuntimeException() throws ConfigException, PackagerException, IOException { + + final var expectedException = new RuntimeException("foo"); + final var ex = testExceptionRethrow(expectedException, expectedException.getClass(), () -> { + throw expectedException; + }); + assertSame(expectedException, ex); + } + + @Test + void testExceptionRethrow_PackagerException() throws ConfigException, PackagerException, IOException { + + final var expectedException = new PackagerException("param.vendor.default"); + final var ex = testExceptionRethrow(expectedException, expectedException.getClass(), () -> { + throw expectedException; + }); + assertSame(expectedException, ex); + } + + @Test + void testExceptionRethrow_Exception() throws ConfigException, PackagerException, IOException { + + final var expectedException = new Exception("foo"); + final var ex = testExceptionRethrow(expectedException, PackagerException.class, () -> { + rethrowUnchecked(expectedException); + }); + assertSame(expectedException, ex.getCause()); + } + + @Test + void testAppImageAction() throws PackagerException, IOException { + + final var app = createApp(TEST_LAYOUT_1); + final var env = dummyBuildEnv(); + + final var executed = new boolean[1]; + + PackagingPipeline.build() + // The pipleline must have at least two tasks, add a dummy. + .task(new TaskID() {}).addDependent(PrimaryTaskID.BUILD_APPLICATION_IMAGE).add() + .task(PrimaryTaskID.BUILD_APPLICATION_IMAGE).appImageAction(ctx -> { + assertSame(app, ctx.app()); + assertSame(env, ctx.env()); + executed[0] = true; + }).add().create().execute(env, app); + + assertTrue(executed[0]); + } + + @Test + void testAppImageActionWithPackage() throws PackagerException, IOException { + + final var pkg = buildPackage(createApp(TEST_LAYOUT_1, TestRuntimeBuilder.INSTANCE)).create(); + final var env = dummyBuildEnv(); + + final var executed = new boolean[1]; + + final var builder = PackagingPipeline.build() + // The pipleline must have at least two tasks, add a dummy. + .task(new TaskID() {}).addDependent(PrimaryTaskID.PACKAGE).add(); + + final var startupParameters = builder.createStartupParameters(env, pkg, Path.of("")); + + builder.task(PrimaryTaskID.PACKAGE).appImageAction(ctx -> { + assertSame(pkg.app(), ctx.app()); + assertSame(startupParameters.packagingEnv(), ctx.env()); + executed[0] = true; + }).add().create().execute(startupParameters); + + assertTrue(executed[0]); + } + + @Test + void testPackageActionWithApplication() throws PackagerException, IOException { + + final var app = createApp(TEST_LAYOUT_1); + final var env = dummyBuildEnv(); + + final var pipeline = PackagingPipeline.build() + // The pipleline must have at least two tasks, add a dummy. + .task(new TaskID() {}).addDependent(PrimaryTaskID.BUILD_APPLICATION_IMAGE).add() + .task(PrimaryTaskID.BUILD_APPLICATION_IMAGE).packageAction(ctx -> { + throw new AssertionError(); + }).add().create(); + + // If the pipeline is building an application, it can not execute actions that take a package as an argument. + assertThrowsExactly(UnsupportedOperationException.class, () -> pipeline.execute(env, app)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testContextMapper(boolean allowAll) throws PackagerException, IOException { + + var builder = PackagingPipeline.buildStandard().contextMapper(ctx -> { + return new TaskContext() { + @Override + public boolean test(TaskID task) { + return allowAll; + } + + @Override + public void execute(TaskAction taskAction) throws IOException, PackagerException { + if (!allowAll) { + throw new AssertionError(); + } + ctx.execute(taskAction); + } + }; + }); + + var actualExecutedTaskActions = dryRun(builder, toConsumer(_ -> { + builder.create().execute(dummyBuildEnv(), createApp(TEST_LAYOUT_1)); + })); + + List expectedExecutedTaskActions; + + if (allowAll) { + expectedExecutedTaskActions = List.of( + BuildApplicationTaskID.RUNTIME, + BuildApplicationTaskID.LAUNCHERS, + BuildApplicationTaskID.CONTENT, + BuildApplicationTaskID.APP_IMAGE_FILE, + CopyAppImageTaskID.COPY, + PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT); + } else { + expectedExecutedTaskActions = List.of(); + } + + assertEquals(expectedExecutedTaskActions, actualExecutedTaskActions); + } + + public static List dryRun(PackagingPipeline.Builder builder, + Consumer callback) { + + List executedTaskActions = new ArrayList<>(); + builder.configuredTasks().filter(PackagingPipeline.Builder.TaskBuilder::hasAction).forEach(taskBuilder -> { + var taskId = taskBuilder.task(); + taskBuilder.action(() -> { + executedTaskActions.add(taskId); + }).add(); + }); + + callback.accept(builder); + + return executedTaskActions; + } + + private static Exception testExceptionRethrow(Exception expectedException, + Class expectedCatchExceptionType, + NoArgTaskAction throwAction) throws PackagerException, IOException { + + final var app = createApp(TEST_LAYOUT_1); + final var env = dummyBuildEnv(); + + var pipeline = PackagingPipeline.build() + // The pipleline must have at least two tasks, add a dummy. + .task(new TaskID() {}).addDependent(PrimaryTaskID.BUILD_APPLICATION_IMAGE).add() + .task(PrimaryTaskID.BUILD_APPLICATION_IMAGE).action(throwAction).add().create(); + + return assertThrowsExactly(expectedCatchExceptionType, () -> pipeline.execute(env, app)); + } + + private static BuildEnv setupBuildEnvForExternalAppImage(Path workDir) throws ConfigException { + // Create an app image in `env.appImageDir()` directory. + final var env = buildEnv(workDir.resolve("build")) + .appImageLayout(TEST_LAYOUT_1.resolveAt(Path.of("a/b/c")).resetRootDirectory()) + .appImageDir(workDir.resolve("app-image")) + .create(); + TestRuntimeBuilder.INSTANCE.create(env.appImageLayout()); + TestLauncher.INSTANCE.create((ApplicationLayout)env.appImageLayout()); + + return env; + } + + private static void createAndVerifyPackage(PackagingPipeline.Builder builder, Package pkg, + BuildEnv env, Path outputDir, String logMsgHeader, ExpectedAppImage expectedAppImage, + TaskID... expectedExecutedTaskActions) throws PackagerException, IOException { + Objects.requireNonNull(logMsgHeader); + + final var startupParameters = builder.createStartupParameters(env, pkg, outputDir); + + assertNotSameAppImageDirs(env, startupParameters.packagingEnv()); + + // Will create an app image in `startupParameters.packagingEnv().appImageDir()` directory + // with `pkg.appImageLayout()` layout using an app image (runtime image) from `env.appImageDir()` as input. + // Will convert the created app image into a package. + // Will not overwrite the contents of `env.appImageDir()` directory. + builder.create().execute(startupParameters); + + final var packagingAppImageDir = startupParameters.packagingEnv().appImageDir(); + + final var expected = createTestPackageFileContents(pkg.appImageLayout().resolveAt(packagingAppImageDir)); + + final var actual = Files.readString(outputDir.resolve(pkg.packageFileNameWithSuffix())); + + assertEquals(expected, actual); + System.out.println(String.format("%s:\n---\n%s\n---", logMsgHeader, actual)); + + assertEquals(expectedAppImage, ExpectedAppImage.load(packagingAppImageDir)); + + var actualExecutedTaskActions = dryRun(builder, toConsumer(_ -> { + builder.create().execute(startupParameters); + })); + + assertEquals(List.of(expectedExecutedTaskActions), actualExecutedTaskActions); + } + + private static Application createApp(AppImageLayout appImageLayout) { + return createApp(appImageLayout, Optional.empty()); + } + + private static Application createApp(AppImageLayout appImageLayout, RuntimeBuilder runtimeBuilder) { + return createApp(appImageLayout, Optional.of(runtimeBuilder)); + } + + private static Application createApp(AppImageLayout appImageLayout, Optional runtimeBuilder) { + Objects.requireNonNull(appImageLayout); + Objects.requireNonNull(runtimeBuilder); + if (appImageLayout.isResolved()) { + throw new IllegalArgumentException(); + } + + return new Application.Stub( + "foo", + "My app", + "1.0", + "Acme", + "copyright", + Optional.empty(), + List.of(), + appImageLayout, + runtimeBuilder, + List.of(), + Map.of()); + } + + + private static final class PackageBuilder { + PackageBuilder(Application app) { + this.app = Objects.requireNonNull(app); + } + + Package create() { + return new Package.Stub( + app, + new PackageType() {}, + "the-package", + "My package", + "1.0", + Optional.empty(), + Optional.empty(), + Optional.ofNullable(predefinedAppImage), + null, + TEST_INSTALL_DIR); + } + + PackageBuilder predefinedAppImage(Path v) { + predefinedAppImage = v; + return this; + } + + private Path predefinedAppImage; + private final Application app; + } + + + private static PackageBuilder buildPackage(Application app) { + return new PackageBuilder(app); + } + + private static BuildEnvBuilder buildEnv(Path rootDir) { + return new BuildEnvBuilder(rootDir); + } + + private static BuildEnv dummyBuildEnv() { + return BuildEnv.create(Path.of("foo"), Optional.empty(), false, PackagingPipeline.class, RuntimeLayout.DEFAULT); + } + + private static PackagingPipeline.Builder buildPipeline() { + return PackagingPipeline.buildStandard() + // Disable building the app image file (.jpackage.xml) as we don't have launchers in the test app. + .task(BuildApplicationTaskID.APP_IMAGE_FILE).noaction().add() + .task(BuildApplicationTaskID.LAUNCHERS).applicationAction(cfg -> { + TestLauncher.INSTANCE.create(cfg.resolvedLayout()); + }).add() + .task(PrimaryTaskID.PACKAGE).packageAction(cfg -> { + var str = createTestPackageFileContents(cfg.resolvedLayout()); + var packageFile = cfg.outputDir().resolve(cfg.pkg().packageFileNameWithSuffix()); + Files.createDirectories(packageFile.getParent()); + Files.writeString(packageFile, str); + }).add(); + } + + private static String createTestPackageFileContents(AppImageLayout pkgLayout) throws IOException { + return ExpectedAppImage.load(pkgLayout.rootDirectory()).toString(); + } + + private static void assertNotSameAppImageDirs(BuildEnv a, BuildEnv b) { + assertNotEquals(a.appImageDir(), b.appImageDir()); + assertEquals(a.buildRoot(), b.buildRoot()); + assertEquals(a.configDir(), b.configDir()); + assertEquals(a.resourceDir(), b.resourceDir()); + } + + + private static final class TestRuntimeBuilder implements RuntimeBuilder { + @Override + public void create(AppImageLayout appImageLayout) { + assertTrue(appImageLayout.isResolved()); + try { + Files.createDirectories(appImageLayout.runtimeDirectory()); + Files.writeString(runtimeFile(appImageLayout), CONTENT); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static Path runtimeFile(AppImageLayout appImageLayout) { + return appImageLayout.runtimeDirectory().resolve("my-runtime"); + } + + static final String CONTENT = "this is the runtime"; + + static final TestRuntimeBuilder INSTANCE = new TestRuntimeBuilder(); + } + + + private static final class TestLauncher { + public void create(ApplicationLayout appLayout) { + assertTrue(appLayout.isResolved()); + try { + Files.createDirectories(appLayout.launchersDirectory()); + Files.writeString(launcherFile(appLayout), CONTENT); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static Path launcherFile(ApplicationLayout appLayout) { + return appLayout.launchersDirectory().resolve("my-launcher"); + } + + static final String CONTENT = "this is the launcher"; + + static final TestLauncher INSTANCE = new TestLauncher(); + } + + + private static final class ExpectedAppImage { + + static ExpectedAppImage build() { + return new ExpectedAppImage(new HashSet<>()); + } + + static ExpectedAppImage load(Path appImageRoot) throws IOException { + try (var walk = Files.walk(appImageRoot)) { + return new ExpectedAppImage(walk.sorted().map(path -> { + var relativePath = appImageRoot.relativize(path); + if (Files.isDirectory(path)) { + return new Directory(relativePath); + } else { + return new File(relativePath, toSupplier(() -> Files.readString(path)).get()); + } + }).collect(Collectors.toSet())); + } + } + + ExpectedAppImage file(Path path, String content) { + return add(new File(path, content)); + } + + ExpectedAppImage file(String path, String content) { + return file(Path.of(path), content); + } + + ExpectedAppImage dir(Path path) { + return add(new Directory(path)); + } + + ExpectedAppImage dir(String path) { + return dir(Path.of(path)); + } + + @Override + public String toString() { + return items.stream().map(AppImageItem::toString).sorted().collect(Collectors.joining("\n")); + } + + @Override + public int hashCode() { + return Objects.hash(items); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + ExpectedAppImage other = (ExpectedAppImage) obj; + return Objects.equals(items, other.items); + } + + private ExpectedAppImage(Set items) { + this.items = Objects.requireNonNull(items); + } + + private ExpectedAppImage add(AppImageItem v) { + var path = v.path(); + if (path.isAbsolute()) { + throw new IllegalArgumentException(); + } + + items.add(v); + while (path.getNameCount() > 1) { + items.add(new Directory(path = path.getParent())); + } + return this; + } + + private interface AppImageItem { + Path path(); + } + + private record File(Path path, String content) implements AppImageItem { + + File { + Objects.requireNonNull(path); + Objects.requireNonNull(content); + } + + @Override + public String toString() { + return String.format("%s[%s]", path, content); + } + } + + private record Directory(Path path) implements AppImageItem { + + Directory { + Objects.requireNonNull(path); + } + + @Override + public String toString() { + return path.toString(); + } + } + + private final Set items; + } + + + private static final ApplicationLayout TEST_LAYOUT_1 = ApplicationLayout.build() + .launchersDirectory("launchers") + .appDirectory("") + .runtimeDirectory("runtime") + .appModsDirectory("") + .contentDirectory("") + .desktopIntegrationDirectory("") + .create(); + + private static final ApplicationLayout TEST_LAYOUT_2 = ApplicationLayout.build() + .launchersDirectory("q/launchers") + .appDirectory("") + .runtimeDirectory("qqq/runtime") + .appModsDirectory("") + .contentDirectory("") + .desktopIntegrationDirectory("") + .create(); + + private static final Path TEST_INSTALL_DIR = Path.of("Acme/My app"); + + private static final ApplicationLayout TEST_LAYOUT_1_WITH_INSTALL_DIR = + TEST_LAYOUT_1.resolveAt(TEST_INSTALL_DIR).resetRootDirectory(); + + private static final ApplicationLayout TEST_LAYOUT_2_WITH_INSTALL_DIR = + TEST_LAYOUT_2.resolveAt(TEST_INSTALL_DIR).resetRootDirectory(); +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/AppImageLayoutTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/AppImageLayoutTest.java index 1c863b653b1..5948d74079e 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/AppImageLayoutTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/AppImageLayoutTest.java @@ -23,12 +23,14 @@ package jdk.jpackage.internal.model; +import static jdk.jpackage.internal.model.AppImageLayout.toPathGroup; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; import java.nio.file.Path; import java.util.List; import java.util.Set; +import jdk.jpackage.internal.util.PathGroup; import org.junit.jupiter.api.Test; @@ -48,7 +50,7 @@ public class AppImageLayoutTest { public void testPathGroup() { final var layout = new AppImageLayout.Stub(Path.of("root"), Path.of("runtime")); - final var pathGroup = AppImageLayout.toPathGroup(layout); + final var pathGroup = toPathGroup(layout); assertEquals(Set.of("runtimeDirectory"), pathGroup.keys()); assertEquals(List.of(layout.runtimeDirectory()), pathGroup.paths()); @@ -56,15 +58,88 @@ public class AppImageLayoutTest { @Test public void testResolveAt() { - final var dir = Path.of("foo/bar"); - - final var layout = new AppImageLayout.Stub(Path.of(""), Path.of("runtime")); - - final var resolvedLayout = layout.resolveAt(dir); - - assertNotSame(layout, resolvedLayout); - - assertEquals(dir.resolve(layout.rootDirectory()), resolvedLayout.rootDirectory()); - assertEquals(dir.resolve(layout.runtimeDirectory()), resolvedLayout.runtimeDirectory()); + testResolveAt(new AppImageLayout.Stub(Path.of("foo"))); } + + @Test + public void testResolveAtRepeat() { + testResolveAtRepeat(new AppImageLayout.Stub(Path.of("foo"))); + } + + @Test + public void testUnresolve() { + testUnresolve(new AppImageLayout.Stub(Path.of("runtime"))); + } + + @Test + public void testEmptyRootDirectory() { + testEmptyRootDirectory(new AppImageLayout.Stub(Path.of("rt"))); + } + + public static void testResolveAt(AppImageLayout testee) { + + var dir = Path.of("foo/bar"); + + assertLayout(testee.resolveAt(dir), true, testee, dir); + } + + public static void testResolveAtRepeat(AppImageLayout testee) { + + var resolvedLayout = testee.resolveAt(Path.of("b/c")).resolveAt(Path.of("a")); + + assertLayout(resolvedLayout, true, testee, Path.of("a/b/c")); + } + + public static void testUnresolve(AppImageLayout testee) { + if (testee.isResolved()) { + throw new IllegalArgumentException(); + } + + var resolvedLayout = testee.resolveAt(Path.of("foo/bar")); + var layout = resolvedLayout.unresolve(); + + assertLayout(layout, false, testee, Path.of("")); + + resolvedLayout = testee.resolveAt(Path.of("").toAbsolutePath()); + layout = resolvedLayout.unresolve(); + + assertLayout(layout, false, testee, Path.of("")); + + assertSame(testee, testee.unresolve()); + } + + public static void testEmptyRootDirectory(AppImageLayout testee) { + if (testee.isResolved()) { + throw new IllegalArgumentException(); + } + + assertEmptyRootDirectory(testee); + + final var resolved = testee.resolveAt(Path.of("t")); + + assertEmptyRootDirectory(resolved); + } + + private static void assertEmptyRootDirectory(AppImageLayout testee) { + if (testee.isResolved()) { + var newLayout = testee.resetRootDirectory(); + assertLayout(newLayout, false, Path.of(""), toPathGroup(testee)); + } else { + assertSame(testee, testee.resetRootDirectory()); + } + } + + private static void assertLayout(AppImageLayout actual, boolean expectedResolved, + AppImageLayout base, Path baseResolveAt) { + assertLayout(actual, expectedResolved, baseResolveAt.resolve(base.rootDirectory()), + toPathGroup(base).resolveAt(baseResolveAt)); + } + + private static void assertLayout(AppImageLayout actual, boolean expectedResolved, + Path expectedRootDir, PathGroup expectedPaths) { + assertEquals(expectedResolved, actual.isResolved()); + assertEquals(expectedRootDir, actual.rootDirectory()); + assertEquals(expectedPaths, toPathGroup(actual)); + } + } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java index 089a363e0e4..063b11ec589 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java @@ -23,20 +23,51 @@ package jdk.jpackage.internal.model; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; public class ApplicationLayoutTest { - public void test(boolean move, Path tempDir) throws IOException { + @Test + public void testMove(@TempDir Path tempDir) throws IOException { + test(true, tempDir); + } + + @Test + public void testCopy(@TempDir Path tempDir) throws IOException { + test(false, tempDir); + } + + @Test + public void testResolveAt() { + AppImageLayoutTest.testResolveAt(createLayout()); + } + + @Test + public void testResolveAtRepeat() { + AppImageLayoutTest.testResolveAtRepeat(createLayout()); + } + + @Test + public void testUnresolve() { + AppImageLayoutTest.testUnresolve(createLayout()); + } + + @Test + public void testEmptyRootDirectory() { + AppImageLayoutTest.testEmptyRootDirectory(createLayout()); + } + + private static void test(boolean move, Path tempDir) throws IOException { final var srcAppImageRoot = tempDir.resolve("src"); Files.createDirectories(srcAppImageRoot); @@ -55,14 +86,7 @@ public class ApplicationLayoutTest { Files.createDirectories(path); } - final var layout = ApplicationLayout.build() - .launchersDirectory("bin") - .appDirectory("lib/app") - .runtimeDirectory("runtime") - .appModsDirectory("mods") - .contentDirectory("content") - .desktopIntegrationDirectory("lib/apps") - .create(); + final var layout = createLayout(); final var dstAppImageRoot = tempDir.resolve("dst"); Files.createDirectories(dstAppImageRoot); @@ -100,13 +124,14 @@ public class ApplicationLayoutTest { } } - @Test - public void testMove(@TempDir Path tempDir) throws IOException { - test(true, tempDir); - } - - @Test - public void testCopy(@TempDir Path tempDir) throws IOException { - test(false, tempDir); + public static ApplicationLayout createLayout() { + return ApplicationLayout.build() + .launchersDirectory("bin") + .appDirectory("lib/app") + .runtimeDirectory("runtime") + .appModsDirectory("mods") + .contentDirectory("content") + .desktopIntegrationDirectory("lib/apps") + .create(); } } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PathGroupTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PathGroupTest.java index 579cf5f083c..c5b5e271960 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PathGroupTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/PathGroupTest.java @@ -65,6 +65,14 @@ public class PathGroupTest { assertThrowsExactly(NullPointerException.class, () -> new PathGroup(Map.of()).getPath(null)); } + @Test + public void equals() { + assertEquals(new PathGroup(Map.of()), new PathGroup(Map.of())); + assertEquals(new PathGroup(Map.of("foo", Path.of("bar"))), new PathGroup(Map.of("foo", Path.of("bar")))); + assertNotEquals(new PathGroup(Map.of("foo", Path.of("bar"))), new PathGroup(Map.of("foo", Path.of("rab")))); + assertNotEquals(new PathGroup(Map.of("foo", Path.of("bar"))), new PathGroup(Map.of("Foo", Path.of("bar")))); + } + @Test public void testEmptyPathGroup() { PathGroup pg = new PathGroup(Map.of()); diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JUnitAdapter.java b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.java similarity index 90% rename from test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JUnitAdapter.java rename to test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.java index b7c50285797..5a605b1642d 100644 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JUnitAdapter.java +++ b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.java @@ -40,10 +40,12 @@ import org.junit.jupiter.api.TestFactory; public class JUnitAdapter { - static { - if (System.getProperty("test.src") == null) { - // Was called by somebody else but not by jtreg - System.setProperty("test.src", Path.of("@@openJdkDir@@/test/jdk/tools/jpackage").toString()); + public static class TestSrcInitializer { + static { + if (System.getProperty("test.src") == null) { + // Was called by somebody else but not by jtreg + System.setProperty("test.src", Path.of("@@openJdkDir@@/test/jdk/tools/jpackage").toString()); + } } } @@ -84,5 +86,9 @@ public class JUnitAdapter { } } + static { + new TestSrcInitializer(); + } + private static final int LOG_MSG_TIMESTAMP_LENGTH = "[HH:mm:ss.SSS] ".length(); } diff --git a/test/jdk/tools/jpackage/junit/windows/junit.java b/test/jdk/tools/jpackage/junit/windows/junit.java index 38b5cd5ca0c..d27a282f0dc 100644 --- a/test/jdk/tools/jpackage/junit/windows/junit.java +++ b/test/jdk/tools/jpackage/junit/windows/junit.java @@ -24,6 +24,7 @@ /* @test * @summary Test function reading OS version from PE file * @requires (os.family == "windows") - * @compile/module=jdk.jpackage jdk/jpackage/internal/ExecutableOSVersionTest.java + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/ExecutableOSVersionTest.java * @run junit jdk.jpackage/jdk.jpackage.internal.ExecutableOSVersionTest */ diff --git a/test/jdk/tools/jpackage/share/AddLShortcutTest.java b/test/jdk/tools/jpackage/share/AddLShortcutTest.java index 7d7d8b50c1d..9c50c6ffc98 100644 --- a/test/jdk/tools/jpackage/share/AddLShortcutTest.java +++ b/test/jdk/tools/jpackage/share/AddLShortcutTest.java @@ -28,16 +28,21 @@ import java.nio.file.Path; import java.time.Duration; import java.util.ArrayList; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import jdk.internal.util.OperatingSystem; import jdk.jpackage.test.AdditionalLauncher; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.FileAssociations; +import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JavaAppDesc; import jdk.jpackage.test.LauncherShortcut; import jdk.jpackage.test.LauncherShortcut.InvokeShortcutSpec; import jdk.jpackage.test.LauncherShortcut.StartupDirectory; @@ -63,6 +68,7 @@ import jdk.jpackage.test.WinShortcutVerifier; * @key jpackagePlatformPackage * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* + * @requires (os.family != "mac") * @requires (jpackage.test.SQETest != null) * @compile -Xlint:all -Werror AddLShortcutTest.java * @run main/othervm/timeout=540 -Xmx512m @@ -76,6 +82,7 @@ import jdk.jpackage.test.WinShortcutVerifier; * @key jpackagePlatformPackage * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* + * @requires (os.family != "mac") * @requires (jpackage.test.SQETest == null) * @compile -Xlint:all -Werror AddLShortcutTest.java * @run main/othervm/timeout=1080 -Xmx512m @@ -85,7 +92,7 @@ import jdk.jpackage.test.WinShortcutVerifier; public class AddLShortcutTest { - @Test + @Test(ifNotOS = OperatingSystem.MACOS) public void test() { // Configure several additional launchers with each combination of // possible shortcut hints in add-launcher property file. @@ -93,6 +100,8 @@ public class AddLShortcutTest { // will have shortcuts while other launchers with some properties set // to "false" will have none. + final var packageName = MethodHandles.lookup().lookupClass().getSimpleName(); + PackageTest packageTest = new PackageTest().configureHelloApp(); packageTest.addInitializer(cmd -> { cmd.addArguments("--arguments", "Duke", "--arguments", "is", @@ -102,11 +111,14 @@ public class AddLShortcutTest { } else if (TKit.isLinux()) { cmd.addArguments("--linux-shortcut"); } + + cmd.setArgumentValue("--name", packageName); + + var addLauncherApp = TKit.TEST_SRC_ROOT.resolve("apps/PrintEnv.java"); + HelloApp.createBundle(JavaAppDesc.parse(addLauncherApp + "*another.jar:Welcome"), cmd.inputDir()); }); - new FileAssociations( - MethodHandles.lookup().lookupClass().getSimpleName()).applyTo( - packageTest); + new FileAssociations(packageName).applyTo(packageTest); new AdditionalLauncher("Foo") .setDefaultArguments("yep!") @@ -131,11 +143,16 @@ public class AddLShortcutTest { .setShortcuts(true, false) .applyTo(packageTest); - new AdditionalLauncher("Launcher5") - .setDefaultArguments() + var launcher5 = new AdditionalLauncher("Launcher5") + .setDefaultArguments("--print-workdir") .setIcon(GOLDEN_ICON) - .setShortcuts(false, true) - .applyTo(packageTest); + .setShortcut(LauncherShortcut.LINUX_SHORTCUT, StartupDirectory.APP_DIR) + .setShortcut(LauncherShortcut.WIN_DESKTOP_SHORTCUT, StartupDirectory.APP_DIR) + .setShortcut(LauncherShortcut.WIN_START_MENU_SHORTCUT, null) + .setProperty("main-jar", "another.jar") + .setProperty("main-class", "Welcome"); + + new ShortcutStartupDirectoryVerifier(packageName).add(launcher5).applyTo(packageTest); packageTest.run(); } @@ -190,10 +207,50 @@ public class AddLShortcutTest { predefinedAppImage[0] = cmd.outputBundle(); }).addInitializer(cmd -> { + cfgs[0].applyToMainLauncher(cmd); cmd.removeArgumentWithValue("--input"); cmd.setArgumentValue("--name", "AddLShortcutDir2Test"); cmd.addArguments("--app-image", predefinedAppImage[0]); - cfgs[0].applyToMainLauncher(cmd); + }).run(RunnablePackageTest.Action.CREATE_AND_UNPACK); + } + + @Test(ifNotOS = OperatingSystem.MACOS) + @Parameter(value = "DEFAULT") + @Parameter(value = "APP_DIR") + public void testLastArg(StartupDirectory startupDirectory) { + final List shortcutArgs = new ArrayList<>(); + if (TKit.isLinux()) { + shortcutArgs.add("--linux-shortcut"); + } else if (TKit.isWindows()) { + shortcutArgs.add("--win-shortcut"); + } else { + TKit.assertUnexpected("Unsupported platform"); + } + + if (startupDirectory == StartupDirectory.APP_DIR) { + shortcutArgs.add(startupDirectory.asStringValue()); + } + + Path[] predefinedAppImage = new Path[1]; + + new PackageTest().addRunOnceInitializer(() -> { + var cmd = JPackageCommand.helloAppImage() + .setArgumentValue("--name", "foo") + .setFakeRuntime(); + + cmd.execute(); + + predefinedAppImage[0] = cmd.outputBundle(); + }).addInitializer(cmd -> { + cmd.removeArgumentWithValue("--input"); + cmd.setArgumentValue("--name", "AddLShortcutDir3Test"); + cmd.addArguments("--app-image", predefinedAppImage[0]); + cmd.ignoreDefaultVerbose(true); + }).addInitializer(cmd -> { + cmd.addArguments(shortcutArgs); + }).addBundleVerifier(cmd -> { + TKit.assertEquals(shortcutArgs.getLast(), cmd.getAllArguments().getLast(), + "Check the last argument of jpackage command line"); }).run(RunnablePackageTest.Action.CREATE_AND_UNPACK); } @@ -207,6 +264,7 @@ public class AddLShortcutTest { @Test(ifNotOS = OperatingSystem.MACOS) @Parameter(value = "DEFAULT") + @Parameter(value = "APP_DIR") public void testInvokeShortcuts(StartupDirectory startupDirectory) { var testApp = TKit.TEST_SRC_ROOT.resolve("apps/PrintEnv.java"); @@ -219,82 +277,118 @@ public class AddLShortcutTest { cmd.addArguments("--arguments", "--print-workdir"); }).addInitializer(JPackageCommand::ignoreFakeRuntime).addHelloAppInitializer(testApp + "*Hello"); - var shortcutStartupDirectoryVerifier = new ShortcutStartupDirectoryVerifier(name, "a"); - - shortcutStartupDirectoryVerifier.applyTo(test, startupDirectory); - - test.addInstallVerifier(cmd -> { - if (!cmd.isPackageUnpacked("Not invoking launcher shortcuts")) { - Collection invokeShortcutSpecs; - if (TKit.isLinux()) { - invokeShortcutSpecs = LinuxHelper.getInvokeShortcutSpecs(cmd); - } else if (TKit.isWindows()) { - invokeShortcutSpecs = WinShortcutVerifier.getInvokeShortcutSpecs(cmd); - } else { - throw new UnsupportedOperationException(); - } - shortcutStartupDirectoryVerifier.verify(invokeShortcutSpecs); - } - }); + new ShortcutStartupDirectoryVerifier(name).add("a", startupDirectory).applyTo(test); test.run(); } - private record ShortcutStartupDirectoryVerifier(String packageName, String launcherName) { - ShortcutStartupDirectoryVerifier { - Objects.requireNonNull(packageName); - Objects.requireNonNull(launcherName); + private static final class ShortcutStartupDirectoryVerifier { + + ShortcutStartupDirectoryVerifier(String packageName) { + this.packageName = Objects.requireNonNull(packageName); } - void applyTo(PackageTest test, StartupDirectory startupDirectory) { - var al = new AdditionalLauncher(launcherName); - al.setShortcut(shortcut(), Objects.requireNonNull(startupDirectory)); - al.addJavaOptions(String.format("-Djpackage.test.appOutput=${%s}/%s", - outputDirVarName(), expectedOutputFilename())); - al.withoutVerifyActions(Action.EXECUTE_LAUNCHER).applyTo(test); - } + void applyTo(PackageTest test) { + verifiers.values().forEach(verifier -> { + verifier.applyTo(test); + }); + test.addInstallVerifier(cmd -> { + if (!cmd.isPackageUnpacked("Not invoking launcher shortcuts")) { + Collection invokeShortcutSpecs; + if (TKit.isLinux()) { + invokeShortcutSpecs = LinuxHelper.getInvokeShortcutSpecs(cmd); + } else if (TKit.isWindows()) { + invokeShortcutSpecs = WinShortcutVerifier.getInvokeShortcutSpecs(cmd); + } else { + throw new UnsupportedOperationException(); + } - void verify(Collection invokeShortcutSpecs) throws IOException { + var invokeShortcutSpecsMap = invokeShortcutSpecs.stream().collect(Collectors.groupingBy(InvokeShortcutSpec::launcherName)); - TKit.trace(String.format("Verify shortcut [%s]", launcherName)); - - var expectedOutputFile = Path.of(System.getenv(outputDirVarName())).resolve(expectedOutputFilename()); - - TKit.deleteIfExists(expectedOutputFile); - - var invokeShortcutSpec = invokeShortcutSpecs.stream().filter(v -> { - return launcherName.equals(v.launcherName()); - }).findAny().orElseThrow(); - - invokeShortcutSpec.execute(); - - // On Linux, "gtk-launch" is used to launch a .desktop file. It is async and there is no - // way to make it wait for exit of a process it triggers. - TKit.waitForFileCreated(expectedOutputFile, Duration.ofSeconds(10), Duration.ofSeconds(3)); - - TKit.assertFileExists(expectedOutputFile); - var actualStr = Files.readAllLines(expectedOutputFile).getFirst(); - - var outputPrefix = "$CD="; - - TKit.assertTrue(actualStr.startsWith(outputPrefix), "Check output starts with '" + outputPrefix+ "' string"); - - invokeShortcutSpec.expectedWorkDirectory().ifPresent(expectedWorkDirectory -> { - TKit.assertEquals( - expectedWorkDirectory, - Path.of(actualStr.substring(outputPrefix.length())), - String.format("Check work directory of %s of launcher [%s]", - invokeShortcutSpec.shortcut().propertyName(), - invokeShortcutSpec.launcherName())); + for (var e : verifiers.entrySet()) { + e.getValue().verify(invokeShortcutSpecsMap.get(e.getKey())); + } + } }); } - private String expectedOutputFilename() { - return String.format("%s-%s.out", packageName, launcherName); + ShortcutStartupDirectoryVerifier add(String launcherName, StartupDirectory startupDirectory) { + return add(new AdditionalLauncher(launcherName) + .setShortcut(shortcut(), Objects.requireNonNull(Objects.requireNonNull(startupDirectory)))); } - private String outputDirVarName() { + ShortcutStartupDirectoryVerifier add(AdditionalLauncher addLauncher) { + var launcherVerifier = new LauncherVerifier(addLauncher); + verifiers.put(launcherVerifier.launcherName(), launcherVerifier); + return this; + } + + + private final class LauncherVerifier { + + private LauncherVerifier(AdditionalLauncher addLauncher) { + this.addLauncher = Objects.requireNonNull(addLauncher); + } + + private String launcherName() { + return addLauncher.name(); + } + + private void applyTo(PackageTest test) { + addLauncher.addJavaOptions(String.format("-Djpackage.test.appOutput=${%s}/%s", + outputDirVarName(), expectedOutputFilename())); + addLauncher.withoutVerifyActions(Action.EXECUTE_LAUNCHER).applyTo(test); + } + + private void verify(Collection invokeShortcutSpecs) throws IOException { + Objects.requireNonNull(invokeShortcutSpecs); + if (invokeShortcutSpecs.isEmpty()) { + throw new IllegalArgumentException(); + } + + TKit.trace(String.format("Verify shortcut [%s]", launcherName())); + + var expectedOutputFile = Path.of(System.getenv(outputDirVarName())).resolve(expectedOutputFilename()); + + TKit.deleteIfExists(expectedOutputFile); + + var invokeShortcutSpec = invokeShortcutSpecs.stream().filter(v -> { + return launcherName().equals(v.launcherName()); + }).findAny().orElseThrow(); + + invokeShortcutSpec.execute(); + + // On Linux, "gtk-launch" is used to launch a .desktop file. It is async and there is no + // way to make it wait for exit of a process it triggers. + TKit.waitForFileCreated(expectedOutputFile, Duration.ofSeconds(10), Duration.ofSeconds(3)); + + TKit.assertFileExists(expectedOutputFile); + var actualStr = Files.readAllLines(expectedOutputFile).getFirst(); + + var outputPrefix = "$CD="; + + TKit.assertTrue(actualStr.startsWith(outputPrefix), "Check output starts with '" + outputPrefix+ "' string"); + + invokeShortcutSpec.expectedWorkDirectory().ifPresent(expectedWorkDirectory -> { + TKit.assertEquals( + expectedWorkDirectory, + Path.of(actualStr.substring(outputPrefix.length())), + String.format("Check work directory of %s of launcher [%s]", + invokeShortcutSpec.shortcut().propertyName(), + invokeShortcutSpec.launcherName())); + }); + } + + private String expectedOutputFilename() { + return String.format("%s-%s.out", packageName, launcherName()); + } + + private final AdditionalLauncher addLauncher; + } + + + private static String outputDirVarName() { if (TKit.isLinux()) { return "HOME"; } else if (TKit.isWindows()) { @@ -304,7 +398,7 @@ public class AddLShortcutTest { } } - private LauncherShortcut shortcut() { + private static LauncherShortcut shortcut() { if (TKit.isLinux()) { return LauncherShortcut.LINUX_SHORTCUT; } else if (TKit.isWindows()) { @@ -313,6 +407,10 @@ public class AddLShortcutTest { throw new UnsupportedOperationException(); } } + + private final String packageName; + // Keep the order + private final Map verifiers = new LinkedHashMap<>(); } diff --git a/test/jdk/tools/jpackage/share/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java index c352decc0f3..2d3d61de864 100644 --- a/test/jdk/tools/jpackage/share/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -576,6 +576,9 @@ public final class ErrorTest { ); }).flatMap(x -> x).map(TestSpec.Builder::create).toList()); + invalidShortcut(testCases::add, "--win-menu"); + invalidShortcut(testCases::add, "--win-shortcut"); + return toTestArgs(testCases.stream()); } @@ -642,6 +645,8 @@ public final class ErrorTest { .error("error.rpm-invalid-value-for-package-name.advice") ).map(TestSpec.Builder::create).toList()); + invalidShortcut(testCases::add, "--linux-shortcut"); + return toTestArgs(testCases.stream()); } @@ -697,6 +702,13 @@ public final class ErrorTest { duplicateAddArgs(builder, accumulator, "--mac-sign"); } + private static void invalidShortcut(Consumer accumulator, String shortcutOption) { + Objects.requireNonNull(shortcutOption); + Stream.of("true", "false", "").map(value -> { + return testSpec().nativeType().addArgs(shortcutOption, value).error("error.invalid-option-value", value, shortcutOption).create(); + }).forEach(accumulator); + } + private record UnsupportedPlatformOption(String name, Optional value) { UnsupportedPlatformOption { Objects.requireNonNull(name); diff --git a/test/jdk/tools/launcher/TooSmallStackSize.java b/test/jdk/tools/launcher/TooSmallStackSize.java index 8485f115d6e..8133dbf4550 100644 --- a/test/jdk/tools/launcher/TooSmallStackSize.java +++ b/test/jdk/tools/launcher/TooSmallStackSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ * @bug 6762191 8222334 * @summary Setting stack size to 16K causes segmentation fault * @compile TooSmallStackSize.java + * @comment VM fails to launch with minimum allowed stack size of 136k when asan is enabled + * @requires !vm.asan * @run main TooSmallStackSize */ diff --git a/test/jdk/tools/sincechecker/SinceChecker.java b/test/jdk/tools/sincechecker/SinceChecker.java index a7ec90d53f8..f9a8e3d49dd 100644 --- a/test/jdk/tools/sincechecker/SinceChecker.java +++ b/test/jdk/tools/sincechecker/SinceChecker.java @@ -297,7 +297,6 @@ public class SinceChecker { Collections.singletonList(SimpleJavaFileObject.forSource(URI.create("myfo:/Test.java"), ""))); ct.analyze(); Elements elements = ct.getElements(); - elements.getModuleElement("java.base"); try (EffectiveSourceSinceHelper javadocHelper = EffectiveSourceSinceHelper.create(ct, List.of(root), this)) { processModuleCheck(elements.getModuleElement(moduleName), ct, moduleDirectory, javadocHelper); } catch (Exception e) { diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 5ac0bea3937..74ea525b415 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -422,7 +422,12 @@ public class VMProps implements Callable> { * @return true if CDS is supported by the VM to be tested. */ protected String vmCDS() { - return "" + WB.isCDSIncluded(); + boolean noJvmtiAdded = allFlags() + .filter(s -> s.startsWith("-agentpath")) + .findAny() + .isEmpty(); + + return "" + (noJvmtiAdded && WB.isCDSIncluded()); } /** diff --git a/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java b/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java index ee1fa538a57..ba846c293a2 100644 --- a/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java +++ b/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java @@ -143,6 +143,9 @@ public class CheckStylesheetClasses { "search-result-desc", "search-result-label", "search-result-link", "selected", "sort-asc", "sort-desc", "two-column-search-results", "visible"); + // used for themes + removeAll(styleSheetNames, "theme-dark", "theme-light", "theme-os"); + // very JDK specific styleSheetNames.remove("module-graph"); styleSheetNames.remove("sealed-graph"); diff --git a/test/langtools/jdk/javadoc/doclet/testHtmlLandmarkRegions/TestHtmlLandmarkRegions.java b/test/langtools/jdk/javadoc/doclet/testHtmlLandmarkRegions/TestHtmlLandmarkRegions.java index a65e474a251..784efa9767d 100644 --- a/test/langtools/jdk/javadoc/doclet/testHtmlLandmarkRegions/TestHtmlLandmarkRegions.java +++ b/test/langtools/jdk/javadoc/doclet/testHtmlLandmarkRegions/TestHtmlLandmarkRegions.java @@ -83,7 +83,7 @@ public class TestHtmlLandmarkRegions extends JavadocTester {

    Document Title

    """, """ -

    NameDescripionDescriptionDefault valueFormatExample values
    {@code maxSize}Specifies the size, measured in bytes, at which data is kept in disk + * Specifies the size, measured in bytes, at which data is kept in the disk * repository. Only works if * {@code disk=true}, otherwise this parameter is ignored.{@code "0"} (no limit)See {@code Paths#getPath} for format.
    * If this method is invoked from another process, the data is written on the * machine where the target JVM is running. If destination is a relative path, it - * is relative to the working directory where the target JVM was started.}
    {@code "c:\recording\recotding.jfr"},
    + * is relative to the working directory where the target JVM was started.
    {@code "c:\recording\recording.jfr"},
    * {@code "/recordings/recording.jfr"}, {@code "recording.jfr"}
    {@code duration}Sets how long the recording should be runningSpecifies the duration of the recording.{@code "0"} (no limit, continuous){@code "0"} if no limit should be imposed, otherwise a string * representation of a positive {@code Long} followed by an empty space and one @@ -287,7 +288,7 @@ public interface FlightRecorderMXBean extends PlatformManagedObject { *
    NameDescripionDescriptionDefault valueFormatExample values{@code "1.0"}A version number with a major and minor.
    *
    - * To be able to read from a running recording the value must be set
    {@code "1.0"} *