diff --git a/bin/generate-symbol-data.sh b/bin/generate-symbol-data.sh index 283757a6918..14d8763ad81 100644 --- a/bin/generate-symbol-data.sh +++ b/bin/generate-symbol-data.sh @@ -38,7 +38,7 @@ # directory. # - open a terminal program and run these commands: # cd "${JDK_CHECKOUT}"/src/jdk.compiler/share/data/symbols -# bash ../../../../../make/scripts/generate-symbol-data.sh "${JDK_N_INSTALL}" +# bash ../../../../../bin/generate-symbol-data.sh "${JDK_N_INSTALL}" # - this command will generate or update data for "--release N" into the ${JDK_CHECKOUT}/src/jdk.compiler/share/data/symbols # directory, updating all registration necessary. If the goal was to update the data, and there are no # new or changed files in the ${JDK_CHECKOUT}/src/jdk.compiler/share/data/symbols directory after running this script, diff --git a/doc/starting-next-release.md b/doc/starting-next-release.md index 10bc364a3e4..5bb9c5839a4 100644 --- a/doc/starting-next-release.md +++ b/doc/starting-next-release.md @@ -65,4 +65,4 @@ to be updated for a particular release. * `test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java` update annotation processor extended by `javac` tests to cover the new source version * `test/langtools/tools/javac/preview/classReaderTest/Client.nopreview.out` and `test/langtools/tools/javac/preview/classReaderTest/Client.preview.out`: update expected messages for preview errors and warnings - +* `test/langtools/tools/javac/versions/Versions.java`: add new source version to the set of valid sources and add new enum constant for the new class file version. diff --git a/make/ToolsJdk.gmk b/make/ToolsJdk.gmk index 629cadbf83a..b04d7820c91 100644 --- a/make/ToolsJdk.gmk +++ b/make/ToolsJdk.gmk @@ -79,7 +79,7 @@ TOOL_GENERATEEXTRAPROPERTIES = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_too build.tools.generateextraproperties.GenerateExtraProperties TOOL_GENERATECASEFOLDING = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ - build.tools.generatecharacter.CaseFolding + build.tools.generatecharacter.GenerateCaseFolding TOOL_MAKEZIPREPRODUCIBLE = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ build.tools.makezipreproducible.MakeZipReproducible diff --git a/make/jdk/src/classes/build/tools/generatecharacter/CaseFolding.java b/make/jdk/src/classes/build/tools/generatecharacter/CaseFolding.java deleted file mode 100644 index 9abc2059b6a..00000000000 --- a/make/jdk/src/classes/build/tools/generatecharacter/CaseFolding.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package build.tools.generatecharacter; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class CaseFolding { - - public static void main(String[] args) throws Throwable { - if (args.length != 3) { - System.err.println("Usage: java CaseFolding TemplateFile CaseFolding.txt CaseFolding.java"); - System.exit(1); - } - var templateFile = Paths.get(args[0]); - var caseFoldingTxt = Paths.get(args[1]); - var genSrcFile = Paths.get(args[2]); - var supportedTypes = "^.*; [CTS]; .*$"; - var caseFoldingEntries = Files.lines(caseFoldingTxt) - .filter(line -> !line.startsWith("#") && line.matches(supportedTypes)) - .map(line -> { - String[] cols = line.split("; "); - return new String[] {cols[0], cols[1], cols[2]}; - }) - .filter(cols -> { - // the folding case doesn't map back to the original char. - var cp1 = Integer.parseInt(cols[0], 16); - var cp2 = Integer.parseInt(cols[2], 16); - return Character.toUpperCase(cp2) != cp1 && Character.toLowerCase(cp2) != cp1; - }) - .map(cols -> String.format(" entry(0x%s, 0x%s)", cols[0], cols[2])) - .collect(Collectors.joining(",\n", "", "")); - - // hack, hack, hack! the logic does not pick 0131. just add manually to support 'I's. - // 0049; T; 0131; # LATIN CAPITAL LETTER I - final String T_0x0131_0x49 = String.format(" entry(0x%04x, 0x%04x),\n", 0x0131, 0x49); - - // Generate .java file - Files.write( - genSrcFile, - Files.lines(templateFile) - .map(line -> line.contains("%%%Entries") ? T_0x0131_0x49 + caseFoldingEntries : line) - .collect(Collectors.toList()), - StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); - } -} diff --git a/make/jdk/src/classes/build/tools/generatecharacter/GenerateCaseFolding.java b/make/jdk/src/classes/build/tools/generatecharacter/GenerateCaseFolding.java new file mode 100644 index 00000000000..2f6a9add5cb --- /dev/null +++ b/make/jdk/src/classes/build/tools/generatecharacter/GenerateCaseFolding.java @@ -0,0 +1,134 @@ +/* + * 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 build.tools.generatecharacter; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class GenerateCaseFolding { + + public static void main(String[] args) throws Throwable { + if (args.length != 3) { + System.err.println("Usage: java GenerateCaseFolding TemplateFile CaseFolding.txt CaseFolding.java"); + System.exit(1); + } + var templateFile = Paths.get(args[0]); + var caseFoldingTxt = Paths.get(args[1]); + var genSrcFile = Paths.get(args[2]); + + // java.lang + var supportedTypes = "^.*; [CF]; .*$"; // full/1:M case folding + String[][] caseFoldings = Files.lines(caseFoldingTxt) + .filter(line -> !line.startsWith("#") && line.matches(supportedTypes)) + .map(line -> { + var fields = line.split("; "); + var cp = fields[0]; + fields = fields[2].trim().split(" "); + var folding = new String[fields.length + 1]; + folding[0] = cp; + System.arraycopy(fields, 0, folding, 1, fields.length); + return folding; + }) + .toArray(size -> new String[size][]); + + // util.regex + var expandedSupportedTypes = "^.*; [CTS]; .*$"; + var expanded_caseFoldingEntries = Files.lines(caseFoldingTxt) + .filter(line -> !line.startsWith("#") && line.matches(expandedSupportedTypes)) + .map(line -> { + String[] cols = line.split("; "); + return new String[]{cols[0], cols[1], cols[2]}; + }) + .filter(cols -> { + // the folding case doesn't map back to the original char. + var cp1 = Integer.parseInt(cols[0], 16); + var cp2 = Integer.parseInt(cols[2], 16); + return Character.toUpperCase(cp2) != cp1 && Character.toLowerCase(cp2) != cp1; + }) + .map(cols -> String.format(" entry(0x%s, 0x%s)", cols[0], cols[2])) + .collect(Collectors.joining(",\n", "", "")); + + // hack, hack, hack! the logic does not pick 0131. just add manually to support 'I's. + // 0049; T; 0131; # LATIN CAPITAL LETTER I + final String T_0x0131_0x49 = String.format(" entry(0x%04x, 0x%04x),\n", 0x0131, 0x49); + + Files.write( + genSrcFile, + Files.lines(templateFile) + .map(line -> line.contains("%%%Entries") ? genFoldingEntries(caseFoldings) : line) + .map(line -> line.contains("%%%Expanded_Case_Map_Entries") ? T_0x0131_0x49 + expanded_caseFoldingEntries : line) + .collect(Collectors.toList()), + StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } + + private static long foldingToLong(String[] folding) { + int cp = Integer.parseInt(folding[0], 16); + long value = (long)Integer.parseInt(folding[1], 16); + if (!Character.isSupplementaryCodePoint(cp) && folding.length != 2) { + var shift = 16; + for (int j = 2; j < folding.length; j++) { + value |= (long)Integer.parseInt(folding[j], 16) << shift; + shift <<= 1; + } + value = value | (long) (folding.length - 1) << 48; + } + return value; + } + + private static String genFoldingEntries(String[][] foldings) { + StringBuilder sb = new StringBuilder(); + sb.append(" private static final int[] CASE_FOLDING_CPS = {\n"); + int width = 10; + for (int i = 0; i < foldings.length; i++) { + if (i % width == 0) + sb.append(" "); + sb.append(String.format("0X%s", foldings[i][0])); + if (i < foldings.length - 1) + sb.append(", "); + if (i % width == width - 1 || i == foldings.length - 1) + sb.append("\n"); + } + sb.append(" };\n\n"); + + sb.append(" private static final long[] CASE_FOLDING_VALUES = {\n"); + width = 6; + for (int i = 0; i < foldings.length; i++) { + if (i % width == 0) + sb.append(" "); // indent + sb.append(String.format("0x%013xL", foldingToLong(foldings[i]))); + if (i < foldings.length - 1) + sb.append(", "); + if (i % width == width - 1 || i == foldings.length - 1) { + sb.append("\n"); + } + } + sb.append(" };\n"); + return sb.toString(); + } +} diff --git a/make/modules/java.base/Gensrc.gmk b/make/modules/java.base/Gensrc.gmk index 79db438934e..e8236f0b0e4 100644 --- a/make/modules/java.base/Gensrc.gmk +++ b/make/modules/java.base/Gensrc.gmk @@ -120,3 +120,25 @@ $(INTPOLY_GEN_DONE): $(INTPLOY_HEADER) $(BUILD_TOOLS_JDK) TARGETS += $(INTPOLY_GEN_DONE) ################################################################################ + +RELEASE_FILE_TEMPLATE := $(TOPDIR)/src/java.base/share/classes/jdk/internal/misc/resources/release.txt.template +RELEASE_FILE_TARGET := $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/jdk/internal/misc/resources/release.txt + +RELEASE_FILE_VARDEPS := $(COMPANY_NAME) $(VERSION_STRING) $(VERSION_DATE) +RELEASE_FILE_VARDEPS_FILE := $(call DependOnVariable, RELEASE_FILE_VARDEPS, \ + $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/jlink_release_txt.vardeps) + +$(eval $(call SetupTextFileProcessing, BUILD_RELEASE_FILE, \ + SOURCE_FILES := $(RELEASE_FILE_TEMPLATE), \ + OUTPUT_FILE := $(RELEASE_FILE_TARGET), \ + REPLACEMENTS := \ + @@COMPANY_NAME@@ => $(COMPANY_NAME) ; \ + @@VERSION_STRING@@ => $(VERSION_STRING) ; \ + @@VERSION_DATE@@ => $(VERSION_DATE) , \ +)) + +$(BUILD_RELEASE_FILE): $(RELEASE_FILE_VARDEPS_FILE) + +TARGETS += $(BUILD_RELEASE_FILE) + +################################################################################ diff --git a/make/modules/java.base/Java.gmk b/make/modules/java.base/Java.gmk index fc091377456..e20db38297d 100644 --- a/make/modules/java.base/Java.gmk +++ b/make/modules/java.base/Java.gmk @@ -34,7 +34,7 @@ DOCLINT += -Xdoclint:all/protected \ '-Xdoclint/package:java.*,javax.*' JAVAC_FLAGS += -XDstringConcat=inline -COPY += .icu .dat .spp .nrm content-types.properties \ +COPY += .icu .dat .spp .nrm .txt content-types.properties \ hijrah-config-Hijrah-umalqura_islamic-umalqura.properties CLEAN += intrinsic.properties diff --git a/make/modules/java.base/gensrc/GensrcCharacterData.gmk b/make/modules/java.base/gensrc/GensrcCharacterData.gmk index c05b126299b..d7947d907e2 100644 --- a/make/modules/java.base/gensrc/GensrcCharacterData.gmk +++ b/make/modules/java.base/gensrc/GensrcCharacterData.gmk @@ -72,5 +72,22 @@ TARGETS += $(GENSRC_CHARACTERDATA) ################################################################################ + +GENSRC_STRINGCASEFOLDING := $(SUPPORT_OUTPUTDIR)/gensrc/java.base/jdk/internal/lang/CaseFolding.java + +STRINGCASEFOLDING_TEMPLATE := $(MODULE_SRC)/share/classes/jdk/internal/lang/CaseFolding.java.template +CASEFOLDINGTXT := $(MODULE_SRC)/share/data/unicodedata/CaseFolding.txt + +$(GENSRC_STRINGCASEFOLDING): $(BUILD_TOOLS_JDK) $(STRINGCASEFOLDING_TEMPLATE) $(CASEFOLDINGTXT) + $(call LogInfo, Generating $@) + $(call MakeTargetDir) + $(TOOL_GENERATECASEFOLDING) \ + $(STRINGCASEFOLDING_TEMPLATE) \ + $(CASEFOLDINGTXT) \ + $(GENSRC_STRINGCASEFOLDING) + +TARGETS += $(GENSRC_STRINGCASEFOLDING) + + endif # include guard include MakeIncludeEnd.gmk diff --git a/make/modules/java.base/gensrc/GensrcRegex.gmk b/make/modules/java.base/gensrc/GensrcRegex.gmk index a30f22b34d4..c46a029e2c2 100644 --- a/make/modules/java.base/gensrc/GensrcRegex.gmk +++ b/make/modules/java.base/gensrc/GensrcRegex.gmk @@ -50,22 +50,5 @@ TARGETS += $(GENSRC_INDICCONJUNCTBREAK) ################################################################################ -GENSRC_CASEFOLDING := $(SUPPORT_OUTPUTDIR)/gensrc/java.base/jdk/internal/util/regex/CaseFolding.java - -CASEFOLDINGTEMP := $(MODULE_SRC)/share/classes/jdk/internal/util/regex/CaseFolding.java.template -CASEFOLDINGTXT := $(MODULE_SRC)/share/data/unicodedata/CaseFolding.txt - -$(GENSRC_CASEFOLDING): $(BUILD_TOOLS_JDK) $(CASEFOLDINGTEMP) $(CASEFOLDINGTXT) - $(call LogInfo, Generating $@) - $(call MakeTargetDir) - $(TOOL_GENERATECASEFOLDING) \ - $(CASEFOLDINGTEMP) \ - $(CASEFOLDINGTXT) \ - $(GENSRC_CASEFOLDING) - -TARGETS += $(GENSRC_CASEFOLDING) - -################################################################################ - endif # include guard include MakeIncludeEnd.gmk diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 8dbc5dbac03..7e2f333ba40 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -2879,7 +2879,7 @@ class StubGenerator: public StubCodeGenerator { // Inputs: // c_rarg0 - source byte array address // c_rarg1 - destination byte array address - // c_rarg2 - K (key) in little endian int array + // c_rarg2 - sessionKe (key) in little endian int array // address generate_aescrypt_encryptBlock() { __ align(CodeEntryAlignment); @@ -2912,7 +2912,7 @@ class StubGenerator: public StubCodeGenerator { // Inputs: // c_rarg0 - source byte array address // c_rarg1 - destination byte array address - // c_rarg2 - K (key) in little endian int array + // c_rarg2 - sessionKd (key) in little endian int array // address generate_aescrypt_decryptBlock() { assert(UseAES, "need AES cryptographic extension support"); @@ -2946,7 +2946,7 @@ class StubGenerator: public StubCodeGenerator { // Inputs: // c_rarg0 - source byte array address // c_rarg1 - destination byte array address - // c_rarg2 - K (key) in little endian int array + // c_rarg2 - sessionKe (key) in little endian int array // c_rarg3 - r vector byte array address // c_rarg4 - input length // @@ -3051,7 +3051,7 @@ class StubGenerator: public StubCodeGenerator { // Inputs: // c_rarg0 - source byte array address // c_rarg1 - destination byte array address - // c_rarg2 - K (key) in little endian int array + // c_rarg2 - sessionKd (key) in little endian int array // c_rarg3 - r vector byte array address // c_rarg4 - input length // @@ -3178,7 +3178,7 @@ class StubGenerator: public StubCodeGenerator { // Inputs: // c_rarg0 - source byte array address // c_rarg1 - destination byte array address - // c_rarg2 - K (key) in little endian int array + // c_rarg2 - sessionKe (key) in little endian int array // c_rarg3 - counter vector byte array address // c_rarg4 - input length // c_rarg5 - saved encryptedCounter start diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index 948092bbb9a..e48778a8b9f 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -2956,7 +2956,7 @@ class StubGenerator: public StubCodeGenerator { // Arguments for generated stub: // R3_ARG1 - source byte array address // R4_ARG2 - destination byte array address - // R5_ARG3 - K (key) in little endian int array + // R5_ARG3 - sessionKe (key) in little endian int array address generate_aescrypt_decryptBlock() { assert(UseAES, "need AES instructions and misaligned SSE support"); StubId stub_id = StubId::stubgen_aescrypt_decryptBlock_id; diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index e5eb15cb8e4..dff9a3d508e 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -2463,7 +2463,7 @@ class StubGenerator: public StubCodeGenerator { // Inputs: // c_rarg0 - source byte array address // c_rarg1 - destination byte array address - // c_rarg2 - K (key) in little endian int array + // c_rarg2 - sessionKe (key) in little endian int array // address generate_aescrypt_encryptBlock() { assert(UseAESIntrinsics, "need AES instructions (Zvkned extension) support"); @@ -2542,7 +2542,7 @@ class StubGenerator: public StubCodeGenerator { // Inputs: // c_rarg0 - source byte array address // c_rarg1 - destination byte array address - // c_rarg2 - K (key) in little endian int array + // c_rarg2 - sessionKe (key) in little endian int array // address generate_aescrypt_decryptBlock() { assert(UseAESIntrinsics, "need AES instructions (Zvkned extension) support"); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp index 1e728ffa279..24de32a6fe7 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp @@ -480,7 +480,7 @@ address StubGenerator::generate_counterMode_VectorAESCrypt() { // Inputs: // c_rarg0 - source byte array address // c_rarg1 - destination byte array address -// c_rarg2 - K (key) in little endian int array +// c_rarg2 - sessionKe (key) in little endian int array // c_rarg3 - counter vector byte array address // Linux // c_rarg4 - input length @@ -1063,7 +1063,7 @@ address StubGenerator::generate_cipherBlockChaining_decryptVectorAESCrypt() { // Inputs: // c_rarg0 - source byte array address // c_rarg1 - destination byte array address -// c_rarg2 - K (key) in little endian int array +// c_rarg2 - sessionKe (key) in little endian int array // address StubGenerator::generate_aescrypt_encryptBlock() { assert(UseAES, "need AES instructions and misaligned SSE support"); @@ -1158,7 +1158,7 @@ address StubGenerator::generate_aescrypt_encryptBlock() { // Inputs: // c_rarg0 - source byte array address // c_rarg1 - destination byte array address -// c_rarg2 - K (key) in little endian int array +// c_rarg2 - sessionKd (key) in little endian int array // address StubGenerator::generate_aescrypt_decryptBlock() { assert(UseAES, "need AES instructions and misaligned SSE support"); @@ -1255,7 +1255,7 @@ address StubGenerator::generate_aescrypt_decryptBlock() { // Inputs: // c_rarg0 - source byte array address // c_rarg1 - destination byte array address -// c_rarg2 - K (key) in little endian int array +// c_rarg2 - sessionKe (key) in little endian int array // c_rarg3 - r vector byte array address // c_rarg4 - input length // @@ -1407,7 +1407,7 @@ address StubGenerator::generate_cipherBlockChaining_encryptAESCrypt() { // Inputs: // c_rarg0 - source byte array address // c_rarg1 - destination byte array address -// c_rarg2 - K (key) in little endian int array +// c_rarg2 - sessionKd (key) in little endian int array // c_rarg3 - r vector byte array address // c_rarg4 - input length // diff --git a/src/hotspot/os/linux/procMapsParser.cpp b/src/hotspot/os/linux/procMapsParser.cpp index 47c5c6cc594..0663cae61f3 100644 --- a/src/hotspot/os/linux/procMapsParser.cpp +++ b/src/hotspot/os/linux/procMapsParser.cpp @@ -50,7 +50,14 @@ ProcSmapsParser::~ProcSmapsParser() { bool ProcSmapsParser::read_line() { _line[0] = '\0'; - return ::fgets(_line, _linelen, _f) != nullptr; + + if (::fgets(_line, _linelen, _f) == nullptr) { + // On error or EOF, ensure deterministic empty buffer + _line[0] = '\0'; + return false; + } else { + return true; + } } bool ProcSmapsParser::is_header_line() { @@ -101,8 +108,6 @@ void ProcSmapsParser::scan_additional_line(ProcSmapsInfo& out) { } } -// Starts or continues parsing. Returns true on success, -// false on EOF or on error. bool ProcSmapsParser::parse_next(ProcSmapsInfo& out) { // Information about a single mapping reaches across several lines. @@ -117,15 +122,13 @@ bool ProcSmapsParser::parse_next(ProcSmapsInfo& out) { assert(is_header_line(), "Not a header line: \"%s\".", _line); scan_header_line(out); - // Now read until we encounter the next header line or EOF or an error. - bool ok = false, stop = false; - do { - ok = read_line(); - stop = !ok || is_header_line(); - if (!stop) { - scan_additional_line(out); + while (true) { + bool ok = read_line(); + if (!ok || is_header_line()) { + break; // EOF or next header } - } while (!stop); + scan_additional_line(out); + } - return ok; + return true; // always return true if a mapping was parsed } diff --git a/src/hotspot/os/linux/procMapsParser.hpp b/src/hotspot/os/linux/procMapsParser.hpp index 06035333b2f..037af91358f 100644 --- a/src/hotspot/os/linux/procMapsParser.hpp +++ b/src/hotspot/os/linux/procMapsParser.hpp @@ -84,8 +84,7 @@ public: ProcSmapsParser(FILE* f); ~ProcSmapsParser(); - // Starts or continues parsing. Returns true on success, - // false on EOF or on error. + // Starts or continues parsing. Returns true iff a mapping was parsed. bool parse_next(ProcSmapsInfo& out); }; diff --git a/src/hotspot/os/windows/sharedRuntimeRem.cpp b/src/hotspot/os/windows/sharedRuntimeRem.cpp index aae93f701ec..fbcf68a5940 100644 --- a/src/hotspot/os/windows/sharedRuntimeRem.cpp +++ b/src/hotspot/os/windows/sharedRuntimeRem.cpp @@ -50,11 +50,9 @@ double SharedRuntime::fmod_winx64(double x, double y) hx ^= sx; /* |x| */ hy &= 0x7fffffff; /* |y| */ -#pragma warning( disable : 4146 ) /* purge off exception values */ if ((hy | ly) == 0 || (hx >= 0x7ff00000) || /* y=0,or x not finite */ - ((hy | ((ly | -ly) >> 31))>0x7ff00000)) /* or y is NaN */ -#pragma warning( default : 4146 ) + ((hy | ((ly | -ly) >> 31))>0x7ff00000)) /* or y is NaN */ return (x*y) / (x*y); if (hx <= hy) { if ((hxis_Loop() || cnt_orig <= 3, "Loop node should have 3 or less inputs"); @@ -1392,7 +1392,7 @@ bool PhiNode::try_clean_memory_phi(PhaseIterGVN* igvn) { } assert(is_diamond_phi() > 0, "sanity"); assert(req() == 3, "same as region"); - const Node* region = in(0); + RegionNode* region = in(0)->as_Region(); for (uint i = 1; i < 3; i++) { Node* phi_input = in(i); if (phi_input != nullptr && phi_input->is_MergeMem() && region->in(i)->outcnt() == 1) { @@ -1400,8 +1400,9 @@ bool PhiNode::try_clean_memory_phi(PhaseIterGVN* igvn) { MergeMemNode* merge_mem = phi_input->as_MergeMem(); uint j = 3 - i; Node* other_phi_input = in(j); - if (other_phi_input != nullptr && other_phi_input == merge_mem->base_memory()) { + if (other_phi_input != nullptr && other_phi_input == merge_mem->base_memory() && !is_data_loop(region, phi_input, igvn)) { // merge_mem is a successor memory to other_phi_input, and is not pinned inside the diamond, so push it out. + // Only proceed if the transformation doesn't create a data loop // This will allow the diamond to collapse completely if there are no other phis left. igvn->replace_node(this, merge_mem); return true; diff --git a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp index 78ad085e03d..bc0b38e2f97 100644 --- a/src/hotspot/share/opto/cfgnode.hpp +++ b/src/hotspot/share/opto/cfgnode.hpp @@ -84,7 +84,7 @@ private: bool _is_unreachable_region; LoopStatus _loop_status; - bool is_possible_unsafe_loop(const PhaseGVN* phase) const; + bool is_possible_unsafe_loop() const; bool is_unreachable_from_root(const PhaseGVN* phase) const; public: // Node layout (parallels PhiNode): diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index 91bb743618b..5533b19897b 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -1059,14 +1059,19 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) { assert(!stopped(), "you should return if you finish the chain"); // Oops, need to call into the VM to resolve the klasses at runtime. - // Note: This call must not deoptimize, since it is not a real at this bci! kill_dead_locals(); - make_runtime_call(RC_NO_LEAF | RC_MUST_THROW, - OptoRuntime::rethrow_Type(), - OptoRuntime::rethrow_stub(), - nullptr, nullptr, - ex_node); + { PreserveReexecuteState preexecs(this); + // When throwing an exception, set the reexecute flag for deoptimization. + // This is mostly needed to pass -XX:+VerifyStack sanity checks. + jvms()->set_should_reexecute(true); + + make_runtime_call(RC_NO_LEAF | RC_MUST_THROW, + OptoRuntime::rethrow_Type(), + OptoRuntime::rethrow_stub(), + nullptr, nullptr, + ex_node); + } // Rethrow is a pure call, no side effects, only a result. // The result cannot be allocated, so we use I_O diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 7a213102efd..dc53cb08396 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -7173,6 +7173,7 @@ Node * LibraryCallKit::field_address_from_object(Node * fromObj, const char * fi bool LibraryCallKit::inline_aescrypt_Block(vmIntrinsics::ID id) { address stubAddr = nullptr; const char *stubName; + bool is_decrypt = false; assert(UseAES, "need AES instruction support"); switch(id) { @@ -7183,6 +7184,7 @@ bool LibraryCallKit::inline_aescrypt_Block(vmIntrinsics::ID id) { case vmIntrinsics::_aescrypt_decryptBlock: stubAddr = StubRoutines::aescrypt_decryptBlock(); stubName = "aescrypt_decryptBlock"; + is_decrypt = true; break; default: break; @@ -7216,7 +7218,7 @@ bool LibraryCallKit::inline_aescrypt_Block(vmIntrinsics::ID id) { // now need to get the start of its expanded key array // this requires a newer class file that has this array as littleEndian ints, otherwise we revert to java - Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object); + Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object, is_decrypt); if (k_start == nullptr) return false; // Call the stub. @@ -7231,7 +7233,7 @@ bool LibraryCallKit::inline_aescrypt_Block(vmIntrinsics::ID id) { bool LibraryCallKit::inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id) { address stubAddr = nullptr; const char *stubName = nullptr; - + bool is_decrypt = false; assert(UseAES, "need AES instruction support"); switch(id) { @@ -7242,6 +7244,7 @@ bool LibraryCallKit::inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id) { case vmIntrinsics::_cipherBlockChaining_decryptAESCrypt: stubAddr = StubRoutines::cipherBlockChaining_decryptAESCrypt(); stubName = "cipherBlockChaining_decryptAESCrypt"; + is_decrypt = true; break; default: break; @@ -7295,7 +7298,7 @@ bool LibraryCallKit::inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id) { aescrypt_object = _gvn.transform(aescrypt_object); // we need to get the start of the aescrypt_object's expanded key array - Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object); + Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object, is_decrypt); if (k_start == nullptr) return false; // similarly, get the start address of the r vector @@ -7319,7 +7322,7 @@ bool LibraryCallKit::inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id) { bool LibraryCallKit::inline_electronicCodeBook_AESCrypt(vmIntrinsics::ID id) { address stubAddr = nullptr; const char *stubName = nullptr; - + bool is_decrypt = false; assert(UseAES, "need AES instruction support"); switch (id) { @@ -7330,6 +7333,7 @@ bool LibraryCallKit::inline_electronicCodeBook_AESCrypt(vmIntrinsics::ID id) { case vmIntrinsics::_electronicCodeBook_decryptAESCrypt: stubAddr = StubRoutines::electronicCodeBook_decryptAESCrypt(); stubName = "electronicCodeBook_decryptAESCrypt"; + is_decrypt = true; break; default: break; @@ -7381,7 +7385,7 @@ bool LibraryCallKit::inline_electronicCodeBook_AESCrypt(vmIntrinsics::ID id) { aescrypt_object = _gvn.transform(aescrypt_object); // we need to get the start of the aescrypt_object's expanded key array - Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object); + Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object, is_decrypt); if (k_start == nullptr) return false; // Call the stub, passing src_start, dest_start, k_start, r_start and src_len @@ -7449,7 +7453,7 @@ bool LibraryCallKit::inline_counterMode_AESCrypt(vmIntrinsics::ID id) { Node* aescrypt_object = new CheckCastPPNode(control(), embeddedCipherObj, xtype); aescrypt_object = _gvn.transform(aescrypt_object); // we need to get the start of the aescrypt_object's expanded key array - Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object); + Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object, /* is_decrypt */ false); if (k_start == nullptr) return false; // similarly, get the start address of the r vector Node* obj_counter = load_field_from_object(counterMode_object, "counter", "[B"); @@ -7474,25 +7478,21 @@ bool LibraryCallKit::inline_counterMode_AESCrypt(vmIntrinsics::ID id) { } //------------------------------get_key_start_from_aescrypt_object----------------------- -Node * LibraryCallKit::get_key_start_from_aescrypt_object(Node *aescrypt_object) { -#if defined(PPC64) || defined(S390) || defined(RISCV64) +Node* LibraryCallKit::get_key_start_from_aescrypt_object(Node* aescrypt_object, bool is_decrypt) { // MixColumns for decryption can be reduced by preprocessing MixColumns with round keys. // Intel's extension is based on this optimization and AESCrypt generates round keys by preprocessing MixColumns. // However, ppc64 vncipher processes MixColumns and requires the same round keys with encryption. - // The ppc64 and riscv64 stubs of encryption and decryption use the same round keys (sessionK[0]). - Node* objSessionK = load_field_from_object(aescrypt_object, "sessionK", "[[I"); - assert (objSessionK != nullptr, "wrong version of com.sun.crypto.provider.AES_Crypt"); - if (objSessionK == nullptr) { - return (Node *) nullptr; - } - Node* objAESCryptKey = load_array_element(objSessionK, intcon(0), TypeAryPtr::OOPS, /* set_ctrl */ true); + // The following platform specific stubs of encryption and decryption use the same round keys. +#if defined(PPC64) || defined(S390) || defined(RISCV64) + bool use_decryption_key = false; #else - Node* objAESCryptKey = load_field_from_object(aescrypt_object, "K", "[I"); -#endif // PPC64 - assert (objAESCryptKey != nullptr, "wrong version of com.sun.crypto.provider.AES_Crypt"); + bool use_decryption_key = is_decrypt; +#endif + Node* objAESCryptKey = load_field_from_object(aescrypt_object, use_decryption_key ? "sessionKd" : "sessionKe", "[I"); + assert(objAESCryptKey != nullptr, "wrong version of com.sun.crypto.provider.AES_Crypt"); if (objAESCryptKey == nullptr) return (Node *) nullptr; - // now have the array, need to get the start address of the K array + // now have the array, need to get the start address of the selected key array Node* k_start = array_element_address(objAESCryptKey, intcon(0), T_INT); return k_start; } @@ -8628,7 +8628,7 @@ bool LibraryCallKit::inline_galoisCounterMode_AESCrypt() { Node* aescrypt_object = new CheckCastPPNode(control(), embeddedCipherObj, xtype); aescrypt_object = _gvn.transform(aescrypt_object); // we need to get the start of the aescrypt_object's expanded key array - Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object); + Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object, /* is_decrypt */ false); if (k_start == nullptr) return false; // similarly, get the start address of the r vector Node* cnt_start = array_element_address(counter, intcon(0), T_BYTE); diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index fbac1363dae..7dbb57c1e5c 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -338,7 +338,7 @@ class LibraryCallKit : public GraphKit { Node* inline_cipherBlockChaining_AESCrypt_predicate(bool decrypting); Node* inline_electronicCodeBook_AESCrypt_predicate(bool decrypting); Node* inline_counterMode_AESCrypt_predicate(); - Node* get_key_start_from_aescrypt_object(Node* aescrypt_object); + Node* get_key_start_from_aescrypt_object(Node* aescrypt_object, bool is_decrypt); bool inline_ghash_processBlocks(); bool inline_chacha20Block(); bool inline_kyberNtt(); diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index a49f3d24fd4..57b94205e5e 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -22,6 +22,8 @@ */ #include "memory/allocation.inline.hpp" +#include "opto/c2_globals.hpp" +#include "opto/compile.hpp" #include "opto/connode.hpp" #include "opto/convertnode.hpp" #include "opto/mulnode.hpp" @@ -1145,7 +1147,14 @@ Node* LoadVectorMaskedNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (ty && ty->is_con()) { BasicType mask_bt = Matcher::vector_element_basic_type(in(3)); int load_sz = type2aelembytes(mask_bt) * ty->get_con(); - assert(load_sz <= MaxVectorSize, "Unexpected load size"); + if (load_sz > MaxVectorSize) { + // After loop opts, cast nodes are aggressively removed, if the input is then transformed + // into a constant that is outside the range of the removed cast, we may encounter it here. + // This should be a dead node then. + assert(Compile::current()->post_loop_opts_phase(), "Unexpected load size"); + return phase->C->top(); + } + if (load_sz == MaxVectorSize) { Node* ctr = in(MemNode::Control); Node* mem = in(MemNode::Memory); @@ -1164,7 +1173,14 @@ Node* StoreVectorMaskedNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (ty && ty->is_con()) { BasicType mask_bt = Matcher::vector_element_basic_type(in(4)); int load_sz = type2aelembytes(mask_bt) * ty->get_con(); - assert(load_sz <= MaxVectorSize, "Unexpected store size"); + if (load_sz > MaxVectorSize) { + // After loop opts, cast nodes are aggressively removed, if the input is then transformed + // into a constant that is outside the range of the removed cast, we may encounter it here. + // This should be a dead node then. + assert(Compile::current()->post_loop_opts_phase(), "Unexpected store size"); + return phase->C->top(); + } + if (load_sz == MaxVectorSize) { Node* ctr = in(MemNode::Control); Node* mem = in(MemNode::Memory); diff --git a/src/hotspot/share/opto/vtransform.cpp b/src/hotspot/share/opto/vtransform.cpp index b437d2e6eac..93aefe0d505 100644 --- a/src/hotspot/share/opto/vtransform.cpp +++ b/src/hotspot/share/opto/vtransform.cpp @@ -40,38 +40,76 @@ void VTransformGraph::add_vtnode(VTransformNode* vtnode) { } \ ) -// This is similar to IGVN optimization. But we are a bit lazy, and don't care about -// notification / worklist, since the list of nodes is rather small, and we don't -// expect optimizations that trickle over the whole graph. -void VTransformGraph::optimize(VTransform& vtransform) { - TRACE_OPTIMIZE( tty->print_cr("\nVTransformGraph::optimize"); ) - - bool progress = true; - DEBUG_ONLY(int pass_count = 0;) - while (progress) { - progress = false; - assert(++pass_count < 10, "ensure we do not have endless loops"); - for (int i = 0; i < _vtnodes.length(); i++) { - VTransformNode* vtn = _vtnodes.at(i); - if (!vtn->is_alive()) { continue; } - progress |= vtn->optimize(_vloop_analyzer, vtransform); - - // Nodes that have no use any more are dead. - if (vtn->out_strong_edges() == 0 && - // There are some exceptions: - // 1. Memory phi uses are not modeled, so they appear to have no use here, but must be kept alive. - // 2. Similarly, some stores may not have their memory uses modeled, but need to be kept alive. - // 3. Outer node with strong inputs: is a use after the loop that we must keep alive. - !(vtn->isa_PhiScalar() != nullptr || - vtn->is_load_or_store_in_loop() || - (vtn->isa_Outer() != nullptr && vtn->has_strong_in_edge()))) { - vtn->mark_dead(); - progress = true; - } - } +void VTransformOptimize::worklist_push(VTransformNode* vtn) { + if (!_worklist_set.test_set(vtn->_idx)) { + _worklist.push(vtn); } } +VTransformNode* VTransformOptimize::worklist_pop() { + VTransformNode* vtn = _worklist.pop(); + _worklist_set.remove(vtn->_idx); + return vtn; +} + +void VTransform::optimize() { + NOT_PRODUCT( if (vloop().is_trace_optimization()) { tty->print_cr("\nVTransform::optimize"); } ) + ResourceMark rm; + VTransformOptimize vtoptimize(_vloop_analyzer, *this); + vtoptimize.optimize(); +} + +void VTransformOptimize::optimize() { + // Initialize: push all nodes to worklist. + for (int i = 0; i < _vtransform.graph().vtnodes().length(); i++) { + VTransformNode* vtn = _vtransform.graph().vtnodes().at(i); + worklist_push(vtn); + } + + // We don't want to iterate too many times. We set some arbitrary limit, + // just to catch infinite loops. + DEBUG_ONLY( int allowed_steps = 100 * _worklist.length(); ) + + // Optimize iteratively. + while (_worklist.is_nonempty()) { + VTransformNode* vtn = worklist_pop(); + optimize_step(vtn); + assert(--allowed_steps > 0, "no endless loop"); + } + + DEBUG_ONLY( verify(); ) +} + +#ifdef ASSERT +void VTransformOptimize::verify() { + for (int i = 0; i < _vtransform.graph().vtnodes().length(); i++) { + VTransformNode* vtn = _vtransform.graph().vtnodes().at(i); + assert(!optimize_step(vtn), "Missed optimization during VTransform::optimize for %s", vtn->name()); + assert(_worklist.is_empty(), "vtnode on worklist despite no progress for %s", vtn->name()); + } +} +#endif + +// Return true if (and only if) we made progress. +bool VTransformOptimize::optimize_step(VTransformNode* vtn) { + if (!vtn->is_alive()) { return false; } + bool progress = vtn->optimize(*this); + + // Nodes that have no use any more are dead. + if (vtn->out_strong_edges() == 0 && + // There are some exceptions: + // 1. Memory phi uses are not modeled, so they appear to have no use here, but must be kept alive. + // 2. Similarly, some stores may not have their memory uses modeled, but need to be kept alive. + // 3. Outer node with strong inputs: is a use after the loop that we must keep alive. + !(vtn->isa_PhiScalar() != nullptr || + vtn->is_load_or_store_in_loop() || + (vtn->isa_Outer() != nullptr && vtn->has_strong_in_edge()))) { + vtn->mark_dead(*this); + return true; + } + return progress; +} + // Compute a linearization of the graph. We do this with a reverse-post-order of a DFS. // This only works if the graph is a directed acyclic graph (DAG). The C2 graph, and // the VLoopDependencyGraph are both DAGs, but after introduction of vectors/packs, the @@ -1141,8 +1179,8 @@ VTransformApplyResult VTransformBoolVectorNode::apply(VTransformApplyState& appl return VTransformApplyResult::make_vector(vn); } -bool VTransformReductionVectorNode::optimize(const VLoopAnalyzer& vloop_analyzer, VTransform& vtransform) { - return optimize_move_non_strict_order_reductions_out_of_loop(vloop_analyzer, vtransform); +bool VTransformReductionVectorNode::optimize(VTransformOptimize& vtoptimize) { + return optimize_move_non_strict_order_reductions_out_of_loop(vtoptimize); } int VTransformReductionVectorNode::vector_reduction_opcode() const { @@ -1213,7 +1251,7 @@ bool VTransformReductionVectorNode::requires_strict_order() const { // become profitable, since the expensive reduction node is moved // outside the loop, and instead cheaper element-wise vector accumulations // are performed inside the loop. -bool VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_out_of_loop_preconditions(VTransform& vtransform) { +bool VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_out_of_loop_preconditions(const VTransform& vtransform) { // We have a phi with a single use. VTransformPhiScalarNode* phi = in_req(1)->isa_PhiScalar(); if (phi == nullptr) { @@ -1260,13 +1298,13 @@ bool VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_ou current_red->element_basic_type() != bt || current_red->vector_length() != vlen) { TRACE_OPTIMIZE( - tty->print(" Cannot move out of loop, other reduction node does not match:"); + tty->print(" Cannot move out of loop, other reduction node does not match: "); print(); tty->print(" other: "); if (current_red != nullptr) { current_red->print(); } else { - tty->print("nullptr"); + tty->print_cr("nullptr"); } ) return false; // not compatible @@ -1314,7 +1352,8 @@ bool VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_ou return true; // success } -bool VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_out_of_loop(const VLoopAnalyzer& vloop_analyzer, VTransform& vtransform) { +bool VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_out_of_loop(VTransformOptimize& vtoptimize) { + VTransform& vtransform = vtoptimize.vtransform(); if (!optimize_move_non_strict_order_reductions_out_of_loop_preconditions(vtransform)) { return false; } @@ -1328,7 +1367,7 @@ bool VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_ou const uint vlen = vector_length(); const BasicType bt = element_basic_type(); const int vopc = VectorNode::opcode(sopc, bt); - PhaseIdealLoop* phase = vloop_analyzer.vloop().phase(); + PhaseIdealLoop* phase = vtoptimize.vloop_analyzer().vloop().phase(); // Create a vector of identity values. Node* identity = ReductionNode::make_identity_con_scalar(phase->igvn(), sopc, bt); @@ -1341,6 +1380,7 @@ bool VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_ou // Look at old scalar phi. VTransformPhiScalarNode* phi_scalar = in_req(1)->isa_PhiScalar(); PhiNode* old_phi = phi_scalar->node(); + vtoptimize.worklist_push(phi_scalar); VTransformNode* init = phi_scalar->in_req(1); TRACE_OPTIMIZE( @@ -1354,6 +1394,7 @@ bool VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_ou phi_vector->init_req(0, phi_scalar->in_req(0)); phi_vector->init_req(1, vtn_identity_vector); // Note: backedge comes later + vtoptimize.worklist_push(phi_vector); // Traverse down the chain of reductions, and replace them with vector_accumulators. VTransformReductionVectorNode* first_red = this; @@ -1365,6 +1406,8 @@ bool VTransformReductionVectorNode::optimize_move_non_strict_order_reductions_ou VTransformVectorNode* vector_accumulator = new (vtransform.arena()) VTransformElementWiseVectorNode(vtransform, 3, current_red->properties(), vopc); vector_accumulator->init_req(1, current_vector_accumulator); vector_accumulator->init_req(2, vector_input); + vtoptimize.worklist_push(current_red); + vtoptimize.worklist_push(vector_accumulator); TRACE_OPTIMIZE( tty->print(" replace "); current_red->print(); diff --git a/src/hotspot/share/opto/vtransform.hpp b/src/hotspot/share/opto/vtransform.hpp index a3e4977494e..b60c71945e1 100644 --- a/src/hotspot/share/opto/vtransform.hpp +++ b/src/hotspot/share/opto/vtransform.hpp @@ -24,6 +24,7 @@ #ifndef SHARE_OPTO_VTRANSFORM_HPP #define SHARE_OPTO_VTRANSFORM_HPP +#include "libadt/vectset.hpp" #include "opto/node.hpp" #include "opto/vectorization.hpp" #include "opto/vectornode.hpp" @@ -192,7 +193,6 @@ public: const GrowableArray& vtnodes() const { return _vtnodes; } const GrowableArray& get_schedule() const { return _schedule; } - void optimize(VTransform& vtransform); bool schedule(); bool has_store_to_load_forwarding_failure(const VLoopAnalyzer& vloop_analyzer) const; float cost_for_vector_loop() const; @@ -257,7 +257,7 @@ public: DEBUG_ONLY( bool has_graph() const { return !_graph.is_empty(); } ) VTransformGraph& graph() { return _graph; } - void optimize() { return _graph.optimize(*this); } + void optimize(); bool schedule() { return _graph.schedule(); } bool is_profitable() const; float cost_for_vector_loop() const { return _graph.cost_for_vector_loop(); } @@ -291,6 +291,36 @@ private: void apply_vectorization() const; }; +// We keep track of the worklist during optimizations. +// The concept is somewhat parallel to IGVN: we keep on +// optimizing vtnodes on the worklist, which may in turn +// add more nodes to the list. We keep on optimizing until +// no more nodes are on the worklist. +class VTransformOptimize : public StackObj { +private: + const VLoopAnalyzer& _vloop_analyzer; + VTransform& _vtransform; + + GrowableArray _worklist; + VectorSet _worklist_set; + +public: + VTransformOptimize(const VLoopAnalyzer& vloop_analyzer, VTransform& vtransform) : + _vloop_analyzer(vloop_analyzer), + _vtransform(vtransform) {} + + const VLoopAnalyzer& vloop_analyzer() const { return _vloop_analyzer; } + VTransform& vtransform() { return _vtransform; } + + void worklist_push(VTransformNode* vtn); + void optimize(); + +private: + VTransformNode* worklist_pop(); + bool optimize_step(VTransformNode* vtn); + DEBUG_ONLY( void verify(); ) +}; + // Keeps track of the state during "VTransform::apply" // -> keep track of the already transformed nodes and the memory state. class VTransformApplyState : public StackObj { @@ -531,10 +561,15 @@ public: bool is_alive() const { return _is_alive; } - void mark_dead() { + void mark_dead(VTransformOptimize& vtoptimize) { _is_alive = false; - // Remove all inputs + // Remove all inputs, and put inputs on worklist in + // case they are also dead. for (uint i = 0; i < req(); i++) { + VTransformNode* in = in_req(i); + if (in != nullptr) { + vtoptimize.worklist_push(in); + } set_req(i, nullptr); } } @@ -558,7 +593,7 @@ public: virtual const VPointer& vpointer() const { ShouldNotReachHere(); } virtual bool is_loop_head_phi() const { return false; } - virtual bool optimize(const VLoopAnalyzer& vloop_analyzer, VTransform& vtransform) { return false; } + virtual bool optimize(VTransformOptimize& vtoptimize) { return false; } virtual float cost(const VLoopAnalyzer& vloop_analyzer) const = 0; @@ -868,7 +903,7 @@ public: VTransformReductionVectorNode(VTransform& vtransform, const VTransformVectorNodeProperties properties) : VTransformVectorNode(vtransform, 3, properties) {} virtual VTransformReductionVectorNode* isa_ReductionVector() override { return this; } - virtual bool optimize(const VLoopAnalyzer& vloop_analyzer, VTransform& vtransform) override; + virtual bool optimize(VTransformOptimize& vtoptimize) override; virtual float cost(const VLoopAnalyzer& vloop_analyzer) const override; virtual VTransformApplyResult apply(VTransformApplyState& apply_state) const override; NOT_PRODUCT(virtual const char* name() const override { return "ReductionVector"; };) @@ -876,8 +911,8 @@ public: private: int vector_reduction_opcode() const; bool requires_strict_order() const; - bool optimize_move_non_strict_order_reductions_out_of_loop_preconditions(VTransform& vtransform); - bool optimize_move_non_strict_order_reductions_out_of_loop(const VLoopAnalyzer& vloop_analyzer, VTransform& vtransform); + bool optimize_move_non_strict_order_reductions_out_of_loop_preconditions(const VTransform& vtransform); + bool optimize_move_non_strict_order_reductions_out_of_loop(VTransformOptimize& vtoptimize); }; class VTransformPhiVectorNode : public VTransformVectorNode { diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp index 169ccbe035f..9df3bbb4b3e 100644 --- a/src/hotspot/share/prims/jvmtiEventController.cpp +++ b/src/hotspot/share/prims/jvmtiEventController.cpp @@ -1077,10 +1077,6 @@ JvmtiEventController::is_global_event(jvmtiEvent event_type) { void JvmtiEventController::set_user_enabled(JvmtiEnvBase *env, JavaThread *thread, oop thread_oop, jvmtiEvent event_type, bool enabled) { - if (event_type == JVMTI_EVENT_OBJECT_FREE) { - JvmtiEventControllerPrivate::flush_object_free_events(env); - } - if (Threads::number_of_threads() == 0) { // during early VM start-up locks don't exist, but we are safely single threaded, // call the functionality without holding the JvmtiThreadState_lock. @@ -1089,6 +1085,11 @@ JvmtiEventController::set_user_enabled(JvmtiEnvBase *env, JavaThread *thread, oo Thread* current = Thread::current(); HandleMark hmi(current); Handle thread_oop_h = Handle(current, thread_oop); + + if (event_type == JVMTI_EVENT_OBJECT_FREE) { + JvmtiEventControllerPrivate::flush_object_free_events(env); + } + MutexLocker mu(JvmtiThreadState_lock); JvmtiEventControllerPrivate::set_user_enabled(env, thread, thread_oop_h, event_type, enabled); } @@ -1238,4 +1239,4 @@ JvmtiEventController::vm_death() { break; } } -} \ No newline at end of file +} diff --git a/src/hotspot/share/runtime/atomicAccess.hpp b/src/hotspot/share/runtime/atomicAccess.hpp index fb06f084366..c9a2dfb9383 100644 --- a/src/hotspot/share/runtime/atomicAccess.hpp +++ b/src/hotspot/share/runtime/atomicAccess.hpp @@ -635,7 +635,6 @@ inline void AtomicAccess::dec(D volatile* dest, atomic_memory_order order) { STATIC_ASSERT(std::is_pointer::value || std::is_integral::value); using I = std::conditional_t::value, ptrdiff_t, D>; // Assumes two's complement integer representation. - #pragma warning(suppress: 4146) AtomicAccess::add(dest, I(-1), order); } @@ -652,7 +651,6 @@ inline D AtomicAccess::sub(D volatile* dest, I sub_value, atomic_memory_order or STATIC_ASSERT(sizeof(I) <= sizeof(AddendType)); AddendType addend = sub_value; // Assumes two's complement integer representation. - #pragma warning(suppress: 4146) // In case AddendType is not signed. return AtomicAccess::add(dest, -addend, order); } diff --git a/src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java b/src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java index 19dceae01af..27429f6a2fd 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/AES_Crypt.java @@ -54,11 +54,11 @@ final class AES_Crypt extends SymmetricCipher { private int rounds; private byte[] prevKey = null; - // Following two attributes are specific to Intrinsics where sessionK is - // used for PPC64, S390, and RISCV64 architectures, whereas K is used for - // everything else. - private int[][] sessionK = null; - private int[] K = null; + // Following attributes are specific to Intrinsics, where sessionKe is the + // unprocessed key that is also used for decryption on PPC64, S390 and + // RISCV64 architectures. Other ones use sessionKd for decryption. + private int[] sessionKe = null; // key for encryption + private int[] sessionKd = null; // preprocessed key for decryption // Round constant private static final int[] RCON = { @@ -904,7 +904,6 @@ final class AES_Crypt extends SymmetricCipher { */ void init(boolean decrypting, String algorithm, byte[] key) throws InvalidKeyException { - int decrypt = decrypting ? 1 : 0; if (!algorithm.equalsIgnoreCase("AES") && !algorithm.equalsIgnoreCase("Rijndael")) { @@ -920,21 +919,25 @@ final class AES_Crypt extends SymmetricCipher { throw new InvalidKeyException("Invalid key length (" + key.length + ")."); } + if (!MessageDigest.isEqual(prevKey, key)) { - if (sessionK == null) { - sessionK = new int[2][]; - } else { - Arrays.fill(sessionK[0], 0); - Arrays.fill(sessionK[1], 0); + if (sessionKe != null) { + Arrays.fill(sessionKe, 0); + } + sessionKe = genRoundKeys(key, rounds); + if (sessionKd != null) { + Arrays.fill(sessionKd, 0); + sessionKd = null; } - sessionK[0] = genRoundKeys(key, rounds); - sessionK[1] = genInvRoundKeys(sessionK[0], rounds); if (prevKey != null) { Arrays.fill(prevKey, (byte) 0); } prevKey = key.clone(); } - K = sessionK[decrypt]; + + if (decrypting && (sessionKd == null)) { + sessionKd = genInvRoundKeys(sessionKe, rounds); + } } /** @@ -1035,6 +1038,7 @@ final class AES_Crypt extends SymmetricCipher { */ @IntrinsicCandidate private void implEncryptBlock(byte[] p, int po, byte[] c, int co) { + int[] K = sessionKe; int ti0, ti1, ti2, ti3; int a0, a1, a2, a3; int w = K.length - WB; @@ -1213,6 +1217,7 @@ final class AES_Crypt extends SymmetricCipher { */ @IntrinsicCandidate private void implDecryptBlock(byte[] c, int co, byte[] p, int po) { + int[] K = sessionKd; int ti0, ti1, ti2, ti3; int a0, a1, a2, a3; diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index 52f908c9e98..d7aef113e15 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -117,9 +117,38 @@ import sun.nio.cs.UTF_8; * Unicode code points (i.e., characters), in addition to those for * dealing with Unicode code units (i.e., {@code char} values). * - *

Unless otherwise noted, methods for comparing Strings do not take locale - * into account. The {@link java.text.Collator} class provides methods for - * finer-grain, locale-sensitive String comparison. + *

String comparison and case-insensitive matching + * + *

There are several related ways to compare {@code String} values; choose + * the one whose semantics fit your purpose: + * + *

    + *
  • Exact content equality — {@link #equals(Object)} checks that two + * strings contain the identical char sequence of UTF-16 code units. This is + * a strict, case-sensitive comparison suitable for exact matching, hashing + * and any situation that requires bit-for-bit stability.
  • + * + *
  • Simple case-insensitive equality — {@link #equalsIgnoreCase(String)} + * (and the corresponding {@link #compareToIgnoreCase(String)} and {@link #CASE_INSENSITIVE_ORDER}) + * performs a per-code-point, locale-independent comparison using + * {@link Character#toUpperCase(int)} and {@link Character#toLowerCase(int)}. + * It is convenient for many common case-insensitive checks.
  • + * + *
  • Unicode case-folded equivalence — {@link #equalsFoldCase(String)} + * (and the corresponding {@link #compareToFoldCase(String)} and {@link #UNICODE_CASEFOLD_ORDER}) + * implement the Unicode {@index "full case folding"} rules defined in + * Unicode CaseFolding.txt. + * Case folding is locale-independent and language-neutral and may map a single code + * point to multiple code points (1:M mappings). For example, the German sharp + * s ({@code U+00DF}) is folded to the sequence {@code "ss"}. + * Use these methods when you need Unicode-compliant + * + * caseless matching, searching, or ordering.
  • + *
+ * + *

Unless otherwise noted, methods for comparing Strings do not take locale into + * account. The {@link java.text.Collator} class provides methods for finer-grain, + * locale-sensitive String comparison. * * @implNote The implementation of the string concatenation operator is left to * the discretion of a Java compiler, as long as the compiler ultimately conforms @@ -2179,6 +2208,7 @@ public final class String * false} otherwise * * @see #equals(Object) + * @see #equalsFoldCase(String) * @see #codePoints() */ public boolean equalsIgnoreCase(String anotherString) { @@ -2188,6 +2218,57 @@ public final class String && regionMatches(true, 0, anotherString, 0, length()); } + /** + * Compares this {@code String} to another {@code String} for equality, + * using {@index "Unicode case folding"}. Two strings are considered equal + * by this method if their case-folded forms are identical. + *

+ * Case folding is defined by the Unicode Standard in + * CaseFolding.txt, + * including 1:M mappings. For example, {@code "Fuß".equalsFoldCase("FUSS")} + * returns {@code true}, since the character {@code U+00DF} (sharp s) folds + * to {@code "ss"}. + *

+ * Case folding is locale-independent and language-neutral, unlike + * locale-sensitive transformations such as {@link #toLowerCase()} or + * {@link #toUpperCase()}. It is intended for caseless matching, + * searching, and indexing. + * + * @apiNote + * This method is the Unicode-compliant alternative to + * {@link #equalsIgnoreCase(String)}. It implements full case folding as + * defined by the Unicode Standard, which may differ from the simpler + * per-character mapping performed by {@code equalsIgnoreCase}. + * For example: + * {@snippet lang=java : + * String a = "Fuß"; + * String b = "FUSS"; + * boolean equalsFoldCase = a.equalsFoldCase(b); // returns true + * boolean equalsIgnoreCase = a.equalsIgnoreCase(b); // returns false + * } + * + * @param anotherString + * The {@code String} to compare this {@code String} against + * + * @return {@code true} if the given object is not {@code null} and represents + * the same sequence of characters as this string under Unicode case + * folding; {@code false} otherwise. + * + * @spec https://www.unicode.org/versions/latest/core-spec/chapter-5/#G21790 Unicode Caseless Matching + * @see #compareToFoldCase(String) + * @see #equalsIgnoreCase(String) + * @since 26 + */ + public boolean equalsFoldCase(String anotherString) { + if (this == anotherString) { + return true; + } + if (anotherString == null) { + return false; + } + return UNICODE_CASEFOLD_ORDER.compare(this, anotherString) == 0; + } + /** * Compares two strings lexicographically. * The comparison is based on the Unicode value of each character in @@ -2303,12 +2384,86 @@ public final class String * than this String, ignoring case considerations. * @see java.text.Collator * @see #codePoints() + * @see #compareToFoldCase(String) * @since 1.2 */ public int compareToIgnoreCase(String str) { return CASE_INSENSITIVE_ORDER.compare(this, str); } + /** + * A Comparator that orders {@code String} objects as by + * {@link #compareToFoldCase(String) compareToFoldCase()}. + * + * @see #compareToFoldCase(String) + * @since 26 + */ + public static final Comparator UNICODE_CASEFOLD_ORDER + = new FoldCaseComparator(); + + private static class FoldCaseComparator implements Comparator { + + @Override + public int compare(String s1, String s2) { + byte[] v1 = s1.value; + byte[] v2 = s2.value; + if (s1.coder == s2.coder()) { + return s1.coder == LATIN1 ? StringLatin1.compareToFC(v1, v2) + : StringUTF16.compareToFC(v1, v2); + } + return s1.coder == LATIN1 ? StringLatin1.compareToFC_UTF16(v1, v2) + : StringUTF16.compareToFC_Latin1(v1, v2); + } + } + + /** + * Compares two strings lexicographically using {@index "Unicode case folding"}. + * This method returns an integer whose sign is that of calling {@code compareTo} + * on the Unicode case folded version of the strings. Unicode Case folding + * eliminates differences in case according to the Unicode Standard, using the + * mappings defined in + * CaseFolding.txt, + * including 1:M mappings, such as {@code"ß"} → {@code }"ss"}. + *

+ * Case folding is a locale-independent, language-neutral form of case mapping, + * primarily intended for caseless matching. Unlike {@link #compareToIgnoreCase(String)}, + * which applies a simpler locale-insensitive uppercase mapping. This method + * follows the Unicode {@index "full"} case folding, providing stable and + * consistent results across all environments. + *

+ * Note that this method does not take locale into account, and may + * produce results that differ from locale-sensitive ordering. Use + * {@link java.text.Collator} for locale-sensitive comparison. + * + * @apiNote + * This method is the Unicode-compliant alternative to + * {@link #compareToIgnoreCase(String)}. It implements the + * {@index "full case folding"} as defined by the Unicode Standard, which + * may differ from the simpler per-character mapping performed by + * {@code compareToIgnoreCase}. + * For example: + * {@snippet lang=java : + * String a = "Fuß"; + * String b = "FUSS"; + * int cmpFoldCase = a.compareToFoldCase(b); // returns 0 + * int cmpIgnoreCase = a.compareToIgnoreCase(b); // returns > 0 + * } + * + * @param str the {@code String} to be compared. + * @return a negative integer, zero, or a positive integer as the specified + * String is greater than, equal to, or less than this String, + * ignoring case considerations by case folding. + * + * @spec https://www.unicode.org/versions/latest/core-spec/chapter-5/#G21790 Unicode Caseless Matching + * @see java.text.Collator + * @see #compareToIgnoreCase(String) + * @see #equalsFoldCase(String) + * @since 26 + */ + public int compareToFoldCase(String str) { + return UNICODE_CASEFOLD_ORDER.compare(this, str); + } + /** * Tests if two string regions are equal. *

diff --git a/src/java.base/share/classes/java/lang/StringLatin1.java b/src/java.base/share/classes/java/lang/StringLatin1.java index 61c62d049bc..21a8b2dd61a 100644 --- a/src/java.base/share/classes/java/lang/StringLatin1.java +++ b/src/java.base/share/classes/java/lang/StringLatin1.java @@ -32,6 +32,8 @@ import java.util.function.Consumer; import java.util.function.IntConsumer; import java.util.stream.Stream; import java.util.stream.StreamSupport; + +import jdk.internal.lang.CaseFolding; import jdk.internal.util.ArraysSupport; import jdk.internal.vm.annotation.IntrinsicCandidate; @@ -179,6 +181,128 @@ final class StringLatin1 { return len1 - len2; } + private static int compareToFC0(byte[] value, int off, int last, byte[] other, int ooff, int olast) { + int k1 = off, k2 = ooff; + boolean lo1 = false, lo2 = false; // true if we have a leftover 's' from u+00df -> ss + while ((k1 < last || lo1) && (k2 < olast || lo2)) { + int c1, c2; + if (lo1) { + c1 = 0x73; // leftover 's' + lo1 = false; + } else { + c1 = getChar(value, k1++); + if (c1 == 0xdf) { + c1 = 0x73; + lo1 = true; + } + } + if (lo2) { + c2 = 0x73; // 's' + lo2 = false; + } else { + c2 = getChar(other, k2++); + if (c2 == 0xdf) { + c2 = 0x73; + lo2 = true; + } + } + if (!CharacterDataLatin1.equalsIgnoreCase((byte)c1, (byte)c2)) { + return Character.toLowerCase(c1) - Character.toLowerCase(c2); + } + } + if (k1 < last || lo1) { + return 1; + } + if (k2 < olast || lo2) { + return -1; + } + return 0; + } + + static int compareToFC(byte[] value, byte[] other) { + int len = value.length; + int olen = other.length; + int lim = Math.min(len, olen); + for (int k = 0; k < lim; k++) { + byte b1 = value[k]; + byte b2 = other[k]; + if (!CharacterDataLatin1.equalsIgnoreCase(b1, b2)) { + int c1 = b1 & 0xff; + int c2 = b2 & 0xff; + if (c1 == 0xdf || c2 == 0xdf) { // 0xdf is the only 1:M in latin1 range + return compareToFC0(value, k, len, other, k, olen); + } + return Character.toLowerCase(c1) - Character.toLowerCase(c2); + } + } + return len - olen; + } + + private static int compareToFC0_UTF16(byte[] value, int off, int last, byte[] other, int ooff, int olast) { + int f1 = 0, f2 = 0; + int k1 = off, k2 = ooff; + while ((k1 < last || f1 != 0) && (k2 < olast || f2 != 0)) { + int c1, c2; + if (f1 != 0) { + c1 = (f1 & 0xffff); f1 >>>= 16; + } else { + c1 = getChar(value, k1++); + var f = CaseFolding.fold(c1); + if (CaseFolding.isSingleCodePoint(f)) { + c1 = (int)(f & 0xfffff); + } else { + c1 = (int)f & 0xffff; + f1 = (int)(f >>> 16); + } + } + if (f2 != 0) { + c2 = f2 & 0xffff; f2 >>>= 16; + } else { + c2 = StringUTF16.codePointAt(other, k2, olast, true); + k2 += Character.charCount(c2); + var f = CaseFolding.fold(c2); + if (CaseFolding.isSingleCodePoint(f)) { + c2 = (int)(f & 0xfffff); + } else { + c2 = (int)(f & 0xffff); + f2 = (int)(f >>> 16); + } + } + if (c1 != c2) { + return c1 - c2; + } + } + if (k1 < last || f1 != 0) { + return 1; + } + if (k2 < olast || f2 != 0) { + return -1; + } + return 0; + } + + // latin1 vs utf16 + static int compareToFC_UTF16(byte[] value, byte[] other) { + int last = length(value); + int olast = StringUTF16.length(other); + int lim = Math.min(last, olast); + for (int k = 0; k < lim; k++) { + int cp1 = getChar(value, k); + int cp2 = StringUTF16.codePointAt(other, k, olast, true); + if (cp1 != cp2) { + long cf1 = CaseFolding.fold(cp1); + long cf2 = CaseFolding.fold(cp2); + if (cf1 != cf2) { + if (!CaseFolding.isSingleCodePoint(cf1) || !CaseFolding.isSingleCodePoint(cf2)) { + return compareToFC0_UTF16(value, k, last, other, k, olast); + } + return (int)(cf1 - cf2); + } + } + } + return last - olast; + } + static int hashCode(byte[] value) { return ArraysSupport.hashCodeOfUnsigned(value, 0, value.length, 0); } diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index 4e31c9728e9..75c9e8239ba 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -34,6 +34,7 @@ import java.util.function.IntConsumer; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import jdk.internal.lang.CaseFolding; import jdk.internal.misc.Unsafe; import jdk.internal.util.ArraysSupport; import jdk.internal.vm.annotation.ForceInline; @@ -93,7 +94,7 @@ final class StringUTF16 { return value.length >> 1; } - private static int codePointAt(byte[] value, int index, int end, boolean checked) { + static int codePointAt(byte[] value, int index, int end, boolean checked) { assert index < end; if (checked) { checkIndex(index, value); @@ -592,6 +593,77 @@ final class StringUTF16 { return -StringLatin1.compareToCI_UTF16(other, value); } + public static int compareToFC_Latin1(byte[] value, byte[] other) { + return -StringLatin1.compareToFC_UTF16(other, value); + } + + private static int compareToFC0(byte[] value, int off, int last, byte[] other, int ooff, int olast) { + int f1 = 0, f2 = 0; + int k1 = off, k2 = ooff; + while ((k1 < last || f1 != 0) && (k2 < olast || f2 != 0)) { + int c1, c2; + if (f1 != 0) { + c1 = f1 & 0xffff; f1 >>>= 16; + } else { + c1 = StringUTF16.codePointAt(value, k1, last, true); + k1 += Character.charCount(c1); + var f = CaseFolding.fold(c1); + if (CaseFolding.isSingleCodePoint(f)) { + c1 = (int)(f & 0xfffff); + } else { + c1 = (int)(f & 0xffff); + f1 = (int)(f >> 16); + } + } + if (f2 != 0) { + c2 = f2 & 0xffff; f2 >>>= 16; + } else { + c2 = StringUTF16.codePointAt(other, k2, olast, true); + k2 += Character.charCount(c2); + var f = CaseFolding.fold(c2); + if (CaseFolding.isSingleCodePoint(f)) { + c2 = (int)(f & 0xfffff); + } else { + c2 = (int)(f & 0xffff); + f2 = (int)(f >>> 16); + } + } + if (c1 != c2) { + return c1 - c2; + } + } + if (k1 < last || f1 != 0) { + return 1; + } + if (k2 < olast || f2 != 0) { + return -1; + } + return 0; + } + + public static int compareToFC(byte[] value, byte[] other) { + int tlast = length(value); + int olast = length(other); + int lim = Math.min(tlast, olast); + int k = 0; + while (k < lim) { + int cp1 = codePointAt(value, k, tlast, true); + int cp2 = codePointAt(other, k, olast, true); + if (cp1 != cp2) { + long cf1 = CaseFolding.fold(cp1); + long cf2 = CaseFolding.fold(cp2); + if (cf1 != cf2) { + if (!CaseFolding.isSingleCodePoint(cf1) || !CaseFolding.isSingleCodePoint(cf2)) { + return compareToFC0(value, k, tlast, other, k, olast); + } + return (int) cf1 - (int) cf2; + } + } + k += Character.charCount(cp1); + } + return tlast - olast; + } + static int hashCode(byte[] value) { return ArraysSupport.hashCodeOfUTF16(value, 0, value.length >> 1, 0); } diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index 6064b46d50a..51543f835c1 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -315,6 +315,18 @@ final class VirtualThread extends BaseVirtualThread { } } + /** + * Submits the given task to the given executor. If the scheduler is a + * ForkJoinPool then the task is first adapted to a ForkJoinTask. + */ + private void submit(Executor executor, Runnable task) { + if (executor instanceof ForkJoinPool pool) { + pool.submit(ForkJoinTask.adapt(task)); + } else { + executor.execute(task); + } + } + /** * Submits the runContinuation task to the scheduler. For the default scheduler, * and calling it on a worker thread, the task will be pushed to the local queue, @@ -335,12 +347,12 @@ final class VirtualThread extends BaseVirtualThread { if (currentThread().isVirtual()) { Continuation.pin(); try { - scheduler.execute(runContinuation); + submit(scheduler, runContinuation); } finally { Continuation.unpin(); } } else { - scheduler.execute(runContinuation); + submit(scheduler, runContinuation); } done = true; } catch (RejectedExecutionException ree) { @@ -1536,4 +1548,4 @@ final class VirtualThread extends BaseVirtualThread { unblocker.setDaemon(true); unblocker.start(); } -} \ No newline at end of file +} diff --git a/src/java.base/share/classes/java/lang/reflect/ClassFileFormatVersion.java b/src/java.base/share/classes/java/lang/reflect/ClassFileFormatVersion.java index 16fe000091c..a4009915922 100644 --- a/src/java.base/share/classes/java/lang/reflect/ClassFileFormatVersion.java +++ b/src/java.base/share/classes/java/lang/reflect/ClassFileFormatVersion.java @@ -379,7 +379,7 @@ public enum ClassFileFormatVersion { * @since 26 * * @see + * href="https://docs.oracle.com/en/java/javase/26/docs/specs/jvms/index.html"> * The Java Virtual Machine Specification, Java SE 26 Edition */ RELEASE_26(70), diff --git a/src/java.base/share/classes/java/lang/reflect/TypeVariable.java b/src/java.base/share/classes/java/lang/reflect/TypeVariable.java index 01746e34385..85e99fd3f92 100644 --- a/src/java.base/share/classes/java/lang/reflect/TypeVariable.java +++ b/src/java.base/share/classes/java/lang/reflect/TypeVariable.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 @@ -72,7 +72,7 @@ public interface TypeVariable extends Type, Annota Type[] getBounds(); /** - * Returns the {@code GenericDeclaration} object representing the + * Returns a {@code GenericDeclaration} object representing the * generic declaration declared for this type variable. * * @return the generic declaration declared for this type variable. diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java index 1f2c8d2ffa6..f289186e0ad 100644 --- a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java +++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java @@ -560,89 +560,70 @@ public class ForkJoinPool extends AbstractExecutorService * access (which is usually needed anyway). * * Signalling. Signals (in signalWork) cause new or reactivated - * workers to scan for tasks. Method signalWork and its callers - * try to approximate the unattainable goal of having the right - * number of workers activated for the tasks at hand, but must err - * on the side of too many workers vs too few to avoid stalls: + * workers to scan for tasks. SignalWork is invoked in two cases: + * (1) When a task is pushed onto an empty queue, and (2) When a + * worker takes a top-level task from a queue that has additional + * tasks. Together, these suffice in O(log(#threads)) steps to + * fully activate with at least enough workers, and ideally no + * more than required. This ideal is unobtainable: Callers do not + * know whether another worker will finish its current task and + * poll for others without need of a signal (which is otherwise an + * advantage of work-stealing vs other schemes), and also must + * conservatively estimate the triggering conditions of emptiness + * or non-emptiness; all of which usually cause more activations + * than necessary (see below). (Method signalWork is also used as + * failsafe in case of Thread failures in deregisterWorker, to + * activate or create a new worker to replace them). * - * * If computations are purely tree structured, it suffices for - * every worker to activate another when it pushes a task into - * an empty queue, resulting in O(log(#threads)) steps to full - * activation. Emptiness must be conservatively approximated, - * which may result in unnecessary signals. Also, to reduce - * resource usages in some cases, at the expense of slower - * startup in others, activation of an idle thread is preferred - * over creating a new one, here and elsewhere. - * - * * At the other extreme, if "flat" tasks (those that do not in - * turn generate others) come in serially from only a single - * producer, each worker taking a task from a queue should - * propagate a signal if there are more tasks in that - * queue. This is equivalent to, but generally faster than, - * arranging the stealer take multiple tasks, re-pushing one or - * more on its own queue, and signalling (because its queue is - * empty), also resulting in logarithmic full activation - * time. If tasks do not not engage in unbounded loops based on - * the actions of other workers with unknown dependencies loop, - * this form of proagation can be limited to one signal per - * activation (phase change). We distinguish the cases by - * further signalling only if the task is an InterruptibleTask - * (see below), which are the only supported forms of task that - * may do so. - * - * * Because we don't know about usage patterns (or most commonly, - * mixtures), we use both approaches, which present even more - * opportunities to over-signal. (Failure to distinguish these - * cases in terms of submission methods was arguably an early - * design mistake.) Note that in either of these contexts, - * signals may be (and often are) unnecessary because active - * workers continue scanning after running tasks without the - * need to be signalled (which is one reason work stealing is - * often faster than alternatives), so additional workers - * aren't needed. - * - * * For rapidly branching tasks that require full pool resources, - * oversignalling is OK, because signalWork will soon have no - * more workers to create or reactivate. But for others (mainly - * externally submitted tasks), overprovisioning may cause very - * noticeable slowdowns due to contention and resource - * wastage. We reduce impact by deactivating workers when - * queues don't have accessible tasks, but reactivating and - * rescanning if other tasks remain. - * - * * Despite these, signal contention and overhead effects still - * occur during ramp-up and ramp-down of small computations. + * Top-Level scheduling + * ==================== * * Scanning. Method runWorker performs top-level scanning for (and * execution of) tasks by polling a pseudo-random permutation of * the array (by starting at a given index, and using a constant * cyclically exhaustive stride.) It uses the same basic polling * method as WorkQueue.poll(), but restarts with a different - * permutation on each invocation. The pseudorandom generator - * need not have high-quality statistical properties in the long + * permutation on each rescan. The pseudorandom generator need + * not have high-quality statistical properties in the long * term. We use Marsaglia XorShifts, seeded with the Weyl sequence - * from ThreadLocalRandom probes, which are cheap and - * suffice. Each queue's polling attempts to avoid becoming stuck - * when other scanners/pollers stall. Scans do not otherwise - * explicitly take into account core affinities, loads, cache - * localities, etc, However, they do exploit temporal locality - * (which usually approximates these) by preferring to re-poll - * from the same queue after a successful poll before trying - * others, which also reduces bookkeeping, cache traffic, and - * scanning overhead. But it also reduces fairness, which is - * partially counteracted by giving up on detected interference - * (which also reduces contention when too many workers try to - * take small tasks from the same queue). + * from ThreadLocalRandom probes, which are cheap and suffice. * * Deactivation. When no tasks are found by a worker in runWorker, - * it tries to deactivate()), giving up (and rescanning) on "ctl" - * contention. To avoid missed signals during deactivation, the - * method rescans and reactivates if there may have been a missed - * signal during deactivation. To reduce false-alarm reactivations - * while doing so, we scan multiple times (analogously to method - * quiescent()) before trying to reactivate. Because idle workers - * are often not yet blocked (parked), we use a WorkQueue field to - * advertise that a waiter actually needs unparking upon signal. + * it invokes deactivate, that first deactivates (to an IDLE + * phase). Avoiding missed signals during deactivation requires a + * (conservative) rescan, reactivating if there may be tasks to + * poll. Because idle workers are often not yet blocked (parked), + * we use a WorkQueue field to advertise that a waiter actually + * needs unparking upon signal. + * + * When tasks are constructed as (recursive) DAGs, top-level + * scanning is usually infrequent, and doesn't encounter most + * of the following problems addressed by runWorker and awaitWork: + * + * Locality. Polls are organized into "runs", continuing until + * empty or contended, while also minimizing interference by + * postponing bookeeping to ends of runs. This may reduce + * fairness. + * + * Contention. When many workers try to poll few queues, they + * often collide, generating CAS failures and disrupting locality + * of workers already running their tasks. This also leads to + * stalls when tasks cannot be taken because other workers have + * not finished poll operations, which is detected by reading + * ahead in queue arrays. In both cases, workers restart scans in a + * way that approximates randomized backoff. + * + * Oversignalling. When many short top-level tasks are present in + * a small number of queues, the above signalling strategy may + * activate many more workers than needed, worsening locality and + * contention problems, while also generating more global + * contention (field ctl is CASed on every activation and + * deactivation). We filter out (both in runWorker and + * signalWork) attempted signals that are surely not needed + * because the signalled tasks are already taken. + * + * Shutdown and Quiescence + * ======================= * * Quiescence. Workers scan looking for work, giving up when they * don't find any, without being sure that none are available. @@ -892,9 +873,7 @@ public class ForkJoinPool extends AbstractExecutorService * shutdown, runners are interrupted so they can cancel. Since * external joining callers never run these tasks, they must await * cancellation by others, which can occur along several different - * paths. The inability to rely on caller-runs may also require - * extra signalling (resulting in scanning and contention) so is - * done only conditionally in methods push and runworker. + * paths. * * Across these APIs, rules for reporting exceptions for tasks * with results accessed via join() differ from those via get(), @@ -961,9 +940,13 @@ public class ForkJoinPool extends AbstractExecutorService * less-contended applications. To help arrange this, some * non-reference fields are declared as "long" even when ints or * shorts would suffice. For class WorkQueue, an - * embedded @Contended region segregates fields most heavily - * updated by owners from those most commonly read by stealers or - * other management. + * embedded @Contended isolates the very busy top index, along + * with status and bookkeeping fields written (mostly) by owners, + * that otherwise interfere with reading array and base + * fields. There are other variables commonly contributing to + * false-sharing-related performance issues (including fields of + * class Thread), but we can't do much about this except try to + * minimize access. * * Initial sizing and resizing of WorkQueue arrays is an even more * delicate tradeoff because the best strategy systematically @@ -972,13 +955,11 @@ public class ForkJoinPool extends AbstractExecutorService * direct false-sharing and indirect cases due to GC bookkeeping * (cardmarks etc), and reduce the number of resizes, which are * not especially fast because they require atomic transfers. - * Currently, arrays for workers are initialized to be just large - * enough to avoid resizing in most tree-structured tasks, but - * larger for external queues where both false-sharing problems - * and the need for resizing are more common. (Maintenance note: - * any changes in fields, queues, or their uses, or JVM layout - * policies, must be accompanied by re-evaluation of these - * placement and sizing decisions.) + * Currently, arrays are initialized to be just large enough to + * avoid resizing in most tree-structured tasks, but grow rapidly + * until large. (Maintenance note: any changes in fields, queues, + * or their uses, or JVM layout policies, must be accompanied by + * re-evaluation of these placement and sizing decisions.) * * Style notes * =========== @@ -1061,17 +1042,11 @@ public class ForkJoinPool extends AbstractExecutorService static final int DEFAULT_COMMON_MAX_SPARES = 256; /** - * Initial capacity of work-stealing queue array for workers. + * Initial capacity of work-stealing queue array. * Must be a power of two, at least 2. See above. */ static final int INITIAL_QUEUE_CAPACITY = 1 << 6; - /** - * Initial capacity of work-stealing queue array for external queues. - * Must be a power of two, at least 2. See above. - */ - static final int INITIAL_EXTERNAL_QUEUE_CAPACITY = 1 << 9; - // conversions among short, int, long static final int SMASK = 0xffff; // (unsigned) short bits static final long LMASK = 0xffffffffL; // lower 32 bits of long @@ -1211,11 +1186,11 @@ public class ForkJoinPool extends AbstractExecutorService @jdk.internal.vm.annotation.Contended("w") int stackPred; // pool stack (ctl) predecessor link @jdk.internal.vm.annotation.Contended("w") + volatile int parking; // nonzero if parked in awaitWork + @jdk.internal.vm.annotation.Contended("w") volatile int source; // source queue id (or DROPPED) @jdk.internal.vm.annotation.Contended("w") int nsteals; // number of steals from other queues - @jdk.internal.vm.annotation.Contended("w") - volatile int parking; // nonzero if parked in awaitWork // Support for atomic operations private static final Unsafe U; @@ -1248,11 +1223,11 @@ public class ForkJoinPool extends AbstractExecutorService */ WorkQueue(ForkJoinWorkerThread owner, int id, int cfg, boolean clearThreadLocals) { - array = new ForkJoinTask[owner == null ? - INITIAL_EXTERNAL_QUEUE_CAPACITY : - INITIAL_QUEUE_CAPACITY]; - this.owner = owner; this.config = (clearThreadLocals) ? cfg | CLEAR_TLS : cfg; + if ((this.owner = owner) == null) { + array = new ForkJoinTask[INITIAL_QUEUE_CAPACITY]; + phase = id | IDLE; + } } /** @@ -1279,27 +1254,27 @@ public class ForkJoinPool extends AbstractExecutorService * @throws RejectedExecutionException if array could not be resized */ final void push(ForkJoinTask task, ForkJoinPool pool, boolean internal) { - int s = top, b = base, m, cap, room; ForkJoinTask[] a; - if ((a = array) != null && (cap = a.length) > 0 && // else disabled - task != null) { - int pk = task.noUserHelp() + 1; // prev slot offset - if ((room = (m = cap - 1) - (s - b)) >= 0) { + int s = top, b = base, m, cap, room; ForkJoinTask[] a, na; + if ((a = array) != null && (cap = a.length) > 0) { // else disabled + int k = (m = cap - 1) & s; + if ((room = m - (s - b)) >= 0) { top = s + 1; - long pos = slotOffset(m & s); + long pos = slotOffset(k); if (!internal) U.putReference(a, pos, task); // inside lock else U.getAndSetReference(a, pos, task); // fully fenced - if (room == 0) // resize - growArray(a, cap, s); + if (room == 0 && (na = growArray(a, cap, s)) != null) + k = ((a = na).length - 1) & s; // resize } if (!internal) unlockPhase(); if (room < 0) throw new RejectedExecutionException("Queue capacity exceeded"); - if ((room == 0 || a[m & (s - pk)] == null) && - pool != null) - pool.signalWork(); // may have appeared empty + if (pool != null && + (room == 0 || + U.getReferenceAcquire(a, slotOffset(m & (s - 1))) == null)) + pool.signalWork(a, k); // may have appeared empty } } @@ -1308,11 +1283,12 @@ public class ForkJoinPool extends AbstractExecutorService * @param a old array * @param cap old array capacity * @param s current top + * @return new array, or null on failure */ - private void growArray(ForkJoinTask[] a, int cap, int s) { - int newCap = cap << 1; + private ForkJoinTask[] growArray(ForkJoinTask[] a, int cap, int s) { + int newCap = (cap >= 1 << 16) ? cap << 1 : cap << 2; + ForkJoinTask[] newArray = null; if (a != null && a.length == cap && cap > 0 && newCap > 0) { - ForkJoinTask[] newArray = null; try { newArray = new ForkJoinTask[newCap]; } catch (OutOfMemoryError ex) { @@ -1329,34 +1305,45 @@ public class ForkJoinPool extends AbstractExecutorService updateArray(newArray); // fully fenced } } + return newArray; } /** - * Takes next task, if one exists, in order specified by mode, - * so acts as either local-pop or local-poll. Called only by owner. - * @param fifo nonzero if FIFO mode + * Takes next task, if one exists, in lifo order. */ - private ForkJoinTask nextLocalTask(int fifo) { + private ForkJoinTask localPop() { ForkJoinTask t = null; - ForkJoinTask[] a = array; - int b = base, p = top, cap; - if (p - b > 0 && a != null && (cap = a.length) > 0) { - for (int m = cap - 1, s, nb;;) { - if (fifo == 0 || (nb = b + 1) == p) { - if ((t = (ForkJoinTask)U.getAndSetReference( - a, slotOffset(m & (s = p - 1)), null)) != null) - updateTop(s); // else lost race for only task - break; + int s = top - 1, cap; long k; ForkJoinTask[] a; + if ((a = array) != null && (cap = a.length) > 0 && + U.getReference(a, k = slotOffset((cap - 1) & s)) != null && + (t = (ForkJoinTask)U.getAndSetReference(a, k, null)) != null) + updateTop(s); + return t; + } + + /** + * Takes next task, if one exists, in fifo order. + */ + private ForkJoinTask localPoll() { + ForkJoinTask t = null; + int p = top, cap; ForkJoinTask[] a; + if ((a = array) != null && (cap = a.length) > 0) { + for (int b = base; p - b > 0; ) { + int nb = b + 1; + long k = slotOffset((cap - 1) & b); + if (U.getReference(a, k) == null) { + if (nb == p) + break; // else base is lagging + while (b == (b = U.getIntAcquire(this, BASE))) + Thread.onSpinWait(); // spin to reduce memory traffic } - if ((t = (ForkJoinTask)U.getAndSetReference( - a, slotOffset(m & b), null)) != null) { + else if ((t = (ForkJoinTask) + U.getAndSetReference(a, k, null)) != null) { updateBase(nb); break; } - while (b == (b = U.getIntAcquire(this, BASE))) - Thread.onSpinWait(); // spin to reduce memory traffic - if (p - b <= 0) - break; + else + b = base; } } return t; @@ -1364,10 +1351,9 @@ public class ForkJoinPool extends AbstractExecutorService /** * Takes next task, if one exists, using configured mode. - * (Always internal, never called for Common pool.) */ final ForkJoinTask nextLocalTask() { - return nextLocalTask(config & FIFO); + return (config & FIFO) == 0 ? localPop() : localPoll(); } /** @@ -1443,12 +1429,12 @@ public class ForkJoinPool extends AbstractExecutorService // specialized execution methods /** - * Runs the given task, as well as remaining local tasks. + * Runs the given task, as well as remaining local tasks */ final void topLevelExec(ForkJoinTask task, int fifo) { while (task != null) { task.doExec(); - task = nextLocalTask(fifo); + task = (fifo != 0) ? localPoll() : localPop(); } } @@ -1578,7 +1564,7 @@ public class ForkJoinPool extends AbstractExecutorService * Cancels all local tasks. Called only by owner. */ final void cancelTasks() { - for (ForkJoinTask t; (t = nextLocalTask(0)) != null; ) { + for (ForkJoinTask t; (t = localPop()) != null; ) { try { t.cancel(false); } catch (Throwable ignore) { @@ -1780,7 +1766,8 @@ public class ForkJoinPool extends AbstractExecutorService * @param w caller's WorkQueue */ final void registerWorker(WorkQueue w) { - if (w != null && (runState & STOP) == 0L) { + if (w != null) { + w.array = new ForkJoinTask[INITIAL_QUEUE_CAPACITY]; ThreadLocalRandom.localInit(); int seed = w.stackPred = ThreadLocalRandom.getProbe(); int phaseSeq = seed & ~((IDLE << 1) - 1); // initial phase tag @@ -1858,17 +1845,18 @@ public class ForkJoinPool extends AbstractExecutorService } if ((tryTerminate(false, false) & STOP) == 0L && phase != 0 && w != null && w.source != DROPPED) { - signalWork(); // possibly replace w.cancelTasks(); // clean queue + signalWork(null, 0); // possibly replace } if (ex != null) ForkJoinTask.rethrow(ex); } /** - * Releases an idle worker, or creates one if not enough exist. + * Releases an idle worker, or creates one if not enough exist, + * giving up if array a is nonnull and task at a[k] already taken. */ - final void signalWork() { + final void signalWork(ForkJoinTask[] a, int k) { int pc = parallelism; for (long c = ctl;;) { WorkQueue[] qs = queues; @@ -1884,13 +1872,15 @@ public class ForkJoinPool extends AbstractExecutorService if (sp == 0) { if ((short)(c >>> TC_SHIFT) >= pc) break; - nc = ((c + TC_UNIT) & TC_MASK); + nc = ((c + TC_UNIT) & TC_MASK) | ac; } else if ((v = w) == null) break; else - nc = (v.stackPred & LMASK) | (c & TC_MASK); - if (c == (c = compareAndExchangeCtl(c, nc | ac))) { + nc = (v.stackPred & LMASK) | (c & TC_MASK) | ac; + if (a != null && k < a.length && k >= 0 && a[k] == null) + break; + if (c == (c = ctl) && c == (c = compareAndExchangeCtl(c, nc))) { if (v == null) createWorker(); else { @@ -1973,178 +1963,196 @@ public class ForkJoinPool extends AbstractExecutorService * @param w caller's WorkQueue (may be null on failed initialization) */ final void runWorker(WorkQueue w) { - if (w != null) { - int phase = w.phase, r = w.stackPred; // seed from registerWorker - int fifo = w.config & FIFO, nsteals = 0, src = -1; - for (;;) { - WorkQueue[] qs; + if (w != null && w.phase != 0) { // else unregistered + WorkQueue[] qs; + int r = w.stackPred; // seed from registerWorker + int fifo = (int)config & FIFO, rescans = 0, inactive = 0, taken = 0, n; + while ((runState & STOP) == 0L && (qs = queues) != null && + (n = qs.length) > 0) { + int i = r, step = (r >>> 16) | 1; r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift - if ((runState & STOP) != 0L || (qs = queues) == null) - break; - int n = qs.length, i = r, step = (r >>> 16) | 1; - boolean rescan = false; - scan: for (int l = n; l > 0; --l, i += step) { // scan queues - int j, cap; WorkQueue q; ForkJoinTask[] a; - if ((q = qs[j = i & (n - 1)]) != null && - (a = q.array) != null && (cap = a.length) > 0) { - for (int m = cap - 1, pb = -1, b = q.base;;) { - ForkJoinTask t; long k; + scan: for (int j = n; j != 0; --j, i += step) { + WorkQueue q; int qid; + if ((q = qs[qid = i & (n - 1)]) != null) { + ForkJoinTask[] a; int cap; // poll queue + while ((a = q.array) != null && (cap = a.length) > 0) { + int b, nb, nk; long bp; ForkJoinTask t; t = (ForkJoinTask)U.getReferenceAcquire( - a, k = slotOffset(m & b)); - if (b != (b = q.base) || t == null || - !U.compareAndSetReference(a, k, t, null)) { - if (a[b & m] == null) { - if (rescan) // end of run - break scan; - if (a[(b + 1) & m] == null && - a[(b + 2) & m] == null) { - break; // probably empty + a, bp = slotOffset((cap - 1) & (b = q.base))); + long np = slotOffset(nk = (nb = b + 1) & (cap - 1)); + if (q.base == b) { // else inconsistent + if (t == null) { + if (q.array == a) { // else resized + if (rescans > 0) // ran or stalled + break scan; + if (U.getReference(a, np) == null && + (rescans >= 0 || + (U.getReferenceAcquire(a, bp) == null && + q.top == q.base))) + break; + rescans = 1; // may be stalled } - if (pb == (pb = b)) { // track progress - rescan = true; // stalled; reorder scan + } + else if (inactive != 0) { + if ((inactive = tryReactivate(w)) != 0) { + rescans = 1; // can't take yet break scan; } } - } - else { - boolean propagate; - int nb = q.base = b + 1, prevSrc = src; - w.nsteals = ++nsteals; - w.source = src = j; // volatile - rescan = true; - int nh = t.noUserHelp(); - if (propagate = - (prevSrc != src || nh != 0) && a[nb & m] != null) - signalWork(); - w.topLevelExec(t, fifo); - if ((b = q.base) != nb && !propagate) - break scan; // reduce interference + else if (U.compareAndSetReference(a, bp, t, null)) { + q.base = nb; + Object nt = U.getReferenceAcquire(a, np); + w.source = qid; + rescans = 1; + ++taken; + if (nt != null && // confirm a[nk] + U.getReferenceAcquire(a, np) == nt) + signalWork(a, nk); // propagate + w.topLevelExec(t, fifo); + } } } } } - if (!rescan) { - if (((phase = deactivate(w, phase)) & IDLE) != 0) - break; - src = -1; // re-enable propagation + if (rescans >= 0) + --rescans; + else if (inactive == 0) { + if ((inactive = deactivate(w, taken)) != 0) + taken = 0; } + else if (awaitWork(w) == 0) + inactive = rescans = 0; + else + break; } } } /** - * Deactivates and if necessary awaits signal or termination. + * Tries to deactivate worker, keeping active on contention * - * @param w the worker - * @param phase current phase - * @return current phase, with IDLE set if worker should exit + * @param w the work queue + * @param taken number of stolen tasks since last deactivation + * @return nonzero if inactive */ - private int deactivate(WorkQueue w, int phase) { - if (w == null) // currently impossible - return IDLE; - int p = phase | IDLE, activePhase = phase + (IDLE << 1); - long pc = ctl, qc = (activePhase & LMASK) | ((pc - RC_UNIT) & UMASK); - int sp = w.stackPred = (int)pc; // set ctl stack link - w.phase = p; - if (!compareAndSetCtl(pc, qc)) // try to enqueue - return w.phase = phase; // back out on possible signal - int ac = (short)(qc >>> RC_SHIFT), n; long e; WorkQueue[] qs; - if (((e = runState) & STOP) != 0L || - ((e & SHUTDOWN) != 0L && ac == 0 && quiescent() > 0) || - (qs = queues) == null || (n = qs.length) <= 0) - return IDLE; // terminating - - for (int prechecks = Math.min(ac, 2), // reactivation threshold - k = Math.max(n + (n << 1), SPIN_WAITS << 1);;) { - WorkQueue q; int cap; ForkJoinTask[] a; long c; - if (w.phase == activePhase) - return activePhase; - if (--k < 0) - return awaitWork(w, p); // block, drop, or exit - if ((q = qs[k & (n - 1)]) == null) - Thread.onSpinWait(); - else if ((a = q.array) != null && (cap = a.length) > 0 && - a[q.base & (cap - 1)] != null && --prechecks < 0 && - (int)(c = ctl) == activePhase && - compareAndSetCtl(c, (sp & LMASK) | ((c + RC_UNIT) & UMASK))) - return w.phase = activePhase; // reactivate + private int deactivate(WorkQueue w, int taken) { + int inactive = 0, phase; + if (w != null && (inactive = (phase = w.phase) & IDLE) == 0) { + long sp = (phase + (IDLE << 1)) & LMASK, pc, c; + w.phase = phase | IDLE; + w.stackPred = (int)(pc = ctl); // set ctl stack link + if (!compareAndSetCtl( // try to enqueue + pc, c = ((pc - RC_UNIT) & UMASK) | sp)) + w.phase = phase; // back out on contention + else { + if (taken != 0) { + w.nsteals += taken; + if ((w.config & CLEAR_TLS) != 0 && + (Thread.currentThread() instanceof ForkJoinWorkerThread f)) + f.resetThreadLocals(); // (instanceof check always true) + } + if (((c & RC_MASK) == 0L && quiescent() > 0) || taken == 0) + inactive = w.phase & IDLE; // check quiescent termination + else { // spin for approx 1 scan cost + int tc = (short)(c >>> TC_SHIFT); + int spins = Math.max((tc << 1) + tc, SPIN_WAITS); + while ((inactive = w.phase & IDLE) != 0 && --spins != 0) + Thread.onSpinWait(); + } + } } + return inactive; + } + + /** + * Reactivates worker w if it is currently top of ctl stack + * + * @param w the work queue + * @return 0 if now active + */ + private int tryReactivate(WorkQueue w) { + int inactive = 0; + if (w != null) { // always true; hoist checks + int sp = w.stackPred, phase, activePhase; long c; + if ((inactive = (phase = w.phase) & IDLE) != 0 && + (int)(c = ctl) == (activePhase = phase + IDLE) && + compareAndSetCtl(c, (sp & LMASK) | ((c + RC_UNIT) & UMASK))) { + w.phase = activePhase; + inactive = 0; + } + } + return inactive; } /** * Awaits signal or termination. * * @param w the work queue - * @param p current phase (known to be idle) - * @return current phase, with IDLE set if worker should exit + * @return 0 if now active */ - private int awaitWork(WorkQueue w, int p) { - if (w != null) { - ForkJoinWorkerThread t; long deadline; - if ((w.config & CLEAR_TLS) != 0 && (t = w.owner) != null) - t.resetThreadLocals(); // clear before reactivate - if ((ctl & RC_MASK) > 0L) - deadline = 0L; - else if ((deadline = - (((w.source != INVALID_ID) ? keepAlive : TIMEOUT_SLOP)) + - System.currentTimeMillis()) == 0L) - deadline = 1L; // avoid zero - int activePhase = p + IDLE; - if ((p = w.phase) != activePhase && (runState & STOP) == 0L) { + private int awaitWork(WorkQueue w) { + int inactive = 0, phase; + if (w != null) { // always true; hoist checks + long waitTime = (w.source == INVALID_ID) ? 0L : keepAlive; + if ((inactive = (phase = w.phase) & IDLE) != 0) { LockSupport.setCurrentBlocker(this); - w.parking = 1; // enable unpark - while ((p = w.phase) != activePhase) { - boolean trimmable = false; int trim; - Thread.interrupted(); // clear status + int activePhase = phase + IDLE; + for (long deadline = 0L;;) { + Thread.interrupted(); // clear status if ((runState & STOP) != 0L) break; - if (deadline != 0L) { - if ((trim = tryTrim(w, p, deadline)) > 0) - break; - else if (trim < 0) - deadline = 0L; - else - trimmable = true; + boolean trimmable = false; // use timed wait if trimmable + long d = 0L, c; + if (((c = ctl) & RC_MASK) == 0L && (int)c == activePhase) { + long now = System.currentTimeMillis(); + if (deadline == 0L) + deadline = waitTime + now; + if (deadline - now <= TIMEOUT_SLOP) { + if (tryTrim(w, c, activePhase)) + break; + continue; // lost race to trim + } + d = deadline; + trimmable = true; } - U.park(trimmable, deadline); + w.parking = 1; // enable unpark and recheck + if ((inactive = w.phase & IDLE) != 0) + U.park(trimmable, d); + w.parking = 0; // close unpark window + if (inactive == 0 || (inactive = w.phase & IDLE) == 0) + break; } - w.parking = 0; LockSupport.setCurrentBlocker(null); } } - return p; + return inactive; } /** * Tries to remove and deregister worker after timeout, and release - * another to do the same. - * @return > 0: trimmed, < 0 : not trimmable, else 0 + * another to do the same unless new tasks are found. */ - private int tryTrim(WorkQueue w, int phase, long deadline) { - long c, nc; int stat, activePhase, vp, i; WorkQueue[] vs; WorkQueue v; - if ((activePhase = phase + IDLE) != (int)(c = ctl) || w == null) - stat = -1; // no longer ctl top - else if (deadline - System.currentTimeMillis() >= TIMEOUT_SLOP) - stat = 0; // spurious wakeup - else if (!compareAndSetCtl( - c, nc = ((w.stackPred & LMASK) | (RC_MASK & c) | - (TC_MASK & (c - TC_UNIT))))) - stat = -1; // lost race to signaller - else { - stat = 1; - w.source = DROPPED; - w.phase = activePhase; - if ((vp = (int)nc) != 0 && (vs = queues) != null && - vs.length > (i = vp & SMASK) && (v = vs[i]) != null && - compareAndSetCtl( // try to wake up next waiter - nc, ((UMASK & (nc + RC_UNIT)) | - (nc & TC_MASK) | (v.stackPred & LMASK)))) { - v.source = INVALID_ID; // enable cascaded timeouts - v.phase = vp; - U.unpark(v.owner); + private boolean tryTrim(WorkQueue w, long c, int activePhase) { + if (w != null) { + int vp, i; WorkQueue[] vs; WorkQueue v; + long nc = ((w.stackPred & LMASK) | + ((RC_MASK & c) | (TC_MASK & (c - TC_UNIT)))); + if (compareAndSetCtl(c, nc)) { + w.source = DROPPED; + w.phase = activePhase; + if ((vp = (int)nc) != 0 && (vs = queues) != null && + vs.length > (i = vp & SMASK) && (v = vs[i]) != null && + compareAndSetCtl( // try to wake up next waiter + nc, ((v.stackPred & LMASK) | + ((UMASK & (nc + RC_UNIT)) | (nc & TC_MASK))))) { + v.source = INVALID_ID; // enable cascaded timeouts + v.phase = vp; + U.unpark(v.owner); + } + return true; } } - return stat; + return false; } /** @@ -2561,52 +2569,35 @@ public class ForkJoinPool extends AbstractExecutorService /** * Finds and locks a WorkQueue for an external submitter, or - * throws RejectedExecutionException if shutdown or terminating. - * @param r current ThreadLocalRandom.getProbe() value + * throws RejectedExecutionException if shutdown * @param rejectOnShutdown true if RejectedExecutionException - * should be thrown when shutdown (else only if terminating) + * should be thrown when shutdown */ - private WorkQueue submissionQueue(int r, boolean rejectOnShutdown) { - int reuse; // nonzero if prefer create - if ((reuse = r) == 0) { - ThreadLocalRandom.localInit(); // initialize caller's probe + final WorkQueue externalSubmissionQueue(boolean rejectOnShutdown) { + int r; + if ((r = ThreadLocalRandom.getProbe()) == 0) { + ThreadLocalRandom.localInit(); // initialize caller's probe r = ThreadLocalRandom.getProbe(); } - for (int probes = 0; ; ++probes) { - int n, i, id; WorkQueue[] qs; WorkQueue q; - if ((qs = queues) == null) - break; - if ((n = qs.length) <= 0) + for (;;) { + WorkQueue q; WorkQueue[] qs; int n, id, i; + if ((qs = queues) == null || (n = qs.length) <= 0) break; if ((q = qs[i = (id = r & EXTERNAL_ID_MASK) & (n - 1)]) == null) { - WorkQueue w = new WorkQueue(null, id, 0, false); - w.phase = id; - boolean reject = ((lockRunState() & SHUTDOWN) != 0 && - rejectOnShutdown); - if (!reject && queues == qs && qs[i] == null) - q = qs[i] = w; // else lost race to install + WorkQueue newq = new WorkQueue(null, id, 0, false); + lockRunState(); + if (qs[i] == null && queues == qs) + q = qs[i] = newq; // else lost race to install unlockRunState(); - if (q != null) - return q; - if (reject) + } + if (q != null && q.tryLockPhase()) { + if (rejectOnShutdown && (runState & SHUTDOWN) != 0L) { + q.unlockPhase(); // check while q lock held break; - reuse = 0; - } - if (reuse == 0 || !q.tryLockPhase()) { // move index - if (reuse == 0) { - if (probes >= n >> 1) - reuse = r; // stop prefering free slot } - else if (q != null) - reuse = 0; // probe on collision - r = ThreadLocalRandom.advanceProbe(r); - } - else if (rejectOnShutdown && (runState & SHUTDOWN) != 0L) { - q.unlockPhase(); // check while q lock held - break; - } - else return q; + } + r = ThreadLocalRandom.advanceProbe(r); // move } throw new RejectedExecutionException(); } @@ -2620,24 +2611,12 @@ public class ForkJoinPool extends AbstractExecutorService } else { // find and lock queue internal = false; - q = submissionQueue(ThreadLocalRandom.getProbe(), true); + q = externalSubmissionQueue(true); } q.push(task, signalIfEmpty ? this : null, internal); return task; } - /** - * Returns queue for an external submission, bypassing call to - * submissionQueue if already established and unlocked. - */ - final WorkQueue externalSubmissionQueue(boolean rejectOnShutdown) { - WorkQueue[] qs; WorkQueue q; int n; - int r = ThreadLocalRandom.getProbe(); - return (((qs = queues) != null && (n = qs.length) > 0 && - (q = qs[r & EXTERNAL_ID_MASK & (n - 1)]) != null && r != 0 && - q.tryLockPhase()) ? q : submissionQueue(r, rejectOnShutdown)); - } - /** * Returns queue for an external thread, if one exists that has * possibly ever submitted to the given pool (nonzero probe), or @@ -3310,11 +3289,14 @@ public class ForkJoinPool extends AbstractExecutorService * @since 19 */ public int setParallelism(int size) { + int prevSize; if (size < 1 || size > MAX_CAP) throw new IllegalArgumentException(); if ((config & PRESET_SIZE) != 0) throw new UnsupportedOperationException("Cannot override System property"); - return getAndSetParallelism(size); + if ((prevSize = getAndSetParallelism(size)) < size) + signalWork(null, 0); // trigger worker activation + return prevSize; } /** diff --git a/src/java.base/share/classes/java/util/regex/Pattern.java b/src/java.base/share/classes/java/util/regex/Pattern.java index 2908370acd5..58c9186924b 100644 --- a/src/java.base/share/classes/java/util/regex/Pattern.java +++ b/src/java.base/share/classes/java/util/regex/Pattern.java @@ -43,8 +43,8 @@ import java.util.function.Predicate; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import jdk.internal.lang.CaseFolding; import jdk.internal.util.ArraysSupport; -import jdk.internal.util.regex.CaseFolding; import jdk.internal.util.regex.Grapheme; /** diff --git a/src/java.base/share/classes/jdk/internal/lang/CaseFolding.java.template b/src/java.base/share/classes/jdk/internal/lang/CaseFolding.java.template new file mode 100644 index 00000000000..24a183c8da0 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/lang/CaseFolding.java.template @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.lang; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static java.util.Map.entry; + +/** + * Utility class that handles Unicode case folding properties defined in + * CasingFolding.txt, including 1:M full case folding. + */ +public final class CaseFolding { + + private CaseFolding() {} + + /** + * Tests whether the specified code point has a folding mapping entry defined. + * + * @param cp + * the Unicode code point to test + * @return {@code true} if the given code point has a case folding mapping entry + * defined in (@code caseFoldingMap}, {@code false} otherwise + */ + public static boolean isDefined(int cp) { + return getDefined(cp) != -1; + } + + /** + * Returns the case-folded form of the specified code point according + * to the Unicode case folding mappings. + *

+ * If the code point has no case folding mapping defined, this method returns + * the original code point. + * + * Possible combinations of the returning case-folding form as a long value + * + * +---+---------+--------+---------+--------+--------+ + * | 1:1 mapping | 0000 | 0000 | 000x | xxxx | 0041 => 0061 or 1E921 => 1E943 + * +---+---------+--------+---------+--------+--------+ + * | 1:2 mapping | 0002 | 0000 | xxxx | xxxx | FB02 => 0066 006C + * +---+---------+--------+---------+--------+--------+ + * | 1:3 mapping | 0003 | xxxx | xxxx | xxxx | FB03 => 0066 0066 0069 + * +---+---------+--------+---------+--------+--------+ + * + * @param cp + * the Unicode code point to fold + * @return a long value representing the case-folded form of the input + * code point, encoded as TBD + */ + public static long fold(int cp) { + var fold = getDefined(cp); + return fold == -1 ? cp : fold; + } + + public static boolean isSingleCodePoint(long fold) { + return (fold >> 48) == 0; + } + + /** + * Returns an expansion set to "close" a given regex Unicode character class range for case-sensitive + * matching, according to the + * Simple Loose Matches + * rule defined in Unicode Technical Standard #18: Unicode Regular Expressions. + *

+ * To conform with Level 1 of UTS #18, specifically RL1.5: Simple Loose Matches, simple case folding must + * be applied to literals and (optionally) to character classes. When applied to character classes, each + * character class is expected to be closed under simple case folding. See the standard for the + * detailed explanation and example of "closed". + *

+ * RL1.5 states: To meet this requirement, an implementation that supports case-sensitive matching should + *

    + *
  1. Provide at least the simple, default Unicode case-insensitive matching, and
  2. + *
  3. Specify which character properties or constructs are closed under the matching.
  4. + *
+ *

+ * In the {@code Pattern} implementation, 5 types of constructs maybe case-sensitive when matching: + * back-refs, string slice (sequences), single, family(char-property) and class range. Single and + * family may appears independently or within a class. + *

+ * For loose/case-insensitive matching, the back-refs, slices and singles apply {@code toUpperCase} and + * {@code toLowerCase} to both the pattern and the input string. This effectively 'close' the class for + * matching. + *

+ * The family/char-properties are not "closed" and should remain unchanged. This is acceptable per RL1.5, + * if their behavior is clearly specified. + *

+ * This method addresses that requirement for the "range" construct within in character class by computing + * the additional characters that should be included to close the range under simple case folding: + *

+ * For each character in the input range {@code [start, end]} (inclusive), if the character has a simple + * case folding mapping in Unicode's CaseFolding.txt, the mapping is not a round-trip map, and the mapped + * character is not already in the range, then that mapped character (typically lowercase) is added to + * the expansion set. + *

+ * This allows regex character class "range" implementation to use the returned expansion set to support + * additional case-insensitive matching, without duplicating characters already covered by the existing + * regex range implementation. The expectation is the matching is done using both the uppercase and + * lowercase forms of the input character, for example + * + *

{@code
+    *
+    *     ch -> inRange(lower, Character.toUpperCase(ch), upper) ||
+    *           inRange(lower, Character.toLower(ch), upper) ||
+    *           additionalClosingCharacters.contains(Character.toUpperCase(ch)) ||
+    *           additionalClosingCharacters.contains(Character.toUpperCase(ch))
+    * }
+ * + * @param start the starting code point of the character range + * @param end the ending code point of the character range + * @return a {@code int[]} containing the all simple case equivalents of characters in the range, excluding + * those already in the range + * @spec https://www.unicode.org/reports/tr18/#Simple_Loose_Matches + */ + public static int[] getClassRangeClosingCharacters(int start, int end) { + int[] expanded = new int[expanded_case_cps.length]; + int off = 0; + for (int cp : expanded_case_cps) { + if (cp >= start && cp <= end) { + int folding = expanded_case_map.get(cp); + if (folding < start || folding > end) { + expanded[off++] = folding; + } + } + } + return Arrays.copyOf(expanded, off); + } + + private static final Map expanded_case_map = Map.ofEntries( +%%%Expanded_Case_Map_Entries + ); + + private static final int[] expanded_case_cps = expanded_case_map.keySet() + .stream() + .mapToInt(Integer::intValue) + .toArray(); + + private static final int HASH_CP = 0; + private static final int HASH_INDEX = 1; + private static final int HASH_NEXT = 2; + + private static int[][] hashKeys(int[] keys) { + var hashes = new int[keys.length << 1][3]; // cp + hash + next + var off = keys.length; + for (int i = 0; i < keys.length; i++) { + var cp = keys[i]; + var hash = cp % keys.length; + while (hashes[hash][HASH_CP] != 0) { + var next = hashes[hash][HASH_NEXT]; + if (next == 0) { + hashes[hash][HASH_NEXT] = off; + hash = off++; + break; + } else { + hash = next; + } + } + hashes[hash][HASH_CP] = cp; + hashes[hash][HASH_INDEX] = i; + } + return Arrays.copyOf(hashes, off); + } + + private static long getDefined(int cp) { + var hashes = CASE_FOLDING_HASHES; + var length = CASE_FOLDING_CPS.length; // hashed based on total defined. + var hash = cp % length; + while (hashes[hash][HASH_CP] != cp) { + var next = hashes[hash][HASH_NEXT]; + if (next == 0) { + return -1; // hash miss + } + hash = next; + } + var index = hashes[hash][HASH_INDEX]; + return CASE_FOLDING_VALUES[index]; + } + +%%%Entries + + private static final int[][] CASE_FOLDING_HASHES = hashKeys(CASE_FOLDING_CPS); +} diff --git a/src/java.base/share/classes/jdk/internal/misc/resources/release.txt.template b/src/java.base/share/classes/jdk/internal/misc/resources/release.txt.template new file mode 100644 index 00000000000..2d46f8f0e60 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/misc/resources/release.txt.template @@ -0,0 +1 @@ +@@COMPANY_NAME@@-@@VERSION_STRING@@-@@VERSION_DATE@@ diff --git a/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java b/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java index d24cc77600c..eb3f25ceca7 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java +++ b/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java @@ -225,6 +225,7 @@ public final class ModulePatcher { private final ModuleReference mref; private final URL delegateCodeSourceURL; private volatile ModuleReader delegate; + private volatile boolean closed; /** * Creates the ModuleReader to reads resources in a patched module. @@ -291,6 +292,15 @@ public final class ModulePatcher { return r; } + /** + * Throws an IOException if the ModuleReader is closed. + */ + private void ensureOpen() throws IOException { + if (closed) { + throw new IOException("ModuleReader is closed"); + } + } + /** * Finds a resources in the patch locations. Returns null if not found * or the name is "module-info.class" as that cannot be overridden. @@ -310,7 +320,7 @@ public final class ModulePatcher { * Finds a resource of the given name in the patched module. */ public Resource findResource(String name) throws IOException { - + assert !closed : "module reader is closed"; // patch locations Resource r = findResourceInPatch(name); if (r != null) @@ -354,6 +364,7 @@ public final class ModulePatcher { @Override public Optional find(String name) throws IOException { + ensureOpen(); Resource r = findResourceInPatch(name); if (r != null) { URI uri = URI.create(r.getURL().toString()); @@ -365,6 +376,7 @@ public final class ModulePatcher { @Override public Optional open(String name) throws IOException { + ensureOpen(); Resource r = findResourceInPatch(name); if (r != null) { return Optional.of(r.getInputStream()); @@ -375,6 +387,7 @@ public final class ModulePatcher { @Override public Optional read(String name) throws IOException { + ensureOpen(); Resource r = findResourceInPatch(name); if (r != null) { ByteBuffer bb = r.getByteBuffer(); @@ -398,6 +411,7 @@ public final class ModulePatcher { @Override public Stream list() throws IOException { + ensureOpen(); Stream s = delegate().list(); for (ResourceFinder finder : finders) { s = Stream.concat(s, finder.list()); @@ -407,6 +421,10 @@ public final class ModulePatcher { @Override public void close() throws IOException { + if (closed) { + return; + } + closed = true; closeAll(finders); delegate().close(); } diff --git a/src/java.base/share/classes/jdk/internal/util/regex/CaseFolding.java.template b/src/java.base/share/classes/jdk/internal/util/regex/CaseFolding.java.template deleted file mode 100644 index 8ffbde6c535..00000000000 --- a/src/java.base/share/classes/jdk/internal/util/regex/CaseFolding.java.template +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.internal.util.regex; - -import java.util.Arrays; -import java.util.Map; -import java.util.Objects; - -import static java.util.Map.entry; - -public final class CaseFolding { - - private static final Map expanded_case_map = Map.ofEntries( -%%%Entries - ); - - private static final int[] expanded_case_cps = expanded_case_map.keySet() - .stream() - .mapToInt(Integer::intValue) - .toArray(); - - private CaseFolding() {} - - /** - * Returns an expansion set to "close" a given regex Unicode character class range for case-sensitive - * matching, according to the - * Simple Loose Matches - * rule defined in Unicode Technical Standard #18: Unicode Regular Expressions. - *

- * To conform with Level 1 of UTS #18, specifically RL1.5: Simple Loose Matches, simple case folding must - * be applied to literals and (optionally) to character classes. When applied to character classes, each - * character class is expected to be closed under simple case folding. See the standard for the - * detailed explanation and example of "closed". - *

- * RL1.5 states: To meet this requirement, an implementation that supports case-sensitive matching should - *

    - *
  1. Provide at least the simple, default Unicode case-insensitive matching, and
  2. - *
  3. Specify which character properties or constructs are closed under the matching.
  4. - *
- *

- * In the {@code Pattern} implementation, 5 types of constructs maybe case-sensitive when matching: - * back-refs, string slice (sequences), single, family(char-property) and class range. Single and - * family may appears independently or within a class. - *

- * For loose/case-insensitive matching, the back-refs, slices and singles apply {code toUpperCase} and - * {@code toLowerCase} to both the pattern and the input string. This effectively 'close' the class for - * matching. - *

- * The family/char-properties are not "closed" and should remain unchanged. This is acceptable per RL1.5, - * if their behavior is clearly specified. - *

- * This method addresses that requirement for the "range" construct within in character class by computing - * the additional characters that should be included to close the range under simple case folding: - *

- * For each character in the input range {@code [start, end]} (inclusive), if the character has a simple - * case folding mapping in Unicode's CaseFolding.txt, the mapping is not a round-trip map, and the mapped - * character is not already in the range, then that mapped character (typically lowercase) is added to - * the expansion set. - *

- * This allows regex character class "range" implementation to use the returned expansion set to support - * additional case-insensitive matching, without duplicating characters already covered by the existing - * regex range implementation. The expectation is the matching is done using both the uppercase and - * lowercase forms of the input character, for example - * - *

{@code
-     *
-     *     ch -> inRange(lower, Character.toUpperCase(ch), upper) ||
-     *           inRange(lower, Character.toLower(ch), upper) ||
-     *           additionalClosingCharacters.contains(Character.toUpperCase(ch)) ||
-     *           additionalClosingCharacters.contains(Character.toUpperCase(ch))
-     * }
- * - *

- * @spec https://www.unicode.org/reports/tr18/#Simple_Loose_Matches - * @param start the starting code point of the character range - * @param end the ending code point of the character range - * @return a {@code int[]} containing the all simple case equivalents of characters in the range, excluding - * those already in the range - */ - public static int[] getClassRangeClosingCharacters(int start, int end) { - int[] expanded = new int[expanded_case_cps.length]; - int off = 0; - for (int cp : expanded_case_cps) { - if (cp >= start && cp <= end) { - int folding = expanded_case_map.get(cp); - if (folding < start || folding > end) { - expanded[off++] = folding; - } - } - } - return Arrays.copyOf(expanded, off); - } -} diff --git a/src/java.base/share/classes/sun/reflect/generics/reflectiveObjects/TypeVariableImpl.java b/src/java.base/share/classes/sun/reflect/generics/reflectiveObjects/TypeVariableImpl.java index 75750d38f2f..560e9a4f7c9 100644 --- a/src/java.base/share/classes/sun/reflect/generics/reflectiveObjects/TypeVariableImpl.java +++ b/src/java.base/share/classes/sun/reflect/generics/reflectiveObjects/TypeVariableImpl.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 @@ -35,6 +35,8 @@ import java.lang.reflect.TypeVariable; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; + +import jdk.internal.reflect.ReflectionFactory; import sun.reflect.annotation.AnnotationSupport; import sun.reflect.annotation.TypeAnnotationParser; import sun.reflect.annotation.AnnotationType; @@ -125,18 +127,24 @@ public class TypeVariableImpl } /** - * Returns the {@code GenericDeclaration} object representing the + * Returns a {@code GenericDeclaration} object representing the * generic declaration that declared this type variable. * - * @return the generic declaration that declared this type variable. + * @return a generic declaration that declared this type variable. * * @since 1.5 */ + @SuppressWarnings("unchecked") public D getGenericDeclaration() { assert genericDeclaration instanceof Class || genericDeclaration instanceof Method || genericDeclaration instanceof Constructor : "Unexpected kind of GenericDeclaration"; - return genericDeclaration; + // If the `genericDeclaration` instance is mutable, we need to make a copy. + return switch (genericDeclaration) { + case Method method -> (D) ReflectionFactory.getReflectionFactory().copyMethod(method); + case Constructor ctor -> (D) ReflectionFactory.getReflectionFactory().copyConstructor(ctor); + default -> genericDeclaration; + }; } diff --git a/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java b/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java index 6b4ae119504..1a46df64ffb 100644 --- a/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java +++ b/src/java.compiler/share/classes/javax/lang/model/SourceVersion.java @@ -476,7 +476,7 @@ public enum SourceVersion { * @since 26 * * @see + * href="https://docs.oracle.com/en/java/javase/26/docs/specs/jls/index.html"> * The Java Language Specification, Java SE 26 Edition */ RELEASE_26, diff --git a/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java b/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java index bad9abc6130..229c8a20d35 100644 --- a/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java +++ b/src/java.desktop/share/classes/java/awt/image/BandedSampleModel.java @@ -293,6 +293,9 @@ public final class BandedSampleModel extends ComponentSampleModel * @param data The DataBuffer containing the image data. * @return the data for the specified pixel. * @see #setDataElements(int, int, Object, DataBuffer) + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if {@code obj} is too small to hold the output. */ public Object getDataElements(int x, int y, Object obj, DataBuffer data) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { @@ -401,15 +404,10 @@ public final class BandedSampleModel extends ComponentSampleModel } /** - * Returns all samples for the specified pixel in an int array. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param iArray If non-null, returns the samples in this array - * @param data The DataBuffer containing the image data - * @return the samples for the specified pixel. - * @see #setPixel(int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if {@code iArray} is too small to hold the output. */ public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { @@ -434,26 +432,19 @@ public final class BandedSampleModel extends ComponentSampleModel } /** - * Returns all samples for the specified rectangle of pixels in - * an int array, one sample per data array element. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the upper left pixel location - * @param y The Y coordinate of the upper left pixel location - * @param w The width of the pixel rectangle - * @param h The height of the pixel rectangle - * @param iArray If non-null, returns the samples in this array - * @param data The DataBuffer containing the image data - * @return the samples for the pixels within the specified region. - * @see #setPixels(int, int, int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or {@code w} or {@code h} is negative. + * or if {@code iArray} is too small to hold the output. */ public int[] getPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); @@ -484,16 +475,10 @@ public final class BandedSampleModel extends ComponentSampleModel } /** - * Returns as int the sample in a specified band for the pixel - * located at (x,y). - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param b The band to return - * @param data The DataBuffer containing the image data - * @return the sample in the specified band for the specified pixel. - * @see #setSample(int, int, int, int, DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public int getSample(int x, int y, int b, DataBuffer data) { // Bounds check for 'b' will be performed automatically @@ -508,16 +493,10 @@ public final class BandedSampleModel extends ComponentSampleModel } /** - * Returns the sample in a specified band - * for the pixel located at (x,y) as a float. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param b The band to return - * @param data The DataBuffer containing the image data - * @return a float value that represents the sample in the specified - * band for the specified pixel. + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public float getSampleFloat(int x, int y, int b, DataBuffer data) { // Bounds check for 'b' will be performed automatically @@ -532,16 +511,10 @@ public final class BandedSampleModel extends ComponentSampleModel } /** - * Returns the sample in a specified band - * for a pixel located at (x,y) as a double. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param b The band to return - * @param data The DataBuffer containing the image data - * @return a double value that represents the sample in the specified - * band for the specified pixel. + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public double getSampleDouble(int x, int y, int b, DataBuffer data) { // Bounds check for 'b' will be performed automatically @@ -556,25 +529,16 @@ public final class BandedSampleModel extends ComponentSampleModel } /** - * Returns the samples in a specified band for the specified rectangle - * of pixels in an int array, one sample per data array element. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the upper left pixel location - * @param y The Y coordinate of the upper left pixel location - * @param w The width of the pixel rectangle - * @param h The height of the pixel rectangle - * @param b The band to return - * @param iArray If non-null, returns the samples in this array - * @param data The DataBuffer containing the image data - * @return the samples in the specified band for the pixels within - * the specified region. - * @see #setSamples(int, int, int, int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * band index are not in bounds, or {@code w} or {@code h} is negative, + * or if {@code iArray} is too small to hold the output. */ public int[] getSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data) { // Bounds check for 'b' will be performed automatically - if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + if ((x < 0) || (y < 0) || (w < 0) || (h < 0) || (x + w > width) || (y + h > height)) { throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); } @@ -633,6 +597,9 @@ public final class BandedSampleModel extends ComponentSampleModel * object * @param data The DataBuffer containing the image data * @see #getDataElements(int, int, Object, DataBuffer) + * @throws NullPointerException if {@code obj} or {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if {@code obj} is too small to hold the input. */ public void setDataElements(int x, int y, Object obj, DataBuffer data) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { @@ -700,14 +667,10 @@ public final class BandedSampleModel extends ComponentSampleModel } /** - * Sets a pixel in the DataBuffer using an int array of samples for input. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param iArray The input samples in an int array - * @param data The DataBuffer containing the image data - * @see #getPixel(int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code iArray} or {code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if {@code iArray} is too small to hold the input. */ public void setPixel(int x, int y, int[] iArray, DataBuffer data) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { @@ -722,25 +685,19 @@ public final class BandedSampleModel extends ComponentSampleModel } /** - * Sets all samples for a rectangle of pixels from an int array containing - * one sample per array element. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the upper left pixel location - * @param y The Y coordinate of the upper left pixel location - * @param w The width of the pixel rectangle - * @param h The height of the pixel rectangle - * @param iArray The input samples in an int array - * @param data The DataBuffer containing the image data - * @see #getPixels(int, int, int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code iArray} or {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or {@code w} or {@code h} is negative. + * or if {@code iArray} is too small to hold the input. */ public void setPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); @@ -763,16 +720,10 @@ public final class BandedSampleModel extends ComponentSampleModel } /** - * Sets a sample in the specified band for the pixel located at (x,y) - * in the DataBuffer using an int for input. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param b The band to set - * @param s The input sample as an int - * @param data The DataBuffer containing the image data - * @see #getSample(int, int, int, DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public void setSample(int x, int y, int b, int s, DataBuffer data) { @@ -786,16 +737,10 @@ public final class BandedSampleModel extends ComponentSampleModel } /** - * Sets a sample in the specified band for the pixel located at (x,y) - * in the DataBuffer using a float for input. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param b The band to set - * @param s The input sample as a float - * @param data The DataBuffer containing the image data - * @see #getSample(int, int, int, DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public void setSample(int x, int y, int b, float s , @@ -810,16 +755,10 @@ public final class BandedSampleModel extends ComponentSampleModel } /** - * Sets a sample in the specified band for the pixel located at (x,y) - * in the DataBuffer using a double for input. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param b The band to set - * @param s The input sample as a double - * @param data The DataBuffer containing the image data - * @see #getSample(int, int, int, DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public void setSample(int x, int y, int b, double s, @@ -834,23 +773,16 @@ public final class BandedSampleModel extends ComponentSampleModel } /** - * Sets the samples in the specified band for the specified rectangle - * of pixels from an int array containing one sample per data array element. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the upper left pixel location - * @param y The Y coordinate of the upper left pixel location - * @param w The width of the pixel rectangle - * @param h The height of the pixel rectangle - * @param b The band to set - * @param iArray The input sample array - * @param data The DataBuffer containing the image data - * @see #getSamples(int, int, int, int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code iArray} or {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * band index are not in bounds, or {@code w} or {@code h} is negative, + * or if {@code iArray} is too small to hold the input. */ public void setSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data) { // Bounds check for 'b' will be performed automatically - if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + if ((x < 0) || (y < 0) || (w < 0) || (h < 0) || (x + w > width) || (y + h > height)) { throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); } diff --git a/src/java.desktop/share/classes/java/awt/image/BufferedImage.java b/src/java.desktop/share/classes/java/awt/image/BufferedImage.java index 09c96a6560f..52bf8a8c4bf 100644 --- a/src/java.desktop/share/classes/java/awt/image/BufferedImage.java +++ b/src/java.desktop/share/classes/java/awt/image/BufferedImage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -100,10 +100,7 @@ public class BufferedImage extends java.awt.Image * Represents an image with 8-bit RGBA color components packed into * integer pixels. The image has a {@code DirectColorModel} * with alpha. The color data in this image is considered not to be - * premultiplied with alpha. When this type is used as the - * {@code imageType} argument to a {@code BufferedImage} - * constructor, the created image is consistent with images - * created in the JDK1.1 and earlier releases. + * premultiplied with alpha. */ public static final int TYPE_INT_ARGB = 2; diff --git a/src/java.desktop/share/classes/java/awt/image/ComponentSampleModel.java b/src/java.desktop/share/classes/java/awt/image/ComponentSampleModel.java index 7a3d67c7e6e..89a72b18dea 100644 --- a/src/java.desktop/share/classes/java/awt/image/ComponentSampleModel.java +++ b/src/java.desktop/share/classes/java/awt/image/ComponentSampleModel.java @@ -600,7 +600,7 @@ public class ComponentSampleModel extends SampleModel * @return the data of the specified pixel * @see #setDataElements(int, int, Object, DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are * not in bounds, or if obj is too small to hold the output. */ @@ -707,20 +707,10 @@ public class ComponentSampleModel extends SampleModel } /** - * Returns all samples for the specified pixel in an int array, - * one sample per array element. - * An {@code ArrayIndexOutOfBoundsException} might be thrown if - * the coordinates are not in bounds. - * @param x the X coordinate of the pixel location - * @param y the Y coordinate of the pixel location - * @param iArray If non-null, returns the samples in this array - * @param data The DataBuffer containing the image data - * @return the samples of the specified pixel. - * @see #setPixel(int, int, int[], DataBuffer) - * - * @throws NullPointerException if data is null. + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if iArray is too small to hold the output. + * not in bounds, or if {@code iArray} is too small to hold the output. */ public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { @@ -742,26 +732,19 @@ public class ComponentSampleModel extends SampleModel } /** - * Returns all samples for the specified rectangle of pixels in - * an int array, one sample per array element. - * An {@code ArrayIndexOutOfBoundsException} might be thrown if - * the coordinates are not in bounds. - * @param x The X coordinate of the upper left pixel location - * @param y The Y coordinate of the upper left pixel location - * @param w The width of the pixel rectangle - * @param h The height of the pixel rectangle - * @param iArray If non-null, returns the samples in this array - * @param data The DataBuffer containing the image data - * @return the samples of the pixels within the specified region. - * @see #setPixels(int, int, int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or {@code w} or {@code h} is negative + * or if {@code iArray} is too small to hold the output. */ public int[] getPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || y > height || y1 < 0 || y1 > height) + if (x < 0 || (w < 0) || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || (h < 0) || y >= height || y > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); @@ -790,16 +773,10 @@ public class ComponentSampleModel extends SampleModel } /** - * Returns as int the sample in a specified band for the pixel - * located at (x,y). - * An {@code ArrayIndexOutOfBoundsException} might be thrown if - * the coordinates are not in bounds. - * @param x the X coordinate of the pixel location - * @param y the Y coordinate of the pixel location - * @param b the band to return - * @param data the {@code DataBuffer} containing the image data - * @return the sample in a specified band for the specified pixel - * @see #setSample(int, int, int, int, DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public int getSample(int x, int y, int b, DataBuffer data) { // Bounds check for 'b' will be performed automatically @@ -814,16 +791,10 @@ public class ComponentSampleModel extends SampleModel } /** - * Returns the sample in a specified band - * for the pixel located at (x,y) as a float. - * An {@code ArrayIndexOutOfBoundsException} might be - * thrown if the coordinates are not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param b The band to return - * @param data The DataBuffer containing the image data - * @return a float value representing the sample in the specified - * band for the specified pixel. + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public float getSampleFloat(int x, int y, int b, DataBuffer data) { // Bounds check for 'b' will be performed automatically @@ -837,18 +808,11 @@ public class ComponentSampleModel extends SampleModel bandOffsets[b]); return sample; } - /** - * Returns the sample in a specified band - * for a pixel located at (x,y) as a double. - * An {@code ArrayIndexOutOfBoundsException} might be - * thrown if the coordinates are not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param b The band to return - * @param data The DataBuffer containing the image data - * @return a double value representing the sample in the specified - * band for the specified pixel. + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public double getSampleDouble(int x, int y, int b, DataBuffer data) { // Bounds check for 'b' will be performed automatically @@ -864,25 +828,16 @@ public class ComponentSampleModel extends SampleModel } /** - * Returns the samples in a specified band for the specified rectangle - * of pixels in an int array, one sample per data array element. - * An {@code ArrayIndexOutOfBoundsException} might be thrown if - * the coordinates are not in bounds. - * @param x The X coordinate of the upper left pixel location - * @param y The Y coordinate of the upper left pixel location - * @param w the width of the pixel rectangle - * @param h the height of the pixel rectangle - * @param b the band to return - * @param iArray if non-{@code null}, returns the samples - * in this array - * @param data the {@code DataBuffer} containing the image data - * @return the samples in the specified band of the specified pixel - * @see #setSamples(int, int, int, int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * band index are not in bounds, or {@code w} or {@code h} is negative, + * or if {@code iArray} is too small to hold the output. */ public int[] getSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data) { // Bounds check for 'b' will be performed automatically - if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + if ((x < 0) || (y < 0) || (w < 0) || ( h < 0) || (x + w > width) || (y + h > height)) { throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); } @@ -943,6 +898,9 @@ public class ComponentSampleModel extends SampleModel * @param obj a primitive array containing pixel data * @param data the DataBuffer containing the image data * @see #getDataElements(int, int, Object, DataBuffer) + * @throws NullPointerException if {@code obj} or {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if obj is too small to hold the input. */ public void setDataElements(int x, int y, Object obj, DataBuffer data) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { @@ -1011,15 +969,10 @@ public class ComponentSampleModel extends SampleModel } /** - * Sets a pixel in the {@code DataBuffer} using an int array of - * samples for input. An {@code ArrayIndexOutOfBoundsException} - * might be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param iArray The input samples in an int array - * @param data The DataBuffer containing the image data - * @see #getPixel(int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code iArray} or {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if {@code iArray} is too small to hold the input. */ public void setPixel(int x, int y, int[] iArray, DataBuffer data) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { @@ -1034,25 +987,19 @@ public class ComponentSampleModel extends SampleModel } /** - * Sets all samples for a rectangle of pixels from an int array containing - * one sample per array element. - * An {@code ArrayIndexOutOfBoundsException} might be thrown if the - * coordinates are not in bounds. - * @param x The X coordinate of the upper left pixel location - * @param y The Y coordinate of the upper left pixel location - * @param w The width of the pixel rectangle - * @param h The height of the pixel rectangle - * @param iArray The input samples in an int array - * @param data The DataBuffer containing the image data - * @see #getPixels(int, int, int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code iArray} or {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or {@code w} or {@code h} is negative + * or if {@code iArray} is too small to hold the input. */ public void setPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); @@ -1075,16 +1022,10 @@ public class ComponentSampleModel extends SampleModel } /** - * Sets a sample in the specified band for the pixel located at (x,y) - * in the {@code DataBuffer} using an int for input. - * An {@code ArrayIndexOutOfBoundsException} might be thrown if the - * coordinates are not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param b the band to set - * @param s the input sample as an int - * @param data the DataBuffer containing the image data - * @see #getSample(int, int, int, DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public void setSample(int x, int y, int b, int s, DataBuffer data) { @@ -1098,16 +1039,10 @@ public class ComponentSampleModel extends SampleModel } /** - * Sets a sample in the specified band for the pixel located at (x,y) - * in the {@code DataBuffer} using a float for input. - * An {@code ArrayIndexOutOfBoundsException} might be thrown if - * the coordinates are not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param b The band to set - * @param s The input sample as a float - * @param data The DataBuffer containing the image data - * @see #getSample(int, int, int, DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public void setSample(int x, int y, int b, float s , @@ -1123,16 +1058,10 @@ public class ComponentSampleModel extends SampleModel } /** - * Sets a sample in the specified band for the pixel located at (x,y) - * in the {@code DataBuffer} using a double for input. - * An {@code ArrayIndexOutOfBoundsException} might be thrown if - * the coordinates are not in bounds. - * @param x The X coordinate of the pixel location - * @param y The Y coordinate of the pixel location - * @param b The band to set - * @param s The input sample as a double - * @param data The DataBuffer containing the image data - * @see #getSample(int, int, int, DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public void setSample(int x, int y, int b, double s, @@ -1148,23 +1077,16 @@ public class ComponentSampleModel extends SampleModel } /** - * Sets the samples in the specified band for the specified rectangle - * of pixels from an int array containing one sample per data array element. - * An {@code ArrayIndexOutOfBoundsException} might be thrown if the - * coordinates are not in bounds. - * @param x The X coordinate of the upper left pixel location - * @param y The Y coordinate of the upper left pixel location - * @param w The width of the pixel rectangle - * @param h The height of the pixel rectangle - * @param b The band to set - * @param iArray The input samples in an int array - * @param data The DataBuffer containing the image data - * @see #getSamples(int, int, int, int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code iArray} or {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * band index are not in bounds, or {@code w} or {@code h} is negative, + * or if {@code iArray} is too small to hold the input. */ public void setSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data) { // Bounds check for 'b' will be performed automatically - if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + if ((x < 0) || (y < 0) || (w < 0) || (h < 0) || (x + w > width) || (y + h > height)) { throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); } diff --git a/src/java.desktop/share/classes/java/awt/image/Kernel.java b/src/java.desktop/share/classes/java/awt/image/Kernel.java index d2b9760aba5..67f62e79b9e 100644 --- a/src/java.desktop/share/classes/java/awt/image/Kernel.java +++ b/src/java.desktop/share/classes/java/awt/image/Kernel.java @@ -25,6 +25,7 @@ package java.awt.image; +import java.util.Objects; /** * The {@code Kernel} class defines a matrix that describes how a @@ -59,7 +60,7 @@ public class Kernel implements Cloneable { * @param width width of the kernel * @param height height of the kernel * @param data kernel data in row major order - * @throws IllegalArgumentException if {@code data} is null + * @throws NullPointerException if {@code data} is null * @throws IllegalArgumentException if {@code width} or {@code height} * is not positive * @throws IllegalArgumentException if product of {@code width} and @@ -69,9 +70,7 @@ public class Kernel implements Cloneable { * {@code height} */ public Kernel(int width, int height, float[] data) { - if (data == null) { - throw new IllegalArgumentException("Data must not be null"); - } + Objects.requireNonNull(data, "Data must not be null"); if ((width <= 0) || (height <= 0)) { throw new IllegalArgumentException("Invalid width or height"); } diff --git a/src/java.desktop/share/classes/java/awt/image/MultiPixelPackedSampleModel.java b/src/java.desktop/share/classes/java/awt/image/MultiPixelPackedSampleModel.java index 9254d9fa717..e40da604887 100644 --- a/src/java.desktop/share/classes/java/awt/image/MultiPixelPackedSampleModel.java +++ b/src/java.desktop/share/classes/java/awt/image/MultiPixelPackedSampleModel.java @@ -343,7 +343,7 @@ public class MultiPixelPackedSampleModel extends SampleModel * coordinates are not in bounds. * @param x the X coordinate of the specified pixel * @param y the Y coordinate of the specified pixel - * @param b the band to return, which is assumed to be 0 + * @param b the band to return, which must be 0 * @param data the {@code DataBuffer} containing the image * data * @return the specified band containing the sample of the specified @@ -374,7 +374,7 @@ public class MultiPixelPackedSampleModel extends SampleModel * coordinates are not in bounds. * @param x the X coordinate of the specified pixel * @param y the Y coordinate of the specified pixel - * @param b the band to return, which is assumed to be 0 + * @param b the band to set, which must be 0 * @param s the input sample as an {@code int} * @param data the {@code DataBuffer} where image data is stored * @throws ArrayIndexOutOfBoundsException if the coordinates are @@ -448,6 +448,9 @@ public class MultiPixelPackedSampleModel extends SampleModel * not in bounds, or if {@code obj} is not {@code null} or * not large enough to hold the pixel data * @see #setDataElements(int, int, Object, DataBuffer) + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if {code obj} is too small to hold the output. */ public Object getDataElements(int x, int y, Object obj, DataBuffer data) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { @@ -527,8 +530,11 @@ public class MultiPixelPackedSampleModel extends SampleModel * @param data the {@code DataBuffer} where image data is stored * @return an array containing the specified pixel. * @throws ArrayIndexOutOfBoundsException if the coordinates - * are not in bounds + * are not in bounds, or if {@code iArray} is too small to hold the output. * @see #setPixel(int, int, int[], DataBuffer) + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if {@code iArray} is too small to hold the output. */ public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { @@ -588,6 +594,9 @@ public class MultiPixelPackedSampleModel extends SampleModel * @param obj a primitive array containing pixel data * @param data the {@code DataBuffer} containing the image data * @see #getDataElements(int, int, Object, DataBuffer) + * @throws NullPointerException if {@code obj} or {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if {@code obj} is too small to hold the input. */ public void setDataElements(int x, int y, Object obj, DataBuffer data) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { @@ -638,6 +647,9 @@ public class MultiPixelPackedSampleModel extends SampleModel * @param iArray the input pixel in an {@code int} array * @param data the {@code DataBuffer} containing the image data * @see #getPixel(int, int, int[], DataBuffer) + * @throws NullPointerException if {@code iArray} or {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if {@code iArray} is too small to hold the input. */ public void setPixel(int x, int y, int[] iArray, DataBuffer data) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { diff --git a/src/java.desktop/share/classes/java/awt/image/SampleModel.java b/src/java.desktop/share/classes/java/awt/image/SampleModel.java index 678925ffd20..0ecd4162c60 100644 --- a/src/java.desktop/share/classes/java/awt/image/SampleModel.java +++ b/src/java.desktop/share/classes/java/awt/image/SampleModel.java @@ -231,9 +231,9 @@ public abstract class SampleModel * @return the samples for the specified pixel. * @see #setPixel(int, int, int[], DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if iArray is too small to hold the output. + * not in bounds, or if {@code iArray} is too small to hold the output. */ public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) { @@ -293,9 +293,9 @@ public abstract class SampleModel * @see java.awt.image.DataBuffer * @see #setDataElements(int, int, Object, DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if obj is too small to hold the output. + * not in bounds, or if {@code obj} is too small to hold the output. */ public abstract Object getDataElements(int x, int y, Object obj, DataBuffer data); @@ -347,9 +347,10 @@ public abstract class SampleModel * @see #setDataElements(int, int, int, int, Object, DataBuffer) * @see java.awt.image.DataBuffer * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if obj is too small to hold the output. + * not in bounds, or {@code w} or {@code h} is negative + * or if {@code obj} is too small to hold the output. */ public Object getDataElements(int x, int y, int w, int h, Object obj, DataBuffer data) { @@ -362,8 +363,8 @@ public abstract class SampleModel int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } @@ -528,7 +529,7 @@ public abstract class SampleModel * @see #getDataElements(int, int, Object, DataBuffer) * @see java.awt.image.DataBuffer * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code obj} or {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are * not in bounds, or if obj is too small to hold the input. */ @@ -577,7 +578,7 @@ public abstract class SampleModel * @see #getDataElements(int, int, int, int, Object, DataBuffer) * @see java.awt.image.DataBuffer * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code obj} or {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are * not in bounds, or if obj is too small to hold the input. */ @@ -592,8 +593,8 @@ public abstract class SampleModel int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } @@ -695,9 +696,9 @@ public abstract class SampleModel * @return the samples for the specified pixel. * @see #setPixel(int, int, float[], DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if fArray is too small to hold the output. + * not in bounds, or if {@code fArray} is too small to hold the output. */ public float[] getPixel(int x, int y, float[] fArray, DataBuffer data) { @@ -726,9 +727,9 @@ public abstract class SampleModel * @return the samples for the specified pixel. * @see #setPixel(int, int, double[], DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if dArray is too small to hold the output. + * not in bounds, or if {@code dArray} is too small to hold the output. */ public double[] getPixel(int x, int y, double[] dArray, DataBuffer data) { @@ -760,9 +761,10 @@ public abstract class SampleModel * @return the samples for the specified region of pixels. * @see #setPixels(int, int, int, int, int[], DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if iArray is too small to hold the output. + * not in bounds, or {@code w} or {@code h} is negative + * or if {@code iArray} is too small to hold the output. */ public int[] getPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { @@ -772,8 +774,8 @@ public abstract class SampleModel int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } @@ -808,9 +810,10 @@ public abstract class SampleModel * @return the samples for the specified region of pixels. * @see #setPixels(int, int, int, int, float[], DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if fArray is too small to hold the output. + * not in bounds, or {@code w} or {@code h} is negative + * or if {@code fArray} is too small to hold the output. */ public float[] getPixels(int x, int y, int w, int h, float[] fArray, DataBuffer data) { @@ -820,8 +823,8 @@ public abstract class SampleModel int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } @@ -856,9 +859,10 @@ public abstract class SampleModel * @return the samples for the specified region of pixels. * @see #setPixels(int, int, int, int, double[], DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if dArray is too small to hold the output. + * not in bounds, or {@code w} or {@code h} is negative + * or if {@code dArray} is too small to hold the output. */ public double[] getPixels(int x, int y, int w, int h, double[] dArray, DataBuffer data) { @@ -867,8 +871,8 @@ public abstract class SampleModel int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } @@ -903,7 +907,7 @@ public abstract class SampleModel * @return the sample in a specified band for the specified pixel. * @see #setSample(int, int, int, int, DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates or * the band index are not in bounds. */ @@ -921,7 +925,7 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @return the sample in a specified band for the specified pixel. * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates or * the band index are not in bounds. */ @@ -943,7 +947,7 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @return the sample in a specified band for the specified pixel. * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates or * the band index are not in bounds. */ @@ -971,10 +975,10 @@ public abstract class SampleModel * of pixels. * @see #setSamples(int, int, int, int, int, int[], DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates or - * the band index are not in bounds, or if iArray is too small to - * hold the output. + * band index are not in bounds, or {@code w} or {@code h} is negative, + * or if {@code iArray} is too small to hold the output. */ public int[] getSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data) { @@ -983,8 +987,8 @@ public abstract class SampleModel int x1 = x + w; int y1 = y + h; - if (x < 0 || x1 < x || x1 > width || - y < 0 || y1 < y || y1 > height) + if (x < 0 || w < 0 || x1 < x || x1 > width || + y < 0 || h < 0 || y1 < y || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } @@ -1019,10 +1023,10 @@ public abstract class SampleModel * of pixels. * @see #setSamples(int, int, int, int, int, float[], DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates or - * the band index are not in bounds, or if fArray is too small to - * hold the output. + * band index are not in bounds, or {@code w} or {@code h} is negative, + * or if {@code iArray} is too small to hold the output. */ public float[] getSamples(int x, int y, int w, int h, int b, float[] fArray, @@ -1032,8 +1036,8 @@ public abstract class SampleModel int x1 = x + w; int y1 = y + h; - if (x < 0 || x1 < x || x1 > width || - y < 0 || y1 < y || y1 > height) + if (x < 0 || w < 0 || x1 < x || x1 > width || + y < 0 || h < 0 || y1 < y || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates"); } @@ -1068,10 +1072,10 @@ public abstract class SampleModel * of pixels. * @see #setSamples(int, int, int, int, int, double[], DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates or - * the band index are not in bounds, or if dArray is too small to - * hold the output. + * band index are not in bounds, or {@code w} or {@code h} is negative, + * or if {@code dArray} is too small to hold the output. */ public double[] getSamples(int x, int y, int w, int h, int b, double[] dArray, @@ -1081,8 +1085,8 @@ public abstract class SampleModel int x1 = x + w; int y1 = y + h; - if (x < 0 || x1 < x || x1 > width || - y < 0 || y1 < y || y1 > height) + if (x < 0 || w < 0 || x1 < x || x1 > width || + y < 0 || h < 0 || y1 < y || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates"); } @@ -1111,9 +1115,9 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @see #getPixel(int, int, int[], DataBuffer) * - * @throws NullPointerException if iArray or data is null. + * @throws NullPointerException if {@code iArray} or {code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if iArray is too small to hold the input. + * not in bounds, or if {@code iArray} is too small to hold the input. */ public void setPixel(int x, int y, int[] iArray, DataBuffer data) { @@ -1131,9 +1135,9 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @see #getPixel(int, int, float[], DataBuffer) * - * @throws NullPointerException if fArray or data is null. + * @throws NullPointerException if {@code fArray} or {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if fArray is too small to hold the input. + * not in bounds, or if {@code fArray} is too small to hold the input. */ public void setPixel(int x, int y, float[] fArray, DataBuffer data) { @@ -1150,9 +1154,9 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @see #getPixel(int, int, double[], DataBuffer) * - * @throws NullPointerException if dArray or data is null. + * @throws NullPointerException if {@code dArray} or {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if fArray is too small to hold the input. + * not in bounds, or if {@code dArray} is too small to hold the input. */ public void setPixel(int x, int y, double[] dArray, DataBuffer data) { @@ -1173,9 +1177,10 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @see #getPixels(int, int, int, int, int[], DataBuffer) * - * @throws NullPointerException if iArray or data is null. + * @throws NullPointerException if {@code iArray} or {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if iArray is too small to hold the input. + * not in bounds, or {@code w} or {@code h} is negative + * or if {@code iArray} is too small to hold the input. */ public void setPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { @@ -1183,8 +1188,8 @@ public abstract class SampleModel int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } @@ -1211,9 +1216,10 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @see #getPixels(int, int, int, int, float[], DataBuffer) * - * @throws NullPointerException if fArray or data is null. + * @throws NullPointerException if {@code fArray} or {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if fArray is too small to hold the input. + * not in bounds, or {@code w} or {@code h} is negative + * or if {@code fArray} is too small to hold the input. */ public void setPixels(int x, int y, int w, int h, float[] fArray, DataBuffer data) { @@ -1221,8 +1227,8 @@ public abstract class SampleModel int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width|| - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } @@ -1249,9 +1255,10 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @see #getPixels(int, int, int, int, double[], DataBuffer) * - * @throws NullPointerException if dArray or data is null. + * @throws NullPointerException if {@code dArray} or {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates are - * not in bounds, or if dArray is too small to hold the input. + * not in bounds, or {@code w} or {@code h} is negative + * or if {@code dArray} is too small to hold the input. */ public void setPixels(int x, int y, int w, int h, double[] dArray, DataBuffer data) { @@ -1259,8 +1266,8 @@ public abstract class SampleModel int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } @@ -1286,7 +1293,7 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @see #getSample(int, int, int, DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates or * the band index are not in bounds. */ @@ -1310,7 +1317,7 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @see #getSample(int, int, int, DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates or * the band index are not in bounds. */ @@ -1338,7 +1345,7 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @see #getSample(int, int, int, DataBuffer) * - * @throws NullPointerException if data is null. + * @throws NullPointerException if {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates or * the band index are not in bounds. */ @@ -1364,10 +1371,10 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @see #getSamples(int, int, int, int, int, int[], DataBuffer) * - * @throws NullPointerException if iArray or data is null. + * @throws NullPointerException if {@code iArray} or {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates or - * the band index are not in bounds, or if iArray is too small to - * hold the input. + * band index are not in bounds, or {@code w} or {@code h} is negative, + * or if {@code iArray} is too small to hold the input. */ public void setSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data) { @@ -1375,8 +1382,8 @@ public abstract class SampleModel int Offset=0; int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } @@ -1402,10 +1409,10 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @see #getSamples(int, int, int, int, int, float[], DataBuffer) * - * @throws NullPointerException if fArray or data is null. + * @throws NullPointerException if {@code fArray} or {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates or - * the band index are not in bounds, or if fArray is too small to - * hold the input. + * band index are not in bounds, or {@code w} or {@code h} is negative, + * or if {@code fArray} is too small to hold the input. */ public void setSamples(int x, int y, int w, int h, int b, float[] fArray, DataBuffer data) { @@ -1413,8 +1420,8 @@ public abstract class SampleModel int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } @@ -1440,10 +1447,10 @@ public abstract class SampleModel * @param data The DataBuffer containing the image data. * @see #getSamples(int, int, int, int, int, double[], DataBuffer) * - * @throws NullPointerException if dArray or data is null. + * @throws NullPointerException if {@code dArray} or {@code data} is {@code null}. * @throws ArrayIndexOutOfBoundsException if the coordinates or - * the band index are not in bounds, or if dArray is too small to - * hold the input. + * band index are not in bounds, or {@code w} or {@code h} is negative, + * or if {@code dArray} is too small to hold the input. */ public void setSamples(int x, int y, int w, int h, int b, double[] dArray, DataBuffer data) { @@ -1452,8 +1459,8 @@ public abstract class SampleModel int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException("Invalid coordinates."); } diff --git a/src/java.desktop/share/classes/java/awt/image/SinglePixelPackedSampleModel.java b/src/java.desktop/share/classes/java/awt/image/SinglePixelPackedSampleModel.java index abb669e02c6..a75b5197c44 100644 --- a/src/java.desktop/share/classes/java/awt/image/SinglePixelPackedSampleModel.java +++ b/src/java.desktop/share/classes/java/awt/image/SinglePixelPackedSampleModel.java @@ -357,6 +357,9 @@ public class SinglePixelPackedSampleModel extends SampleModel * @param data The DataBuffer containing the image data. * @return the data for the specified pixel. * @see #setDataElements(int, int, Object, DataBuffer) + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if obj is too small to hold the output. */ public Object getDataElements(int x, int y, Object obj, DataBuffer data) { // Bounds check for 'b' will be performed automatically @@ -416,15 +419,10 @@ public class SinglePixelPackedSampleModel extends SampleModel } /** - * Returns all samples in for the specified pixel in an int array. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the pixel location. - * @param y The Y coordinate of the pixel location. - * @param iArray If non-null, returns the samples in this array - * @param data The DataBuffer containing the image data. - * @return all samples for the specified pixel. - * @see #setPixel(int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if {@code iArray} is too small to hold the output. */ public int [] getPixel(int x, int y, int[] iArray, DataBuffer data) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { @@ -446,26 +444,20 @@ public class SinglePixelPackedSampleModel extends SampleModel } /** - * Returns all samples for the specified rectangle of pixels in - * an int array, one sample per array element. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the upper left pixel location. - * @param y The Y coordinate of the upper left pixel location. - * @param w The width of the pixel rectangle. - * @param h The height of the pixel rectangle. - * @param iArray If non-null, returns the samples in this array. - * @param data The DataBuffer containing the image data. - * @return all samples for the specified region of pixels. - * @see #setPixels(int, int, int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or {@code w} or {@code h} is negative + * or if {@code iArray} is too small to hold the output. */ + @Override public int[] getPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); @@ -493,17 +485,10 @@ public class SinglePixelPackedSampleModel extends SampleModel } /** - * Returns as int the sample in a specified band for the pixel - * located at (x,y). - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the pixel location. - * @param y The Y coordinate of the pixel location. - * @param b The band to return. - * @param data The DataBuffer containing the image data. - * @return the sample in a specified band for the specified - * pixel. - * @see #setSample(int, int, int, int, DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public int getSample(int x, int y, int b, DataBuffer data) { // Bounds check for 'b' will be performed automatically @@ -516,25 +501,16 @@ public class SinglePixelPackedSampleModel extends SampleModel } /** - * Returns the samples for a specified band for the specified rectangle - * of pixels in an int array, one sample per array element. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the upper left pixel location. - * @param y The Y coordinate of the upper left pixel location. - * @param w The width of the pixel rectangle. - * @param h The height of the pixel rectangle. - * @param b The band to return. - * @param iArray If non-null, returns the samples in this array. - * @param data The DataBuffer containing the image data. - * @return the samples for the specified band for the specified - * region of pixels. - * @see #setSamples(int, int, int, int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * band index are not in bounds, or {@code w} or {@code h} is negative, + * or if {@code iArray} is too small to hold the output. */ public int[] getSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data) { // Bounds check for 'b' will be performed automatically - if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + if ((x < 0) || (y < 0) || (w < 0) || (h < 0) || (x + w > width) || (y + h > height)) { throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); } @@ -592,6 +568,9 @@ public class SinglePixelPackedSampleModel extends SampleModel * @param obj A primitive array containing pixel data. * @param data The DataBuffer containing the image data. * @see #getDataElements(int, int, Object, DataBuffer) + * @throws NullPointerException if {@code obj} or {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if obj is too small to hold the input. */ public void setDataElements(int x, int y, Object obj, DataBuffer data) { if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) { @@ -624,14 +603,10 @@ public class SinglePixelPackedSampleModel extends SampleModel } /** - * Sets a pixel in the DataBuffer using an int array of samples for input. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the pixel location. - * @param y The Y coordinate of the pixel location. - * @param iArray The input samples in an int array. - * @param data The DataBuffer containing the image data. - * @see #getPixel(int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code iArray} or {code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or if {@code iArray} is too small to hold the input. */ public void setPixel(int x, int y, int[] iArray, @@ -650,25 +625,19 @@ public class SinglePixelPackedSampleModel extends SampleModel } /** - * Sets all samples for a rectangle of pixels from an int array containing - * one sample per array element. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the upper left pixel location. - * @param y The Y coordinate of the upper left pixel location. - * @param w The width of the pixel rectangle. - * @param h The height of the pixel rectangle. - * @param iArray The input samples in an int array. - * @param data The DataBuffer containing the image data. - * @see #getPixels(int, int, int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code iArray} or {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates are + * not in bounds, or {@code w} or {@code h} is negative + * or if {@code iArray} is too small to hold the input. */ public void setPixels(int x, int y, int w, int h, int[] iArray, DataBuffer data) { int x1 = x + w; int y1 = y + h; - if (x < 0 || x >= width || w > width || x1 < 0 || x1 > width || - y < 0 || y >= height || h > height || y1 < 0 || y1 > height) + if (x < 0 || w < 0 || x >= width || w > width || x1 < 0 || x1 > width || + y < 0 || h < 0 || y >= height || h > height || y1 < 0 || y1 > height) { throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); @@ -692,17 +661,11 @@ public class SinglePixelPackedSampleModel extends SampleModel } } - /** - * Sets a sample in the specified band for the pixel located at (x,y) - * in the DataBuffer using an int for input. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the pixel location. - * @param y The Y coordinate of the pixel location. - * @param b The band to set. - * @param s The input sample as an int. - * @param data The DataBuffer containing the image data. - * @see #getSample(int, int, int, DataBuffer) + /* + * {inheritDoc} + * @throws NullPointerException if {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * the band index are not in bounds. */ public void setSample(int x, int y, int b, int s, DataBuffer data) { @@ -718,23 +681,16 @@ public class SinglePixelPackedSampleModel extends SampleModel } /** - * Sets the samples in the specified band for the specified rectangle - * of pixels from an int array containing one sample per array element. - * ArrayIndexOutOfBoundsException may be thrown if the coordinates are - * not in bounds. - * @param x The X coordinate of the upper left pixel location. - * @param y The Y coordinate of the upper left pixel location. - * @param w The width of the pixel rectangle. - * @param h The height of the pixel rectangle. - * @param b The band to set. - * @param iArray The input samples in an int array. - * @param data The DataBuffer containing the image data. - * @see #getSamples(int, int, int, int, int, int[], DataBuffer) + * {@inheritDoc} + * @throws NullPointerException if {@code iArray} or {@code data} is {@code null}. + * @throws ArrayIndexOutOfBoundsException if the coordinates or + * band index are not in bounds, or {@code w} or {@code h} is negative, + * or if {@code iArray} is too small to hold the input. */ public void setSamples(int x, int y, int w, int h, int b, int[] iArray, DataBuffer data) { // Bounds check for 'b' will be performed automatically - if ((x < 0) || (y < 0) || (x + w > width) || (y + h > height)) { + if ((x < 0) || (y < 0) || (w < 0) || (h < 0) || (x + w > width) || (y + h > height)) { throw new ArrayIndexOutOfBoundsException ("Coordinate out of bounds!"); } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java b/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java index 4b59a777de2..9a7ccd8f3a1 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java @@ -31,7 +31,6 @@ import java.io.UncheckedIOException; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.net.ProtocolException; -import java.net.URI; import java.net.http.HttpResponse.BodyHandler; import java.net.http.HttpResponse.ResponseInfo; import java.nio.ByteBuffer; @@ -973,36 +972,6 @@ class Stream extends ExchangeImpl { return headers; } - private static HttpHeaders createPseudoHeaders(HttpRequest request) { - HttpHeadersBuilder hdrs = new HttpHeadersBuilder(); - String method = request.method(); - hdrs.setHeader(":method", method); - URI uri = request.uri(); - hdrs.setHeader(":scheme", uri.getScheme()); - String host = uri.getHost(); - int port = uri.getPort(); - assert host != null; - if (port != -1) { - hdrs.setHeader(":authority", host + ":" + port); - } else { - hdrs.setHeader(":authority", host); - } - String query = uri.getRawQuery(); - String path = uri.getRawPath(); - if (path == null || path.isEmpty()) { - if (method.equalsIgnoreCase("OPTIONS")) { - path = "*"; - } else { - path = "/"; - } - } - if (query != null) { - path += "?" + query; - } - hdrs.setHeader(":path", Utils.encode(path)); - return hdrs.build(); - } - HttpHeaders getRequestPseudoHeaders() { return requestPseudoHeaders; } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/packets/QuicPacketEncoder.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/packets/QuicPacketEncoder.java index 890c1a63a35..db519dc7557 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/packets/QuicPacketEncoder.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/packets/QuicPacketEncoder.java @@ -577,7 +577,6 @@ public class QuicPacketEncoder { final byte[] encodedPacketNumber; final List frames; final int payloadSize; - private int tagSize; OutgoingHandshakePacket(QuicConnectionId sourceId, QuicConnectionId destinationId, @@ -590,7 +589,6 @@ public class QuicPacketEncoder { this.encodedPacketNumber = encodedPacketNumber; this.frames = List.copyOf(frames); this.payloadSize = frames.stream().mapToInt(QuicFrame::size).reduce(0, Math::addExact); - this.tagSize = tagSize; this.length = computeLength(payloadSize, encodedPacketNumber.length, tagSize); this.size = computeSize(length); } @@ -676,7 +674,6 @@ public class QuicPacketEncoder { final int size; final byte[] encodedPacketNumber; final List frames; - private int tagSize; final int payloadSize; OutgoingZeroRttPacket(QuicConnectionId sourceId, @@ -689,7 +686,6 @@ public class QuicPacketEncoder { this.packetNumber = packetNumber; this.encodedPacketNumber = encodedPacketNumber; this.frames = List.copyOf(frames); - this.tagSize = tagSize; this.payloadSize = this.frames.stream().mapToInt(QuicFrame::size) .reduce(0, Math::addExact); this.length = computeLength(payloadSize, encodedPacketNumber.length, tagSize); @@ -778,7 +774,6 @@ public class QuicPacketEncoder { final int size; final byte[] encodedPacketNumber; final List frames; - private int tagSize; final int payloadSize; OutgoingOneRttPacket(QuicConnectionId destinationId, @@ -789,7 +784,6 @@ public class QuicPacketEncoder { this.packetNumber = packetNumber; this.encodedPacketNumber = encodedPacketNumber; this.frames = List.copyOf(frames); - this.tagSize = tagSize; this.payloadSize = this.frames.stream().mapToInt(QuicFrame::size) .reduce(0, Math::addExact); this.size = computeSize(payloadSize, encodedPacketNumber.length, tagSize); @@ -853,7 +847,6 @@ public class QuicPacketEncoder { final int size; final byte[] encodedPacketNumber; final List frames; - private int tagSize; final int payloadSize; private record InitialPacketVariableComponents(int length, byte[] token, QuicConnectionId sourceId, @@ -873,7 +866,6 @@ public class QuicPacketEncoder { this.packetNumber = packetNumber; this.encodedPacketNumber = encodedPacketNumber; this.frames = List.copyOf(frames); - this.tagSize = tagSize; this.payloadSize = this.frames.stream() .mapToInt(QuicFrame::size) .reduce(0, Math::addExact); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java index 4d679403ee7..af7d79d4195 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java @@ -226,6 +226,8 @@ public final class SourceLauncher { Object instance = null; + // Similar to sun.launcher.LauncherHelper#checkAndLoadMain, including + // checks performed in LauncherHelper#validateMainMethod if (!isStatic) { if (Modifier.isAbstract(mainClass.getModifiers())) { throw new Fault(Errors.CantInstantiate(mainClassName)); @@ -238,6 +240,10 @@ public final class SourceLauncher { throw new Fault(Errors.CantFindConstructor(mainClassName)); } + if (Modifier.isPrivate(constructor.getModifiers())) { + throw new Fault(Errors.CantUsePrivateConstructor(mainClassName)); + } + try { constructor.setAccessible(true); instance = constructor.newInstance(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java index c14767a7a8c..664ebc4c9c6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java @@ -33,6 +33,8 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.text.Collator; +import java.util.Collection; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; @@ -45,7 +47,6 @@ import java.util.ServiceLoader; import java.util.Set; import java.util.StringJoiner; import java.util.TreeMap; -import java.util.TreeSet; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -323,12 +324,13 @@ public enum Option { @Override protected void help(Log log) { - StringJoiner sj = new StringJoiner(", "); + List releases = new ArrayList<>(); for(Source source : Source.values()) { if (source.isSupported()) - sj.add(source.name); + releases.add(source.name); } - super.help(log, log.localize(PrefixKind.JAVAC, descrKey, sj.toString())); + String formatted = formatAbbreviatedList(releases); + super.help(log, log.localize(PrefixKind.JAVAC, descrKey, formatted)); } }, @@ -344,12 +346,13 @@ public enum Option { @Override protected void help(Log log) { - StringJoiner sj = new StringJoiner(", "); + List releases = new ArrayList<>(); for(Target target : Target.values()) { if (target.isSupported()) - sj.add(target.name); + releases.add(target.name); } - super.help(log, log.localize(PrefixKind.JAVAC, descrKey, sj.toString())); + String formatted = formatAbbreviatedList(releases); + super.help(log, log.localize(PrefixKind.JAVAC, descrKey, formatted)); } }, @@ -364,15 +367,8 @@ public enum Option { false)) .collect(Collectors.toCollection(LinkedHashSet :: new)); - StringBuilder targets = new StringBuilder(); - String delim = ""; - for (String platform : platforms) { - targets.append(delim); - targets.append(platform); - delim = ", "; - } - - super.help(log, log.localize(PrefixKind.JAVAC, descrKey, targets.toString())); + String formatted = formatAbbreviatedList(platforms); + super.help(log, log.localize(PrefixKind.JAVAC, descrKey, formatted)); } }, @@ -1369,6 +1365,41 @@ public enum Option { log.printRawLines(WriterKind.STDOUT, LARGE_INDENT + descr.replace("\n", "\n" + LARGE_INDENT)); } + /** + * Formats a collection of values as an abbreviated, comma separated list + * for use in javac help output. + * + * This helper assumes that the supported values form a dense sequence + * between the fourth and the (n - 3)rd entries. + * That matches the current policy for these values but is not + * guaranteed, and should be reconsidered if the structure of the values changes. + * + * @param values the values to format + * @return a comma separated representation of the values + */ + private static String formatAbbreviatedList(Collection values) { + List list = (values instanceof List) + ? (List) values + : new ArrayList<>(values); + + int size = list.size(); + if (size == 0) { + return ""; + } + if (size <= 6) { + return String.join(", ", list); + } + StringJoiner sj = new StringJoiner(", "); + for (int i = 0; i < 3; i++) { + sj.add(list.get(i)); + } + sj.add("..."); + for (int i = size - 3; i < size; i++) { + sj.add(list.get(i)); + } + return sj.toString(); + } + /** * Composes the initial synopsis of one of the forms for this option. * @param name the name of this form of the option diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties index d5ee9469d22..7824772b1f3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties @@ -100,7 +100,8 @@ javac.opt.arg.Werror=\ (,)* javac.opt.Werror.custom=\ Specify lint categories for which warnings should terminate compilation,\n\ - separated by comma. Precede a key by ''-'' to exclude the specified category.\n\ + separated by comma.\n\ + Precede a key by ''-'' to exclude the specified category.\n\ Use --help-lint to see the supported keys. javac.opt.A=\ Options to pass to annotation processors @@ -358,7 +359,8 @@ javac.opt.prefer=\ are found for an implicitly compiled class # L10N: do not localize: ''preview'' javac.opt.preview=\ - Enable preview language features. Also disables the ''preview'' lint category.\n\ + Enable preview language features.\n\ + Also disables the ''preview'' lint category.\n\ To be used in conjunction with either -source or --release. javac.opt.AT=\ Read options and filenames from file diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/launcher.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/launcher.properties index 122e5dca80d..36d50afad0f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/launcher.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/launcher.properties @@ -124,6 +124,12 @@ launcher.err.cant.access.main.method=\ launcher.err.cant.find.constructor=\ can''t find no argument constructor in class: {0} +# 0: string +launcher.err.cant.use.private.constructor=\ + no non-private zero argument constructor found in class {0}\n\ + remove private from existing constructor or define as:\n\ +\ public {0}() + # 0: string launcher.err.cant.access.constructor=\ can''t access no argument constructor in class: {0} diff --git a/src/jdk.compiler/share/data/symbols/README b/src/jdk.compiler/share/data/symbols/README index 4c9f38f77b0..50f68bbe2f2 100644 --- a/src/jdk.compiler/share/data/symbols/README +++ b/src/jdk.compiler/share/data/symbols/README @@ -1,3 +1,3 @@ This directory contains history data for -release. -Please see $JDK_TOP_DIR/make/scripts/generate-symbol-data.sh for main usage. +Please see $JDK_TOP_DIR/bin/generate-symbol-data.sh for main usage. diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java index 10f6881d010..efbb613994d 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,9 @@ package sun.jvm.hotspot.debugger.bsd; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.stream.IntStream; import sun.jvm.hotspot.debugger.Address; import sun.jvm.hotspot.debugger.DebuggerBase; @@ -75,10 +77,11 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger { // CDebugger support private BsdCDebugger cdbg; - // threadList and loadObjectList are filled by attach0 method - private List threadList; + // loadObjectList is filled by attach0 method private List loadObjectList; + private List javaThreadList; + // called by native method lookupByAddress0 private ClosestSymbol createClosestSymbol(String name, long offset) { return new ClosestSymbol(name, offset); @@ -241,10 +244,21 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger { } } + private void fillJavaThreadList() { + // TODO: thread list on macOS is now supported for corefile only. + if (!isCore && isDarwin) { + javaThreadList = Collections.emptyList(); + } else { + Threads threads = VM.getVM().getThreads(); + javaThreadList = IntStream.range(0, threads.getNumberOfThreads()) + .mapToObj(threads::getJavaThreadAt) + .toList(); + } + } + /** From the Debugger interface via JVMDebugger */ public synchronized void attach(int processID) throws DebuggerException { checkAttached(); - threadList = new ArrayList<>(); loadObjectList = new ArrayList<>(); class AttachTask implements WorkerThreadTask { int pid; @@ -264,7 +278,6 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger { /** From the Debugger interface via JVMDebugger */ public synchronized void attach(String execName, String coreName) { checkAttached(); - threadList = new ArrayList<>(); loadObjectList = new ArrayList<>(); attach0(execName, coreName); attached = true; @@ -278,7 +291,7 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger { return false; } - threadList = null; + javaThreadList = null; loadObjectList = null; if (isCore) { @@ -492,7 +505,12 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger { /** From the BsdCDebugger interface */ public List getThreadList() { requireAttach(); - return threadList; + if (javaThreadList == null) { + fillJavaThreadList(); + } + return javaThreadList.stream() + .map(JavaThread::getThreadProxy) + .toList(); } /** From the BsdCDebugger interface */ @@ -561,21 +579,19 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger { /** this functions used for core file reading and called from native attach0, it returns an array of long integers as [thread_id, stack_start, stack_end, thread_id, stack_start, stack_end, ....] for - all java threads recorded in Threads. Also adds the ThreadProxy to threadList */ + all java threads recorded in Threads. */ public long[] getJavaThreadsInfo() { requireAttach(); - Threads threads = VM.getVM().getThreads(); - int len = threads.getNumberOfThreads(); - long[] result = new long[len * 3]; // triple + if (javaThreadList == null) { + fillJavaThreadList(); + } + long[] result = new long[javaThreadList.size() * 3]; // triple long beg, end; int i = 0; - for (int k = 0; k < threads.getNumberOfThreads(); k++) { - JavaThread t = threads.getJavaThreadAt(k); + for (var t : javaThreadList) { end = t.getStackBaseValue(); beg = end - t.getStackSize(); - BsdThread bsdt = (BsdThread)t.getThreadProxy(); - long uid = bsdt.getUniqueThreadId(); - if (threadList != null) threadList.add(bsdt); + long uid = ((BsdThread)t.getThreadProxy()).getUniqueThreadId(); result[i] = uid; result[i + 1] = beg; result[i + 2] = end; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java index 47f41fe1383..bc366ef02b5 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/cdbg/CFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package sun.jvm.hotspot.debugger.cdbg; import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.utilities.*; /** Models a "C" programming language frame on the stack -- really just an arbitrary frame with hooks to access C and C++ debug @@ -37,7 +39,7 @@ public interface CFrame { public CFrame sender(ThreadProxy th); /** Find sender frame with given FP and PC */ - public default CFrame sender(ThreadProxy th, Address fp, Address pc) { + public default CFrame sender(ThreadProxy th, Address sp, Address fp, Address pc) { return sender(th); } @@ -70,4 +72,9 @@ public interface CFrame { /** Visit all local variables in this frame if debug information is available. Automatically descends into compound types and arrays. */ public void iterateLocals(ObjectVisitor v); + + /** Get Frame instance assosiated with this CFrame. */ + public default Frame toFrame() { + throw new UnsupportedPlatformException(); + } } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java index 894e31949b2..e3543503216 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.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. * Copyright (c) 2015, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -81,9 +81,11 @@ class LinuxCDebugger implements CDebugger { String cpu = dbg.getCPU(); if (cpu.equals("amd64")) { AMD64ThreadContext context = (AMD64ThreadContext) thread.getContext(); + Address sp = context.getRegisterAsAddress(AMD64ThreadContext.RSP); + if (sp == null) return null; Address pc = context.getRegisterAsAddress(AMD64ThreadContext.RIP); if (pc == null) return null; - return LinuxAMD64CFrame.getTopFrame(dbg, pc, context); + return LinuxAMD64CFrame.getTopFrame(dbg, sp, pc, context); } else if (cpu.equals("ppc64")) { PPC64ThreadContext context = (PPC64ThreadContext) thread.getContext(); Address sp = context.getRegisterAsAddress(PPC64ThreadContext.SP); @@ -93,18 +95,22 @@ class LinuxCDebugger implements CDebugger { return new LinuxPPC64CFrame(dbg, sp, pc, LinuxDebuggerLocal.getAddressSize()); } else if (cpu.equals("aarch64")) { AARCH64ThreadContext context = (AARCH64ThreadContext) thread.getContext(); + Address sp = context.getRegisterAsAddress(AARCH64ThreadContext.SP); + if (sp == null) return null; Address fp = context.getRegisterAsAddress(AARCH64ThreadContext.FP); if (fp == null) return null; Address pc = context.getRegisterAsAddress(AARCH64ThreadContext.PC); if (pc == null) return null; - return new LinuxAARCH64CFrame(dbg, fp, pc); + return new LinuxAARCH64CFrame(dbg, sp, fp, pc); } else if (cpu.equals("riscv64")) { RISCV64ThreadContext context = (RISCV64ThreadContext) thread.getContext(); + Address sp = context.getRegisterAsAddress(RISCV64ThreadContext.SP); + if (sp == null) return null; Address fp = context.getRegisterAsAddress(RISCV64ThreadContext.FP); if (fp == null) return null; Address pc = context.getRegisterAsAddress(RISCV64ThreadContext.PC); if (pc == null) return null; - return new LinuxRISCV64CFrame(dbg, fp, pc); + return new LinuxRISCV64CFrame(dbg, sp, fp, pc); } else { // Runtime exception thrown by LinuxThreadContextFactory if unknown cpu ThreadContext context = thread.getContext(); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java index 93edb43eb82..5f76e6308e9 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/aarch64/LinuxAARCH64CFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,10 +30,14 @@ import sun.jvm.hotspot.debugger.aarch64.*; import sun.jvm.hotspot.debugger.linux.*; import sun.jvm.hotspot.debugger.cdbg.*; import sun.jvm.hotspot.debugger.cdbg.basic.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.aarch64.*; public final class LinuxAARCH64CFrame extends BasicCFrame { - public LinuxAARCH64CFrame(LinuxDebugger dbg, Address fp, Address pc) { + public LinuxAARCH64CFrame(LinuxDebugger dbg, Address sp, Address fp, Address pc) { super(dbg.getCDebugger()); + this.sp = sp; this.fp = fp; this.pc = pc; this.dbg = dbg; @@ -55,11 +59,11 @@ public final class LinuxAARCH64CFrame extends BasicCFrame { @Override public CFrame sender(ThreadProxy thread) { - return sender(thread, null, null); + return sender(thread, null, null, null); } @Override - public CFrame sender(ThreadProxy thread, Address nextFP, Address nextPC) { + public CFrame sender(ThreadProxy thread, Address nextSP, Address nextFP, Address nextPC) { // Check fp // Skip if both nextFP and nextPC are given - do not need to load from fp. if (nextFP == null && nextPC == null) { @@ -86,7 +90,32 @@ public final class LinuxAARCH64CFrame extends BasicCFrame { if (nextPC == null) { return null; } - return new LinuxAARCH64CFrame(dbg, nextFP, nextPC); + + if (nextSP == null) { + CodeCache cc = VM.getVM().getCodeCache(); + CodeBlob currentBlob = cc.findBlobUnsafe(pc()); + + // This case is different from HotSpot. See JDK-8371194 for details. + if (currentBlob != null && (currentBlob.isContinuationStub() || currentBlob.isNativeMethod())) { + // Use FP since it should always be valid for these cases. + // TODO: These should be walked as Frames not CFrames. + nextSP = fp.addOffsetTo(2 * ADDRESS_SIZE); + } else { + CodeBlob codeBlob = cc.findBlobUnsafe(nextPC); + boolean useCodeBlob = codeBlob != null && codeBlob.getFrameSize() > 0; + nextSP = useCodeBlob ? nextFP.addOffsetTo((2 * ADDRESS_SIZE) - codeBlob.getFrameSize()) : nextFP; + } + } + if (nextSP == null) { + return null; + } + + return new LinuxAARCH64CFrame(dbg, nextSP, nextFP, nextPC); + } + + @Override + public Frame toFrame() { + return new AARCH64Frame(sp, fp, pc); } // package/class internals only diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java index 9fa0fc20e99..612203634f3 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java @@ -29,10 +29,12 @@ import sun.jvm.hotspot.debugger.amd64.*; import sun.jvm.hotspot.debugger.linux.*; import sun.jvm.hotspot.debugger.cdbg.*; import sun.jvm.hotspot.debugger.cdbg.basic.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.amd64.*; public final class LinuxAMD64CFrame extends BasicCFrame { - public static LinuxAMD64CFrame getTopFrame(LinuxDebugger dbg, Address rip, ThreadContext context) { + public static LinuxAMD64CFrame getTopFrame(LinuxDebugger dbg, Address rsp, Address rip, ThreadContext context) { Address libptr = dbg.findLibPtrByAddress(rip); Address cfa = context.getRegisterAsAddress(AMD64ThreadContext.RBP); DwarfParser dwarf = null; @@ -45,7 +47,7 @@ public final class LinuxAMD64CFrame extends BasicCFrame { // DWARF processing should succeed when the frame is native // but it might fail if Common Information Entry (CIE) has language // personality routine and/or Language Specific Data Area (LSDA). - return new LinuxAMD64CFrame(dbg, cfa, rip, dwarf, true); + return new LinuxAMD64CFrame(dbg, rsp, cfa, rip, dwarf, true); } cfa = context.getRegisterAsAddress(dwarf.getCFARegister()) @@ -53,19 +55,20 @@ public final class LinuxAMD64CFrame extends BasicCFrame { } return (cfa == null) ? null - : new LinuxAMD64CFrame(dbg, cfa, rip, dwarf); + : new LinuxAMD64CFrame(dbg, rsp, cfa, rip, dwarf); } - private LinuxAMD64CFrame(LinuxDebugger dbg, Address cfa, Address rip, DwarfParser dwarf) { - this(dbg, cfa, rip, dwarf, false); + private LinuxAMD64CFrame(LinuxDebugger dbg, Address rsp, Address cfa, Address rip, DwarfParser dwarf) { + this(dbg, rsp, cfa, rip, dwarf, false); } - private LinuxAMD64CFrame(LinuxDebugger dbg, Address cfa, Address rip, DwarfParser dwarf, boolean finalFrame) { - this(dbg, cfa, rip, dwarf, finalFrame, false); + private LinuxAMD64CFrame(LinuxDebugger dbg, Address rsp, Address cfa, Address rip, DwarfParser dwarf, boolean finalFrame) { + this(dbg, rsp, cfa, rip, dwarf, finalFrame, false); } - private LinuxAMD64CFrame(LinuxDebugger dbg, Address cfa, Address rip, DwarfParser dwarf, boolean finalFrame, boolean use1ByteBeforeToLookup) { + private LinuxAMD64CFrame(LinuxDebugger dbg, Address rsp, Address cfa, Address rip, DwarfParser dwarf, boolean finalFrame, boolean use1ByteBeforeToLookup) { super(dbg.getCDebugger()); + this.rsp = rsp; this.cfa = cfa; this.rip = rip; this.dbg = dbg; @@ -107,7 +110,14 @@ public final class LinuxAMD64CFrame extends BasicCFrame { (!isNative || (isNative && nextCFA.greaterThan(cfa))); } - private Address getNextCFA(DwarfParser nextDwarf, ThreadContext context, Address senderFP) { + private Address getNextRSP() { + // next RSP should be previous slot of return address. + var bp = dwarf == null ? cfa.addOffsetTo(ADDRESS_SIZE) // top of BP points callser BP + : cfa.addOffsetTo(dwarf.getReturnAddressOffsetFromCFA()); + return bp.addOffsetTo(ADDRESS_SIZE); + } + + private Address getNextCFA(DwarfParser nextDwarf, ThreadContext context, Address senderFP, Address senderPC) { Address nextCFA; boolean isNative = false; @@ -115,13 +125,17 @@ public final class LinuxAMD64CFrame extends BasicCFrame { senderFP = cfa.getAddressAt(0); // RBP by default } - if (nextDwarf == null) { // Next frame is Java + if (VM.getVM().getCodeCache().contains(senderPC)) { // Next frame is Java nextCFA = (dwarf == null) ? senderFP // Current frame is Java : cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA()); // Current frame is Native } else { // Next frame is Native - if (dwarf == null) { // Current frame is Java + if (VM.getVM().getCodeCache().contains(pc())) { // Current frame is Java nextCFA = senderFP.addOffsetTo(-nextDwarf.getBasePointerOffsetFromCFA()); } else { // Current frame is Native + if (nextDwarf == null) { // maybe runtime entrypoint (_start()) + throw new DebuggerException("nextDwarf is null even though native call"); + } + isNative = true; int nextCFAReg = nextDwarf.getCFARegister(); if (nextCFAReg == AMD64ThreadContext.RBP) { @@ -130,10 +144,7 @@ public final class LinuxAMD64CFrame extends BasicCFrame { Address nextRBP = rbp.getAddressAt(0); nextCFA = nextRBP.addOffsetTo(-nextDwarf.getBasePointerOffsetFromCFA()); } else if (nextCFAReg == AMD64ThreadContext.RSP) { - // next RSP should be previous slot of return address. - Address nextRSP = cfa.addOffsetTo(dwarf.getReturnAddressOffsetFromCFA()) - .addOffsetTo(ADDRESS_SIZE); - nextCFA = nextRSP.addOffsetTo(nextDwarf.getCFAOffset()); + nextCFA = getNextRSP().addOffsetTo(nextDwarf.getCFAOffset()); } else { throw new DebuggerException("Unsupported CFA register: " + nextCFAReg); } @@ -153,17 +164,22 @@ public final class LinuxAMD64CFrame extends BasicCFrame { @Override public CFrame sender(ThreadProxy th) { - return sender(th, null, null); + return sender(th, null, null, null); } @Override - public CFrame sender(ThreadProxy th, Address fp, Address pc) { + public CFrame sender(ThreadProxy th, Address sp, Address fp, Address pc) { if (finalFrame) { return null; } ThreadContext context = th.getContext(); + Address nextRSP = sp != null ? sp : getNextRSP(); + if (nextRSP == null) { + return null; + } + Address nextPC = pc != null ? pc : getNextPC(dwarf != null); if (nextPC == null) { return null; @@ -183,13 +199,16 @@ public final class LinuxAMD64CFrame extends BasicCFrame { // DWARF processing should succeed when the frame is native // but it might fail if Common Information Entry (CIE) has language // personality routine and/or Language Specific Data Area (LSDA). - return new LinuxAMD64CFrame(dbg, null, nextPC, nextDwarf, true); + return null; } } - Address nextCFA = getNextCFA(nextDwarf, context, fp); - return nextCFA == null ? null - : new LinuxAMD64CFrame(dbg, nextCFA, nextPC, nextDwarf, false, fallback); + try { + Address nextCFA = getNextCFA(nextDwarf, context, fp, nextPC); + return new LinuxAMD64CFrame(dbg, nextRSP, nextCFA, nextPC, nextDwarf, false, fallback); + } catch (DebuggerException _) { + return null; + } } private DwarfParser createDwarfParser(Address pc) throws DebuggerException { @@ -210,8 +229,14 @@ public final class LinuxAMD64CFrame extends BasicCFrame { return nextDwarf; } + @Override + public Frame toFrame() { + return new AMD64Frame(rsp, cfa, rip); + } + // package/class internals only private static final int ADDRESS_SIZE = 8; + private Address rsp; private Address rip; private Address cfa; private LinuxDebugger dbg; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/ppc64/LinuxPPC64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/ppc64/LinuxPPC64CFrame.java index 424766fb1b4..c3724f14c2a 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/ppc64/LinuxPPC64CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/ppc64/LinuxPPC64CFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, 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 @@ -29,6 +29,8 @@ import sun.jvm.hotspot.debugger.ppc64.*; import sun.jvm.hotspot.debugger.linux.*; import sun.jvm.hotspot.debugger.cdbg.*; import sun.jvm.hotspot.debugger.cdbg.basic.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.ppc64.*; public final class LinuxPPC64CFrame extends BasicCFrame { // package/class internals only @@ -71,6 +73,12 @@ public final class LinuxPPC64CFrame extends BasicCFrame { return new LinuxPPC64CFrame(dbg, nextSP, nextPC, address_size); } + @Override + public Frame toFrame() { + // 2nd arg (raw_fp) would be derived from sp in c'tor of PPC64Frame. + return new PPC64Frame(sp, null, pc); + } + public static int PPC64_STACK_BIAS = 0; private static int address_size; private Address pc; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/riscv64/LinuxRISCV64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/riscv64/LinuxRISCV64CFrame.java index 76f45891c47..65563be59ec 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/riscv64/LinuxRISCV64CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/riscv64/LinuxRISCV64CFrame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, Red Hat Inc. * Copyright (c) 2021, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -31,13 +31,17 @@ import sun.jvm.hotspot.debugger.riscv64.*; import sun.jvm.hotspot.debugger.linux.*; import sun.jvm.hotspot.debugger.cdbg.*; import sun.jvm.hotspot.debugger.cdbg.basic.*; +import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.riscv64.*; public final class LinuxRISCV64CFrame extends BasicCFrame { private static final int C_FRAME_LINK_OFFSET = -2; private static final int C_FRAME_RETURN_ADDR_OFFSET = -1; + private static final int C_FRAME_SENDER_SP_OFFSET = 0; - public LinuxRISCV64CFrame(LinuxDebugger dbg, Address fp, Address pc) { + public LinuxRISCV64CFrame(LinuxDebugger dbg, Address sp, Address fp, Address pc) { super(dbg.getCDebugger()); + this.sp = sp; this.fp = fp; this.pc = pc; this.dbg = dbg; @@ -59,11 +63,11 @@ public final class LinuxRISCV64CFrame extends BasicCFrame { @Override public CFrame sender(ThreadProxy thread) { - return sender(thread, null, null); + return sender(thread, null, null, null); } @Override - public CFrame sender(ThreadProxy thread, Address nextFP, Address nextPC) { + public CFrame sender(ThreadProxy thread, Address nextSP, Address nextFP, Address nextPC) { // Check fp // Skip if both nextFP and nextPC are given - do not need to load from fp. if (nextFP == null && nextPC == null) { @@ -77,6 +81,13 @@ public final class LinuxRISCV64CFrame extends BasicCFrame { } } + if (nextSP == null) { + nextSP = fp.getAddressAt(C_FRAME_SENDER_SP_OFFSET * ADDRESS_SIZE); + } + if (nextSP == null) { + return null; + } + if (nextFP == null) { nextFP = fp.getAddressAt(C_FRAME_LINK_OFFSET * ADDRESS_SIZE); } @@ -91,7 +102,12 @@ public final class LinuxRISCV64CFrame extends BasicCFrame { return null; } - return new LinuxRISCV64CFrame(dbg, nextFP, nextPC); + return new LinuxRISCV64CFrame(dbg, nextSP, nextFP, nextPC); + } + + @Override + public Frame toFrame() { + return new RISCV64Frame(sp, fp, pc); } // package/class internals only diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java index 5ae4cb703b3..7233d508cbc 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java @@ -272,9 +272,7 @@ public class AARCH64Frame extends Frame { if (cb != null) { if (cb.isUpcallStub()) { return senderForUpcallStub(map, (UpcallStub)cb); - } else if (cb.isContinuationStub()) { - return senderForContinuationStub(map, cb); - } else { + } else if (cb.getFrameSize() > 0) { return senderForCompiledFrame(map, cb); } } @@ -389,7 +387,11 @@ public class AARCH64Frame extends Frame { if (Assert.ASSERTS_ENABLED) { Assert.that(cb.getFrameSize() > 0, "must have non-zero frame size"); } - Address senderSP = getUnextendedSP().addOffsetTo(cb.getFrameSize()); + + // TODO: senderSP should consider not only PreserveFramePointer but also _sp_is_trusted. + Address senderSP = !VM.getVM().getCommandLineBooleanFlag("PreserveFramePointer") + ? getUnextendedSP().addOffsetTo(cb.getFrameSize()) + : getSenderSP(); // The return_address is always the word on the stack Address senderPC = stripPAC(senderSP.getAddressAt(-1 * VM.getVM().getAddressSize())); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java index 360f62a253d..fa9d50160e1 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java @@ -272,9 +272,7 @@ public class AMD64Frame extends Frame { if (cb != null) { if (cb.isUpcallStub()) { return senderForUpcallStub(map, (UpcallStub)cb); - } else if (cb.isContinuationStub()) { - return senderForContinuationStub(map, cb); - } else { + } else if (cb.getFrameSize() > 0) { return senderForCompiledFrame(map, cb); } } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java index 9865fbbe0f2..29d2954efdc 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java @@ -70,7 +70,6 @@ public class PStack extends Tool { if (cdbg != null) { ConcurrentLocksPrinter concLocksPrinter = null; // compute and cache java Vframes. - initJFrameCache(); if (concurrentLocks) { concLocksPrinter = new ConcurrentLocksPrinter(out); } @@ -96,6 +95,7 @@ public class PStack extends Tool { return; } final boolean cdbgCanDemangle = cdbg.canDemangle(); + Map proxyToThread = createProxyToThread();; String fillerForAddress = " ".repeat(2 + 2 * (int) VM.getVM().getAddressSize()) + "\t"; for (Iterator itr = l.iterator() ; itr.hasNext();) { ThreadProxy th = itr.next(); @@ -109,6 +109,7 @@ public class PStack extends Tool { jthread.printThreadInfoOn(out); } while (f != null) { + Address senderSP = null; Address senderFP = null; Address senderPC = null; ClosestSymbol sym = f.closestSymbolToPC(); @@ -131,7 +132,7 @@ public class PStack extends Tool { // check interpreter frame Interpreter interp = VM.getVM().getInterpreter(); if (interp.contains(pc)) { - nameInfo = getJavaNames(th, f.localVariableBase()); + nameInfo = getJavaNames(jthread, f); // print codelet name if we can't determine method if (nameInfo == null || nameInfo.names() == null || nameInfo.names().length == 0) { out.print(" "); @@ -156,7 +157,7 @@ public class PStack extends Tool { } out.println(" (Native method)"); } else { - nameInfo = getJavaNames(th, f.localVariableBase()); + nameInfo = getJavaNames(jthread, f); // just print compiled code, if can't determine method if (nameInfo == null || nameInfo.names() == null || nameInfo.names().length == 0) { out.println(""); @@ -164,6 +165,12 @@ public class PStack extends Tool { } } else { out.println("<" + cb.getName() + ">"); + if (cb.getFrameSize() > 0) { + Frame senderFrame = f.toFrame().sender(jthread.newRegisterMap(true)); + senderSP = senderFrame.getSP(); + senderFP = senderFrame.getFP(); + senderPC = senderFrame.getPC(); + } } } else { printUnknown(out); @@ -180,11 +187,12 @@ public class PStack extends Tool { out.println(nameInfo.names()[i]); } } + senderSP = nameInfo.senderSP(); senderFP = nameInfo.senderFP(); senderPC = nameInfo.senderPC(); } } - f = f.sender(th, senderFP, senderPC); + f = f.sender(th, senderSP, senderFP, senderPC); } } catch (Exception exp) { exp.printStackTrace(); @@ -212,95 +220,74 @@ public class PStack extends Tool { } // -- Internals only below this point - private Map jframeCache; - private Map proxyToThread; private PrintStream out; private boolean verbose; private boolean concurrentLocks; - private void initJFrameCache() { - // cache frames for subsequent reference - jframeCache = new HashMap<>(); - proxyToThread = new HashMap<>(); + private Map createProxyToThread() { + Map proxyToThread = new HashMap<>(); Threads threads = VM.getVM().getThreads(); for (int i = 0; i < threads.getNumberOfThreads(); i++) { - JavaThread cur = threads.getJavaThreadAt(i); - List tmp = new ArrayList<>(10); - try { - for (JavaVFrame vf = cur.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) { - tmp.add(vf); - } - } catch (Exception exp) { - // may be we may get frames for other threads, continue - // after printing stack trace. - exp.printStackTrace(); - } - JavaVFrame[] jvframes = tmp.toArray(new JavaVFrame[0]); - jframeCache.put(cur.getThreadProxy(), jvframes); - proxyToThread.put(cur.getThreadProxy(), cur); + JavaThread jthread = threads.getJavaThreadAt(i); + proxyToThread.put(jthread.getThreadProxy(), jthread); } + return proxyToThread; } private void printUnknown(PrintStream out) { out.println("\t????????"); } - private static record JavaNameInfo(String[] names, Address senderFP, Address senderPC) {}; - - private JavaNameInfo getJavaNames(ThreadProxy th, Address fp) { - if (fp == null) { - return null; - } - JavaVFrame[] jvframes = jframeCache.get(th); - if (jvframes == null) return null; // not a java thread + private static record JavaNameInfo(String[] names, Address senderSP, Address senderFP, Address senderPC) {}; + private JavaNameInfo getJavaNames(JavaThread jthread, CFrame f) { List names = new ArrayList<>(10); - JavaVFrame bottomJVFrame = null; - for (int fCount = 0; fCount < jvframes.length; fCount++) { - JavaVFrame vf = jvframes[fCount]; - Frame f = vf.getFrame(); - if (fp.equals(f.getFP())) { - bottomJVFrame = vf; - StringBuilder sb = new StringBuilder(); - Method method = vf.getMethod(); - // a special char to identify java frames in output - sb.append("* "); - sb.append(method.externalNameAndSignature()); - sb.append(" bci:").append(vf.getBCI()); - int lineNumber = method.getLineNumberFromBCI(vf.getBCI()); - if (lineNumber != -1) { - sb.append(" line:").append(lineNumber); - } - - if (verbose) { - sb.append(" Method*:").append(method.getAddress()); - } - - if (vf.isCompiledFrame()) { - sb.append(" (Compiled frame"); - if (vf.isDeoptimized()) { - sb.append(" [deoptimized]"); - } - } else if (vf.isInterpretedFrame()) { - sb.append(" (Interpreted frame"); - } - if (vf.mayBeImpreciseDbg()) { - sb.append("; information may be imprecise"); - } - sb.append(")"); - names.add(sb.toString()); - } - } - + Address senderSP = null; Address senderFP = null; Address senderPC = null; - if (bottomJVFrame != null) { - Frame senderFrame = bottomJVFrame.getFrame().sender((RegisterMap)bottomJVFrame.getRegisterMap().clone()); + VFrame vf = VFrame.newVFrame(f.toFrame(), jthread.newRegisterMap(true), jthread, true, true); + while (vf != null && vf.isJavaFrame()) { + StringBuilder sb = new StringBuilder(); + Method method = ((JavaVFrame)vf).getMethod(); + // a special char to identify java frames in output + sb.append("* "); + sb.append(method.externalNameAndSignature()); + sb.append(" bci:").append(((JavaVFrame)vf).getBCI()); + int lineNumber = method.getLineNumberFromBCI(((JavaVFrame)vf).getBCI()); + if (lineNumber != -1) { + sb.append(" line:").append(lineNumber); + } + + if (verbose) { + sb.append(" Method*:").append(method.getAddress()); + } + + if (vf.isCompiledFrame()) { + sb.append(" (Compiled frame"); + if (vf.isDeoptimized()) { + sb.append(" [deoptimized]"); + } + } else if (vf.isInterpretedFrame()) { + sb.append(" (Interpreted frame"); + } + if (vf.mayBeImpreciseDbg()) { + sb.append("; information may be imprecise"); + } + sb.append(")"); + names.add(sb.toString()); + + // Keep registers in sender Frame + Frame senderFrame = vf.getFrame() + .sender((RegisterMap)vf.getRegisterMap().clone()); + senderSP = senderFrame.getSP(); senderFP = senderFrame.getFP(); senderPC = senderFrame.getPC(); + + // Get sender VFrame for next stack walking + vf = vf.sender(true); } - return new JavaNameInfo(names.toArray(new String[0]), senderFP, senderPC); + return new JavaNameInfo(names.toArray(new String[0]), senderSP, senderFP, senderPC); } public void setVerbose(boolean verbose) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_en.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_en.properties new file mode 100644 index 00000000000..e1312e77aa4 --- /dev/null +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard_en.properties @@ -0,0 +1,28 @@ +# +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# Empty resource bundle to be used with English default bundle as parent. +# This is necessary to make English resources available on systems using +# one of the supported non-English locales as default locale. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_en.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_en.properties new file mode 100644 index 00000000000..e1312e77aa4 --- /dev/null +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets_en.properties @@ -0,0 +1,28 @@ +# +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# Empty resource bundle to be used with English default bundle as parent. +# This is necessary to make English resources available on systems using +# one of the supported non-English locales as default locale. diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java index 928b9a47934..03b9611c179 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java @@ -27,9 +27,11 @@ package jdk.tools.jlink.internal; import static jdk.tools.jlink.internal.TaskHelper.JLINK_BUNDLE; import java.io.BufferedInputStream; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.UncheckedIOException; import java.lang.module.Configuration; @@ -56,6 +58,7 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -238,6 +241,27 @@ public class JlinkTask { } public static final String OPTIONS_RESOURCE = "jdk/tools/jlink/internal/options"; + // Release information stored in the java.base module + private static final String JDK_RELEASE_RESOURCE = "jdk/internal/misc/resources/release.txt"; + + /** + * Read the release.txt from the module. + */ + private static Optional getReleaseInfo(ModuleReference mref) { + try { + Optional release = mref.open().open(JDK_RELEASE_RESOURCE); + + if (release.isEmpty()) { + return Optional.empty(); + } + + try (var r = new BufferedReader(new InputStreamReader(release.get()))) { + return Optional.of(r.readLine()); + } + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } int run(String[] args) { if (log == null) { @@ -410,7 +434,8 @@ public class JlinkTask { // Sanity check version if we use JMODs if (!isLinkFromRuntime) { - checkJavaBaseVersion(finder); + assert(finder.find("java.base").isPresent()); + checkJavaBaseVersion(finder.find("java.base").get()); } // Determine the roots set @@ -561,32 +586,34 @@ public class JlinkTask { return finder; } + private static String getCurrentRuntimeVersion() { + ModuleReference current = ModuleLayer.boot() + .configuration() + .findModule("java.base") + .get() + .reference(); + // This jlink runtime should always have the release.txt + return getReleaseInfo(current).get(); + } + /* - * Checks the version of the module descriptor of java.base for compatibility - * with the current runtime version. + * Checks the release information of the java.base used for target image + * for compatibility with the java.base used by jlink. * - * @throws IllegalArgumentException the descriptor of java.base has no - * version or the java.base version is not the same as the current runtime's - * version. + * @throws IllegalArgumentException If the `java.base` module reference `target` + * is not compatible with this jlink. */ - private static void checkJavaBaseVersion(ModuleFinder finder) { - assert finder.find("java.base").isPresent(); + private static void checkJavaBaseVersion(ModuleReference target) { + String currentRelease = getCurrentRuntimeVersion(); - // use the version of java.base module, if present, as - // the release version for multi-release JAR files - ModuleDescriptor.Version v = finder.find("java.base").get() - .descriptor().version().orElseThrow(() -> - new IllegalArgumentException("No version in java.base descriptor") - ); + String targetRelease = getReleaseInfo(target).orElseThrow(() -> new IllegalArgumentException( + taskHelper.getMessage("err.jlink.version.missing", currentRelease))); - Runtime.Version version = Runtime.Version.parse(v.toString()); - if (Runtime.version().feature() != version.feature() || - Runtime.version().interim() != version.interim()) { - // jlink version and java.base version do not match. - // We do not (yet) support this mode. + if (!currentRelease.equals(targetRelease)) { + // Current runtime image and the target runtime image are not compatible build throw new IllegalArgumentException(taskHelper.getMessage("err.jlink.version.mismatch", - Runtime.version().feature(), Runtime.version().interim(), - version.feature(), version.interim())); + currentRelease, + targetRelease)); } } diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties index 080d51506a6..374ed78f608 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties @@ -130,7 +130,9 @@ err.runtime.link.patched.module=jlink does not support linking from the run-time err.no.module.path=--module-path option must be specified with --add-modules ALL-MODULE-PATH err.empty.module.path=No module found in module path ''{0}'' with --add-modules ALL-MODULE-PATH err.limit.modules=--limit-modules not allowed with --add-modules ALL-MODULE-PATH -err.jlink.version.mismatch=jlink version {0}.{1} does not match target java.base version {2}.{3} +err.jlink.version.mismatch=jlink build ''{0}'' does not match target java.base build ''{1}'' +err.jlink.version.missing=jlink build ''{0}'' cannot find the build signature\ +\ in the java.base specified on module path, likely from an earlier build. err.automatic.module:automatic module cannot be used with jlink: {0} from {1} err.unknown.byte.order:unknown byte order {0} err.launcher.main.class.empty:launcher main class name cannot be empty: {0} diff --git a/test/hotspot/gtest/runtime/test_procMapsParser_linux.cpp b/test/hotspot/gtest/runtime/test_procMapsParser_linux.cpp new file mode 100644 index 00000000000..0177acda49f --- /dev/null +++ b/test/hotspot/gtest/runtime/test_procMapsParser_linux.cpp @@ -0,0 +1,109 @@ +/* + * 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. + * + */ + +#ifdef LINUX + +#include "procMapsParser.hpp" +#include "unittest.hpp" + +#include + +TEST(ProcSmapsParserTest, ParseMappings) { + const char* smaps_content = + "7f5a00000000-7f5a00001000 r--p 00000000 00:00 0 [anon]\n" + "Size: 4 kB\n" + "KernelPageSize: 4 kB\n" + "MMUPageSize: 4 kB\n" + "Rss: 0 kB\n" + "Pss: 0 kB\n" + "Shared_Clean: 0 kB\n" + "Shared_Dirty: 0 kB\n" + "Private_Clean: 0 kB\n" + "Private_Dirty: 0 kB\n" + "Referenced: 0 kB\n" + "Anonymous: 0 kB\n" + "LazyFree: 0 kB\n" + "AnonHugePages: 0 kB\n" + "ShmemPmdMapped: 0 kB\n" + "FilePmdMapped: 0 kB\n" + "Shared_Hugetlb: 0 kB\n" + "Private_Hugetlb: 0 kB\n" + "Swap: 0 kB\n" + "SwapPss: 0 kB\n" + "Locked: 0 kB\n" + "THPeligible: 0\n" + "VmFlags: rd mr mw me ac \n" + "7f5a00001000-7f5a00002000 rw-p 00000000 00:00 0 [anon]\n" + "Size: 4 kB\n" + "KernelPageSize: 4 kB\n" + "MMUPageSize: 4 kB\n" + "Rss: 4 kB\n" + "Pss: 4 kB\n" + "Shared_Clean: 0 kB\n" + "Shared_Dirty: 0 kB\n" + "Private_Clean: 0 kB\n" + "Private_Dirty: 4 kB\n" + "Referenced: 4 kB\n" + "Anonymous: 4 kB\n" + "LazyFree: 0 kB\n" + "AnonHugePages: 0 kB\n" + "ShmemPmdMapped: 0 kB\n" + "FilePmdMapped: 0 kB\n" + "Shared_Hugetlb: 0 kB\n" + "Private_Hugetlb: 0 kB\n" + "Swap: 0 kB\n" + "SwapPss: 0 kB\n" + "Locked: 0 kB\n" + "THPeligible: 0\n" + "VmFlags: rd wr mr mw me ac \n"; + + FILE* f = fmemopen((void*)smaps_content, strlen(smaps_content), "r"); + ASSERT_TRUE(f != nullptr); + + ProcSmapsParser parser(f); + ProcSmapsInfo info; + + // First mapping + ASSERT_TRUE(parser.parse_next(info)); + EXPECT_EQ((uintptr_t)info.from, 0x7f5a00000000ULL); + EXPECT_EQ((uintptr_t)info.to, 0x7f5a00001000ULL); + EXPECT_STREQ(info.prot, "r--p"); + EXPECT_TRUE(info.rd); + EXPECT_FALSE(info.wr); + + // Second mapping + ASSERT_TRUE(parser.parse_next(info)); + EXPECT_EQ((uintptr_t)info.from, 0x7f5a00001000ULL); + EXPECT_EQ((uintptr_t)info.to, 0x7f5a00002000ULL); + EXPECT_STREQ(info.prot, "rw-p"); + EXPECT_TRUE(info.rd); + EXPECT_TRUE(info.wr); + + // End of file + ASSERT_FALSE(parser.parse_next(info)); + + fclose(f); +} + +#endif // LINUX diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 423bcdecaa8..f379f57a8e5 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -49,7 +49,6 @@ compiler/cpuflags/TestAESIntrinsicsOnSupportedConfig.java 8190680 generic-all compiler/runtime/Test8168712.java#with-dtrace 8211769,8211771 generic-ppc64,generic-ppc64le,linux-s390x compiler/runtime/Test8168712.java#without-dtrace 8211769,8211771 generic-ppc64,generic-ppc64le,linux-s390x -compiler/loopopts/TestUnreachableInnerLoop.java 8288981 linux-s390x compiler/c2/Test8004741.java 8235801 generic-all compiler/c2/irTests/TestDuplicateBackedge.java 8318904 generic-all @@ -143,7 +142,6 @@ serviceability/sa/TestJmapCore.java 8318754 macosx-aarch64 serviceability/sa/TestJmapCoreMetaspace.java 8318754 macosx-aarch64 serviceability/sa/ClhsdbThreadContext.java 8356704 windows-x64 -serviceability/sa/TestJhsdbJstackMixedWithXComp.java 8371194 linux-x64 serviceability/jvmti/stress/StackTrace/NotSuspended/GetStackTraceNotSuspendedStressTest.java 8315980 linux-all,windows-x64 diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestArrayCopyDisjoint.java b/test/hotspot/jtreg/compiler/arraycopy/TestArrayCopyDisjoint.java index 162ba20048d..73b1af90548 100644 --- a/test/hotspot/jtreg/compiler/arraycopy/TestArrayCopyDisjoint.java +++ b/test/hotspot/jtreg/compiler/arraycopy/TestArrayCopyDisjoint.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 @@ -26,7 +26,7 @@ import java.util.Random; /** * @test - * @bug 8251871 8285301 + * @bug 8251871 8285301 8371964 * @summary Optimize arrayCopy using AVX-512 masked instructions. * * @run main/othervm/timeout=600 -XX:-TieredCompilation -Xbatch -XX:+IgnoreUnrecognizedVMOptions @@ -52,6 +52,9 @@ import java.util.Random; * compiler.arraycopy.TestArrayCopyDisjoint * @run main/othervm/timeout=600 -XX:-TieredCompilation -Xbatch -XX:+UnlockExperimentalVMOptions -XX:+AlwaysAtomicAccesses * compiler.arraycopy.TestArrayCopyDisjoint + * @run main/othervm/timeout=600 -XX:-TieredCompilation -Xbatch -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:UseAVX=3 -XX:MaxVectorSize=32 -XX:ArrayOperationPartialInlineSize=32 -XX:+StressIGVN + * compiler.arraycopy.TestArrayCopyDisjoint * */ diff --git a/test/hotspot/jtreg/compiler/c2/TestDeadLoopAtMergeMem.java b/test/hotspot/jtreg/compiler/c2/TestDeadLoopAtMergeMem.java new file mode 100644 index 00000000000..5c4dcc76a76 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestDeadLoopAtMergeMem.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2025 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8371464 + * @summary C2: assert(no_dead_loop) failed: dead loop detected + * @run main/othervm -Xcomp -XX:CompileOnly=TestDeadLoopAtMergeMem::test TestDeadLoopAtMergeMem + * @run main TestDeadLoopAtMergeMem + */ + +public class TestDeadLoopAtMergeMem { + static final int N = 400; + static long instanceCount; + boolean bFld; + float fArrFld[]; + static int iArrFld[] = new int[N]; + long vMeth_check_sum; + + public static void main(String[] strArr) { + TestDeadLoopAtMergeMem r = new TestDeadLoopAtMergeMem(); + for (int i = 0; i < 1000; i++) { + r.test((short) 0, instanceCount); + } + } + + void test(short s, long l) { + int i11 = 6, i12, i13 = 6, i14 = 2; + byte byArr2[] = new byte[N]; + init(byArr2, (byte) 4); + helper(66.118169, i11); + for (i12 = 3; i12 < 23; i12++) { + if (bFld) { + instanceCount = 5; + } else if (bFld) { + fArrFld[i12] = s; + do { + try { + i11 = i13 / i12 % i12; + } catch (ArithmeticException a_e) { + } + } while (i14 < 8); + } + } + for (int i15 : iArrFld) { + try { + i11 = 1 / i15; + } catch (ArithmeticException a_e) { + } + } + vMeth_check_sum += i11; + } + + void helper(double d, int i) { + int i1[] = new int[N]; + } + + public static void init(byte[] a, byte seed) { + for (int j = 0; j < a.length; j++) { + a[j] = (byte) ((j % 2 == 0) ? seed + j : seed - j); + } + } +} diff --git a/test/hotspot/jtreg/compiler/exceptions/TestAccessErrorInCatch.java b/test/hotspot/jtreg/compiler/exceptions/TestAccessErrorInCatch.java index 46541d76016..44cfd60cf38 100644 --- a/test/hotspot/jtreg/compiler/exceptions/TestAccessErrorInCatch.java +++ b/test/hotspot/jtreg/compiler/exceptions/TestAccessErrorInCatch.java @@ -23,16 +23,18 @@ /* * @test - * @bug 8367002 + * @bug 8367002 8370766 * @summary Compilers might not generate handlers for recursive exceptions * * @compile IllegalAccessInCatch.jasm * @run main/othervm -Xbatch * -XX:CompileCommand=compileonly,IllegalAccessInCatch*::test + * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyStack * -XX:-TieredCompilation * TestAccessErrorInCatch * @run main/othervm -Xbatch * -XX:CompileCommand=compileonly,IllegalAccessInCatch*::test + * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyStack * -XX:TieredStopAtLevel=3 * TestAccessErrorInCatch */ diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestLongReductionChain.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestLongReductionChain.java new file mode 100644 index 00000000000..37dd964048f --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestLongReductionChain.java @@ -0,0 +1,85 @@ +/* + * 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 jdk.test.lib.Utils; +import java.util.Random; + +/* + * @test + * @bug 8372451 + * @summary Test long reduction chain. Triggered bug with long chain of dead ReductionVector + * vtnodes after optimize_move_non_strict_order_reductions_out_of_loop. + * @library /test/lib / + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:LoopUnrollLimit=1000 -XX:MaxVectorSize=8 -Xbatch + * -XX:CompileCommand=compileonly,${test.main.class}::test + * ${test.main.class} + * @run main ${test.main.class} + */ + +public class TestLongReductionChain { + static int RANGE = 1024*8; + private static final Random RANDOM = Utils.getRandomInstance(); + + public static void main(String[] args) { + int[] aI = generateI(); + int[] bI = generateI(); + int gold = test(aI, bI); + + for (int i = 0; i < 1000; i++) { + int result = test(aI, bI); + if (result != gold) { + throw new RuntimeException("wrong value"); + } + } + } + + static int[] generateI() { + int[] a = new int[RANGE]; + for (int i = 0; i < a.length; i++) { + a[i] = RANDOM.nextInt(); + } + return a; + } + + // Test creates a very long reduction chain, especially with -XX:LoopUnrollLimit=1000. + // Limiting the reduction vectors to 2 elements gets us a very long chain -XX:MaxVectorSize=8. + // During VTransform::optimize this means a long chain of nodes needs to be found as dead. + // Before the fix, this took too many rounds, and we hit an assert. + static int test(int[] a, int[] b) { + int s = 0; + for (int i = 0; i < RANGE; i+=8) { + s += a[i+0] * b[i+0]; + s += a[i+1] * b[i+1]; + s += a[i+2] * b[i+2]; + s += a[i+3] * b[i+3]; + + s += a[i+4] & b[i+4]; + s += a[i+5] & b[i+5]; + s += a[i+6] & b[i+6]; + s += a[i+7] & b[i+7]; + } + return s; + } +} diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java index 043a198331e..a26fc4532df 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithXComp.java @@ -23,6 +23,7 @@ */ import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import jdk.test.lib.JDKToolLauncher; @@ -32,7 +33,7 @@ import jdk.test.lib.apps.LingeredApp; import jdk.test.lib.process.OutputAnalyzer; /** - * @test + * @test id=xcomp * @bug 8370176 * @requires vm.hasSA * @requires os.family == "linux" @@ -40,6 +41,28 @@ import jdk.test.lib.process.OutputAnalyzer; * @library /test/lib * @run driver TestJhsdbJstackMixedWithXComp */ + +/** + * @test id=xcomp-preserve-frame-pointer + * @bug 8370176 + * @requires vm.hasSA + * @requires os.family == "linux" + * @requires os.arch == "amd64" + * @library /test/lib + * @run driver TestJhsdbJstackMixedWithXComp -XX:+PreserveFramePointer + */ + +/** + * @test id=xcomp-disable-tiered-compilation + * @bug 8370176 + * @requires vm.hasSA + * @requires os.family == "linux" + * @requires os.arch == "amd64" + * @library /test/lib + * @run driver TestJhsdbJstackMixedWithXComp -XX:-TieredCompilation + */ + + public class TestJhsdbJstackMixedWithXComp { private static void runJstack(LingeredApp app) throws Exception { @@ -89,8 +112,12 @@ public class TestJhsdbJstackMixedWithXComp { LingeredApp app = null; try { + List jvmOpts = new ArrayList<>(); + jvmOpts.add("-Xcomp"); + jvmOpts.addAll(Arrays.asList(args)); + app = new LingeredAppWithVirtualThread(); - LingeredApp.startApp(app, "-Xcomp"); + LingeredApp.startApp(app, jvmOpts.toArray(new String[0])); System.out.println("Started LingeredApp with pid " + app.getPid()); runJstack(app); System.out.println("Test Completed"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/GC.java b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/GC.java index 6b33783306f..15bbc9eb964 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/GC.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/GC.java @@ -48,6 +48,9 @@ public class GC extends nsk.share.test.Tests { public GCTestRunner(Test test, String[] args) { super(test, args); + // GC tests often run at the brink of OOME, make sure + // LocalRandom is loaded, initialized, and has enough memory. + LocalRandom.init(); } private GCParams getGCParams(String[] args) { diff --git a/test/hotspot/jtreg/vmTestbase/vm/runtime/defmeth/StressTest.java b/test/hotspot/jtreg/vmTestbase/vm/runtime/defmeth/StressTest.java index e8fe73cb48b..4106f12587b 100644 --- a/test/hotspot/jtreg/vmTestbase/vm/runtime/defmeth/StressTest.java +++ b/test/hotspot/jtreg/vmTestbase/vm/runtime/defmeth/StressTest.java @@ -44,6 +44,7 @@ import java.util.Arrays; import java.util.List; import java.util.Random; +import jdk.test.lib.thread.TestThreadFactory; import nsk.share.TestFailure; import nsk.share.test.StressOptions; import nsk.share.test.Stresser; @@ -82,16 +83,18 @@ public class StressTest implements Runnable { @Option(name="ignoreTestFailures", default_value="false", description="ignore failures of the executed tests") private boolean ignoreTestFailures; - class Worker extends Thread { + class Worker implements Runnable { private final Random rand; private volatile DefMethTest failedTest; private Throwable reason; private volatile long executedTests = 0; - public Worker(String id, long seed) { - setName(id); - this.rand = new Random(seed); + private final Thread thread; + + Worker(String id, long seed) { + this.rand = new Random(seed); + this.thread = TestThreadFactory.newThread(this, id); } @Override @@ -247,13 +250,13 @@ public class StressTest implements Runnable { } for (Worker worker : workers) { - worker.start(); + worker.thread.start(); } } private void interruptWorkers() { for (Worker worker : workers) { - worker.interrupt(); + worker.thread.interrupt(); } } @@ -261,14 +264,14 @@ public class StressTest implements Runnable { boolean isFailed = false; for (Worker worker : workers) { - while (worker.isAlive()) { + while (worker.thread.isAlive()) { try { - worker.join(); + worker.thread.join(); } catch (InterruptedException e) {} } System.out.printf("%s: %s (executed: %d)\n", - worker.getName(), + worker.thread.getName(), worker.isFailed() ? "FAILED: " + worker.getFailedTest() : "PASSED", worker.getExecutedTests()); @@ -288,7 +291,7 @@ public class StressTest implements Runnable { private boolean workersAlive() { for (Worker worker : workers) { - if (!worker.isAlive()) { + if (!worker.thread.isAlive()) { return false; } } diff --git a/test/jdk/java/awt/image/ConvolveOp/KernelInitialisationTest.java b/test/jdk/java/awt/image/ConvolveOp/KernelInitialisationTest.java index 607f0a7a8dc..0d58472385c 100644 --- a/test/jdk/java/awt/image/ConvolveOp/KernelInitialisationTest.java +++ b/test/jdk/java/awt/image/ConvolveOp/KernelInitialisationTest.java @@ -25,36 +25,49 @@ * @test * @bug 8368729 * @summary Tests that passing invalid values to Kernel constructor - * throws only IllegalArgumentException + * throws only IllegalArgumentException or NullPointerException */ import java.awt.image.Kernel; public class KernelInitialisationTest { - private static void expectIllegalArgumentException(Runnable code) { + + private static void test(int width, int height, float[] data, + Class expected) + { + System.out.printf("Testing for width: %d, height: %d, data: %s%n", + width, height, data == null ? "null" : "not null"); + Class actual = null; try { - code.run(); - throw new RuntimeException("Expected IllegalArgumentException" + - " but no exception was thrown"); - } catch (IllegalArgumentException e) { - // we expect IllegalArgumentException + new Kernel(width, height, data); + } catch (Exception e) { + actual = e.getClass(); + } + if (actual != expected) { + System.err.println("Expected: " + expected); + System.err.println("Actual: " + actual); + throw new RuntimeException("Test failed"); } } - private static void testKernel(int width, int height, float[] data) { - System.out.println("Testing for width: " + width + ", height: " - + height + ", data: " + (data == null ? "null" : "not null")); - expectIllegalArgumentException(() -> new Kernel(width, height, data)); + private static void testIAE(int width, int height, int len) { + test(width, height, new float[len], IllegalArgumentException.class); + } + + private static void testNPE(int width, int height) { + test(width, height, null, NullPointerException.class); } public static void main(String[] args) { - testKernel(-1, 1, new float[100]); - testKernel(1, -1, new float[100]); - testKernel(-1, -1, new float[100]); - testKernel(1, 1, null); - - int width = 50; - int height = Integer.MAX_VALUE; - testKernel(width, height, new float[100]); + int[][] sizes = {{-1, 1}, {1, -1}, {-1, -1}, {50, Integer.MAX_VALUE}}; + int[] lens = {1, 100}; + for (int[] kernelSize : sizes) { + for (int len : lens) { + testIAE(kernelSize[0], kernelSize[1], len); + } + testNPE(kernelSize[0], kernelSize[1]); + } + testNPE(10, 10); // NPE on valid width and height + testIAE(10, 10, 10); // IAE on valid width and height but small data } } diff --git a/test/jdk/java/awt/image/SampleModelGetSamplesAndPixelsTest.java b/test/jdk/java/awt/image/SampleModelGetSamplesAndPixelsTest.java new file mode 100644 index 00000000000..483bbe43d9e --- /dev/null +++ b/test/jdk/java/awt/image/SampleModelGetSamplesAndPixelsTest.java @@ -0,0 +1,337 @@ +/* + * 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 6185110 + * @summary Verify get/set/Pixels/Samples APIs for bad parameters. + * + * @run main SampleModelGetSamplesAndPixelsTest + */ + +import java.awt.image.BandedSampleModel; +import java.awt.image.ComponentSampleModel; +import java.awt.image.DataBuffer; +import java.awt.image.MultiPixelPackedSampleModel; +import java.awt.image.PixelInterleavedSampleModel; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; +import java.util.Vector; + +public class SampleModelGetSamplesAndPixelsTest { + + static final int WIDTH = 100; + static final int HEIGHT = 100; + static final int DATATYPE = DataBuffer.TYPE_BYTE; + static final int NUMBANDS = 4; + static final int[] INTS = new int[WIDTH * HEIGHT + NUMBANDS]; + static final float[] FLOATS = new float[WIDTH * HEIGHT + NUMBANDS]; + static final double[] DOUBLES = new double[WIDTH * HEIGHT + NUMBANDS]; + static final int[][] COORDS = { + { 1, 1, 1, 1, -1 }, // bad band + { 1, 1, 1, 1, NUMBANDS }, // bad band + { 1, 1, -1, 1, 0 }, // negative w + { 1, 1, -1, -1, 0 }, // negative w and h + { -4, 1, 1, 1, 0 }, // negative x + { -4, -4, 1, 1, 0 }, // negative x and y + { WIDTH+10, 0, 1, 1, 0 }, // x > width + { 0, HEIGHT+10, 1, 1, 0 }, // y > height + { WIDTH+10, HEIGHT+10, 1, 1, 0 }, // both x > width and y > height + }; + + public static void main(String[] args) { + Vector> classes = new Vector>(); + + classes.add(ComponentSampleModel.class); + classes.add(MultiPixelPackedSampleModel.class); + classes.add(SinglePixelPackedSampleModel.class); + classes.add(BandedSampleModel.class); + classes.add(PixelInterleavedSampleModel.class); + + for (Class c : classes) { + doTest(c); + } + } + + static void noException(SampleModel sm) { + System.err.println(sm); + throw new RuntimeException("No expected exception"); + } + + private static void doTest(Class c) { + System.out.println("Test for: " + c.getName()); + SampleModel sm = createSampleModel(c); + doTestNull(sm); + for (int i = 0; i < COORDS.length; i++) { + int x = COORDS[i][0]; + int y = COORDS[i][1]; + int w = COORDS[i][2]; + int h = COORDS[i][3]; + int b = COORDS[i][4]; + doTest(sm, x, y, w, h, b); + } + } + + private static void doTestNull(SampleModel sm) { + doTestNull(sm, INTS); + doTestNull(sm, FLOATS); + doTestNull(sm, DOUBLES); + } + + private static void doTestNull(SampleModel sm, int[] INTS) { + try { + sm.getSamples(1, 1, 1, 1, 0, INTS, null); + noException(sm); + } catch (NullPointerException e) { + System.out.println(e.getMessage()); + } + + try { + sm.getSamples(1, 1, 1, 1, 0, INTS, null); + noException(sm); + } catch (NullPointerException e) { + System.out.println(e.getMessage()); + } + + try { + sm.getPixels(1, 1, 1, 1, INTS, null); + noException(sm); + } catch (NullPointerException e) { + System.out.println(e.getMessage()); + } + + try { + sm.setPixels(1, 1, 1, 1, INTS, null); + noException(sm); + } catch (NullPointerException e) { + System.out.println(e.getMessage()); + } + } + + private static void doTestNull(SampleModel sm, float[] FLOATS) { + try { + sm.getSamples(1, 1, 1, 1, 0, FLOATS, null); + noException(sm); + } catch (NullPointerException e) { + System.out.println(e.getMessage()); + } + + try { + sm.getSamples(1, 1, 1, 1, 0, FLOATS, null); + noException(sm); + } catch (NullPointerException e) { + System.out.println(e.getMessage()); + } + + try { + sm.getPixels(1, 1, 1, 1, FLOATS, null); + noException(sm); + } catch (NullPointerException e) { + System.out.println(e.getMessage()); + } + + try { + sm.setPixels(1, 1, 1, 1, FLOATS, null); + noException(sm); + } catch (NullPointerException e) { + System.out.println(e.getMessage()); + } + } + + private static void doTestNull(SampleModel sm, double[] DOUBLES) { + try { + sm.getSamples(1, 1, 1, 1, 0, DOUBLES, null); + noException(sm); + } catch (NullPointerException e) { + System.out.println(e.getMessage()); + } + + try { + sm.getSamples(1, 1, 1, 1, 0, DOUBLES, null); + noException(sm); + } catch (NullPointerException e) { + System.out.println(e.getMessage()); + } + + try { + sm.getPixels(1, 1, 1, 1, DOUBLES, null); + noException(sm); + } catch (NullPointerException e) { + System.out.println(e.getMessage()); + } + + try { + sm.setPixels(1, 1, 1, 1, DOUBLES, null); + noException(sm); + } catch (NullPointerException e) { + System.out.println(e.getMessage()); + } + } + + private static void doTest(SampleModel sm, int x, int y, int w, int h, int b) { + doTest(sm, x, y, w, h, b, INTS); + doTest(sm, x, y, w, h, b, FLOATS); + doTest(sm, x, y, w, h, b, DOUBLES); + } + + private static void doTest(SampleModel sm, int x, int y, int w, int h, int b, int[] INTS) { + + // Now test each API with a non-null buffer and the specified values. + DataBuffer db = sm.createDataBuffer(); + + try { + sm.getSamples(x, y, w, h, b, INTS, db); + noException(sm); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e.getMessage()); + } + + try { + sm.setSamples(x, y, w, h, b, INTS, db); + noException(sm); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e.getMessage()); + } + + if (b < 0 || b >= NUMBANDS) { + return; // Values were to test illegal bands, skip the rest. + } + + try { + sm.getPixels(x, y, w, h, INTS, db); + noException(sm); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e.getMessage()); + } + + try { + sm.setPixels(x, y, w, h, INTS, db); + noException(sm); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e.getMessage()); + } + } + + private static void doTest(SampleModel sm, int x, int y, int w, int h, int b, float[] FLOATS) { + + // Now test each API with a non-null buffer and the specified values. + DataBuffer db = sm.createDataBuffer(); + + try { + sm.getSamples(x, y, w, h, b, FLOATS, db); + noException(sm); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e.getMessage()); + } + + try { + sm.setSamples(x, y, w, h, b, FLOATS, db); + noException(sm); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e.getMessage()); + } + + if (b < 0 || b >= NUMBANDS) { + return; // Values were to test illegal bands, skip the rest. + } + + try { + sm.getPixels(x, y, w, h, FLOATS, db); + noException(sm); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e.getMessage()); + } + + try { + sm.setPixels(x, y, w, h, FLOATS, db); + noException(sm); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e.getMessage()); + } + } + + private static void doTest(SampleModel sm, int x, int y, int w, int h, int b, double[] DOUBLES) { + + // Now test each API with a non-null buffer and the specified values. + DataBuffer db = sm.createDataBuffer(); + + try { + sm.getSamples(x, y, w, h, b, DOUBLES, db); + noException(sm); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e.getMessage()); + } + + try { + sm.setSamples(x, y, w, h, b, DOUBLES, db); + noException(sm); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e.getMessage()); + } + + if (b < 0 || b >= NUMBANDS) { + return; // Values were to test illegal bands, skip the rest. + } + + try { + sm.getPixels(x, y, w, h, DOUBLES, db); + noException(sm); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e.getMessage()); + } + + try { + sm.setPixels(x, y, w, h, DOUBLES, db); + noException(sm); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(e.getMessage()); + } + + try { + sm.setDataElements(0, 0, null, db); + noException(sm); + } catch (NullPointerException e) { + System.out.println(e.getMessage()); + } + } + + private static SampleModel createSampleModel(Class cls) { + SampleModel res = null; + + if (cls == ComponentSampleModel.class) { + res = new ComponentSampleModel(DATATYPE, WIDTH, HEIGHT, 4, WIDTH * 4, new int[] { 0, 1, 2, 3 } ); + } else if (cls == MultiPixelPackedSampleModel.class) { + res = new MultiPixelPackedSampleModel(DATATYPE, WIDTH, HEIGHT, 4); + } else if (cls == SinglePixelPackedSampleModel.class) { + res = new SinglePixelPackedSampleModel(DATATYPE, WIDTH, HEIGHT, + new int[]{ 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff }); + } else if (cls == BandedSampleModel.class) { + res = new BandedSampleModel(DATATYPE, WIDTH, HEIGHT, NUMBANDS); + } else if (cls == PixelInterleavedSampleModel.class) { + res = new PixelInterleavedSampleModel(DATATYPE, WIDTH, HEIGHT, 4, WIDTH * 4, new int[] { 0, 1, 2, 3 }); + } else { + throw new RuntimeException("Unknown class " + cls); + } + return res; + } +} diff --git a/test/jdk/java/lang/String/UnicodeCaseFoldingTest.java b/test/jdk/java/lang/String/UnicodeCaseFoldingTest.java new file mode 100644 index 00000000000..86b3fba0a27 --- /dev/null +++ b/test/jdk/java/lang/String/UnicodeCaseFoldingTest.java @@ -0,0 +1,329 @@ +/* + * 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 tests unicode case-folding based String comparison and equality + * @bug 4397357 + * @library /lib/testlibrary/java/lang + * @modules java.base/jdk.internal.lang:+open + * @run junit/othervm + * UnicodeCaseFoldingTest + */ + +import java.nio.file.Files; +import java.util.stream.Stream; +import java.util.stream.Collectors; +import java.util.ArrayList; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import jdk.internal.lang.CaseFolding; + +public class UnicodeCaseFoldingTest { + + @Test + void testAllCommnFullCodePointsListedInCaseFoldinigTxt() throws Throwable { + var filter = "^.*; [CF]; .*$"; // C=common, F=full, for full case folding + var results = Files.lines(UCDFiles.CASEFOLDING) + .filter(line -> !line.startsWith("#") && line.matches(filter)) + .map(line -> { + var fields = line.split("; "); + var cp = Integer.parseInt(fields[0], 16); + fields = fields[2].trim().split(" "); + var folding = new int[fields.length]; + for (int i = 0; i < folding.length; i++) { + folding[i] = Integer.parseInt(fields[i], 16); + } + var source = new String(Character.toChars(cp)); + var expected = new String(folding, 0, folding.length); + // (1) Verify the folding result matches expected + assertEquals(expected, foldCase(source), "CaseFolding.fold(): "); + + // (2) Verify compareToFoldCase() result + assertEquals(0, source.compareToFoldCase(expected), "source.compareToFoldCase(expected)"); + assertEquals(0, expected.compareToFoldCase(source), "expected.compareToFoldCase(source)"); + + // (3) Verify equalsFoldCase() result + assertEquals(true, source.equalsFoldCase(expected), "source.equalsFoldCase(expected)"); + assertEquals(true, expected.equalsFoldCase(source), "expected.equalsFoldCase(source)"); + return null; + }) + .filter(error -> error != null) + .toArray(); + assertEquals(0, results.length); + } + + @Test + void testAllSimpleCodePointsListedInCaseFoldinigTxt() throws Throwable { + // S=simple, for simple case folding. The simple case folding should still matches + var filter = "^.*; [S]; .*$"; + var results = Files.lines(UCDFiles.CASEFOLDING) + .filter(line -> !line.startsWith("#") && line.matches(filter)) + .map(line -> { + var fields = line.split("; "); + var cp = Integer.parseInt(fields[0], 16); + fields = fields[2].trim().split(" "); + var folding = new int[fields.length]; + for (int i = 0; i < folding.length; i++) { + folding[i] = Integer.parseInt(fields[i], 16); + } + var source = new String(Character.toChars(cp)); + var expected = new String(folding, 0, folding.length); + + // (1) Verify compareToFoldCase() result + assertEquals(0, source.compareToFoldCase(expected), "source.compareToFoldCase(expected)"); + assertEquals(0, expected.compareToFoldCase(source), "expected.compareToFoldCase(source)"); + + // (2) Verify equalsFoldCase() result + assertEquals(true, source.equalsFoldCase(expected), "source.equalsFoldCase(expected)"); + assertEquals(true, expected.equalsFoldCase(source), "expected.equalsFoldCase(source)"); + return null; + }) + .filter(error -> error != null) + .toArray(); + assertEquals(0, results.length); + } + + @Test + public void testAllCodePointsFoldToThemselvesIfNotListed() throws Exception { + // Collect all code points that appear in CaseFolding.txt + var listed = Files.lines(UCDFiles.CASEFOLDING) + .filter(line -> !line.startsWith("#") && line.matches("^.*; [CF]; .*$")) + .map(line -> Integer.parseInt(line.split("; ")[0], 16)) + .collect(Collectors.toSet()); + + var failures = new ArrayList(); + + // Scan BMP + Supplementary Plane 1 (U+0000..U+1FFFF) + for (int cp = Character.MIN_CODE_POINT; cp <= 0x1FFFF; cp++) { + if (!Character.isDefined(cp)) { + continue; // skip undefined + } + if (Character.isSurrogate((char) cp)) { + continue; // skip surrogate code units + } + if (listed.contains(cp)) { + continue; // already tested separately + } + String s = new String(Character.toChars(cp)); + String folded = foldCase(s); + if (!s.equals(folded)) { + failures.add(String.format("Unexpected folding: U+%04X '%s' → '%s'", cp, s, folded)); + } + } + + assertEquals(0, failures.size(), + () -> "Some unlisted code points folded unexpectedly:\n" + + String.join("\n", failures)); + } + + @ParameterizedTest(name = "CaseFold \"{0}\" → \"{1}\"") + @MethodSource("caseFoldTestCases") + void testIndividualCaseFolding(String input, String expected) { + assertEquals(expected, foldCase(input)); + } + + static Stream caseFoldTestCases() { + return Stream.of( + // ASCII simple cases + Arguments.of("ABC", "abc"), + Arguments.of("already", "already"), + Arguments.of("MiXeD123", "mixed123"), + // --- Latin-1 to non-Latin-1 fold --- + Arguments.of("aBc\u00B5Efg", "abc\u03BCefg"), // "µ" → "μ" + Arguments.of("test\u00B5\ud801\udc00X", "test\u03bc\ud801\udc28x"), + // German Eszett + Arguments.of("Stra\u00DFe", "strasse"), // "Straße" + Arguments.of("\u1E9E", "ss"), // "ẞ" capital sharp S + // Turkish dotted I / dotless i + Arguments.of("I", "i"), + Arguments.of("\u0130", "i\u0307"), // capital dotted I → "i + dot above" + Arguments.of("\u0069\u0307", "i\u0307"), // small i + dot above remains + Arguments.of("\u0131", "\u0131"), // "ı" (dotless i stays dotless) + + // Greek special cases --- + Arguments.of("\u039F\u03A3", "\u03BF\u03C3"), // "ΟΣ" → "οσ" final sigma always folds to normal sigma + Arguments.of("\u1F88", "\u1F00\u03B9"), // "ᾈ" → "ἀι" Alpha with psili + ypogegrammeni + Arguments.of("\u039C\u03AC\u03CA\u03BF\u03C2", "\u03BC\u03AC\u03CA\u03BF\u03C3"), // "Μάϊος" → "μάϊοσ" + Arguments.of("\u1F08", "\u1F00"), // Ἀ (Capital Alpha with psili) → ἀ + + // Supplementary Plane characters + Arguments.of("\uD801\uDC00", "\uD801\uDC28"), // Deseret Capital Letter Long I → Small + Arguments.of("\uD801\uDC01", "\uD801\uDC29"), // Deseret Capital Letter Long E → Small + + // Supplementary inside ASCII + Arguments.of("abc\uD801\uDC00def", "abc\uD801\uDC28def"), + // Ligatures and compatibility folds + Arguments.of("\uFB00", "ff"), // ff → ff + Arguments.of("\uFB03", "ffi"), // ffi → ffi + Arguments.of("\u212A", "k"), // Kelvin sign → k + + Arguments.of("abc\uFB00def", "abcffdef"), // ff → ff + Arguments.of("abc\uFB03def", "abcffidef"), // ffi → ffi + Arguments.of("abc\u212Adef", "abckdef"), // Kelvin sign → k + + // --- Fullwidth --- + Arguments.of("\uFF21\uFF22\uFF23", "\uFF41\uFF42\uFF43"), // "ABC" → "abc" + + // --- Armenian --- + Arguments.of("\u0531", "\u0561"), // "Ա" → "ա" + + // --- Cherokee --- + Arguments.of("\u13A0", "\u13A0"), // Capital Cherokee A folds to itself + Arguments.of("\uAB70", "\u13A0") // Small Cherokee A folds Capital Cherokee A + ); + } + + static Stream caseFoldEqualProvider() { + return Stream.of( + Arguments.of("abc", "ABC"), + Arguments.of("aBcDe", "AbCdE"), + Arguments.of("\u00C0\u00E7", "\u00E0\u00C7"), // Àç vs àÇ + Arguments.of("straße", "STRASSE"), // ß → ss + Arguments.of("\uD83C\uDDE6", "\uD83C\uDDE6"), // 🇦 vs 🇦 + Arguments.of("\u1E9E", "ss"), // ẞ (capital sharp S) + Arguments.of("\u03A3", "\u03C3"), // Σ vs σ (Greek Sigma) + Arguments.of("\u03C3", "\u03C2"), // σ vs ς (Greek sigma/final sigma) + Arguments.of("\u212B", "\u00E5"), // Å (Angstrom sign) vs å + Arguments.of("\uFB00", "ff"), // ff (ligature) + Arguments.of("\u01C5", "\u01C5"), // Dž (Latin capital D with small z with caron) + Arguments.of("Caf\u00E9", "CAF\u00C9"), // Café vs CAFÉ + Arguments.of("\u03BA\u03B1\u03BB\u03B7\u03BC\u03AD\u03C1\u03B1", "\u039A\u0391\u039B\u0397\u039C\u0388\u03A1\u0391"), // καλημέρα vs ΚΑΛΗΜΕΡΑ + Arguments.of("\u4E2D\u56FD", "\u4E2D\u56FD"), // 中国 + Arguments.of("\u03B1", "\u0391"), // α vs Α (Greek alpha) + Arguments.of("\u212B", "\u00C5"), // Å vs Å + // from StringCompareToIgnoreCase + Arguments.of("\u0100\u0102\u0104\u0106\u0108", "\u0100\u0102\u0104\u0106\u0109"), // ĀĂĄĆĈ vs ĀĂĄĆĉ + Arguments.of("\u0101\u0103\u0105\u0107\u0109", "\u0100\u0102\u0104\u0106\u0109"), // āăąćĉ vs ĀĂĄĆĉ + Arguments.of("\ud801\udc00\ud801\udc01\ud801\udc02\ud801\udc03\ud801\udc04", + "\ud801\udc00\ud801\udc01\ud801\udc02\ud801\udc03\ud801\udc2c"), // 𐐀𐐁𐐂𐐃𐐄 vs 𐐀𐐁𐐂𐐃𐐬 + Arguments.of("\ud801\udc28\ud801\udc29\ud801\udc2a\ud801\udc2b\ud801\udc2c", + "\ud801\udc00\ud801\udc01\ud801\udc02\ud801\udc03\ud801\udc2c") // 𐐨𐐩𐐪𐐫𐐬 vs 𐐀𐐁𐐂𐐃𐐬 + ); + } + + @ParameterizedTest + @MethodSource("caseFoldEqualProvider") + void testcompareToFoldCaseEquals(String s1, String s2) { + assertEquals(0, s1.compareToFoldCase(s2)); + assertEquals(0, s2.compareToFoldCase(s1)); + assertEquals(true, s1.equalsFoldCase(s2)); + assertEquals(true, s2.equalsFoldCase(s1)); + assertEquals(foldCase(s1), foldCase(s2)); + } + + static Stream caseFoldOrderingProvider() { + return Stream.of( + Arguments.of("asa", "aß", -1), // ß → ss → "asa" < "ass" + Arguments.of("aß", "asa", +1), + Arguments.of("a\u00DF", "ass", 0), // aß vs ass + Arguments.of("\uFB03", "ffi", 0), // ffi (ligature) + Arguments.of("\u00C5", "Z", 1), // Å vs Z + Arguments.of("A", "\u00C0", -1), // A vs À + Arguments.of("\u03A9", "\u03C9", 0), // Ω vs ω + Arguments.of("\u03C2", "\u03C3", 0), // ς vs σ + Arguments.of("\uD835\uDD23", "R", 1), // 𝔯 (fraktur r) vs R + Arguments.of("\uFF26", "E", 1), // F (full-width F) vs E + Arguments.of("\u00C9clair", "Eclair", 1), // Éclair vs Eclair + Arguments.of("\u03bc\u00df", "\u00b5s", 1), + Arguments.of("\u00b5s", "\u03bc\u00df", -1) + ); + } + + @ParameterizedTest + @MethodSource("caseFoldOrderingProvider") + void testcompareToFoldCaseOrdering(String s1, String s2, int expectedSign) { + int cmp = s1.compareToFoldCase(s2); + assertEquals(expectedSign, Integer.signum(cmp)); + } + + static Stream roundTripProvider() { + return Stream.of( + Arguments.of("abc"), + Arguments.of("ABC"), + Arguments.of("straße"), + Arguments.of("Àç"), + Arguments.of("aß"), + Arguments.of("\uFB02uff"), // fluff (ligature in "fluff") + Arguments.of("\u00C9COLE") // ÉCOLE + ); + } + + @ParameterizedTest + @MethodSource("roundTripProvider") + void testCaseFoldRoundTrip(String s) { + String folded = foldCase(s); + assertEquals(0, s.compareToFoldCase(folded)); + assertEquals(0, folded.compareToFoldCase(s)); + assertEquals(true, s.equalsFoldCase(folded)); + assertEquals(true, folded.equalsFoldCase(s)); + } + + // helper to test the integrity of folding mapping + private static int[] longToFolding(long value) { + int len = (int) (value >>> 48); + if (len == 0) { + return new int[]{(int) (value & 0xFFFFF)}; + } else { + var folding = new int[len]; + for (int i = 0; i < len; i++) { + folding[i] = (int) (value & 0xFFFF); + value >>= 16; + } + return folding; + } + } + + private static String foldCase(String s) { + int first; + int len = s.length(); + int cpCnt = 1; + for (first = 0; first < len; first += cpCnt) { + int cp = s.codePointAt(first); + if (CaseFolding.isDefined(cp)) { + break; + } + cpCnt = Character.charCount(cp); + } + if (first == len) { + return s; + } + StringBuilder sb = new StringBuilder(len); + sb.append(s, 0, first); + for (int i = first; i < len; i += cpCnt) { + int cp = s.codePointAt(i); + int[] folded = longToFolding(CaseFolding.fold(cp)); + for (int f : folded) { + sb.appendCodePoint(f); + } + cpCnt = Character.charCount(cp); + } + return sb.toString(); + } +} diff --git a/test/jdk/java/lang/module/ModuleReader/patched/PatchedModuleReaderTest.java b/test/jdk/java/lang/module/ModuleReader/patched/PatchedModuleReaderTest.java new file mode 100644 index 00000000000..80f4e324627 --- /dev/null +++ b/test/jdk/java/lang/module/ModuleReader/patched/PatchedModuleReaderTest.java @@ -0,0 +1,132 @@ +/* + * 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.io.IOException; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReader; +import java.lang.module.ModuleReference; +import java.net.URI; +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @bug 8372787 + * @summary Test the behaviour of ModuleReader when using --patch-module + * @comment patch the java.base module with a test specific resource + * @compile/module=java.base java/lang/PatchedFoo.java + * @run junit/othervm ${test.main.class} + */ +class PatchedModuleReaderTest { + + private static ModuleReference patchedModuleRef; + + @BeforeAll + static void beforeAll() { + patchedModuleRef = ModuleFinder.ofSystem() + .find("java.base") + .orElseThrow(); + } + + /* + * Verifies that the resource that was patched into a module + * is found by the ModuleReader. + */ + @Test + void testResourceFound() throws Exception { + try (ModuleReader reader = patchedModuleRef.open()) { + String resourceName = "java/lang/PatchedFoo.class"; + Optional res = reader.find(resourceName); + assertTrue(res.isPresent(), resourceName + " is missing in " + + patchedModuleRef.descriptor().name() + " module"); + URI uri = res.get(); + assertEquals("file", uri.getScheme(), + "unexpected scheme in resource URI " + uri); + assertTrue(uri.getPath().endsWith(resourceName), + "unexpected path component " + uri.getPath() + + " in resource URI " + uri); + + } + } + + /* + * Verifies the ModuleReader against a resource which isn't + * expected to be part of the patched module. + */ + @Test + void testResourceNotFound() throws Exception { + try (ModuleReader reader = patchedModuleRef.open()) { + String nonExistentResource = "foo/bar/NonExistent.class"; + Optional res = reader.find(nonExistentResource); + assertTrue(res.isEmpty(), "unexpected resource " + nonExistentResource + + " in " + patchedModuleRef.descriptor().name() + " module"); + } + } + + /* + * This test opens a ModuleReader for a patched module, accumulates + * the Stream of resources from that ModuleReader and then closes that + * ModuleReader. It then verifies that the closed ModuleReader + * throws the specified IOException whenever it is used for subsequent + * operations on the Stream of resources. + */ + @Test + void testIOExceptionAfterClose() throws Exception { + ModuleReader reader; + Stream resources; + try (var _ = reader = patchedModuleRef.open()) { + // hold on to the available resources, to test them after the + // ModuleReader is closed + resources = reader.list(); + } // close the ModuleReader + + // verify IOException is thrown by the closed ModuleReader + + assertThrows(IOException.class, () -> reader.list(), + "ModuleReader.list()"); + + resources.forEach(rn -> { + assertThrows(IOException.class, () -> reader.read(rn), + "ModuleReader.read(String)"); + assertThrows(IOException.class, () -> reader.open(rn), + "ModuleReader.open(String)"); + assertThrows(IOException.class, () -> reader.find(rn), + "ModuleReader.find(String)"); + }); + + // repeat the test for a non-existent resource + String nonExistentResource = "foo/bar/NonExistent.class"; + assertThrows(IOException.class, () -> reader.read(nonExistentResource), + "ModuleReader.read(String)"); + assertThrows(IOException.class, () -> reader.open(nonExistentResource), + "ModuleReader.open(String)"); + assertThrows(IOException.class, () -> reader.find(nonExistentResource), + "ModuleReader.find(String)"); + } +} diff --git a/test/jdk/java/lang/module/ModuleReader/patched/java.base/java/lang/PatchedFoo.java b/test/jdk/java/lang/module/ModuleReader/patched/java.base/java/lang/PatchedFoo.java new file mode 100644 index 00000000000..814757134d6 --- /dev/null +++ b/test/jdk/java/lang/module/ModuleReader/patched/java.base/java/lang/PatchedFoo.java @@ -0,0 +1,26 @@ +/* + * 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 java.lang; + +public class PatchedFoo { +} diff --git a/test/jdk/java/lang/reflect/Generics/ProtectInnerStateOfTypeVariableImplTest.java b/test/jdk/java/lang/reflect/Generics/ProtectInnerStateOfTypeVariableImplTest.java new file mode 100644 index 00000000000..05a2a8e84de --- /dev/null +++ b/test/jdk/java/lang/reflect/Generics/ProtectInnerStateOfTypeVariableImplTest.java @@ -0,0 +1,70 @@ +/* + * 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 8372258 + * @summary Test if a copy of the internal state is provided + * @run junit ProtectInnerStateOfTypeVariableImplTest + */ + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.TypeVariable; + +import static org.junit.jupiter.api.Assertions.*; + +final class ProtectInnerStateOfTypeVariableImplTest { + + static final class Foo { + public Foo() { + } + + X x() { + return null; + } + } + + @Test + void testMethod() throws NoSuchMethodException { + Method method = Foo.class.getDeclaredMethod("x"); + TypeVariable tv = method.getTypeParameters()[0]; + + Method gd = tv.getGenericDeclaration(); + Method gd2 = tv.getGenericDeclaration(); + assertNotSame(gd, gd2); + } + + @Test + void testConstructor() throws NoSuchMethodException { + Constructor ctor = Foo.class.getConstructor(); + TypeVariable> tv = ctor.getTypeParameters()[0]; + + Constructor gd = tv.getGenericDeclaration(); + Constructor gd2 = tv.getGenericDeclaration(); + assertNotSame(gd, gd2); + } + +} diff --git a/test/jdk/java/util/concurrent/forkjoin/Starvation.java b/test/jdk/java/util/concurrent/forkjoin/Starvation.java index 8397e852ffa..d864fa0ba33 100644 --- a/test/jdk/java/util/concurrent/forkjoin/Starvation.java +++ b/test/jdk/java/util/concurrent/forkjoin/Starvation.java @@ -28,6 +28,7 @@ */ import java.util.concurrent.Callable; import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; import java.util.concurrent.atomic.AtomicInteger; public class Starvation { @@ -42,7 +43,7 @@ public class Starvation { while (count.get() == c) Thread.onSpinWait(); return null; }}; - public static void main(String[] args) throws Exception { + static void testSubmitExternalCallable() throws Exception { try (var pool = new ForkJoinPool(2)) { for (int i = 0; i < 100_000; i++) { var future1 = pool.submit(new AwaitCount(i)); @@ -53,4 +54,21 @@ public class Starvation { } } } + + static void testSubmitAdaptedCallable() throws Exception { + try (var pool = new ForkJoinPool(2)) { + for (int i = 0; i < 100_000; i++) { + var future1 = pool.submit(new AwaitCount(i)); + var future2 = pool.submit(ForkJoinTask.adapt(noop)); + future2.get(); + count.set(i + 1); + future1.get(); + } + } + } + + public static void main(String[] args) throws Exception { + testSubmitExternalCallable(); + testSubmitAdaptedCallable(); + } } diff --git a/test/langtools/jdk/javadoc/tool/testLocaleOption/TestSupportedLocales.java b/test/langtools/jdk/javadoc/tool/testLocaleOption/TestSupportedLocales.java new file mode 100644 index 00000000000..6d4e24f0c3a --- /dev/null +++ b/test/langtools/jdk/javadoc/tool/testLocaleOption/TestSupportedLocales.java @@ -0,0 +1,106 @@ +/* + * 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 8372708 + * @summary Javadoc ignores "-locale" and uses default locale for all messages and texts + * @library /tools/lib ../../lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.javadoc/jdk.javadoc.internal.tool + * @build toolbox.ToolBox javadoc.tester.* + * @run main TestSupportedLocales + */ + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Locale; + +import javadoc.tester.JavadocTester; +import toolbox.ToolBox; + +public class TestSupportedLocales extends JavadocTester { + + public static void main(String... args) throws Exception { + var tester = new TestSupportedLocales(); + tester.runTests(); + } + + private final ToolBox tb = new ToolBox(); + + // A locale with an associated output message + record LocalizedOutput(Locale locale, String message) {} + + // Console messages are determined by the system default locale + private final LocalizedOutput[] consoleOutput = new LocalizedOutput[] { + new LocalizedOutput(Locale.CHINA, "\u6b63\u5728\u6784\u9020 Javadoc \u4fe1\u606f..."), + new LocalizedOutput(Locale.GERMANY, "Javadoc-Informationen werden erstellt..."), + new LocalizedOutput(Locale.JAPAN, "Javadoc\u60c5\u5831\u3092\u69cb\u7bc9\u3057\u3066\u3044\u307e\u3059..."), + new LocalizedOutput(Locale.US, "Constructing Javadoc information..."), + }; + + // Documentation messages are determined by the -locale option + private final LocalizedOutput[] documentationOutput = new LocalizedOutput[] { + new LocalizedOutput(Locale.CHINA, "\u7c7b\u548c\u63a5\u53e3"), + new LocalizedOutput(Locale.GERMANY, "Klassen und Schnittstellen"), + new LocalizedOutput(Locale.JAPAN, "\u30af\u30e9\u30b9\u3068\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9"), + new LocalizedOutput(Locale.US, "Classes and Interfaces"), + }; + + // Test all combinations of system and documentation locales + @Test + public void testSupportedLocales(Path base) throws Exception { + var src = base.resolve("src"); + initSource(src); + for (var console : consoleOutput) { + for (var documentation : documentationOutput) { + test(base, console, documentation); + } + } + } + + void test(Path base, LocalizedOutput console, LocalizedOutput documentation) throws Exception { + var src = base.resolve("src"); + var out = base.resolve(console.locale + "-" + documentation.locale); + Locale.setDefault(console.locale); + javadoc("-d", out.toString(), + "-locale", documentation.locale.toString(), + "--source-path", src.toString(), + "p"); + checkExit(Exit.OK); + checkOutput(Output.OUT, true, console.message); + checkOutput("p/package-summary.html", true, documentation.message); + } + + private void initSource(Path src) throws IOException { + tb.writeJavaFiles(src, """ + package p; + /** + * A class. + */ + public class C { + private C() { } + }"""); + } +} diff --git a/test/langtools/tools/javac/launcher/SourceLauncherTest.java b/test/langtools/tools/javac/launcher/SourceLauncherTest.java index 6b147d32d00..37d50674855 100644 --- a/test/langtools/tools/javac/launcher/SourceLauncherTest.java +++ b/test/langtools/tools/javac/launcher/SourceLauncherTest.java @@ -798,6 +798,22 @@ public class SourceLauncherTest extends TestRunner { } } + @Test + public void testPrivateConstructor(Path base) throws IOException { + tb.writeJavaFiles(base, + """ + class PrivateConstructor { + private PrivateConstructor() {} + void main() {} + } + """); + testError(base.resolve("PrivateConstructor.java"), "", + """ + error: no non-private zero argument constructor found in class PrivateConstructor + remove private from existing constructor or define as: + public PrivateConstructor()"""); + } + @Test public void testAbstractClassInstanceMain(Path base) throws IOException { tb.writeJavaFiles(base, @@ -904,14 +920,6 @@ public class SourceLauncherTest extends TestRunner { } } - void checkContains(String name, String found, String expect) { - expect = expect.replace("\n", tb.lineSeparator); - out.println(name + ": " + found); - if (!found.contains(expect)) { - error("Expected output not found: " + expect); - } - } - void checkEqual(String name, List found, List expect) { out.println(name + ": " + found); tb.checkEqual(expect, found); @@ -939,7 +947,6 @@ public class SourceLauncherTest extends TestRunner { } void checkFault(String name, Throwable found, String expect) { - expect = expect.replace("\n", tb.lineSeparator); out.println(name + ": " + found); if (found == null) { error("No exception thrown; expected Fault"); @@ -947,8 +954,14 @@ public class SourceLauncherTest extends TestRunner { if (!(found instanceof Fault)) { error("Unexpected exception; expected Fault"); } - if (!(found.getMessage().equals(expect))) { - error("Unexpected detail message; expected: " + expect); + String actual = found.getMessage(); + List actualLines = actual.lines().toList(); + List expectLines = expect.lines().toList(); + if (!(actualLines.equals(expectLines))) { + error("Unexpected detail message; expected: \n" + + expect.indent(2) + + "\nactual:\n" + + actual.indent(2)); } } } diff --git a/test/langtools/tools/javac/options/HelpOutputColumnWidthTest.java b/test/langtools/tools/javac/options/HelpOutputColumnWidthTest.java index 4cef7e1916b..f46ef0219b5 100644 --- a/test/langtools/tools/javac/options/HelpOutputColumnWidthTest.java +++ b/test/langtools/tools/javac/options/HelpOutputColumnWidthTest.java @@ -47,7 +47,7 @@ import toolbox.Task; public class HelpOutputColumnWidthTest extends TestRunner { - public static final int MAX_COLUMNS = 84; + public static final int MAX_COLUMNS = 80; protected ToolBox tb; diff --git a/test/micro/org/openjdk/bench/java/lang/StringCompareToFoldCase.java b/test/micro/org/openjdk/bench/java/lang/StringCompareToFoldCase.java new file mode 100644 index 00000000000..dff4d874705 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/StringCompareToFoldCase.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang; + +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +/* + * This benchmark naively explores String::compareToFoldCase performance + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +public class StringCompareToFoldCase { + + private String asciiUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private String asciiUpperLower = "ABCDEFGHIJKLMNOpqrstuvwxyz"; + private String asciiLower = "abcdefghijklmnopqrstuvwxyz"; + + private String asciiWithDF = "abcdßßßßßßßßßßßßßßßßWXYZ"; + private String asciiWithDFSS = "abcdssssssssssssssssßßßßßßßßWXYZ"; + + private String asciiLatine1 = "ABCDEFGHIJKLMNOpqrstuvwxyz0"; + private String asciiLatin1UTF16 = "abcdefghijklmnopqrstuvwxyz\u0391"; + + private String greekUpper = "\u0391\u0392\u0393\u0394\u0395\u0391\u0392\u0393\u0394\u0395"; // ΑΒΓΔΕ + private String greekUpperLower = "\u0391\u0392\u0393\u0394\u0395\u0391\u0392\u0393\u0394\u03B5"; // ΑΒΓΔε + private String greekLower = "\u03B1\u03B2\u03B3\u03B4\u03B5\u03B1\u03B2\u03B3\u03B4\u03B5"; // αβγδε + + public String supUpper = "\ud801\udc00\ud801\udc01\ud801\udc02\ud801\udc03\ud801\udc04"; + public String supUpperLower = "\ud801\udc00\ud801\udc01\ud801\udc02\ud801\udc03\ud801\udc2c"; + public String supLower = "\ud801\udc28\ud801\udc29\ud801\udc2a\ud801\udc2b\ud801\udc2c"; + + @Benchmark + public int asciiUpperLower() { + return asciiUpper.compareToIgnoreCase(asciiUpperLower); + } + + @Benchmark + public int asciiLower() { + return asciiUpper.compareToIgnoreCase(asciiLower); + } + + @Benchmark + public int greekUpperLower() { + return greekUpper.compareToIgnoreCase(greekUpperLower); + } + + @Benchmark + public int greekLower() { + return greekUpper.compareToIgnoreCase(greekLower); + } + + @Benchmark + public int latin1UTF16() { + return asciiLatine1.compareToIgnoreCase(asciiLatin1UTF16); + } + + @Benchmark + public int supUpperLower() { + return supUpper.compareToIgnoreCase(supUpperLower); + } + + @Benchmark + public int supLower() { + return supUpper.compareToIgnoreCase(supLower); + } + + @Benchmark + public int asciiUpperLowerFC() { + return asciiUpper.compareToFoldCase(asciiUpperLower); + } + + @Benchmark + public int asciiLowerFC() { + return asciiUpper.compareToFoldCase(asciiLower); + } + + @Benchmark + public int asciiWithDFFC() { + return asciiWithDF.compareToFoldCase(asciiWithDFSS); + } + + @Benchmark + public int greekUpperLowerFC() { + return greekUpper.compareToFoldCase(greekUpperLower); + } + + @Benchmark + public int greekLowerFC() { + return greekUpper.compareToFoldCase(greekLower); + } + + @Benchmark + public int latin1UTF16FC() { + return asciiLatine1.compareToFoldCase(asciiLatin1UTF16); } + + @Benchmark + public int supUpperLowerFC() { + return supUpper.compareToFoldCase(supUpperLower); + } + + @Benchmark + public int supLowerFC() { + return supUpper.compareToFoldCase(supLower); + } + + @Benchmark + public boolean asciiUpperLowerEQ() { + return asciiUpper.equalsIgnoreCase(asciiUpperLower); + } + + @Benchmark + public boolean asciiLowerEQ() { + return asciiUpper.equalsIgnoreCase(asciiLower); + } + + @Benchmark + public boolean greekUpperLowerEQ() { + return greekUpper.equalsIgnoreCase(greekUpperLower); + } + + @Benchmark + public boolean greekLowerEQ() { + return greekUpper.equalsIgnoreCase(greekLower); + } + + @Benchmark + public boolean latin1UTF16EQ() { + return asciiLatine1.equalsIgnoreCase(asciiLatin1UTF16); + } + + @Benchmark + public boolean supUpperLowerEQ() { + return supUpper.equalsIgnoreCase(supUpperLower); + } + + @Benchmark + public boolean supLowerEQ() { + return supUpper.equalsIgnoreCase(supLower); + } + + @Benchmark + public boolean asciiUpperLowerEQFC() { + return asciiUpper.equalsFoldCase(asciiUpperLower); + } + + @Benchmark + public boolean asciiLowerEQFC() { + return asciiUpper.equalsFoldCase(asciiLower); + } + + @Benchmark + public boolean greekUpperLowerEQFC() { + return greekUpper.equalsFoldCase(greekUpperLower); + } + + @Benchmark + public boolean greekLowerEQFC() { + return greekUpper.equalsFoldCase(greekLower); + } + + @Benchmark + public boolean latin1UTF16EQFC() { + return asciiLatine1.equalsFoldCase(asciiLatin1UTF16); + } + + @Benchmark + public boolean supUpperLowerEQFC() { + return supUpper.equalsFoldCase(supUpperLower); + } + + @Benchmark + public boolean supLowerEQFC() { + return supUpper.equalsFoldCase(supLower); + } + }