Merge branch 'master' of https://github.com/openjdk/jdk into fix/vh-adapt-cache

This commit is contained in:
Chen Liang 2025-12-02 16:33:28 -06:00
commit fa80b4ebf7
92 changed files with 3658 additions and 1422 deletions

View File

@ -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,

View File

@ -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.

View File

@ -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

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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)
################################################################################

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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");

View File

@ -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
//

View File

@ -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
}

View File

@ -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);
};

View File

@ -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 ((hx<hy) || (lx<ly)) return x; /* |x|<|y| return x */

View File

@ -77,10 +77,11 @@ void G1Arguments::initialize_alignments() {
}
size_t G1Arguments::conservative_max_heap_alignment() {
if (FLAG_IS_DEFAULT(G1HeapRegionSize)) {
return G1HeapRegion::max_ergonomics_size();
}
return G1HeapRegion::max_region_size();
const size_t region_size = FLAG_IS_DEFAULT(G1HeapRegionSize)
? G1HeapRegion::max_ergonomics_size()
: G1HeapRegion::max_region_size();
return calculate_heap_alignment(region_size);
}
void G1Arguments::initialize_verification_types() {

View File

@ -307,9 +307,13 @@ HeapWord* ParallelScavengeHeap::mem_allocate_cas_noexpand(size_t size, bool is_t
HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size, bool is_tlab) {
for (uint loop_count = 0; /* empty */; ++loop_count) {
HeapWord* result = mem_allocate_cas_noexpand(size, is_tlab);
if (result != nullptr) {
return result;
HeapWord* result;
{
ConditionalMutexLocker locker(Heap_lock, !is_init_completed());
result = mem_allocate_cas_noexpand(size, is_tlab);
if (result != nullptr) {
return result;
}
}
// Read total_collections() under the lock so that multiple
@ -326,10 +330,15 @@ HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size, bool is_tlab) {
}
if (!is_init_completed()) {
// Can't do GC; try heap expansion to satisfy the request.
result = expand_heap_and_allocate(size, is_tlab);
if (result != nullptr) {
return result;
// Double checked locking, this ensure that is_init_completed() does not
// transition while expanding the heap.
MonitorLocker ml(InitCompleted_lock, Monitor::_no_safepoint_check_flag);
if (!is_init_completed()) {
// Can't do GC; try heap expansion to satisfy the request.
result = expand_heap_and_allocate(size, is_tlab);
if (result != nullptr) {
return result;
}
}
}

View File

@ -304,9 +304,12 @@ HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) {
HeapWord* result = nullptr;
for (uint try_count = 1; /* break */; try_count++) {
result = mem_allocate_cas_noexpand(size, is_tlab);
if (result != nullptr) {
break;
{
ConditionalMutexLocker locker(Heap_lock, !is_init_completed());
result = mem_allocate_cas_noexpand(size, is_tlab);
if (result != nullptr) {
break;
}
}
uint gc_count_before; // Read inside the Heap_lock locked region.
{
@ -320,10 +323,15 @@ HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) {
}
if (!is_init_completed()) {
// Can't do GC; try heap expansion to satisfy the request.
result = expand_heap_and_allocate(size, is_tlab);
if (result != nullptr) {
return result;
// Double checked locking, this ensure that is_init_completed() does not
// transition while expanding the heap.
MonitorLocker ml(InitCompleted_lock, Monitor::_no_safepoint_check_flag);
if (!is_init_completed()) {
// Can't do GC; try heap expansion to satisfy the request.
result = expand_heap_and_allocate(size, is_tlab);
if (result != nullptr) {
return result;
}
}
}

View File

@ -326,7 +326,7 @@ bool RegionNode::is_unreachable_region(const PhaseGVN* phase) {
// First, cut the simple case of fallthrough region when NONE of
// region's phis references itself directly or through a data node.
if (is_possible_unsafe_loop(phase)) {
if (is_possible_unsafe_loop()) {
// If we have a possible unsafe loop, check if the region node is actually unreachable from root.
if (is_unreachable_from_root(phase)) {
_is_unreachable_region = true;
@ -336,7 +336,7 @@ bool RegionNode::is_unreachable_region(const PhaseGVN* phase) {
return false;
}
bool RegionNode::is_possible_unsafe_loop(const PhaseGVN* phase) const {
bool RegionNode::is_possible_unsafe_loop() const {
uint max = outcnt();
uint i;
for (i = 0; i < max; i++) {
@ -634,8 +634,8 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) {
}
} else if (can_reshape && cnt == 1) {
// Is it dead loop?
// If it is LoopNopde it had 2 (+1 itself) inputs and
// one of them was cut. The loop is dead if it was EntryContol.
// If it is LoopNode it had 2 (+1 itself) inputs and
// one of them was cut. The loop is dead if it was EntryControl.
// Loop node may have only one input because entry path
// is removed in PhaseIdealLoop::Dominators().
assert(!this->is_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;

View File

@ -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):

View File

@ -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

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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();

View File

@ -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<VTransformNode*>& vtnodes() const { return _vtnodes; }
const GrowableArray<VTransformNode*>& 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<VTransformNode*> _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 {

View File

@ -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;
}
}
}
}

View File

@ -635,7 +635,6 @@ inline void AtomicAccess::dec(D volatile* dest, atomic_memory_order order) {
STATIC_ASSERT(std::is_pointer<D>::value || std::is_integral<D>::value);
using I = std::conditional_t<std::is_pointer<D>::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);
}

View File

@ -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;

View File

@ -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).
*
* <p>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.
* <p><b>String comparison and case-insensitive matching</b>
*
* <p>There are several related ways to compare {@code String} values; choose
* the one whose semantics fit your purpose:
*
* <ul>
* <li><b>Exact content equality</b> {@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.</li>
*
* <li><b>Simple case-insensitive equality</b> {@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.</li>
*
* <li><b>Unicode case-folded equivalence</b> {@link #equalsFoldCase(String)}
* (and the corresponding {@link #compareToFoldCase(String)} and {@link #UNICODE_CASEFOLD_ORDER})
* implement the Unicode <em>{@index "full case folding"}</em> rules defined in
* <a href="https://www.unicode.org/Public/UCD/latest/ucd/CaseFolding.txt">Unicode CaseFolding.txt</a>.
* 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
* <a href="https://www.unicode.org/versions/latest/core-spec/chapter-5/#G21790">
* caseless matching</a>, searching, or ordering.</li>
* </ul>
*
* <p>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 <em>{@index "Unicode case folding"}</em>. Two strings are considered equal
* by this method if their case-folded forms are identical.
* <p>
* Case folding is defined by the Unicode Standard in
* <a href="https://www.unicode.org/Public/UCD/latest/ucd/CaseFolding.txt">CaseFolding.txt</a>,
* 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"}.
* <p>
* 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<String> UNICODE_CASEFOLD_ORDER
= new FoldCaseComparator();
private static class FoldCaseComparator implements Comparator<String> {
@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 <em>{@index "Unicode case folding"}</em>.
* 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
* <a href="https://www.unicode.org/Public/UCD/latest/ucd/CaseFolding.txt">CaseFolding.txt</a>,
* including 1:M mappings, such as {@code"ß"} {@code }"ss"}.
* <p>
* 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 <em>{@index "full"}</em> case folding, providing stable and
* consistent results across all environments.
* <p>
* Note that this method does <em>not</em> 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
* <em>{@index "full case folding"}</em> 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.
* <p>

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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();
}
}
}

View File

@ -379,7 +379,7 @@ public enum ClassFileFormatVersion {
* @since 26
*
* @see <a
* href="https://docs.oracle.com/javase/specs/jvms/se26/html/index.html">
* href="https://docs.oracle.com/en/java/javase/26/docs/specs/jvms/index.html">
* <cite>The Java Virtual Machine Specification, Java SE 26 Edition</cite></a>
*/
RELEASE_26(70),

View File

@ -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<D extends GenericDeclaration> 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.

View File

@ -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;
}
/**

View File

@ -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;
/**

View File

@ -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.
* <p>
* 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
* <a href="https://www.unicode.org/reports/tr18/#Simple_Loose_Matches">Simple Loose Matches</a>
* rule defined in Unicode Technical Standard #18: Unicode Regular Expressions.
* <p>
* 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".
* <p>
* RL1.5 states: To meet this requirement, an implementation that supports case-sensitive matching should
* <ol>
* <li>Provide at least the simple, default Unicode case-insensitive matching, and</li>
* <li>Specify which character properties or constructs are closed under the matching.</li>
* </ol>
* <p>
* 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.
* <p>
* 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.
* <p>
* The family/char-properties are not "closed" and should remain unchanged. This is acceptable per RL1.5,
* if their behavior is clearly specified.
* <p>
* 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:
* <p>
* 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.
* <p>
* 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
*
* <pre>{@code
*
* ch -> inRange(lower, Character.toUpperCase(ch), upper) ||
* inRange(lower, Character.toLower(ch), upper) ||
* additionalClosingCharacters.contains(Character.toUpperCase(ch)) ||
* additionalClosingCharacters.contains(Character.toUpperCase(ch))
* }</pre>
*
* @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<Integer, Integer> 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);
}

View File

@ -0,0 +1 @@
@@COMPANY_NAME@@-@@VERSION_STRING@@-@@VERSION_DATE@@

View File

@ -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<URI> 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<InputStream> 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<ByteBuffer> 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<String> list() throws IOException {
ensureOpen();
Stream<String> 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();
}

View File

@ -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<Integer, Integer> 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
* <a href="https://www.unicode.org/reports/tr18/#Simple_Loose_Matches">Simple Loose Matches</a>
* rule defined in Unicode Technical Standard #18: Unicode Regular Expressions.
* <p>
* 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".
* <p>
* RL1.5 states: To meet this requirement, an implementation that supports case-sensitive matching should
* <ol>
* <li>Provide at least the simple, default Unicode case-insensitive matching, and</li>
* <li>Specify which character properties or constructs are closed under the matching.</li>
* </ol>
* <p>
* 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.
* <p>
* 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.
* <p>
* The family/char-properties are not "closed" and should remain unchanged. This is acceptable per RL1.5,
* if their behavior is clearly specified.
* <p>
* 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:
* <p>
* 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.
* <p>
* 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
*
* <pre>{@code
*
* ch -> inRange(lower, Character.toUpperCase(ch), upper) ||
* inRange(lower, Character.toLower(ch), upper) ||
* additionalClosingCharacters.contains(Character.toUpperCase(ch)) ||
* additionalClosingCharacters.contains(Character.toUpperCase(ch))
* }</pre>
*
* <p>
* @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);
}
}

View File

@ -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<D extends GenericDeclaration>
}
/**
* 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;
};
}

View File

@ -476,7 +476,7 @@ public enum SourceVersion {
* @since 26
*
* @see <a
* href="https://docs.oracle.com/javase/specs/jls/se26/html/index.html">
* href="https://docs.oracle.com/en/java/javase/26/docs/specs/jls/index.html">
* <cite>The Java Language Specification, Java SE 26 Edition</cite></a>
*/
RELEASE_26,

View File

@ -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!");
}

View File

@ -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;

View File

@ -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!");
}

View File

@ -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");
}

View File

@ -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)) {

View File

@ -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.");
}

View File

@ -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!");
}

View File

@ -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<T> extends ExchangeImpl<T> {
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;
}

View File

@ -577,7 +577,6 @@ public class QuicPacketEncoder {
final byte[] encodedPacketNumber;
final List<QuicFrame> 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<QuicFrame> 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<QuicFrame> 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<QuicFrame> 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);

View File

@ -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();

View File

@ -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<String> 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<String> 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<String> values) {
List<String> list = (values instanceof List)
? (List<String>) 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

View File

@ -100,7 +100,8 @@ javac.opt.arg.Werror=\
<key>(,<key>)*
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

View File

@ -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}

View File

@ -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.

View File

@ -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<ThreadProxy> threadList;
// loadObjectList is filled by attach0 method
private List<LoadObject> loadObjectList;
private List<JavaThread> 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<ThreadProxy> 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;

View File

@ -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();
}
}

View File

@ -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();

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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()));

View File

@ -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);
}
}

View File

@ -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<ThreadProxy, JavaThread> proxyToThread = createProxyToThread();;
String fillerForAddress = " ".repeat(2 + 2 * (int) VM.getVM().getAddressSize()) + "\t";
for (Iterator<ThreadProxy> 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("<interpreter> ");
@ -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("<Unknown compiled code>");
@ -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<ThreadProxy, JavaVFrame[]> jframeCache;
private Map<ThreadProxy, JavaThread> 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<ThreadProxy, JavaThread> createProxyToThread() {
Map<ThreadProxy, JavaThread> proxyToThread = new HashMap<>();
Threads threads = VM.getVM().getThreads();
for (int i = 0; i < threads.getNumberOfThreads(); i++) {
JavaThread cur = threads.getJavaThreadAt(i);
List<JavaVFrame> 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<String> 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) {

View File

@ -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.

View File

@ -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.

View File

@ -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<String> getReleaseInfo(ModuleReference mref) {
try {
Optional<InputStream> 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));
}
}

View File

@ -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}

View File

@ -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 <stdio.h>
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

View File

@ -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

View File

@ -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
*
*/

View File

@ -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);
}
}
}

View File

@ -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
*/

View File

@ -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;
}
}

View File

@ -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<String> 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");

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -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<Class<? extends SampleModel>> classes = new Vector<Class<? extends SampleModel>>();
classes.add(ComponentSampleModel.class);
classes.add(MultiPixelPackedSampleModel.class);
classes.add(SinglePixelPackedSampleModel.class);
classes.add(BandedSampleModel.class);
classes.add(PixelInterleavedSampleModel.class);
for (Class<? extends SampleModel> c : classes) {
doTest(c);
}
}
static void noException(SampleModel sm) {
System.err.println(sm);
throw new RuntimeException("No expected exception");
}
private static void doTest(Class<? extends SampleModel> 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<? extends SampleModel> 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;
}
}

View File

@ -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<String>();
// 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<Arguments> 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
Arguments.of("\uFB03", "ffi"), // ffi
Arguments.of("\u212A", "k"), // Kelvin sign k
Arguments.of("abc\uFB00def", "abcffdef"), // ff
Arguments.of("abc\uFB03def", "abcffidef"), // ffi
Arguments.of("abc\u212Adef", "abckdef"), // Kelvin sign k
// --- Fullwidth ---
Arguments.of("\uFF21\uFF22\uFF23", "\uFF41\uFF42\uFF43"), // "" ""
// --- 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<Arguments> 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"), // (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<Arguments> caseFoldOrderingProvider() {
return Stream.of(
Arguments.of("asa", "", -1), // ß ss "asa" < "ass"
Arguments.of("", "asa", +1),
Arguments.of("a\u00DF", "ass", 0), // vs ass
Arguments.of("\uFB03", "ffi", 0), // (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), // (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<Arguments> roundTripProvider() {
return Stream.of(
Arguments.of("abc"),
Arguments.of("ABC"),
Arguments.of("straße"),
Arguments.of("Àç"),
Arguments.of(""),
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();
}
}

View File

@ -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<URI> 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<URI> 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<String> 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)");
}
}

View File

@ -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 {
}

View File

@ -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 <X> Foo() {
}
<X> X x() {
return null;
}
}
@Test
void testMethod() throws NoSuchMethodException {
Method method = Foo.class.getDeclaredMethod("x");
TypeVariable<Method> 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<? extends Constructor<?>> tv = ctor.getTypeParameters()[0];
Constructor<?> gd = tv.getGenericDeclaration();
Constructor<?> gd2 = tv.getGenericDeclaration();
assertNotSame(gd, gd2);
}
}

View File

@ -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();
}
}

View File

@ -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() { }
}""");
}
}

View File

@ -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<String> found, List<String> 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<String> actualLines = actual.lines().toList();
List<String> expectLines = expect.lines().toList();
if (!(actualLines.equals(expectLines))) {
error("Unexpected detail message; expected: \n"
+ expect.indent(2)
+ "\nactual:\n"
+ actual.indent(2));
}
}
}

View File

@ -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;

View File

@ -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);
}
}