@@ -719,11 +722,14 @@ href="http://wg21.link/p0127r2">p0127r2) auto may
be used as a placeholder for the type of a non-type template parameter.
The type is deduced from the value provided in a template
instantiation.
* Function return type
+deduction (n3638) Only
use if the function body has a very small number of return
-statements, and generally relatively little other code.
Class template argument deduction (n3602, p0091r3) The template arguments
of a class template may be deduced from the arguments to a constructor.
@@ -736,7 +742,7 @@ harder to understand, because explicit type information is lacking. But
it can also remove the need to be explicit about types that are either
obvious, or that are very hard to write. For example, these allow the
addition of a scope-guard mechanism with nice syntax; something like
-this
A function's return type may be specified after the parameters and
+qualifiers (n2541).
+In such a declaration the normal return type is auto and
+the return type is indicated by -> followed by the type.
+Although both use auto in the "normal" leading return type
+position, this differs from function return type
+deduction, in that the return type is explicit rather than deduced,
+but specified in a trailing position.
+
Use of trailing return types is permitted. However, the normal,
+leading position for the return type is preferred. A trailing return
+type should only be used where it provides some benefit. Such benefits
+usually arise because a trailing return type is in a different scope
+than a leading return type.
+
+
If the function identifier is a nested name specifier, then the
+trailing return type occurs in the nested scope. This may permit simpler
+naming in the return type because of the different name lookup
+context.
+
The trailing return type is in the scope of the parameters,
+making their types accessible via decltype. For
+example
+
+
template<typename T, typename U> auto add(T t, U u) -> decltype(t + u);
Complex calculated leading return types may obscure the normal
+syntactic boundaries, making it more difficult for a reader to find the
+function name and parameters. This is particularly common in cases where
+the return type is being used for SFINAE. A trailing
+return type may be preferable in such situations.
+
Non-type template parameter
values
C++17 extended the arguments permitted for non-type template
diff --git a/doc/hotspot-style.md b/doc/hotspot-style.md
index e3ba4b470ce..3fd5468d531 100644
--- a/doc/hotspot-style.md
+++ b/doc/hotspot-style.md
@@ -642,6 +642,7 @@ use can make code much harder to understand.
parameter. The type is deduced from the value provided in a template
instantiation.
+
* Function return type deduction
([n3638](https://isocpp.org/files/papers/N3638.html))
Only use if the function body has a very small number of `return`
@@ -691,6 +692,42 @@ Here are a few closely related example bugs:
+### Trailing return type syntax for functions
+
+A function's return type may be specified after the parameters and qualifiers
+([n2541](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2541.htm)).
+In such a declaration the normal return type is `auto` and the return type is
+indicated by `->` followed by the type. Although both use `auto` in the
+"normal" leading return type position, this differs from
+[function return type deduction](#function-return-type-deduction),
+in that the return type is explicit rather than deduced, but specified in a
+trailing position.
+
+Use of trailing return types is permitted. However, the normal, leading
+position for the return type is preferred. A trailing return type should only
+be used where it provides some benefit. Such benefits usually arise because a
+trailing return type is in a different scope than a leading return type.
+
+* If the function identifier is a nested name specifier, then the trailing
+return type occurs in the nested scope. This may permit simpler naming in the
+return type because of the different name lookup context.
+
+* The trailing return type is in the scope of the parameters, making their
+types accessible via `decltype`. For example
+```
+template auto add(T t, U u) -> decltype(t + u);
+```
+rather than
+```
+template decltype((*(T*)0) + (*(U*)0)) add(T t, U u);
+```
+
+* Complex calculated leading return types may obscure the normal syntactic
+boundaries, making it more difficult for a reader to find the function name and
+parameters. This is particularly common in cases where the return type is
+being used for [SFINAE]. A trailing return type may be preferable in such
+situations.
+
### Non-type template parameter values
C++17 extended the arguments permitted for non-type template parameters
From 8ab8d02e40e987a5eb5e8036ff4f12146ac2b16a Mon Sep 17 00:00:00 2001
From: David Holmes
Date: Wed, 10 Sep 2025 05:45:31 +0000
Subject: [PATCH 010/120] 8366938: Test
runtime/handshake/HandshakeTimeoutTest.java crashed
Reviewed-by: kbarrett
---
.../hotspot/jtreg/runtime/handshake/HandshakeTimeoutTest.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/hotspot/jtreg/runtime/handshake/HandshakeTimeoutTest.java b/test/hotspot/jtreg/runtime/handshake/HandshakeTimeoutTest.java
index a1a5ff68c31..7f1ee4be711 100644
--- a/test/hotspot/jtreg/runtime/handshake/HandshakeTimeoutTest.java
+++ b/test/hotspot/jtreg/runtime/handshake/HandshakeTimeoutTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -36,7 +36,7 @@ import jdk.test.whitebox.WhiteBox;
* @library /testlibrary /test/lib
* @build HandshakeTimeoutTest
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
- * @run driver HandshakeTimeoutTest
+ * @run driver/timeout=480 HandshakeTimeoutTest
*/
public class HandshakeTimeoutTest {
From 2705e880b64825044e67487f01263121780d8f7a Mon Sep 17 00:00:00 2001
From: Disha
Date: Wed, 10 Sep 2025 06:16:12 +0000
Subject: [PATCH 011/120] 8366764: Deproblemlist
java/awt/ScrollPane/ScrollPositionTest.java
Reviewed-by: azvegint
---
test/jdk/ProblemList.txt | 1 -
1 file changed, 1 deletion(-)
diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt
index aac9d9a8a21..475704f7e95 100644
--- a/test/jdk/ProblemList.txt
+++ b/test/jdk/ProblemList.txt
@@ -455,7 +455,6 @@ java/awt/Focus/TranserFocusToWindow/TranserFocusToWindow.java 6848810 macosx-all
java/awt/FileDialog/ModalFocus/FileDialogModalFocusTest.java 8194751 linux-all
java/awt/image/VolatileImage/BitmaskVolatileImage.java 8133102 linux-all
java/awt/SplashScreen/MultiResolutionSplash/unix/UnixMultiResolutionSplashTest.java 8203004 linux-all
-java/awt/ScrollPane/ScrollPositionTest.java 8040070 linux-all
java/awt/ScrollPane/ScrollPaneEventType.java 8296516 macosx-all
java/awt/Robot/AcceptExtraMouseButtons/AcceptExtraMouseButtons.java 7107528 linux-all,macosx-all
java/awt/Mouse/MouseDragEvent/MouseDraggedTest.java 8080676 linux-all
From b7b01d6f564ae34e913ae51bd2f8243a32807136 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Jeli=C5=84ski?=
Date: Wed, 10 Sep 2025 06:16:39 +0000
Subject: [PATCH 012/120] 8366984: Remove delay slot support
Reviewed-by: dlong, epeter
---
.../cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 5 -
src/hotspot/cpu/arm/arm.ad | 20 +-
src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp | 5 -
src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp | 5 -
.../cpu/riscv/c1_LIRAssembler_riscv.cpp | 2 -
src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp | 4 -
src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp | 5 -
src/hotspot/share/adlc/adlparse.cpp | 32 +-
src/hotspot/share/adlc/formsopt.cpp | 4 -
src/hotspot/share/adlc/formsopt.hpp | 4 -
src/hotspot/share/adlc/output_c.cpp | 23 +-
src/hotspot/share/adlc/output_h.cpp | 42 +--
src/hotspot/share/c1/c1_LIR.cpp | 20 --
src/hotspot/share/c1/c1_LIR.hpp | 24 --
src/hotspot/share/c1/c1_LIRAssembler.cpp | 3 +-
src/hotspot/share/c1/c1_LIRAssembler.hpp | 6 +-
src/hotspot/share/code/relocInfo.hpp | 4 +-
src/hotspot/share/opto/output.cpp | 304 ++----------------
src/hotspot/share/runtime/sharedRuntime.cpp | 2 -
19 files changed, 54 insertions(+), 460 deletions(-)
diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
index e9bb2350b5b..d788c0c201a 100644
--- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
@@ -2585,11 +2585,6 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) {
}
-void LIR_Assembler::emit_delay(LIR_OpDelay*) {
- Unimplemented();
-}
-
-
void LIR_Assembler::monitor_address(int monitor_no, LIR_Opr dst) {
__ lea(dst->as_register(), frame_map()->address_for_monitor_lock(monitor_no));
}
diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad
index 45d51aaac57..2835a256153 100644
--- a/src/hotspot/cpu/arm/arm.ad
+++ b/src/hotspot/cpu/arm/arm.ad
@@ -3281,18 +3281,18 @@ pipe_class loadPollP(iRegP poll) %{
%}
pipe_class br(Universe br, label labl) %{
- single_instruction_with_delay_slot;
+ single_instruction;
BR : R;
%}
pipe_class br_cc(Universe br, cmpOp cmp, flagsReg cr, label labl) %{
- single_instruction_with_delay_slot;
+ single_instruction;
cr : E(read);
BR : R;
%}
pipe_class br_reg(Universe br, cmpOp cmp, iRegI op1, label labl) %{
- single_instruction_with_delay_slot;
+ single_instruction;
op1 : E(read);
BR : R;
MS : R;
@@ -3323,14 +3323,14 @@ pipe_class call(method meth) %{
%}
pipe_class tail_call(Universe ignore, label labl) %{
- single_instruction; has_delay_slot;
+ single_instruction;
fixed_latency(100);
BR : R(1);
MS : R(1);
%}
pipe_class ret(Universe ignore) %{
- single_instruction; has_delay_slot;
+ single_instruction;
BR : R(1);
MS : R(1);
%}
@@ -3373,14 +3373,6 @@ pipe_class cadd_cmpltmask( iRegI p, iRegI q, iRegI y ) %{
IALU : R(3)
%}
-// Perform a compare, then move conditionally in a branch delay slot.
-pipe_class min_max( iRegI src2, iRegI srcdst ) %{
- src2 : E(read);
- srcdst : E(read);
- IALU : R;
- BR : R;
-%}
-
// Define the class for the Nop node
define %{
MachNop = ialu_nop;
@@ -9053,7 +9045,7 @@ instruct clear_array(iRegX cnt, iRegP base, iRegI temp, iRegX zero, Universe dum
format %{ "MOV $zero,0\n"
" MOV $temp,$cnt\n"
"loop: SUBS $temp,$temp,4\t! Count down a dword of bytes\n"
- " STR.ge $zero,[$base+$temp]\t! delay slot"
+ " STR.ge $zero,[$base+$temp]\n"
" B.gt loop\t\t! Clearing loop\n" %}
ins_encode %{
__ mov($zero$$Register, 0);
diff --git a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp
index c3b91e8c76f..d6ed82dcdb2 100644
--- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp
+++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp
@@ -2552,11 +2552,6 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) {
fatal("Type profiling not implemented on this platform");
}
-void LIR_Assembler::emit_delay(LIR_OpDelay*) {
- Unimplemented();
-}
-
-
void LIR_Assembler::monitor_address(int monitor_no, LIR_Opr dst) {
Address mon_addr = frame_map()->address_for_monitor_lock(monitor_no);
__ add_slow(dst->as_pointer_register(), mon_addr.base(), mon_addr.disp());
diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
index 73509c22134..3ca75305eca 100644
--- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
@@ -2747,11 +2747,6 @@ void LIR_Assembler::align_backward_branch_target() {
}
-void LIR_Assembler::emit_delay(LIR_OpDelay* op) {
- Unimplemented();
-}
-
-
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest, LIR_Opr tmp) {
// tmp must be unused
assert(tmp->is_illegal(), "wasting a register if tmp is allocated");
diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
index f60be85a141..c3fe72870cf 100644
--- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
@@ -1590,8 +1590,6 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) {
}
}
-void LIR_Assembler::emit_delay(LIR_OpDelay*) { Unimplemented(); }
-
void LIR_Assembler::monitor_address(int monitor_no, LIR_Opr dst) {
__ la(dst->as_register(), frame_map()->address_for_monitor_lock(monitor_no));
}
diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp
index 87dc8b9286d..b875eeca9ad 100644
--- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp
+++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp
@@ -2816,10 +2816,6 @@ void LIR_Assembler::align_backward_branch_target() {
__ align(OptoLoopAlignment);
}
-void LIR_Assembler::emit_delay(LIR_OpDelay* op) {
- ShouldNotCallThis(); // There are no delay slots on ZARCH_64.
-}
-
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest, LIR_Opr tmp) {
// tmp must be unused
assert(tmp->is_illegal(), "wasting a register if tmp is allocated");
diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp
index a30bbe08c55..98759295bb1 100644
--- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp
@@ -3001,11 +3001,6 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) {
__ bind(next);
}
-void LIR_Assembler::emit_delay(LIR_OpDelay*) {
- Unimplemented();
-}
-
-
void LIR_Assembler::monitor_address(int monitor_no, LIR_Opr dst) {
__ lea(dst->as_register(), frame_map()->address_for_monitor_lock(monitor_no));
}
diff --git a/src/hotspot/share/adlc/adlparse.cpp b/src/hotspot/share/adlc/adlparse.cpp
index 15dbf070674..356c24760e8 100644
--- a/src/hotspot/share/adlc/adlparse.cpp
+++ b/src/hotspot/share/adlc/adlparse.cpp
@@ -1389,13 +1389,8 @@ void ADLParser::pipe_parse(void) {
}
if (!strcmp(ident, "branch_has_delay_slot")) {
- skipws();
- if (_curchar == ';') {
- next_char(); skipws();
- }
-
- pipeline->_branchHasDelaySlot = true;
- continue;
+ parse_err(SYNERR, "Using obsolete token, branch_has_delay_slot");
+ break;
}
if (!strcmp(ident, "max_instructions_per_bundle")) {
@@ -1762,16 +1757,8 @@ void ADLParser::pipe_class_parse(PipelineForm &pipeline) {
if (!strcmp(ident, "one_instruction_with_delay_slot") ||
!strcmp(ident, "single_instruction_with_delay_slot")) {
- skipws();
- if (_curchar != ';') {
- parse_err(SYNERR, "missing \";\" in latency definition\n");
- return;
- }
-
- pipe_class->setInstructionCount(1);
- pipe_class->setBranchDelay(true);
- next_char(); skipws();
- continue;
+ parse_err(SYNERR, "Using obsolete token, %s", ident);
+ return;
}
if (!strcmp(ident, "one_instruction") ||
@@ -1831,15 +1818,8 @@ void ADLParser::pipe_class_parse(PipelineForm &pipeline) {
}
if (!strcmp(ident, "has_delay_slot")) {
- skipws();
- if (_curchar != ';') {
- parse_err(SYNERR, "missing \";\" after \"has_delay_slot\"\n");
- return;
- }
-
- pipe_class->setBranchDelay(true);
- next_char(); skipws();
- continue;
+ parse_err(SYNERR, "Using obsolete token, %s", ident);
+ return;
}
if (!strcmp(ident, "force_serialization")) {
diff --git a/src/hotspot/share/adlc/formsopt.cpp b/src/hotspot/share/adlc/formsopt.cpp
index 01fe6288c53..92489da2f5a 100644
--- a/src/hotspot/share/adlc/formsopt.cpp
+++ b/src/hotspot/share/adlc/formsopt.cpp
@@ -512,7 +512,6 @@ PipelineForm::PipelineForm()
, _classlist ()
, _classcnt (0)
, _variableSizeInstrs (false)
- , _branchHasDelaySlot (false)
, _maxInstrsPerBundle (0)
, _maxBundlesPerCycle (1)
, _instrUnitSize (0)
@@ -546,8 +545,6 @@ void PipelineForm::output(FILE *fp) { // Write info to output files
fprintf(fp," fixed-sized bundles of %d bytes", _bundleUnitSize);
else
fprintf(fp," fixed-sized instructions");
- if (_branchHasDelaySlot)
- fprintf(fp,", branch has delay slot");
if (_maxInstrsPerBundle > 0)
fprintf(fp,", max of %d instruction%s in parallel",
_maxInstrsPerBundle, _maxInstrsPerBundle > 1 ? "s" : "");
@@ -637,7 +634,6 @@ PipeClassForm::PipeClassForm(const char *id, int num)
, _fixed_latency(0)
, _instruction_count(0)
, _has_multiple_bundles(false)
- , _has_branch_delay_slot(false)
, _force_serialization(false)
, _may_have_no_code(false) {
}
diff --git a/src/hotspot/share/adlc/formsopt.hpp b/src/hotspot/share/adlc/formsopt.hpp
index db7b9dbd8d8..34cbc24bed0 100644
--- a/src/hotspot/share/adlc/formsopt.hpp
+++ b/src/hotspot/share/adlc/formsopt.hpp
@@ -387,7 +387,6 @@ public:
int _classcnt; // Number of classes
bool _variableSizeInstrs; // Indicates if this architecture has variable sized instructions
- bool _branchHasDelaySlot; // Indicates that branches have delay slot instructions
int _maxInstrsPerBundle; // Indicates the maximum number of instructions for ILP
int _maxBundlesPerCycle; // Indicates the maximum number of bundles for ILP
int _instrUnitSize; // The minimum instruction unit size, in bytes
@@ -499,7 +498,6 @@ public:
int _fixed_latency; // Always takes this number of cycles
int _instruction_count; // Number of instructions in first bundle
bool _has_multiple_bundles; // Indicates if 1 or multiple bundles
- bool _has_branch_delay_slot; // Has branch delay slot as last instruction
bool _force_serialization; // This node serializes relative to surrounding nodes
bool _may_have_no_code; // This node may generate no code based on register allocation
@@ -518,13 +516,11 @@ public:
void setInstructionCount(int i) { _instruction_count = i; }
void setMultipleBundles(bool b) { _has_multiple_bundles = b; }
- void setBranchDelay(bool s) { _has_branch_delay_slot = s; }
void setForceSerialization(bool s) { _force_serialization = s; }
void setMayHaveNoCode(bool s) { _may_have_no_code = s; }
int InstructionCount() const { return _instruction_count; }
bool hasMultipleBundles() const { return _has_multiple_bundles; }
- bool hasBranchDelay() const { return _has_branch_delay_slot; }
bool forceSerialization() const { return _force_serialization; }
bool mayHaveNoCode() const { return _may_have_no_code; }
diff --git a/src/hotspot/share/adlc/output_c.cpp b/src/hotspot/share/adlc/output_c.cpp
index abebf39a2b2..caf2c9952a6 100644
--- a/src/hotspot/share/adlc/output_c.cpp
+++ b/src/hotspot/share/adlc/output_c.cpp
@@ -794,8 +794,8 @@ void ArchDesc::build_pipe_classes(FILE *fp_cpp) {
// Create the pipeline class description
- fprintf(fp_cpp, "static const Pipeline pipeline_class_Zero_Instructions(0, 0, true, 0, 0, false, false, false, false, nullptr, nullptr, nullptr, Pipeline_Use(0, 0, 0, nullptr));\n\n");
- fprintf(fp_cpp, "static const Pipeline pipeline_class_Unknown_Instructions(0, 0, true, 0, 0, false, true, true, false, nullptr, nullptr, nullptr, Pipeline_Use(0, 0, 0, nullptr));\n\n");
+ fprintf(fp_cpp, "static const Pipeline pipeline_class_Zero_Instructions(0, 0, true, 0, 0, false, false, false, nullptr, nullptr, nullptr, Pipeline_Use(0, 0, 0, nullptr));\n\n");
+ fprintf(fp_cpp, "static const Pipeline pipeline_class_Unknown_Instructions(0, 0, true, 0, 0, true, true, false, nullptr, nullptr, nullptr, Pipeline_Use(0, 0, 0, nullptr));\n\n");
fprintf(fp_cpp, "const Pipeline_Use_Element Pipeline_Use::elaborated_elements[%d] = {\n", _pipeline->_rescount);
for (int i1 = 0; i1 < _pipeline->_rescount; i1++) {
@@ -895,12 +895,11 @@ void ArchDesc::build_pipe_classes(FILE *fp_cpp) {
fprintf(fp_cpp, "(uint)stage_%s", _pipeline->_stages.name(maxWriteStage));
else
fprintf(fp_cpp, "((uint)stage_%s)+%d", _pipeline->_stages.name(maxWriteStage), maxMoreInstrs);
- fprintf(fp_cpp, ", %d, %s, %d, %d, %s, %s, %s, %s,\n",
+ fprintf(fp_cpp, ", %d, %s, %d, %d, %s, %s, %s,\n",
paramcount,
pipeclass->hasFixedLatency() ? "true" : "false",
pipeclass->fixedLatency(),
pipeclass->InstructionCount(),
- pipeclass->hasBranchDelay() ? "true" : "false",
pipeclass->hasMultipleBundles() ? "true" : "false",
pipeclass->forceSerialization() ? "true" : "false",
pipeclass->mayHaveNoCode() ? "true" : "false" );
@@ -979,16 +978,6 @@ void ArchDesc::build_pipe_classes(FILE *fp_cpp) {
fprintf(fp_cpp, "#ifndef PRODUCT\n");
fprintf(fp_cpp, "void Bundle::dump(outputStream *st) const {\n");
- fprintf(fp_cpp, " static const char * bundle_flags[] = {\n");
- fprintf(fp_cpp, " \"\",\n");
- fprintf(fp_cpp, " \"use nop delay\",\n");
- fprintf(fp_cpp, " \"use unconditional delay\",\n");
- fprintf(fp_cpp, " \"use conditional delay\",\n");
- fprintf(fp_cpp, " \"used in conditional delay\",\n");
- fprintf(fp_cpp, " \"used in unconditional delay\",\n");
- fprintf(fp_cpp, " \"used in all conditional delays\",\n");
- fprintf(fp_cpp, " };\n\n");
-
fprintf(fp_cpp, " static const char *resource_names[%d] = {", _pipeline->_rescount);
// Don't add compound resources to the list of resource names
const char* resource;
@@ -1003,12 +992,8 @@ void ArchDesc::build_pipe_classes(FILE *fp_cpp) {
// See if the same string is in the table
fprintf(fp_cpp, " bool needs_comma = false;\n\n");
- fprintf(fp_cpp, " if (_flags) {\n");
- fprintf(fp_cpp, " st->print(\"%%s\", bundle_flags[_flags]);\n");
- fprintf(fp_cpp, " needs_comma = true;\n");
- fprintf(fp_cpp, " };\n");
fprintf(fp_cpp, " if (instr_count()) {\n");
- fprintf(fp_cpp, " st->print(\"%%s%%d instr%%s\", needs_comma ? \", \" : \"\", instr_count(), instr_count() != 1 ? \"s\" : \"\");\n");
+ fprintf(fp_cpp, " st->print(\"%%d instr%%s\", instr_count(), instr_count() != 1 ? \"s\" : \"\");\n");
fprintf(fp_cpp, " needs_comma = true;\n");
fprintf(fp_cpp, " };\n");
fprintf(fp_cpp, " uint r = resources_used();\n");
diff --git a/src/hotspot/share/adlc/output_h.cpp b/src/hotspot/share/adlc/output_h.cpp
index 78cf5ea7988..e3fde235443 100644
--- a/src/hotspot/share/adlc/output_h.cpp
+++ b/src/hotspot/share/adlc/output_h.cpp
@@ -935,8 +935,6 @@ void ArchDesc::declare_pipe_classes(FILE *fp_hpp) {
_pipeline->_variableSizeInstrs ? 1 : 0);
fprintf(fp_hpp, " _fixed_size_instructions = %d,\n",
_pipeline->_variableSizeInstrs ? 0 : 1);
- fprintf(fp_hpp, " _branch_has_delay_slot = %d,\n",
- _pipeline->_branchHasDelaySlot ? 1 : 0);
fprintf(fp_hpp, " _max_instrs_per_bundle = %d,\n",
_pipeline->_maxInstrsPerBundle);
fprintf(fp_hpp, " _max_bundles_per_cycle = %d,\n",
@@ -983,7 +981,6 @@ void ArchDesc::declare_pipe_classes(FILE *fp_hpp) {
fprintf(fp_hpp, " const unsigned char _fixed_latency;\n");
fprintf(fp_hpp, " const unsigned char _instruction_count;\n");
fprintf(fp_hpp, " const bool _has_fixed_latency;\n");
- fprintf(fp_hpp, " const bool _has_branch_delay;\n");
fprintf(fp_hpp, " const bool _has_multiple_bundles;\n");
fprintf(fp_hpp, " const bool _force_serialization;\n");
fprintf(fp_hpp, " const bool _may_have_no_code;\n");
@@ -998,7 +995,6 @@ void ArchDesc::declare_pipe_classes(FILE *fp_hpp) {
fprintf(fp_hpp, " bool has_fixed_latency,\n");
fprintf(fp_hpp, " uint fixed_latency,\n");
fprintf(fp_hpp, " uint instruction_count,\n");
- fprintf(fp_hpp, " bool has_branch_delay,\n");
fprintf(fp_hpp, " bool has_multiple_bundles,\n");
fprintf(fp_hpp, " bool force_serialization,\n");
fprintf(fp_hpp, " bool may_have_no_code,\n");
@@ -1011,7 +1007,6 @@ void ArchDesc::declare_pipe_classes(FILE *fp_hpp) {
fprintf(fp_hpp, " , _fixed_latency(fixed_latency)\n");
fprintf(fp_hpp, " , _instruction_count(instruction_count)\n");
fprintf(fp_hpp, " , _has_fixed_latency(has_fixed_latency)\n");
- fprintf(fp_hpp, " , _has_branch_delay(has_branch_delay)\n");
fprintf(fp_hpp, " , _has_multiple_bundles(has_multiple_bundles)\n");
fprintf(fp_hpp, " , _force_serialization(force_serialization)\n");
fprintf(fp_hpp, " , _may_have_no_code(may_have_no_code)\n");
@@ -1046,8 +1041,6 @@ void ArchDesc::declare_pipe_classes(FILE *fp_hpp) {
fprintf(fp_hpp, " return (_resource_use._count); }\n\n");
fprintf(fp_hpp, " uint instructionCount() const {\n");
fprintf(fp_hpp, " return (_instruction_count); }\n\n");
- fprintf(fp_hpp, " bool hasBranchDelay() const {\n");
- fprintf(fp_hpp, " return (_has_branch_delay); }\n\n");
fprintf(fp_hpp, " bool hasMultipleBundles() const {\n");
fprintf(fp_hpp, " return (_has_multiple_bundles); }\n\n");
fprintf(fp_hpp, " bool forceSerialization() const {\n");
@@ -1071,50 +1064,19 @@ void ArchDesc::declare_pipe_classes(FILE *fp_hpp) {
uint rshift = rescount;
fprintf(fp_hpp, "protected:\n");
- fprintf(fp_hpp, " enum {\n");
- fprintf(fp_hpp, " _unused_delay = 0x%x,\n", 0);
- fprintf(fp_hpp, " _use_nop_delay = 0x%x,\n", 1);
- fprintf(fp_hpp, " _use_unconditional_delay = 0x%x,\n", 2);
- fprintf(fp_hpp, " _use_conditional_delay = 0x%x,\n", 3);
- fprintf(fp_hpp, " _used_in_conditional_delay = 0x%x,\n", 4);
- fprintf(fp_hpp, " _used_in_unconditional_delay = 0x%x,\n", 5);
- fprintf(fp_hpp, " _used_in_all_conditional_delays = 0x%x,\n", 6);
- fprintf(fp_hpp, "\n");
- fprintf(fp_hpp, " _use_delay = 0x%x,\n", 3);
- fprintf(fp_hpp, " _used_in_delay = 0x%x\n", 4);
- fprintf(fp_hpp, " };\n\n");
- fprintf(fp_hpp, " uint _flags : 3,\n");
- fprintf(fp_hpp, " _starts_bundle : 1,\n");
+ fprintf(fp_hpp, " uint _starts_bundle : 1,\n");
fprintf(fp_hpp, " _instr_count : %d,\n", mshift);
fprintf(fp_hpp, " _resources_used : %d;\n", rshift);
fprintf(fp_hpp, "public:\n");
- fprintf(fp_hpp, " Bundle() : _flags(_unused_delay), _starts_bundle(0), _instr_count(0), _resources_used(0) {}\n\n");
+ fprintf(fp_hpp, " Bundle() : _starts_bundle(0), _instr_count(0), _resources_used(0) {}\n\n");
fprintf(fp_hpp, " void set_instr_count(uint i) { _instr_count = i; }\n");
fprintf(fp_hpp, " void set_resources_used(uint i) { _resources_used = i; }\n");
- fprintf(fp_hpp, " void clear_usage() { _flags = _unused_delay; }\n");
fprintf(fp_hpp, " void set_starts_bundle() { _starts_bundle = true; }\n");
- fprintf(fp_hpp, " uint flags() const { return (_flags); }\n");
fprintf(fp_hpp, " uint instr_count() const { return (_instr_count); }\n");
fprintf(fp_hpp, " uint resources_used() const { return (_resources_used); }\n");
fprintf(fp_hpp, " bool starts_bundle() const { return (_starts_bundle != 0); }\n");
- fprintf(fp_hpp, " void set_use_nop_delay() { _flags = _use_nop_delay; }\n");
- fprintf(fp_hpp, " void set_use_unconditional_delay() { _flags = _use_unconditional_delay; }\n");
- fprintf(fp_hpp, " void set_use_conditional_delay() { _flags = _use_conditional_delay; }\n");
- fprintf(fp_hpp, " void set_used_in_unconditional_delay() { _flags = _used_in_unconditional_delay; }\n");
- fprintf(fp_hpp, " void set_used_in_conditional_delay() { _flags = _used_in_conditional_delay; }\n");
- fprintf(fp_hpp, " void set_used_in_all_conditional_delays() { _flags = _used_in_all_conditional_delays; }\n");
-
- fprintf(fp_hpp, " bool use_nop_delay() { return (_flags == _use_nop_delay); }\n");
- fprintf(fp_hpp, " bool use_unconditional_delay() { return (_flags == _use_unconditional_delay); }\n");
- fprintf(fp_hpp, " bool use_conditional_delay() { return (_flags == _use_conditional_delay); }\n");
- fprintf(fp_hpp, " bool used_in_unconditional_delay() { return (_flags == _used_in_unconditional_delay); }\n");
- fprintf(fp_hpp, " bool used_in_conditional_delay() { return (_flags == _used_in_conditional_delay); }\n");
- fprintf(fp_hpp, " bool used_in_all_conditional_delays() { return (_flags == _used_in_all_conditional_delays); }\n");
- fprintf(fp_hpp, " bool use_delay() { return ((_flags & _use_delay) != 0); }\n");
- fprintf(fp_hpp, " bool used_in_delay() { return ((_flags & _used_in_delay) != 0); }\n\n");
-
fprintf(fp_hpp, "#ifndef PRODUCT\n");
fprintf(fp_hpp, " void dump(outputStream *st = tty) const;\n");
fprintf(fp_hpp, "#endif\n");
diff --git a/src/hotspot/share/c1/c1_LIR.cpp b/src/hotspot/share/c1/c1_LIR.cpp
index 4c8ebd5a09d..f11e178bd55 100644
--- a/src/hotspot/share/c1/c1_LIR.cpp
+++ b/src/hotspot/share/c1/c1_LIR.cpp
@@ -800,15 +800,6 @@ void LIR_OpVisitState::visit(LIR_Op* op) {
}
-// LIR_OpDelay
- case lir_delay_slot: {
- assert(op->as_OpDelay() != nullptr, "must be");
- LIR_OpDelay* opDelay = (LIR_OpDelay*)op;
-
- visit(opDelay->delay_op());
- break;
- }
-
// LIR_OpTypeCheck
case lir_instanceof:
case lir_checkcast:
@@ -1073,10 +1064,6 @@ void LIR_OpAssert::emit_code(LIR_Assembler* masm) {
}
#endif
-void LIR_OpDelay::emit_code(LIR_Assembler* masm) {
- masm->emit_delay(this);
-}
-
void LIR_OpProfileCall::emit_code(LIR_Assembler* masm) {
masm->emit_profile_call(this);
}
@@ -1761,8 +1748,6 @@ const char * LIR_Op::name() const {
// LIR_OpLock
case lir_lock: s = "lock"; break;
case lir_unlock: s = "unlock"; break;
- // LIR_OpDelay
- case lir_delay_slot: s = "delay"; break;
// LIR_OpTypeCheck
case lir_instanceof: s = "instanceof"; break;
case lir_checkcast: s = "checkcast"; break;
@@ -2044,11 +2029,6 @@ void LIR_OpAssert::print_instr(outputStream* out) const {
#endif
-void LIR_OpDelay::print_instr(outputStream* out) const {
- _op->print_on(out);
-}
-
-
// LIR_OpProfileCall
void LIR_OpProfileCall::print_instr(outputStream* out) const {
profiled_method()->name()->print_symbol_on(out);
diff --git a/src/hotspot/share/c1/c1_LIR.hpp b/src/hotspot/share/c1/c1_LIR.hpp
index c7726bf5c3f..0427c868e6f 100644
--- a/src/hotspot/share/c1/c1_LIR.hpp
+++ b/src/hotspot/share/c1/c1_LIR.hpp
@@ -879,7 +879,6 @@ class LIR_OpConvert;
class LIR_OpAllocObj;
class LIR_OpReturn;
class LIR_Op2;
-class LIR_OpDelay;
class LIR_Op3;
class LIR_OpAllocArray;
class LIR_Op4;
@@ -985,9 +984,6 @@ enum LIR_Code {
, lir_lock
, lir_unlock
, end_opLock
- , begin_delay_slot
- , lir_delay_slot
- , end_delay_slot
, begin_opTypeCheck
, lir_instanceof
, lir_checkcast
@@ -1124,7 +1120,6 @@ class LIR_Op: public CompilationResourceObj {
virtual LIR_OpCall* as_OpCall() { return nullptr; }
virtual LIR_OpJavaCall* as_OpJavaCall() { return nullptr; }
virtual LIR_OpLabel* as_OpLabel() { return nullptr; }
- virtual LIR_OpDelay* as_OpDelay() { return nullptr; }
virtual LIR_OpLock* as_OpLock() { return nullptr; }
virtual LIR_OpAllocArray* as_OpAllocArray() { return nullptr; }
virtual LIR_OpAllocObj* as_OpAllocObj() { return nullptr; }
@@ -1886,25 +1881,6 @@ class LIR_OpLoadKlass: public LIR_Op {
void print_instr(outputStream* out) const PRODUCT_RETURN;
};
-class LIR_OpDelay: public LIR_Op {
- friend class LIR_OpVisitState;
-
- private:
- LIR_Op* _op;
-
- public:
- LIR_OpDelay(LIR_Op* op, CodeEmitInfo* info):
- LIR_Op(lir_delay_slot, LIR_OprFact::illegalOpr, info),
- _op(op) {
- assert(op->code() == lir_nop, "should be filling with nops");
- }
- virtual void emit_code(LIR_Assembler* masm);
- virtual LIR_OpDelay* as_OpDelay() { return this; }
- void print_instr(outputStream* out) const PRODUCT_RETURN;
- LIR_Op* delay_op() const { return _op; }
- CodeEmitInfo* call_info() const { return info(); }
-};
-
#ifdef ASSERT
// LIR_OpAssert
class LIR_OpAssert : public LIR_Op2 {
diff --git a/src/hotspot/share/c1/c1_LIRAssembler.cpp b/src/hotspot/share/c1/c1_LIRAssembler.cpp
index 7cf414ae7dc..6dbd35f054f 100644
--- a/src/hotspot/share/c1/c1_LIRAssembler.cpp
+++ b/src/hotspot/share/c1/c1_LIRAssembler.cpp
@@ -194,8 +194,7 @@ void LIR_Assembler::emit_exception_entries(ExceptionInfoList* info_list) {
XHandler* handler = handlers->handler_at(j);
assert(handler->lir_op_id() != -1, "handler not processed by LinearScan");
assert(handler->entry_code() == nullptr ||
- handler->entry_code()->instructions_list()->last()->code() == lir_branch ||
- handler->entry_code()->instructions_list()->last()->code() == lir_delay_slot, "last operation must be branch");
+ handler->entry_code()->instructions_list()->last()->code() == lir_branch, "last operation must be branch");
if (handler->entry_pco() == -1) {
// entry code not emitted yet
diff --git a/src/hotspot/share/c1/c1_LIRAssembler.hpp b/src/hotspot/share/c1/c1_LIRAssembler.hpp
index a4c5fd61d4c..4cb313af901 100644
--- a/src/hotspot/share/c1/c1_LIRAssembler.hpp
+++ b/src/hotspot/share/c1/c1_LIRAssembler.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -154,8 +154,7 @@ class LIR_Assembler: public CompilationResourceObj {
void emit_block(BlockBegin* block);
void emit_lir_list(LIR_List* list);
- // any last minute peephole optimizations are performed here. In
- // particular sparc uses this for delay slot filling.
+ // any last minute peephole optimizations are performed here.
void peephole(LIR_List* list);
void return_op(LIR_Opr result, C1SafepointPollStub* code_stub);
@@ -204,7 +203,6 @@ class LIR_Assembler: public CompilationResourceObj {
void emit_rtcall(LIR_OpRTCall* op);
void emit_profile_call(LIR_OpProfileCall* op);
void emit_profile_type(LIR_OpProfileType* op);
- void emit_delay(LIR_OpDelay* op);
void arith_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest, CodeEmitInfo* info);
void arithmetic_idiv(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr temp, LIR_Opr result, CodeEmitInfo* info);
diff --git a/src/hotspot/share/code/relocInfo.hpp b/src/hotspot/share/code/relocInfo.hpp
index 714a964b28d..a6a08815d10 100644
--- a/src/hotspot/share/code/relocInfo.hpp
+++ b/src/hotspot/share/code/relocInfo.hpp
@@ -186,8 +186,7 @@ class nmethod;
// relative offset. (Both n and l are relative to the call's first byte.)
//
// The limit l of the search is exclusive. However, if it points within
-// the call (e.g., offset zero), it is adjusted to point after the call and
-// any associated machine-specific delay slot.
+// the call (e.g., offset zero), it is adjusted to point after the call.
//
// Since the offsets could be as wide as 32-bits, these conventions
// put no restrictions whatever upon code reorganization.
@@ -1109,7 +1108,6 @@ class virtual_call_Relocation : public CallRelocation {
// data is packed as scaled offsets in "2_ints" format: [f l] or [Ff Ll]
// oop_limit is set to 0 if the limit falls somewhere within the call.
// When unpacking, a zero oop_limit is taken to refer to the end of the call.
- // (This has the effect of bringing in the call's delay slot on SPARC.)
void pack_data_to(CodeSection* dest) override;
void unpack_data() override;
diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp
index 9a6970ebf20..90d24b609a7 100644
--- a/src/hotspot/share/opto/output.cpp
+++ b/src/hotspot/share/opto/output.cpp
@@ -106,12 +106,6 @@ private:
// Remember the next node
Node *_next_node;
- // Use this for an unconditional branch delay slot
- Node *_unconditional_delay_slot;
-
- // Pointer to a Nop
- MachNopNode *_nop;
-
// Length of the current bundle, in instructions
uint _bundle_instr_count;
@@ -128,9 +122,6 @@ private:
public:
Scheduling(Arena *arena, Compile &compile);
- // Destructor
- NOT_PRODUCT( ~Scheduling(); )
-
// Step ahead "i" cycles
void step(uint i);
@@ -194,10 +185,7 @@ public:
#ifndef PRODUCT
private:
// Gather information on size of nops relative to total
- uint _branches, _unconditional_delays;
-
static uint _total_nop_size, _total_method_size;
- static uint _total_branches, _total_unconditional_delays;
static uint _total_instructions_per_bundle[Pipeline::_max_instrs_per_cycle+1];
public:
@@ -1472,7 +1460,6 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) {
}
// Now fill in the code buffer
- Node* delay_slot = nullptr;
for (uint i = 0; i < nblocks; i++) {
Block* block = C->cfg()->get_block(i);
_block = block;
@@ -1511,15 +1498,6 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) {
// Get the node
Node* n = block->get_node(j);
- // See if delay slots are supported
- if (valid_bundle_info(n) && node_bundling(n)->used_in_unconditional_delay()) {
- assert(delay_slot == nullptr, "no use of delay slot node");
- assert(n->size(C->regalloc()) == Pipeline::instr_unit_size(), "delay slot instruction wrong size");
-
- delay_slot = n;
- continue;
- }
-
// If this starts a new instruction group, then flush the current one
// (but allow split bundles)
if (Pipeline::requires_bundling() && starts_bundle(n))
@@ -1538,9 +1516,6 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) {
current_offset = masm->offset();
}
- // A padding may be needed again since a previous instruction
- // could be moved to delay slot.
-
// align the instruction if necessary
int padding = mach->compute_padding(current_offset);
// Make sure safepoint node for polling is distinct from a call's
@@ -1613,13 +1588,10 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) {
// This requires the TRUE branch target be in succs[0]
uint block_num = block->non_connector_successor(0)->_pre_order;
- // Try to replace long branch if delay slot is not used,
+ // Try to replace long branch,
// it is mostly for back branches since forward branch's
// distance is not updated yet.
- bool delay_slot_is_used = valid_bundle_info(n) &&
- C->output()->node_bundling(n)->use_unconditional_delay();
- if (!delay_slot_is_used && mach->may_be_short_branch()) {
- assert(delay_slot == nullptr, "not expecting delay slot node");
+ if (mach->may_be_short_branch()) {
int br_size = n->size(C->regalloc());
int offset = blk_starts[block_num] - current_offset;
if (block_num >= i) {
@@ -1753,44 +1725,6 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) {
last_avoid_back_to_back_offset = current_offset;
}
- // See if this instruction has a delay slot
- if (valid_bundle_info(n) && node_bundling(n)->use_unconditional_delay()) {
- guarantee(delay_slot != nullptr, "expecting delay slot node");
-
- // Back up 1 instruction
- masm->code()->set_insts_end(masm->code()->insts_end() - Pipeline::instr_unit_size());
-
- // Save the offset for the listing
-#if defined(SUPPORT_OPTO_ASSEMBLY)
- if ((node_offsets != nullptr) && (delay_slot->_idx < node_offset_limit)) {
- node_offsets[delay_slot->_idx] = masm->offset();
- }
-#endif
-
- // Support a SafePoint in the delay slot
- if (delay_slot->is_MachSafePoint()) {
- MachNode *mach = delay_slot->as_Mach();
- // !!!!! Stubs only need an oopmap right now, so bail out
- if (!mach->is_MachCall() && mach->as_MachSafePoint()->jvms()->method() == nullptr) {
- // Write the oopmap directly to the code blob??!!
- delay_slot = nullptr;
- continue;
- }
-
- int adjusted_offset = current_offset - Pipeline::instr_unit_size();
- non_safepoints.observe_safepoint(mach->as_MachSafePoint()->jvms(),
- adjusted_offset);
- // Generate an OopMap entry
- Process_OopMap_Node(mach, adjusted_offset);
- }
-
- // Insert the delay slot instruction
- delay_slot->emit(masm, C->regalloc());
-
- // Don't reuse it
- delay_slot = nullptr;
- }
-
} // End for all instructions in block
// If the next block is the top of a loop, pad this block out to align
@@ -2031,8 +1965,6 @@ void PhaseOutput::FillExceptionTables(uint cnt, uint *call_returns, uint *inct_s
#ifndef PRODUCT
uint Scheduling::_total_nop_size = 0;
uint Scheduling::_total_method_size = 0;
-uint Scheduling::_total_branches = 0;
-uint Scheduling::_total_unconditional_delays = 0;
uint Scheduling::_total_instructions_per_bundle[Pipeline::_max_instrs_per_cycle+1];
#endif
@@ -2050,14 +1982,7 @@ Scheduling::Scheduling(Arena *arena, Compile &compile)
_bundle_instr_count(0),
_bundle_cycle_number(0),
_bundle_use(0, 0, resource_count, &_bundle_use_elements[0])
-#ifndef PRODUCT
- , _branches(0)
- , _unconditional_delays(0)
-#endif
{
- // Create a MachNopNode
- _nop = new MachNopNode();
-
// Save the count
_node_bundling_limit = compile.unique();
uint node_max = _regalloc->node_regs_max_index();
@@ -2087,14 +2012,6 @@ Scheduling::Scheduling(Arena *arena, Compile &compile)
_next_node = block->get_node(block->number_of_nodes() - 1);
}
-#ifndef PRODUCT
-// Scheduling destructor
-Scheduling::~Scheduling() {
- _total_branches += _branches;
- _total_unconditional_delays += _unconditional_delays;
-}
-#endif
-
// Step ahead "i" cycles
void Scheduling::step(uint i) {
@@ -2199,15 +2116,6 @@ void PhaseOutput::print_scheduling(outputStream* output_stream) {
bool Scheduling::NodeFitsInBundle(Node *n) {
uint n_idx = n->_idx;
- // If this is the unconditional delay instruction, then it fits
- if (n == _unconditional_delay_slot) {
-#ifndef PRODUCT
- if (_cfg->C->trace_opto_output())
- tty->print("# NodeFitsInBundle [%4d]: TRUE; is in unconditional delay slot\n", n->_idx);
-#endif
- return (true);
- }
-
// If the node cannot be scheduled this cycle, skip it
if (_current_latency[n_idx] > _bundle_cycle_number) {
#ifndef PRODUCT
@@ -2223,8 +2131,6 @@ bool Scheduling::NodeFitsInBundle(Node *n) {
uint instruction_count = node_pipeline->instructionCount();
if (node_pipeline->mayHaveNoCode() && n->size(_regalloc) == 0)
instruction_count = 0;
- else if (node_pipeline->hasBranchDelay() && !_unconditional_delay_slot)
- instruction_count++;
if (_bundle_instr_count + instruction_count > Pipeline::_max_instrs_per_cycle) {
#ifndef PRODUCT
@@ -2436,99 +2342,6 @@ void Scheduling::AddNodeToBundle(Node *n, const Block *bb) {
const Pipeline *node_pipeline = n->pipeline();
const Pipeline_Use& node_usage = node_pipeline->resourceUse();
- // Check for instructions to be placed in the delay slot. We
- // do this before we actually schedule the current instruction,
- // because the delay slot follows the current instruction.
- if (Pipeline::_branch_has_delay_slot &&
- node_pipeline->hasBranchDelay() &&
- !_unconditional_delay_slot) {
-
- uint siz = _available.size();
-
- // Conditional branches can support an instruction that
- // is unconditionally executed and not dependent by the
- // branch, OR a conditionally executed instruction if
- // the branch is taken. In practice, this means that
- // the first instruction at the branch target is
- // copied to the delay slot, and the branch goes to
- // the instruction after that at the branch target
- if ( n->is_MachBranch() ) {
-
- assert( !n->is_MachNullCheck(), "should not look for delay slot for Null Check" );
- assert( !n->is_Catch(), "should not look for delay slot for Catch" );
-
-#ifndef PRODUCT
- _branches++;
-#endif
-
- // At least 1 instruction is on the available list
- // that is not dependent on the branch
- for (uint i = 0; i < siz; i++) {
- Node *d = _available[i];
- const Pipeline *avail_pipeline = d->pipeline();
-
- // Don't allow safepoints in the branch shadow, that will
- // cause a number of difficulties
- if ( avail_pipeline->instructionCount() == 1 &&
- !avail_pipeline->hasMultipleBundles() &&
- !avail_pipeline->hasBranchDelay() &&
- Pipeline::instr_has_unit_size() &&
- d->size(_regalloc) == Pipeline::instr_unit_size() &&
- NodeFitsInBundle(d) &&
- !node_bundling(d)->used_in_delay()) {
-
- if (d->is_Mach() && !d->is_MachSafePoint()) {
- // A node that fits in the delay slot was found, so we need to
- // set the appropriate bits in the bundle pipeline information so
- // that it correctly indicates resource usage. Later, when we
- // attempt to add this instruction to the bundle, we will skip
- // setting the resource usage.
- _unconditional_delay_slot = d;
- node_bundling(n)->set_use_unconditional_delay();
- node_bundling(d)->set_used_in_unconditional_delay();
- _bundle_use.add_usage(avail_pipeline->resourceUse());
- _current_latency[d->_idx] = _bundle_cycle_number;
- _next_node = d;
- ++_bundle_instr_count;
-#ifndef PRODUCT
- _unconditional_delays++;
-#endif
- break;
- }
- }
- }
- }
-
- // No delay slot, add a nop to the usage
- if (!_unconditional_delay_slot) {
- // See if adding an instruction in the delay slot will overflow
- // the bundle.
- if (!NodeFitsInBundle(_nop)) {
-#ifndef PRODUCT
- if (_cfg->C->trace_opto_output())
- tty->print("# *** STEP(1 instruction for delay slot) ***\n");
-#endif
- step(1);
- }
-
- _bundle_use.add_usage(_nop->pipeline()->resourceUse());
- _next_node = _nop;
- ++_bundle_instr_count;
- }
-
- // See if the instruction in the delay slot requires a
- // step of the bundles
- if (!NodeFitsInBundle(n)) {
-#ifndef PRODUCT
- if (_cfg->C->trace_opto_output())
- tty->print("# *** STEP(branch won't fit) ***\n");
-#endif
- // Update the state information
- _bundle_instr_count = 0;
- _bundle_cycle_number += 1;
- _bundle_use.step(1);
- }
- }
// Get the number of instructions
uint instruction_count = node_pipeline->instructionCount();
@@ -2556,47 +2369,40 @@ void Scheduling::AddNodeToBundle(Node *n, const Block *bb) {
}
}
- // If this was placed in the delay slot, ignore it
- if (n != _unconditional_delay_slot) {
-
- if (delay == 0) {
- if (node_pipeline->hasMultipleBundles()) {
+ if (delay == 0) {
+ if (node_pipeline->hasMultipleBundles()) {
#ifndef PRODUCT
- if (_cfg->C->trace_opto_output())
- tty->print("# *** STEP(multiple instructions) ***\n");
+ if (_cfg->C->trace_opto_output())
+ tty->print("# *** STEP(multiple instructions) ***\n");
#endif
- step(1);
- }
-
- else if (instruction_count + _bundle_instr_count > Pipeline::_max_instrs_per_cycle) {
-#ifndef PRODUCT
- if (_cfg->C->trace_opto_output())
- tty->print("# *** STEP(%d >= %d instructions) ***\n",
- instruction_count + _bundle_instr_count,
- Pipeline::_max_instrs_per_cycle);
-#endif
- step(1);
- }
+ step(1);
}
- if (node_pipeline->hasBranchDelay() && !_unconditional_delay_slot)
- _bundle_instr_count++;
-
- // Set the node's latency
- _current_latency[n->_idx] = _bundle_cycle_number;
-
- // Now merge the functional unit information
- if (instruction_count > 0 || !node_pipeline->mayHaveNoCode())
- _bundle_use.add_usage(node_usage);
-
- // Increment the number of instructions in this bundle
- _bundle_instr_count += instruction_count;
-
- // Remember this node for later
- if (n->is_Mach())
- _next_node = n;
+ else if (instruction_count + _bundle_instr_count > Pipeline::_max_instrs_per_cycle) {
+#ifndef PRODUCT
+ if (_cfg->C->trace_opto_output())
+ tty->print("# *** STEP(%d >= %d instructions) ***\n",
+ instruction_count + _bundle_instr_count,
+ Pipeline::_max_instrs_per_cycle);
+#endif
+ step(1);
+ }
}
+ // Set the node's latency
+ _current_latency[n->_idx] = _bundle_cycle_number;
+
+ // Now merge the functional unit information
+ if (instruction_count > 0 || !node_pipeline->mayHaveNoCode())
+ _bundle_use.add_usage(node_usage);
+
+ // Increment the number of instructions in this bundle
+ _bundle_instr_count += instruction_count;
+
+ // Remember this node for later
+ if (n->is_Mach())
+ _next_node = n;
+
// It's possible to have a BoxLock in the graph and in the _bbs mapping but
// not in the bb->_nodes array. This happens for debug-info-only BoxLocks.
// 'Schedule' them (basically ignore in the schedule) but do not insert them
@@ -2647,9 +2453,6 @@ void Scheduling::ComputeUseCount(const Block *bb) {
_available.clear();
_scheduled.clear();
- // No delay slot specified
- _unconditional_delay_slot = nullptr;
-
#ifdef ASSERT
for( uint i=0; i < bb->number_of_nodes(); i++ )
assert( _uses[bb->get_node(i)->_idx] == 0, "_use array not clean" );
@@ -2767,11 +2570,7 @@ void Scheduling::DoScheduling() {
break; // Funny loop structure to be sure...
}
// Compute last "interesting" instruction in block - last instruction we
- // might schedule. _bb_end points just after last schedulable inst. We
- // normally schedule conditional branches (despite them being forced last
- // in the block), because they have delay slots we can fill. Calls all
- // have their delay slots filled in the template expansions, so we don't
- // bother scheduling them.
+ // might schedule. _bb_end points just after last schedulable inst.
Node *last = bb->get_node(_bb_end);
// Ignore trailing NOPs.
while (_bb_end > 0 && last->is_Mach() &&
@@ -2837,7 +2636,7 @@ void Scheduling::DoScheduling() {
Node *n = bb->get_node(j);
if( valid_bundle_info(n) ) {
Bundle *bundle = node_bundling(n);
- if (bundle->instr_count() > 0 || bundle->flags() > 0) {
+ if (bundle->instr_count() > 0) {
tty->print("*** Bundle: ");
bundle->dump();
}
@@ -3273,16 +3072,6 @@ void Scheduling::print_statistics() {
((double)_total_nop_size) / ((double) _total_method_size) * 100.0);
tty->print("\n");
- // Print the number of branch shadows filled
- if (Pipeline::_branch_has_delay_slot) {
- tty->print("Of %d branches, %d had unconditional delay slots filled",
- _total_branches, _total_unconditional_delays);
- if (_total_branches > 0)
- tty->print(", for %.2f%%",
- ((double)_total_unconditional_delays) / ((double)_total_branches) * 100.0);
- tty->print("\n");
- }
-
uint total_instructions = 0, total_bundles = 0;
for (uint i = 1; i <= Pipeline::_max_instrs_per_cycle; i++) {
@@ -3572,7 +3361,6 @@ void PhaseOutput::dump_asm_on(outputStream* st, int* pcs, uint pc_limit) {
}
// For all instructions
- Node *delay = nullptr;
for (uint j = 0; j < block->number_of_nodes(); j++) {
if (VMThread::should_terminate()) {
cut_short = true;
@@ -3581,10 +3369,6 @@ void PhaseOutput::dump_asm_on(outputStream* st, int* pcs, uint pc_limit) {
n = block->get_node(j);
if (valid_bundle_info(n)) {
Bundle* bundle = node_bundling(n);
- if (bundle->used_in_unconditional_delay()) {
- delay = n;
- continue;
- }
if (bundle->starts_bundle()) {
starts_bundle = '+';
}
@@ -3617,29 +3401,6 @@ void PhaseOutput::dump_asm_on(outputStream* st, int* pcs, uint pc_limit) {
st->cr();
}
- // If we have an instruction with a delay slot, and have seen a delay,
- // then back up and print it
- if (valid_bundle_info(n) && node_bundling(n)->use_unconditional_delay()) {
- // Coverity finding - Explicit null dereferenced.
- guarantee(delay != nullptr, "no unconditional delay instruction");
- if (WizardMode) delay->dump();
-
- if (node_bundling(delay)->starts_bundle())
- starts_bundle = '+';
- if ((pcs != nullptr) && (n->_idx < pc_limit)) {
- pc = pcs[n->_idx];
- st->print("%*.*x", pc_digits, pc_digits, pc);
- } else {
- st->fill_to(pc_digits);
- }
- st->print(" %c ", starts_bundle);
- starts_bundle = ' ';
- st->fill_to(prefix_len);
- delay->format(C->regalloc(), st);
- st->cr();
- delay = nullptr;
- }
-
// Dump the exception table as well
if( n->is_Catch() && (Verbose || WizardMode) ) {
// Print the exception table for this offset
@@ -3648,7 +3409,6 @@ void PhaseOutput::dump_asm_on(outputStream* st, int* pcs, uint pc_limit) {
st->bol(); // Make sure we start on a new line
}
st->cr(); // one empty line between blocks
- assert(cut_short || delay == nullptr, "no unconditional delay branch");
} // End of per-block dump
if (cut_short) st->print_cr("*** disassembly is cut short ***");
diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp
index 60161643315..710d34c3ccb 100644
--- a/src/hotspot/share/runtime/sharedRuntime.cpp
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp
@@ -3502,8 +3502,6 @@ frame SharedRuntime::look_for_reserved_stack_annotated_method(JavaThread* curren
if (cb != nullptr && cb->is_nmethod()) {
nm = cb->as_nmethod();
method = nm->method();
- // scope_desc_near() must be used, instead of scope_desc_at() because on
- // SPARC, the pcDesc can be on the delay slot after the call instruction.
for (ScopeDesc *sd = nm->scope_desc_near(fr.pc()); sd != nullptr; sd = sd->sender()) {
method = sd->method();
if (method != nullptr && method->has_reserved_stack_access()) {
From 9e3fa3216fd4ebd73da6e003a7b767cf001a1169 Mon Sep 17 00:00:00 2001
From: Kazuhisa Takakuri
Date: Wed, 10 Sep 2025 06:37:17 +0000
Subject: [PATCH 013/120] 8349288:
runtime/os/windows/TestAvailableProcessors.java fails on localized Windows
platform
Reviewed-by: dholmes, alanb
---
.../os/windows/TestAvailableProcessors.java | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/test/hotspot/jtreg/runtime/os/windows/TestAvailableProcessors.java b/test/hotspot/jtreg/runtime/os/windows/TestAvailableProcessors.java
index 795b3d76e54..f15514d024e 100644
--- a/test/hotspot/jtreg/runtime/os/windows/TestAvailableProcessors.java
+++ b/test/hotspot/jtreg/runtime/os/windows/TestAvailableProcessors.java
@@ -34,6 +34,7 @@
* @run testng/othervm/native TestAvailableProcessors
*/
+import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.HashSet;
@@ -58,7 +59,23 @@ public class TestAvailableProcessors {
private static String getWindowsVersion() throws IOException {
String systeminfoPath = "systeminfo.exe";
- var processBuilder = new ProcessBuilder(systeminfoPath);
+ List command = new ArrayList<>();
+
+ String systemRoot = System.getenv("SystemRoot");
+ if (systemRoot == null) {
+ systemRoot = System.getenv("WINDIR");
+ if (systemRoot == null) {
+ throw new RuntimeException("SystemRoot or WINDIR environment variable is not set.");
+ }
+ }
+ String system32 = Path.of(systemRoot, "System32").toString();
+
+ // It switches the active code page to cp437, the default code page for US english.
+ command.addAll(List.of("cmd.exe", "/c", "set", "PATH=%PATH%;" + system32 + ";" + system32 + "\\wbem", "&&"));
+ command.addAll(List.of("chcp", "437", ">nul", "2>&1", "&&"));
+ command.add(systeminfoPath);
+
+ var processBuilder = new ProcessBuilder(command);
OutputAnalyzer outputAnalyzer = new OutputAnalyzer(processBuilder.start());
outputAnalyzer.shouldHaveExitValue(0);
outputAnalyzer.shouldContain(osVersionMessage);
From f3de386263e16e33c2812706cf41410da2cd58c6 Mon Sep 17 00:00:00 2001
From: David Holmes
Date: Wed, 10 Sep 2025 08:46:07 +0000
Subject: [PATCH 014/120] 8367309: Test
runtime/os/windows/TestAvailableProcessors.java fails to compile after
mis-merge
Reviewed-by: shade, alanb
---
.../jtreg/runtime/os/windows/TestAvailableProcessors.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/test/hotspot/jtreg/runtime/os/windows/TestAvailableProcessors.java b/test/hotspot/jtreg/runtime/os/windows/TestAvailableProcessors.java
index f15514d024e..d5e5b1626b6 100644
--- a/test/hotspot/jtreg/runtime/os/windows/TestAvailableProcessors.java
+++ b/test/hotspot/jtreg/runtime/os/windows/TestAvailableProcessors.java
@@ -34,8 +34,10 @@
* @run testng/othervm/native TestAvailableProcessors
*/
-import java.io.File;
+
import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
From 1d3364b00725f9d2afa8274e2244357a109be545 Mon Sep 17 00:00:00 2001
From: Daniel Fuchs
Date: Wed, 10 Sep 2025 09:45:05 +0000
Subject: [PATCH 015/120] 8365239: Spec Clarification -
InterfaceAddress:getBroadcast() returning null for loop back address
Reviewed-by: msheppar, djelinski, jpai
---
src/java.base/share/classes/java/net/InterfaceAddress.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/java.base/share/classes/java/net/InterfaceAddress.java b/src/java.base/share/classes/java/net/InterfaceAddress.java
index 6df2de353e3..44b3456c6fc 100644
--- a/src/java.base/share/classes/java/net/InterfaceAddress.java
+++ b/src/java.base/share/classes/java/net/InterfaceAddress.java
@@ -63,8 +63,8 @@ public final class InterfaceAddress {
* Only IPv4 networks have broadcast address therefore, in the case
* of an IPv6 network, {@code null} will be returned.
*
- * Certain network interfaces, such as the loopback interface, do not support
- * broadcasting and will also return {@code null}.
+ * Some network interfaces do not support broadcasting and may
+ * also return {@code null}.
*
* @return the {@code InetAddress} representing the broadcast
* address or {@code null} if there is no broadcast address.
From 5c9f60dc5a6e64be55819469bbf10948803d0fd5 Mon Sep 17 00:00:00 2001
From: Magnus Ihse Bursie
Date: Wed, 10 Sep 2025 09:57:44 +0000
Subject: [PATCH 016/120] 8367259: Clean up make/scripts and bin directory
Reviewed-by: erikj
---
{make/scripts => bin}/generate-symbol-data.sh | 31 ++-
{make/scripts => bin}/lic_check.sh | 4 +-
{make/scripts => bin}/normalizer.pl | 0
bin/unshuffle_list.txt | 191 --------------
bin/unshuffle_patch.sh | 237 ------------------
.../scripts => bin}/update_copyright_year.sh | 0
{make/scripts => bin}/update_pch.sh | 12 +-
make/autoconf/compare.sh.template | 2 +-
make/scripts/{logger.sh => compare-logger.sh} | 0
.../hide_important_warnings_from_javac.sh | 36 ---
10 files changed, 43 insertions(+), 470 deletions(-)
rename {make/scripts => bin}/generate-symbol-data.sh (83%)
rename {make/scripts => bin}/lic_check.sh (98%)
rename {make/scripts => bin}/normalizer.pl (100%)
delete mode 100644 bin/unshuffle_list.txt
delete mode 100644 bin/unshuffle_patch.sh
rename {make/scripts => bin}/update_copyright_year.sh (100%)
rename {make/scripts => bin}/update_pch.sh (92%)
rename make/scripts/{logger.sh => compare-logger.sh} (100%)
delete mode 100644 make/scripts/hide_important_warnings_from_javac.sh
diff --git a/make/scripts/generate-symbol-data.sh b/bin/generate-symbol-data.sh
similarity index 83%
rename from make/scripts/generate-symbol-data.sh
rename to bin/generate-symbol-data.sh
index 6f38d873009..283757a6918 100644
--- a/make/scripts/generate-symbol-data.sh
+++ b/bin/generate-symbol-data.sh
@@ -1,6 +1,6 @@
#!/bin/sh
#
-# Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -52,12 +52,39 @@
# include the SCM state that was used to build it, which can be found in ${JDK_N_INSTALL}/release,
# in property "SOURCE".
+source_path="$(dirname ${0})"
+this_script_dir="$(cd -- "${source_path}" > /dev/null && pwd)"
+if test -z "${this_script_dir}"; then
+ echo "Error: Could not determine location of this script"
+ exit 1
+fi
+
+symbols_dir="$(dirname $this_script_dir)/src/jdk.compiler/share/data/symbols"
+if [ ! -d $symbols_dir ] ; then
+ echo "Cannot locate symbols directory: $symbols_dir" >&2
+ exit 1
+fi
+
+generator_dir="$(dirname $this_script_dir)/make/langtools/src/classes/build/tools/symbolgenerator"
+
if [ "$1x" = "x" ] ; then
echo "Must provide the target JDK as a parameter:" >&2
echo "$0 " >&2
exit 1
fi;
+if [ ! -d $1 ] ; then
+ echo "Target JDK argument is not a directory:" $1 >&2
+ exit 1
+fi;
+
+if [ ! -x $1/bin/java ] ; then
+ echo "Target JDK argument is not a valid JDK: $1" >&2
+ exit 1
+fi;
+
+cd $symbols_dir
+
if [ ! -f symbols ] ; then
echo "Must run inside the src/jdk.compiler/share/data/symbols directory" >&2
exit 1
@@ -72,5 +99,5 @@ $1/bin/java --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \
--add-modules jdk.jdeps \
- ../../../../../make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java \
+ $generator_dir/CreateSymbols.java \
build-description-incremental symbols include.list
diff --git a/make/scripts/lic_check.sh b/bin/lic_check.sh
similarity index 98%
rename from make/scripts/lic_check.sh
rename to bin/lic_check.sh
index d70d8914181..2fc6abf4d82 100644
--- a/make/scripts/lic_check.sh
+++ b/bin/lic_check.sh
@@ -1,6 +1,6 @@
#! /bin/sh -f
#
-# Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -62,7 +62,7 @@ B=`basename "${script_directory}"`
script_dir="`cd \"${D}\" 2>/dev/null && pwd || echo \"${D}\"`/${B}"
# set up a variable for the template directory
-template_dir=${script_dir}/../data/license-templates
+template_dir=${script_dir}/../make/data/license-templates
# Check existence of the template directory.
if [ ! -d ${template_dir} ] ; then
diff --git a/make/scripts/normalizer.pl b/bin/normalizer.pl
similarity index 100%
rename from make/scripts/normalizer.pl
rename to bin/normalizer.pl
diff --git a/bin/unshuffle_list.txt b/bin/unshuffle_list.txt
deleted file mode 100644
index a910f6b4621..00000000000
--- a/bin/unshuffle_list.txt
+++ /dev/null
@@ -1,191 +0,0 @@
-#
-# 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
-# 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.
-#
-
-src/bsd : jdk/src/bsd
-src/demo : jdk/src/demo
-src/java.activation : jaxws/src/java.activation
-src/java.base : jdk/src/java.base
-src/java.compiler : langtools/src/java.compiler
-src/java.corba : corba/src/java.corba
-src/java.datatransfer : jdk/src/java.datatransfer
-src/java.desktop : jdk/src/java.desktop
-src/java.instrument : jdk/src/java.instrument
-src/java.logging : jdk/src/java.logging
-src/java.management : jdk/src/java.management
-src/java.management.rmi : jdk/src/java.management.rmi
-src/java.naming : jdk/src/java.naming
-src/java.prefs : jdk/src/java.prefs
-src/java.rmi : jdk/src/java.rmi
-src/java.scripting : jdk/src/java.scripting
-src/java.se : jdk/src/java.se
-src/java.security.jgss : jdk/src/java.security.jgss
-src/java.security.sasl : jdk/src/java.security.sasl
-src/java.se.ee : jdk/src/java.se.ee
-src/java.smartcardio : jdk/src/java.smartcardio
-src/java.sql : jdk/src/java.sql
-src/java.sql.rowset : jdk/src/java.sql.rowset
-src/java.transaction : jdk/src/java.transaction
-src/java.xml : jaxp/src/java.xml
-src/java.xml.bind : jaxws/src/java.xml.bind
-src/java.xml.crypto : jdk/src/java.xml.crypto
-src/java.xml.ws : jaxws/src/java.xml.ws
-src/java.xml.ws.annotation : jaxws/src/java.xml.ws.annotation
-src/jdk.accessibility : jdk/src/jdk.accessibility
-src/jdk.aot : hotspot/src/jdk.aot
-src/jdk.attach : jdk/src/jdk.attach
-src/jdk.charsets : jdk/src/jdk.charsets
-src/jdk.compiler : jdk/src/jdk.compiler langtools/src/jdk.compiler
-src/jdk.crypto.cryptoki : jdk/src/jdk.crypto.cryptoki
-src/jdk.crypto.ec : jdk/src/jdk.crypto.ec
-src/jdk.crypto.mscapi : jdk/src/jdk.crypto.mscapi
-src/jdk.dynalink : nashorn/src/jdk.dynalink
-src/jdk.editpad : jdk/src/jdk.editpad
-src/jdk.hotspot.agent : hotspot/src/jdk.hotspot.agent
-src/jdk.httpserver : jdk/src/jdk.httpserver
-src/jdk.incubator.httpclient : jdk/src/jdk.incubator.httpclient
-src/jdk.internal.ed : jdk/src/jdk.internal.ed
-src/jdk.internal.jvmstat : jdk/src/jdk.internal.jvmstat
-src/jdk.internal.le : jdk/src/jdk.internal.le
-src/jdk.internal.opt : jdk/src/jdk.internal.opt
-src/jdk.internal.vm.ci : hotspot/src/jdk.internal.vm.ci
-src/jdk.internal.vm.compiler : hotspot/src/jdk.internal.vm.compiler
-src/jdk.jartool : jdk/src/jdk.jartool
-src/jdk.javadoc : langtools/src/jdk.javadoc
-src/jdk.jcmd : jdk/src/jdk.jcmd
-src/jdk.jconsole : jdk/src/jdk.jconsole
-src/jdk.jdeps : langtools/src/jdk.jdeps
-src/jdk.jdi : jdk/src/jdk.jdi
-src/jdk.jdwp.agent : jdk/src/jdk.jdwp.agent
-src/jdk.jlink : jdk/src/jdk.jlink
-src/jdk.jshell : langtools/src/jdk.jshell
-src/jdk.jstatd : jdk/src/jdk.jstatd
-src/jdk.localedata : jdk/src/jdk.localedata
-src/jdk.management : jdk/src/jdk.management
-src/jdk.management.agent : jdk/src/jdk.management.agent
-src/jdk.naming.dns : jdk/src/jdk.naming.dns
-src/jdk.naming.rmi : jdk/src/jdk.naming.rmi
-src/jdk.net : jdk/src/jdk.net
-src/jdk.pack : jdk/src/jdk.pack
-src/jdk.scripting.nashorn : nashorn/src/jdk.scripting.nashorn
-src/jdk.scripting.nashorn.shell : nashorn/src/jdk.scripting.nashorn.shell
-src/jdk.sctp : jdk/src/jdk.sctp
-src/jdk.security.auth : jdk/src/jdk.security.auth
-src/jdk.security.jgss : jdk/src/jdk.security.jgss
-src/jdk.unsupported : jdk/src/jdk.unsupported
-src/jdk.xml.bind : jaxws/src/jdk.xml.bind
-src/jdk.xml.dom : jaxp/src/jdk.xml.dom
-src/jdk.xml.ws : jaxws/src/jdk.xml.ws
-src/jdk.zipfs : jdk/src/jdk.zipfs
-src/langtools/sample : langtools/src/sample
-src/linux : jdk/src/linux
-src/sample : jdk/src/sample
-src/hotspot/share : hotspot/src/share/vm
-src/hotspot/cpu/aarch64 : hotspot/src/cpu/aarch64/vm
-src/hotspot/cpu/arm : hotspot/src/cpu/arm/vm
-src/hotspot/cpu/ppc : hotspot/src/cpu/ppc/vm
-src/hotspot/cpu/s390 : hotspot/src/cpu/s390/vm
-src/hotspot/cpu/x86 : hotspot/src/cpu/x86/vm
-src/hotspot/cpu/zero : hotspot/src/cpu/zero/vm
-src/hotspot/os/aix : hotspot/src/os/aix/vm
-src/hotspot/os/bsd : hotspot/src/os/bsd/vm
-src/hotspot/os/linux : hotspot/src/os/linux/vm
-src/hotspot/os/posix/dtrace : hotspot/src/os/posix/dtrace
-src/hotspot/os/posix : hotspot/src/os/posix/vm
-src/hotspot/os/windows : hotspot/src/os/windows/vm
-src/hotspot/os_cpu/aix_ppc : hotspot/src/os_cpu/aix_ppc/vm
-src/hotspot/os_cpu/bsd_x86 : hotspot/src/os_cpu/bsd_x86/vm
-src/hotspot/os_cpu/bsd_zero : hotspot/src/os_cpu/bsd_zero/vm
-src/hotspot/os_cpu/linux_aarch64 : hotspot/src/os_cpu/linux_aarch64/vm
-src/hotspot/os_cpu/linux_arm : hotspot/src/os_cpu/linux_arm/vm
-src/hotspot/os_cpu/linux_ppc : hotspot/src/os_cpu/linux_ppc/vm
-src/hotspot/os_cpu/linux_s390 : hotspot/src/os_cpu/linux_s390/vm
-src/hotspot/os_cpu/linux_x86 : hotspot/src/os_cpu/linux_x86/vm
-src/hotspot/os_cpu/linux_zero : hotspot/src/os_cpu/linux_zero/vm
-src/hotspot/os_cpu/windows_x86 : hotspot/src/os_cpu/windows_x86/vm
-src/hotspot : hotspot/src
-src/utils/IdealGraphVisualizer : hotspot/src/share/tools/IdealGraphVisualizer
-src/utils/LogCompilation : hotspot/src/share/tools/LogCompilation
-src/utils/hsdis : hotspot/src/share/tools/hsdis
-src/utils/reorder : jdk/make/non-build-utils/reorder
-src/utils/src/build : jdk/make/non-build-utils/src/build
-make/BuildNashorn.gmk : nashorn/make/BuildNashorn.gmk
-make/CompileDemos.gmk : jdk/make/CompileDemos.gmk
-make/CompileInterimLangtools.gmk : langtools/make/CompileInterim.gmk
-make/CompileModuleTools.gmk : jdk/make/CompileModuleTools.gmk
-make/CompileToolsHotspot.gmk : hotspot/make/CompileTools.gmk
-make/CompileToolsJdk.gmk : jdk/make/CompileTools.gmk
-make/CopyInterimCLDRConverter.gmk : jdk/make/CopyInterimCLDRConverter.gmk
-make/GenerateModuleSummary.gmk : jdk/make/GenerateModuleSummary.gmk
-make/ModuleTools.gmk : jdk/make/ModuleTools.gmk
-make/ToolsJdk.gmk : jdk/make/Tools.gmk
-make/ToolsLangtools.gmk : langtools/make/Tools.gmk
-make/UnpackSecurity.gmk : jdk/make/UnpackSecurity.gmk
-make/autoconf : common/autoconf
-make/conf : common/conf
-make/copy : jdk/make/copy
-make/copy/Copy-java.corba.gmk : corba/make/copy/Copy-java.corba.gmk
-make/corba : corba/make
-make/data : jdk/make/data
-make/gendata : jdk/make/gendata
-make/gendata/Gendata-jdk.compiler.gmk : langtools/make/gendata/Gendata-jdk.compiler.gmk
-make/gensrc : jdk/make/gensrc
-make/gensrc/Gensrc-java.corba.gmk : corba/make/gensrc/Gensrc-java.corba.gmk
-make/gensrc/Gensrc-jdk.compiler.gmk : langtools/make/gensrc/Gensrc-jdk.compiler.gmk
-make/gensrc/Gensrc-jdk.hotspot.agent.gmk : hotspot/make/gensrc/Gensrc-jdk.hotspot.agent.gmk
-make/gensrc/Gensrc-jdk.internal.vm.compiler.gmk : hotspot/make/gensrc/Gensrc-jdk.internal.vm.compiler.gmk
-make/gensrc/Gensrc-jdk.javadoc.gmk : langtools/make/gensrc/Gensrc-jdk.javadoc.gmk
-make/gensrc/Gensrc-jdk.jdeps.gmk : langtools/make/gensrc/Gensrc-jdk.jdeps.gmk
-make/gensrc/Gensrc-jdk.jshell.gmk : langtools/make/gensrc/Gensrc-jdk.jshell.gmk
-make/gensrc/GensrcCommonLangtools.gmk : langtools/make/gensrc/GensrcCommon.gmk
-make/hotspot : hotspot/make
-make/jdk : jdk/make
-make/langtools : langtools/make
-make/launcher : jdk/make/launcher
-make/lib : jdk/make/lib
-make/lib/Lib-jdk.hotspot.agent.gmk : hotspot/make/lib/Lib-jdk.hotspot.agent.gmk
-make/mapfiles : jdk/make/mapfiles
-make/mapfiles/libjsig : hotspot/make/mapfiles/libjsig
-make/mapfiles/libjvm_db : hotspot/make/mapfiles/libjvm_db
-make/mapfiles/libjvm_dtrace : hotspot/make/mapfiles/libjvm_dtrace
-make/mapfiles/libsaproc : hotspot/make/mapfiles/libsaproc
-make/nashorn : nashorn/make
-make/nb_native : common/nb_native
-make/scripts/addNotices.sh : jdk/make/scripts/addNotices.sh
-make/scripts/compare.sh : common/bin/compare.sh
-make/scripts/compare_exceptions.sh.incl : common/bin/compare_exceptions.sh.incl
-make/scripts/genExceptions.sh : jdk/make/scripts/genExceptions.sh
-make/scripts/hide_important_warnings_from_javac.sh : common/bin/hide_important_warnings_from_javac.sh
-make/scripts/logger.sh : common/bin/logger.sh
-make/src/native/fixpath.c : common/src/fixpath.c
-make/test/JtregNativeHotspot.gmk : hotspot/make/test/JtregNative.gmk
-make/test/JtregNativeJdk.gmk : jdk/make/test/JtregNative.gmk
-test/jdk : jdk/test
-test/langtools : langtools/test
-test/nashorn : nashorn/test
-test/jaxp : jaxp/test
-test/hotspot/gtest : hotspot/test/native
-test/hotspot/jtreg : hotspot/test
-bin : common/bin
-bin/nashorn : nashorn/bin
-doc : common/doc
-doc/nashorn : nashorn/docs
diff --git a/bin/unshuffle_patch.sh b/bin/unshuffle_patch.sh
deleted file mode 100644
index c5cdc3851c3..00000000000
--- a/bin/unshuffle_patch.sh
+++ /dev/null
@@ -1,237 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
-# This code is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation.
-#
-# 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.
-#
-
-# Script for updating a patch file as per the shuffled/unshuffled source location.
-
-usage() {
- echo "Usage: $0 [-h|--help] [-v|--verbose] [-to9|-to10] [-r ] "
- echo "where:"
- echo " -to9 create patches appropriate for a JDK 9 source tree"
- echo " When going to 9, the output patches will be suffixed with the"
- echo " repo name"
- echo " -to10 create patches appropriate for a JDK 10 source tree"
- echo " -r specify repo for source patch, set to 'top' for top repo"
- echo " is the input patch file, that needs shuffling/unshuffling"
- echo " is the updated patch file "
- echo " "
- exit 1
-}
-
-SCRIPT_DIR=`dirname $0`
-UNSHUFFLE_LIST=$SCRIPT_DIR"/unshuffle_list.txt"
-
-if [ ! -f "$UNSHUFFLE_LIST" ] ; then
- echo "FATAL: cannot find $UNSHUFFLE_LIST" >&2
- exit 1
-fi
-
-vflag="false"
-while [ $# -gt 0 ]
-do
- case $1 in
- -h | --help )
- usage
- ;;
-
- -v | --verbose )
- vflag="true"
- ;;
-
- -r)
- repo="$2"
- shift
- ;;
-
- -to9)
- shuffle_to=9
- ;;
-
- -to10)
- shuffle_to=10
- ;;
-
- -*) # bad option
- usage
- ;;
-
- * ) # non option
- break
- ;;
- esac
- shift
-done
-
-# Make sure we have the right number of arguments
-if [ ! $# -eq 2 ] ; then
- echo "ERROR: Invalid number of arguments." >&2
- usage
-fi
-
-# Check the given repo
-repos="top corba jaxp jaxws jdk langtools nashorn hotspot"
-found="false"
-if [ -n "$repo" ]; then
- for r in $repos ; do
- if [ $repo = "$r" ] ; then
- found="true"
- break;
- fi
- done
- if [ $found = "false" ] ; then
- echo "ERROR: Unknown repo: $repo. Should be one of [$repos]." >&2
- usage
- fi
-fi
-
-if [ "$shuffle_to" != "9" -a "$shuffle_to" != "10" ]; then
- echo "ERROR: Must pick either -to9 or -to10"
- exit 1
-fi
-
-# When going to 10, a repo must be specified for the source patch
-if [ "$shuffle_to" = "10" -a -z "$repo" ]; then
- echo "ERROR: Must specify src repo for JDK 9 patch"
- exit 1
-fi
-
-# Check given input/output files
-input="$1"
-if [ "x$input" = "x-" ] ; then
- input="/dev/stdin"
-fi
-
-if [ ! -f $input -a "x$input" != "x/dev/stdin" ] ; then
- echo "ERROR: Cannot find input patch file: $input" >&2
- exit 1
-fi
-
-output="$2"
-if [ "x$output" = "x-" ] ; then
- output="/dev/stdout"
-fi
-base_output="$output"
-
-if [ "$shuffle_to" = "10" ]; then
- if [ -f $output -a "x$output" != "x/dev/stdout" ] ; then
- echo "ERROR: Output patch already exists: $output" >&2
- exit 1
- fi
-else
- for r in $repos; do
- if [ -f "$output.$r" ]; then
- echo "ERROR: Output patch already exists: $output.$r" >&2
- exit 1
- fi
- done
-fi
-
-verbose() {
- if [ ${vflag} = "true" ] ; then
- echo "$@" >&2
- fi
-}
-
-unshuffle() {
- line=$@
- verbose "Attempting to rewrite: \"$line\""
-
- # Retrieve the file name
- path=
- if echo "$line" | egrep '^diff' > /dev/null ; then
- if ! echo "$line" | egrep '\-\-git' > /dev/null ; then
- echo "ERROR: Only git patches supported. Please use 'hg export --git ...'." >&2
- exit 1
- fi
- path="`echo "$line" | sed -e s@'diff --git a/'@@ -e s@' b/.*$'@@`"
- elif echo "$line" | egrep '^\-\-\-' > /dev/null ; then
- path="`echo "$line" | sed -e s@'--- a/'@@`"
- elif echo "$line" | egrep '^\+\+\+' > /dev/null ; then
- path="`echo "$line" | sed s@'+++ b/'@@`"
- fi
- verbose "Extracted path: \"$path\""
-
- # Find the most specific matches in the shuffle list
- matches=
- if [ -n "$repo" -a "$repo" != "top" ]; then
- matchpath="$repo"/"$path"/x
- else
- matchpath="$path"/x
- fi
- while [ "$matchpath" != "" ] ; do
- matchpath="`echo $matchpath | sed s@'\(.*\)/.*$'@'\1'@`"
-
- if [ "$shuffle_to" = "10" ] ; then
- pattern=": $matchpath$"
- else
- pattern="^$matchpath :"
- fi
- verbose "Attempting to find \"$matchpath\""
- matches=`egrep "$pattern" "$UNSHUFFLE_LIST"`
- if ! [ "x${matches}" = "x" ] ; then
- verbose "Got matches: [$matches]"
- break;
- fi
-
- if ! echo "$matchpath" | egrep '.*/.*' > /dev/null ; then
- break;
- fi
- done
-
- # Rewrite the line, if we have a match
- if ! [ "x${matches}" = "x" ] ; then
- shuffled="${matches%% : *}"
- unshuffled="${matches#* : }"
- patch_suffix_9=""
- for r in $repos; do
- if [ "$unshuffled" != "${unshuffled#$r}" ]; then
- unshuffled="${unshuffled#$r\/}"
- patch_suffix_9=".$r"
- fi
- done
- verbose "shuffled: $shuffled"
- verbose "unshuffled: $unshuffled"
- verbose "patch_suffix_9: $patch_suffix_9"
- if [ "$shuffle_to" = "10" ] ; then
- newline="`echo "$line" | sed -e s@"$unshuffled"@"$shuffled"@g`"
- else
- newline="`echo "$line" | sed -e s@"$shuffled"@"$unshuffled"@g`"
- output=$base_output$patch_suffix_9
- verbose "Writing to $output"
- fi
- verbose "Rewriting to \"$newline\""
- echo "$newline" >> $output
- else
- echo "WARNING: no match found for $path"
- echo "$line" >> $output
- fi
-}
-
-while IFS= read -r line
-do
- if echo "$line" | egrep '^diff|^\-\-\-|^\+\+\+' > /dev/null ; then
- unshuffle "$line"
- else
- printf "%s\n" "$line" >> $output
- fi
-done < "$input"
diff --git a/make/scripts/update_copyright_year.sh b/bin/update_copyright_year.sh
similarity index 100%
rename from make/scripts/update_copyright_year.sh
rename to bin/update_copyright_year.sh
diff --git a/make/scripts/update_pch.sh b/bin/update_pch.sh
similarity index 92%
rename from make/scripts/update_pch.sh
rename to bin/update_pch.sh
index 534525353fd..d7871fdd753 100644
--- a/make/scripts/update_pch.sh
+++ b/bin/update_pch.sh
@@ -23,9 +23,19 @@
# The output of this script may require some degree of human curation:
# - Redundant headers, e.g. both x.hpp, x.inline.hpp are included;
# - Headers relative to a non-default feature should be protected by an
-# appropriate 'if' clause to make sure all variants can build without
+# appropriate 'if' clause to make sure all variants can build without
# errors.
+source_path="$(dirname ${0})"
+this_script_dir="$(cd -- "${source_path}" > /dev/null && pwd)"
+if test -z "${this_script_dir}"; then
+ echo "Error: Could not determine location of this script"
+ exit 1
+fi
+
+# Work in top directory
+cd $this_script_dir/..
+
# Time threshold for header compilation, if the time exceeds the
# threshold the header will be precompiled.
if [ -z "$MIN_MS" ]; then
diff --git a/make/autoconf/compare.sh.template b/make/autoconf/compare.sh.template
index bcb5608855a..84421035ab9 100644
--- a/make/autoconf/compare.sh.template
+++ b/make/autoconf/compare.sh.template
@@ -110,4 +110,4 @@ $MV $OUTPUTDIR/compare.log $OUTPUTDIR/compare.log.old 2> /dev/null
export SCRIPT_DIR="$( cd "$( dirname "$0" )" > /dev/null && pwd )"
-$BASH $TOPDIR/make/scripts/logger.sh $OUTPUTDIR/compare.log $BASH "$REAL_COMPARE_SCRIPT" "$@"
+$BASH $TOPDIR/make/scripts/compare-logger.sh $OUTPUTDIR/compare.log $BASH "$REAL_COMPARE_SCRIPT" "$@"
diff --git a/make/scripts/logger.sh b/make/scripts/compare-logger.sh
similarity index 100%
rename from make/scripts/logger.sh
rename to make/scripts/compare-logger.sh
diff --git a/make/scripts/hide_important_warnings_from_javac.sh b/make/scripts/hide_important_warnings_from_javac.sh
deleted file mode 100644
index 392ed33247a..00000000000
--- a/make/scripts/hide_important_warnings_from_javac.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
-# This code is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation.
-#
-# This code is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# version 2 for more details (a copy is included in the LICENSE file that
-# accompanied this code).
-#
-# You should have received a copy of the GNU General Public License version
-# 2 along with this work; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-# or visit www.oracle.com if you need additional information or have any
-# questions.
-#
-
-GREP=grep
-
-#
-EXP="Note: Some input files use or override a deprecated API."
-EXP="${EXP}|Note: Recompile with -Xlint:deprecation for details."
-EXP="${EXP}|Note: Some input files use unchecked or unsafe operations."
-EXP="${EXP}|Note: Recompile with -Xlint:unchecked for details."
-EXP="${EXP}| warning"
-EXP="${EXP}|uses or overrides a deprecated API."
-EXP="${EXP}|uses unchecked or unsafe operations."
-#
-${GREP} --line-buffered -v -E "${EXP}"
From 33244c82445994131a9168451275216916ce635c Mon Sep 17 00:00:00 2001
From: Magnus Ihse Bursie
Date: Wed, 10 Sep 2025 10:00:15 +0000
Subject: [PATCH 017/120] 8344030: Improved handling of TOOLCHAIN_PATH
Reviewed-by: erikj
---
make/autoconf/basic.m4 | 24 ++++--------------------
make/autoconf/basic_tools.m4 | 19 ++-----------------
make/autoconf/build-performance.m4 | 7 +------
make/autoconf/flags-ldflags.m4 | 2 +-
make/autoconf/toolchain.m4 | 17 ++++-------------
make/autoconf/util_paths.m4 | 12 ++++++------
6 files changed, 18 insertions(+), 63 deletions(-)
diff --git a/make/autoconf/basic.m4 b/make/autoconf/basic.m4
index 2d3e071dd52..316bfc5037d 100644
--- a/make/autoconf/basic.m4
+++ b/make/autoconf/basic.m4
@@ -210,17 +210,8 @@ AC_DEFUN([BASIC_SETUP_XCODE_SYSROOT],
if test $? -ne 0; then
AC_MSG_ERROR([The xcodebuild tool in the devkit reports an error: $XCODEBUILD_OUTPUT])
fi
- elif test "x$TOOLCHAIN_PATH" != x; then
- UTIL_LOOKUP_PROGS(XCODEBUILD, xcodebuild, $TOOLCHAIN_PATH)
- if test "x$XCODEBUILD" != x; then
- XCODEBUILD_OUTPUT=`"$XCODEBUILD" -version 2>&1`
- if test $? -ne 0; then
- AC_MSG_WARN([Ignoring the located xcodebuild tool $XCODEBUILD due to an error: $XCODEBUILD_OUTPUT])
- XCODEBUILD=
- fi
- fi
else
- UTIL_LOOKUP_PROGS(XCODEBUILD, xcodebuild)
+ UTIL_LOOKUP_TOOLCHAIN_PROGS(XCODEBUILD, xcodebuild)
if test "x$XCODEBUILD" != x; then
XCODEBUILD_OUTPUT=`"$XCODEBUILD" -version 2>&1`
if test $? -ne 0; then
@@ -348,21 +339,11 @@ AC_DEFUN_ONCE([BASIC_SETUP_DEVKIT],
# You can force the sysroot if the sysroot encoded into the compiler tools
# is not correct.
- AC_ARG_WITH(sys-root, [AS_HELP_STRING([--with-sys-root],
- [alias for --with-sysroot for backwards compatibility])],
- [SYSROOT=$with_sys_root]
- )
-
AC_ARG_WITH(sysroot, [AS_HELP_STRING([--with-sysroot],
[use this directory as sysroot])],
[SYSROOT=$with_sysroot]
)
- AC_ARG_WITH([tools-dir], [AS_HELP_STRING([--with-tools-dir],
- [alias for --with-toolchain-path for backwards compatibility])],
- [UTIL_PREPEND_TO_PATH([TOOLCHAIN_PATH],$with_tools_dir)]
- )
-
AC_ARG_WITH([toolchain-path], [AS_HELP_STRING([--with-toolchain-path],
[prepend these directories when searching for toolchain binaries (compilers etc)])],
[UTIL_PREPEND_TO_PATH([TOOLCHAIN_PATH],$with_toolchain_path)]
@@ -371,6 +352,9 @@ AC_DEFUN_ONCE([BASIC_SETUP_DEVKIT],
AC_ARG_WITH([xcode-path], [AS_HELP_STRING([--with-xcode-path],
[set up toolchain on Mac OS using a path to an Xcode installation])])
+ UTIL_DEPRECATED_ARG_WITH(sys-root)
+ UTIL_DEPRECATED_ARG_WITH(tools-dir)
+
if test "x$with_xcode_path" != x; then
if test "x$OPENJDK_BUILD_OS" = "xmacosx"; then
UTIL_PREPEND_TO_PATH([TOOLCHAIN_PATH],
diff --git a/make/autoconf/basic_tools.m4 b/make/autoconf/basic_tools.m4
index 5815c55c962..5367db46679 100644
--- a/make/autoconf/basic_tools.m4
+++ b/make/autoconf/basic_tools.m4
@@ -207,29 +207,14 @@ AC_DEFUN([BASIC_CHECK_GNU_MAKE],
UTIL_SETUP_TOOL(MAKE,
[
# Try our hardest to locate a correct version of GNU make
- UTIL_LOOKUP_PROGS(CHECK_GMAKE, gmake)
+ UTIL_LOOKUP_TOOLCHAIN_PROGS(CHECK_GMAKE, gmake)
BASIC_CHECK_MAKE_VERSION("$CHECK_GMAKE", [gmake in PATH])
if test "x$FOUND_MAKE" = x; then
- UTIL_LOOKUP_PROGS(CHECK_MAKE, make)
+ UTIL_LOOKUP_TOOLCHAIN_PROGS(CHECK_MAKE, make)
BASIC_CHECK_MAKE_VERSION("$CHECK_MAKE", [make in PATH])
fi
- if test "x$FOUND_MAKE" = x; then
- if test "x$TOOLCHAIN_PATH" != x; then
- # We have a toolchain path, check that as well before giving up.
- OLD_PATH=$PATH
- PATH=$TOOLCHAIN_PATH:$PATH
- UTIL_LOOKUP_PROGS(CHECK_TOOLSDIR_GMAKE, gmake)
- BASIC_CHECK_MAKE_VERSION("$CHECK_TOOLSDIR_GMAKE", [gmake in tools-dir])
- if test "x$FOUND_MAKE" = x; then
- UTIL_LOOKUP_PROGS(CHECK_TOOLSDIR_MAKE, make)
- BASIC_CHECK_MAKE_VERSION("$CHECK_TOOLSDIR_MAKE", [make in tools-dir])
- fi
- PATH=$OLD_PATH
- fi
- fi
-
if test "x$FOUND_MAKE" = x; then
AC_MSG_ERROR([Cannot find GNU make $MAKE_REQUIRED_VERSION or newer! Please put it in the path, or add e.g. MAKE=/opt/gmake3.81/make as argument to configure.])
fi
diff --git a/make/autoconf/build-performance.m4 b/make/autoconf/build-performance.m4
index 10e86e75199..dfc9e979d2f 100644
--- a/make/autoconf/build-performance.m4
+++ b/make/autoconf/build-performance.m4
@@ -162,12 +162,7 @@ AC_DEFUN([BPERF_SETUP_CCACHE],
# Check if ccache is available
CCACHE_AVAILABLE=true
- OLD_PATH="$PATH"
- if test "x$TOOLCHAIN_PATH" != x; then
- PATH=$TOOLCHAIN_PATH:$PATH
- fi
- UTIL_LOOKUP_PROGS(CCACHE, ccache)
- PATH="$OLD_PATH"
+ UTIL_LOOKUP_TOOLCHAIN_PROGS(CCACHE, ccache)
AC_MSG_CHECKING([if ccache is available])
if test "x$TOOLCHAIN_TYPE" != "xgcc" && test "x$TOOLCHAIN_TYPE" != "xclang"; then
diff --git a/make/autoconf/flags-ldflags.m4 b/make/autoconf/flags-ldflags.m4
index 509e0dd825f..a12a6e7f9a6 100644
--- a/make/autoconf/flags-ldflags.m4
+++ b/make/autoconf/flags-ldflags.m4
@@ -74,7 +74,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
# Clang needs the lld linker to work correctly
BASIC_LDFLAGS="-fuse-ld=lld -Wl,--exclude-libs,ALL"
if test "x$CXX_IS_USER_SUPPLIED" = xfalse && test "x$CC_IS_USER_SUPPLIED" = xfalse; then
- UTIL_REQUIRE_PROGS(LLD, lld, $TOOLCHAIN_PATH:$PATH)
+ UTIL_REQUIRE_TOOLCHAIN_PROGS(LLD, lld)
fi
fi
if test "x$OPENJDK_TARGET_OS" = xaix; then
diff --git a/make/autoconf/toolchain.m4 b/make/autoconf/toolchain.m4
index f3ef44d382b..4662c62d901 100644
--- a/make/autoconf/toolchain.m4
+++ b/make/autoconf/toolchain.m4
@@ -276,9 +276,6 @@ AC_DEFUN_ONCE([TOOLCHAIN_PRE_DETECTION],
ORG_CFLAGS="$CFLAGS"
ORG_CXXFLAGS="$CXXFLAGS"
- # autoconf magic only relies on PATH, so update it if tools dir is specified
- OLD_PATH="$PATH"
-
if test "x$OPENJDK_BUILD_OS" = "xmacosx"; then
if test "x$XCODEBUILD" != x; then
XCODE_VERSION_OUTPUT=`"$XCODEBUILD" -version 2> /dev/null | $HEAD -n 1`
@@ -300,9 +297,10 @@ AC_DEFUN_ONCE([TOOLCHAIN_PRE_DETECTION],
fi
AC_SUBST(TOOLCHAIN_VERSION)
- # Finally prepend TOOLCHAIN_PATH to the PATH, to allow --with-tools-dir to
- # override all other locations.
- if test "x$TOOLCHAIN_PATH" != x; then
+ # For the microsoft toolchain the toolchain path needs to be added to the
+ # normal path, or the compiler will not work in some situations in later
+ # configure checks.
+ if test "x$TOOLCHAIN_TYPE" = "xmicrosoft" && test "x$TOOLCHAIN_PATH" != x; then
export PATH=$TOOLCHAIN_PATH:$PATH
fi
])
@@ -310,13 +308,6 @@ AC_DEFUN_ONCE([TOOLCHAIN_PRE_DETECTION],
# Restore path, etc
AC_DEFUN_ONCE([TOOLCHAIN_POST_DETECTION],
[
- # Restore old path, except for the microsoft toolchain, which requires the
- # toolchain path to remain in place. Otherwise the compiler will not work in
- # some situations in later configure checks.
- if test "x$TOOLCHAIN_TYPE" != "xmicrosoft"; then
- PATH="$OLD_PATH"
- fi
-
# Restore the flags to the user specified values.
# This is necessary since AC_PROG_CC defaults CFLAGS to "-g -O2"
CFLAGS="$ORG_CFLAGS"
diff --git a/make/autoconf/util_paths.m4 b/make/autoconf/util_paths.m4
index 40864680aad..abe79c40fb6 100644
--- a/make/autoconf/util_paths.m4
+++ b/make/autoconf/util_paths.m4
@@ -458,17 +458,18 @@ AC_DEFUN([UTIL_LOOKUP_PROGS],
################################################################################
# Call UTIL_SETUP_TOOL with AC_CHECK_TOOLS to locate the tool. This will look
-# first for cross-compilation tools.
+# first for tools using the cross-compilation prefix, and then for tools without
+# this prefix. For each of these name variants, it will look first in the
+# toolchain path, and then in the normal path.
# $1: variable to set
# $2: executable name (or list of names) to look for
-# $3: [path]
AC_DEFUN([UTIL_LOOKUP_TOOLCHAIN_PROGS],
[
if test "x$ac_tool_prefix" = x; then
- UTIL_LOOKUP_PROGS($1, $2, $3)
+ UTIL_LOOKUP_PROGS($1, $2, [$TOOLCHAIN_PATH:$PATH])
else
prefixed_names=$(for name in $2; do echo ${ac_tool_prefix}${name} $name; done)
- UTIL_LOOKUP_PROGS($1, $prefixed_names, $3)
+ UTIL_LOOKUP_PROGS($1, $prefixed_names, [$TOOLCHAIN_PATH:$PATH])
fi
])
@@ -497,10 +498,9 @@ AC_DEFUN([UTIL_REQUIRE_PROGS],
# Like UTIL_LOOKUP_PROGS but fails if no tool was found.
# $1: variable to set
# $2: executable name (or list of names) to look for
-# $3: [path]
AC_DEFUN([UTIL_REQUIRE_TOOLCHAIN_PROGS],
[
- UTIL_LOOKUP_TOOLCHAIN_PROGS($1, $2, $3)
+ UTIL_LOOKUP_TOOLCHAIN_PROGS($1, $2)
UTIL_CHECK_NONEMPTY($1)
])
From edae355e95f23294eda092dbedcb7f6cf165b0f8 Mon Sep 17 00:00:00 2001
From: Magnus Ihse Bursie
Date: Wed, 10 Sep 2025 10:27:38 +0000
Subject: [PATCH 018/120] 8246325: Add DRYRUN facility to SetupExecute
Reviewed-by: erikj
---
make/Bundles.gmk | 4 ++--
make/autoconf/spec.gmk.template | 10 ++++++----
make/common/Execute.gmk | 20 ++++++++++++++++----
test/make/TestExecute.gmk | 24 ++++++++++++++++++++++--
4 files changed, 46 insertions(+), 12 deletions(-)
diff --git a/make/Bundles.gmk b/make/Bundles.gmk
index ba8ec0c864b..cf3b77e4e52 100644
--- a/make/Bundles.gmk
+++ b/make/Bundles.gmk
@@ -301,7 +301,7 @@ ifneq ($(filter product-bundles% legacy-bundles, $(MAKECMDGOALS)), )
$(call LogWarn, Signing $(JDK_BUNDLE_NAME))
$(CODESIGN) -s "$(MACOSX_CODESIGN_IDENTITY)" \
--timestamp --options runtime --deep --force \
- $(JDK_MACOSX_BUNDLE_DIR_SIGNED)/$(JDK_MACOSX_BUNDLE_TOP_DIR) $(LOG_DEBUG)
+ $(JDK_MACOSX_BUNDLE_DIR_SIGNED)/$(JDK_MACOSX_BUNDLE_TOP_SUBDIR) $(LOG_DEBUG)
$(TOUCH) $@
$(eval $(call SetupBundleFile, BUILD_JDK_BUNDLE, \
@@ -330,7 +330,7 @@ ifneq ($(filter product-bundles% legacy-bundles, $(MAKECMDGOALS)), )
$(call LogWarn, Signing $(JRE_BUNDLE_NAME))
$(CODESIGN) -s "$(MACOSX_CODESIGN_IDENTITY)" \
--timestamp --options runtime --deep --force \
- $(JRE_MACOSX_BUNDLE_DIR_SIGNED)/$(JRE_MACOSX_BUNDLE_TOP_DIR) $(LOG_DEBUG)
+ $(JRE_MACOSX_BUNDLE_DIR_SIGNED)/$(JRE_MACOSX_BUNDLE_TOP_SUBDIR) $(LOG_DEBUG)
$(TOUCH) $@
$(eval $(call SetupBundleFile, BUILD_JRE_BUNDLE, \
diff --git a/make/autoconf/spec.gmk.template b/make/autoconf/spec.gmk.template
index e3345940101..ab6bb51c27e 100644
--- a/make/autoconf/spec.gmk.template
+++ b/make/autoconf/spec.gmk.template
@@ -898,12 +898,14 @@ JDK_MACOSX_BUNDLE_DIR = $(IMAGES_OUTPUTDIR)/$(JDK_MACOSX_BUNDLE_SUBDIR)
JRE_MACOSX_BUNDLE_DIR = $(IMAGES_OUTPUTDIR)/$(JRE_MACOSX_BUNDLE_SUBDIR)
JDK_MACOSX_BUNDLE_DIR_SIGNED = $(IMAGES_OUTPUTDIR)/$(JDK_MACOSX_BUNDLE_SUBDIR_SIGNED)
JRE_MACOSX_BUNDLE_DIR_SIGNED = $(IMAGES_OUTPUTDIR)/$(JRE_MACOSX_BUNDLE_SUBDIR_SIGNED)
-JDK_MACOSX_BUNDLE_TOP_DIR = jdk-$(VERSION_NUMBER).jdk
-JRE_MACOSX_BUNDLE_TOP_DIR = jre-$(VERSION_NUMBER).jre
-JDK_MACOSX_CONTENTS_SUBDIR = $(JDK_MACOSX_BUNDLE_TOP_DIR)/Contents
-JRE_MACOSX_CONTENTS_SUBDIR = $(JRE_MACOSX_BUNDLE_TOP_DIR)/Contents
+JDK_MACOSX_BUNDLE_TOP_SUBDIR = jdk-$(VERSION_NUMBER).jdk
+JRE_MACOSX_BUNDLE_TOP_SUBDIR = jre-$(VERSION_NUMBER).jre
+JDK_MACOSX_CONTENTS_SUBDIR = $(JDK_MACOSX_BUNDLE_TOP_SUBDIR)/Contents
+JRE_MACOSX_CONTENTS_SUBDIR = $(JRE_MACOSX_BUNDLE_TOP_SUBDIR)/Contents
JDK_MACOSX_CONTENTS_DIR = $(JDK_MACOSX_BUNDLE_DIR)/$(JDK_MACOSX_CONTENTS_SUBDIR)
JRE_MACOSX_CONTENTS_DIR = $(JRE_MACOSX_BUNDLE_DIR)/$(JRE_MACOSX_CONTENTS_SUBDIR)
+JDK_MACOSX_BUNDLE_TOP_DIR = $(JDK_MACOSX_BUNDLE_DIR)/$(JDK_MACOSX_BUNDLE_TOP_SUBDIR)
+JRE_MACOSX_BUNDLE_TOP_DIR = $(JRE_MACOSX_BUNDLE_DIR)/$(JRE_MACOSX_BUNDLE_TOP_SUBDIR)
# Bundle names
ifneq ($(VERSION_BUILD), )
diff --git a/make/common/Execute.gmk b/make/common/Execute.gmk
index 1ee3c28261b..4199e8f13b7 100644
--- a/make/common/Execute.gmk
+++ b/make/common/Execute.gmk
@@ -82,6 +82,8 @@ ifeq ($(INCLUDE), true)
# INFO : Message to display at LOG=info level when running command (optional)
# WARN : Message to display at LOG=warn level when running command (optional)
# DEPS : Dependencies for the execution to take place
+# DRYRUN : Set to true to perform everything but executing the command \
+# (defaults to false, primarily intended for debugging)
#
# Setup make rules for copying files, with an option to do more complex
@@ -161,8 +163,13 @@ define SetupExecuteBody
$$(TOUCH) $$@
$$($1_EXEC_RESULT): $$($1_PRE_MARKER)
- $$(call ExecuteWithLog, $$($1_BASE)_exec, \
- cd $$($1_WORKING_DIR) && $$($1_COMMAND))
+ ifneq ($$($1_DRYRUN), true)
+ $$(call ExecuteWithLog, $$($1_BASE)_exec, \
+ cd $$($1_WORKING_DIR) && $$($1_COMMAND))
+ else
+ $$(call LogWarn, DRYRUN enabled for $1, not actually running command)
+ $$(TOUCH) $$@
+ endif
ifeq ($$($1_EXEC_RESULT), $$($1_EXEC_MARKER))
$$(TOUCH) $$@
endif
@@ -177,8 +184,13 @@ define SetupExecuteBody
$$(call LogInfo, $$($1_INFO))
endif
$$(call MakeDir, $$(call EncodeSpace, $$($1_WORKING_DIR)) $$(call EncodeSpace, $$($1_SUPPORT_DIR)) $$(call EncodeSpace, $$($1_OUTPUT_DIR)))
- $$(call ExecuteWithLog, $$($1_BASE)_exec, \
- cd $$($1_WORKING_DIR) && $$($1_COMMAND))
+ ifneq ($$($1_DRYRUN), true)
+ $$(call ExecuteWithLog, $$($1_BASE)_exec, \
+ cd $$($1_WORKING_DIR) && $$($1_COMMAND))
+ else
+ $$(call LogWarn, DRYRUN enabled for $1, not actually running command)
+ $$(TOUCH) $$@
+ endif
ifeq ($$($1_EXEC_RESULT), $$($1_EXEC_MARKER))
$$(TOUCH) $$@
endif
diff --git a/test/make/TestExecute.gmk b/test/make/TestExecute.gmk
index f71a402502e..5d57d244c39 100644
--- a/test/make/TestExecute.gmk
+++ b/test/make/TestExecute.gmk
@@ -60,10 +60,30 @@ $(eval $(call SetupExecute, EXEC_2, \
run-test2: $(EXEC_2)
test -f $(OUTPUT_DIR)/exec_2/special/specialfile
+$(eval $(call SetupExecute, EXEC_3, \
+ INFO := Testing that SetupExecute with DRYRUN does nothing, \
+ OUTPUT_DIR := $(OUTPUT_DIR)/exec_3, \
+ DRYRUN := true, \
+ COMMAND := $(ECHO) "This should not happen" > $(OUTPUT_DIR)/exec_3/dryrunfile, \
+))
-TEST_TARGETS += run-test1 run-test2
+run-test3: $(EXEC_3)
+ test ! -f $(OUTPUT_DIR)/exec_3/dryrunfile
-.PHONY: run-test1 run-test2
+$(eval $(call SetupExecute, EXEC_4, \
+ INFO := Testing that SetupExecute with DRYRUN does nothing but touches output file, \
+ OUTPUT_FILE := $(OUTPUT_DIR)/exec_4/output, \
+ DRYRUN := true, \
+ COMMAND := $(ECHO) "This should not happen" > $(OUTPUT_DIR)/exec_4/dryrunfile, \
+))
+
+run-test4: $(EXEC_4)
+ test ! -f $(OUTPUT_DIR)/exec_4/dryrunfile
+ test -f $(OUTPUT_DIR)/exec_4/output
+
+TEST_TARGETS += run-test1 run-test2 run-test3 run-test4
+
+.PHONY: run-test1 run-test2 run-test3 run-test4
################################################################################
From 4d4e51c41fed79427fb621fd9fcc8e5e23bfb287 Mon Sep 17 00:00:00 2001
From: David Beaumont
Date: Wed, 10 Sep 2025 11:49:02 +0000
Subject: [PATCH 019/120] 8365483: Test
sun/rmi/runtime/Log/6409194/NoConsoleOutput.java sometimes fails
Reviewed-by: dfuchs, jpai
---
.../java/util/logging/StreamHandler.java | 13 +-
.../logging/StreamHandlerRacyCloseTest.java | 137 ++++++++++++++++++
2 files changed, 145 insertions(+), 5 deletions(-)
create mode 100644 test/jdk/java/util/logging/StreamHandlerRacyCloseTest.java
diff --git a/src/java.logging/share/classes/java/util/logging/StreamHandler.java b/src/java.logging/share/classes/java/util/logging/StreamHandler.java
index ddfea9674dc..3b9af54814d 100644
--- a/src/java.logging/share/classes/java/util/logging/StreamHandler.java
+++ b/src/java.logging/share/classes/java/util/logging/StreamHandler.java
@@ -214,13 +214,16 @@ public class StreamHandler extends Handler {
try {
synchronized (this) {
+ // Re-check writer between isLoggable() and here.
Writer writer = this.writer;
- if (!doneHeader) {
- writer.write(formatter.getHead(this));
- doneHeader = true;
+ if (writer != null) {
+ if (!doneHeader) {
+ writer.write(formatter.getHead(this));
+ doneHeader = true;
+ }
+ writer.write(msg);
+ synchronousPostWriteHook();
}
- writer.write(msg);
- synchronousPostWriteHook();
}
} catch (Exception ex) {
// We don't want to throw an exception here, but we
diff --git a/test/jdk/java/util/logging/StreamHandlerRacyCloseTest.java b/test/jdk/java/util/logging/StreamHandlerRacyCloseTest.java
new file mode 100644
index 00000000000..5bb465088e5
--- /dev/null
+++ b/test/jdk/java/util/logging/StreamHandlerRacyCloseTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.ErrorManager;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.SimpleFormatter;
+import java.util.logging.StreamHandler;
+
+import org.junit.jupiter.api.Test;
+
+/*
+ * @test
+ * @bug 8365483
+ * @summary verify that concurrent calls to publish() and close() on a
+ * StreamHandler do not cause unexpected exceptions
+ * @run junit StreamHandlerRacyCloseTest
+ */
+public class StreamHandlerRacyCloseTest {
+
+ private static final class ExceptionTrackingErrorManager extends ErrorManager {
+ private final AtomicReference firstError = new AtomicReference<>();
+
+ @Override
+ public void error(String msg, Exception ex, int code) {
+ // just track one/first exception, that's good enough for this test
+ this.firstError.compareAndSet(null, new RuntimeException(msg, ex));
+ }
+ }
+
+ @Test
+ void testRacyClose() throws Exception {
+ final int numTimes = 100;
+ try (ExecutorService executor = Executors.newFixedThreadPool(numTimes)) {
+ final List> tasks = new ArrayList<>();
+ for (int i = 1; i <= numTimes; i++) {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ // construct a StreamHandler with an ErrorManager which propagates
+ // any errors that happen during publish()
+ final StreamHandler handler = new StreamHandler(baos, new SimpleFormatter());
+ handler.setErrorManager(new ExceptionTrackingErrorManager());
+
+ final CountDownLatch latch = new CountDownLatch(2);
+ // create a publisher and closer task which will run concurrently
+ tasks.add(new Publisher(handler, latch));
+ tasks.add(new Closer(handler, latch));
+ }
+ // submit the tasks and expect successful completion of each
+ final List> completed = executor.invokeAll(tasks);
+ for (var f : completed) {
+ f.get();
+ }
+ }
+ }
+
+ private static final class Closer implements Callable {
+ private final StreamHandler handler;
+ private final CountDownLatch startLatch;
+
+ private Closer(final StreamHandler handler, final CountDownLatch startLatch) {
+ this.handler = handler;
+ this.startLatch = startLatch;
+ }
+
+ @Override
+ public Void call() throws Exception {
+ // notify the other task of our readiness
+ this.startLatch.countDown();
+ // wait for the other task to arrive
+ this.startLatch.await();
+ // close the handler
+ this.handler.close();
+ // propagate any exception that may have been caught by the error manager
+ final var errMgr = (ExceptionTrackingErrorManager) this.handler.getErrorManager();
+ if (errMgr.firstError.get() != null) {
+ throw errMgr.firstError.get();
+ }
+ return null;
+ }
+ }
+
+ private static final class Publisher implements Callable {
+ private final StreamHandler handler;
+ private final CountDownLatch startLatch;
+
+ private Publisher(final StreamHandler handler, final CountDownLatch startLatch) {
+ this.handler = handler;
+ this.startLatch = startLatch;
+ }
+
+ @Override
+ public Void call() throws Exception {
+ final LogRecord record = new LogRecord(Level.WARNING, "hello world");
+ // notify the other task of our readiness
+ this.startLatch.countDown();
+ // wait for the other task to arrive
+ this.startLatch.await();
+ // publish the record
+ this.handler.publish(record);
+ // propagate any exception that may have been caught by the error manager
+ final var errMgr = (ExceptionTrackingErrorManager) this.handler.getErrorManager();
+ if (errMgr.firstError.get() != null) {
+ throw errMgr.firstError.get();
+ }
+ return null;
+ }
+ }
+}
From 703d930e4d52a6f9741cf9affee8caade550e67b Mon Sep 17 00:00:00 2001
From: Stefan Karlsson
Date: Wed, 10 Sep 2025 11:55:31 +0000
Subject: [PATCH 020/120] 8366980: TestTransparentHugePagesHeap.java fails when
run with -UseCompressedOops
Reviewed-by: aboldtch, tschatzl
---
.../gc/TestTransparentHugePagesHeap.java | 81 +++++++++++++------
1 file changed, 55 insertions(+), 26 deletions(-)
diff --git a/test/hotspot/jtreg/gc/TestTransparentHugePagesHeap.java b/test/hotspot/jtreg/gc/TestTransparentHugePagesHeap.java
index 25044d2c3e4..2c2e19b87c4 100644
--- a/test/hotspot/jtreg/gc/TestTransparentHugePagesHeap.java
+++ b/test/hotspot/jtreg/gc/TestTransparentHugePagesHeap.java
@@ -49,6 +49,7 @@
* @run driver TestTransparentHugePagesHeap Serial
*/
+import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -98,47 +99,76 @@ public class TestTransparentHugePagesHeap {
class VerifyTHPEnabledForHeap {
public static void main(String args[]) throws Exception {
- String heapAddress = readHeapAddressInLog();
+ // Extract the heap start from pagesize logging
+ BigInteger heapStart = extractHeapStartFromLog();
+
Path smaps = makeSmapsCopy();
- final Pattern heapSection = Pattern.compile("^" + heapAddress + ".*");
- final Pattern thpEligible = Pattern.compile("THPeligible:\\s+(\\d)\\s*");
+ final Pattern addressRangePattern = Pattern.compile("([0-9a-f]*?)-([0-9a-f]*?) .*");
+ final Pattern thpEligiblePattern = Pattern.compile("THPeligible:\\s+(\\d)\\s*");
Scanner smapsFile = new Scanner(smaps);
while (smapsFile.hasNextLine()) {
- Matcher heapMatcher = heapSection.matcher(smapsFile.nextLine());
-
- if (heapMatcher.matches()) {
- // Found the first heap section, verify that it is THP eligible
- while (smapsFile.hasNextLine()) {
- Matcher m = thpEligible.matcher(smapsFile.nextLine());
- if (m.matches()) {
- if (Integer.parseInt(m.group(1)) == 1) {
- // THPeligible is 1, heap can be backed by huge pages
- return;
- }
-
- throw new RuntimeException("First heap section at 0x" + heapAddress + " is not THPeligible");
- }
- }
+ Matcher addressRangeMatcher = addressRangePattern.matcher(smapsFile.nextLine());
+ if (!addressRangeMatcher.matches()) {
+ continue;
}
+
+ // Found an address range line in the smaps file
+
+ BigInteger addressStart = new BigInteger(addressRangeMatcher.group(1), 16);
+ BigInteger addressEnd = new BigInteger(addressRangeMatcher.group(2), 16);
+
+ // Linux sometimes merges adjacent VMAs so we can't search for a range that
+ // exactly matches the heap range. Instead we look for the first range that
+ // contains the start of the heap and verify that that range is THP eligible.
+
+ if (addressStart.compareTo(heapStart) > 0 || heapStart.compareTo(addressEnd) >= 0) {
+ continue;
+ }
+
+ // Found a range that contains the start of the heap, verify that it is THP eligible.
+
+ while (smapsFile.hasNextLine()) {
+ Matcher m = thpEligiblePattern.matcher(smapsFile.nextLine());
+ if (!m.matches()) {
+ continue;
+ }
+
+ // Found the THPeligible line
+
+ if (m.group(1).equals("1")) {
+ // Success - THPeligible is 1, heap can be backed by huge pages
+ return;
+ }
+
+ throw new RuntimeException("The address range 0x" + addressStart.toString(16)
+ + "-0x" + addressEnd.toString(16)
+ + " that contains the heap start" + heapStart
+ + " is not THPeligible");
+ }
+
+ throw new RuntimeException("Couldn't find THPeligible in the smaps file");
}
- // Failed to verify THP for heap
- throw new RuntimeException("Could not find heap section in smaps file");
+ throw new RuntimeException("Could not find an address range containing the heap start " + heapStart + " in the smaps file");
}
- private static String readHeapAddressInLog() throws Exception {
- final Pattern heapAddress = Pattern.compile(".* Heap: .*base=(0x[0-9A-Fa-f]*).*");
+ private static BigInteger extractHeapStartFromLog() throws Exception {
+ // [0.041s][info][pagesize] Heap: min=128M max=128M base=0x0000ffff5c600000 size=128M page_size=2M
+ final Pattern heapAddress = Pattern.compile(".* Heap: .*base=0x([0-9A-Fa-f]*).*");
Scanner logFile = new Scanner(Paths.get("thp-" + ProcessHandle.current().pid() + ".log"));
while (logFile.hasNextLine()) {
- Matcher m = heapAddress.matcher(logFile.nextLine());
+ String line = logFile.nextLine();
+
+ Matcher m = heapAddress.matcher(line);
if (m.matches()) {
- return Long.toHexString(Long.decode(m.group(1)));
+ return new BigInteger(m.group(1), 16);
}
}
- throw new RuntimeException("Failed to parse heap address, failing test");
+
+ throw new RuntimeException("Failed to find heap start");
}
private static Path makeSmapsCopy() throws Exception {
@@ -149,4 +179,3 @@ public class TestTransparentHugePagesHeap {
}
}
}
-
From 46ae1ee87152742082e6047d0556944d7ae4567d Mon Sep 17 00:00:00 2001
From: Evgeny Astigeevich
Date: Wed, 10 Sep 2025 12:33:06 +0000
Subject: [PATCH 021/120] 8277444: Data race between
JvmtiClassFileReconstituter::copy_bytecodes and class linking
Reviewed-by: dholmes, amenkov, coleenp
---
.../prims/jvmtiClassFileReconstituter.cpp | 5 +
src/hotspot/share/prims/jvmtiEnv.cpp | 22 ++-
.../instrument/RetransformBigClassTest.java | 161 ++++++++++++++++++
3 files changed, 187 insertions(+), 1 deletion(-)
create mode 100644 test/jdk/java/lang/instrument/RetransformBigClassTest.java
diff --git a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp
index 831f407e7ec..a441d405f8d 100644
--- a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp
+++ b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp
@@ -996,6 +996,11 @@ void JvmtiClassFileReconstituter::write_u8(u8 x) {
void JvmtiClassFileReconstituter::copy_bytecodes(const methodHandle& mh,
unsigned char* bytecodes) {
+ // We must copy bytecodes only from linked classes.
+ // Being linked guarantees we are not getting bytecodes at
+ // the same time the linking process is rewriting them.
+ guarantee(mh->method_holder()->is_linked(), "Bytecodes must be copied from a linked class");
+
// use a BytecodeStream to iterate over the bytecodes. JVM/fast bytecodes
// and the breakpoint bytecode are converted to their original bytecodes.
diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp
index 30dd1f77d23..3eb507ba5e3 100644
--- a/src/hotspot/share/prims/jvmtiEnv.cpp
+++ b/src/hotspot/share/prims/jvmtiEnv.cpp
@@ -450,6 +450,18 @@ JvmtiEnv::RetransformClasses(jint class_count, const jclass* classes) {
InstanceKlass* ik = InstanceKlass::cast(klass);
if (ik->get_cached_class_file_bytes() == nullptr) {
+ // Link the class to avoid races with the rewriter. This will call the verifier also
+ // on the class. Linking is also done in VM_RedefineClasses below, but we need
+ // to keep that for other VM_RedefineClasses callers.
+ JavaThread* THREAD = current_thread;
+ ik->link_class(THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ // Retransform/JVMTI swallows error messages. Using this class will rerun the verifier in a context
+ // that propagates the VerifyError, if thrown.
+ CLEAR_PENDING_EXCEPTION;
+ return JVMTI_ERROR_INVALID_CLASS;
+ }
+
// Not cached, we need to reconstitute the class file from the
// VM representation. We don't attach the reconstituted class
// bytes to the InstanceKlass here because they have not been
@@ -3407,7 +3419,8 @@ jvmtiError
JvmtiEnv::GetBytecodes(Method* method, jint* bytecode_count_ptr, unsigned char** bytecodes_ptr) {
NULL_CHECK(method, JVMTI_ERROR_INVALID_METHODID);
- methodHandle mh(Thread::current(), method);
+ JavaThread* current_thread = JavaThread::current();
+ methodHandle mh(current_thread, method);
jint size = (jint)mh->code_size();
jvmtiError err = allocate(size, bytecodes_ptr);
if (err != JVMTI_ERROR_NONE) {
@@ -3416,6 +3429,13 @@ JvmtiEnv::GetBytecodes(Method* method, jint* bytecode_count_ptr, unsigned char**
(*bytecode_count_ptr) = size;
// get byte codes
+ // Make sure the class is verified and rewritten first.
+ JavaThread* THREAD = current_thread;
+ mh->method_holder()->link_class(THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
+ return JVMTI_ERROR_INVALID_CLASS;
+ }
JvmtiClassFileReconstituter::copy_bytecodes(mh, *bytecodes_ptr);
return JVMTI_ERROR_NONE;
diff --git a/test/jdk/java/lang/instrument/RetransformBigClassTest.java b/test/jdk/java/lang/instrument/RetransformBigClassTest.java
new file mode 100644
index 00000000000..c4788217be1
--- /dev/null
+++ b/test/jdk/java/lang/instrument/RetransformBigClassTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @bug 8277444
+ *
+ * @library /test/lib
+ * @compile SimpleIdentityTransformer.java
+ * @run shell MakeJAR.sh retransformAgent
+ * @run main/othervm -javaagent:retransformAgent.jar RetransformBigClassTest
+ */
+
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+/*
+ * JvmtiClassFileReconstituter::copy_bytecodes restores bytecodes rewritten
+ * by the linking process. It is used by RetransformClasses.
+ * JDK-8277444 is a data race between copy_bytecodes and the linking process.
+ * This test puts the linking process in one thread and the retransforming process
+ * in another thread. The test uses Class.forName("BigClass", false, classLoader)
+ * which does not link the class. When the class is used, the linking process starts.
+ * In another thread retransforming of the class is happening.
+ * We generate a class with big methods. A number of methods and their size are
+ * chosen to make the linking and retransforming processes run concurrently.
+ * We delay the retransforming process to follow the linking process.
+ * If there is no synchronization between the processes, a data race will happen.
+ */
+public class RetransformBigClassTest extends AInstrumentationTestCase {
+
+ private static final Object LOCK = new Object();
+ private static final int COUNTER_INC_COUNT = 2000; // A number of 'c+=1;' statements in methods of a class.
+ private static final int MIN_LINK_TIME_MS = 60; // Large enough so the linking and retransforming processes run in parallel.
+ private static final int RETRANSFORM_CLASSES_DELAY_MS = 37; // We manage to create a data race when a delay is in the range 0.52x - 0.62x of MIN_LINK_TIME_MS.
+
+ private static Class> bigClass;
+ private static byte[] bigClassBytecode;
+
+ private Thread retransformThread;
+
+ RetransformBigClassTest() {
+ super("RetransformBigClassTest");
+ }
+
+ public static void main(String[] args) throws Throwable {
+ new RetransformBigClassTest().runTest();
+ }
+
+ protected final void doRunTest() throws Throwable {
+ ClassLoader classLoader = new ClassLoader() {
+ @Override
+ protected Class> findClass(String name) throws ClassNotFoundException {
+ if (name.equals("BigClass")) {
+ return defineClass(name, bigClassBytecode, 0, bigClassBytecode.length);
+ }
+
+ return super.findClass(name);
+ }
+ };
+ synchronized (LOCK) {
+ bigClass = Class.forName("BigClass", false, classLoader);
+ LOCK.notify();
+ }
+ // Make a use of the BigClass
+ assertTrue(bigClass.getConstructor().newInstance().hashCode() != 0);
+ retransformThread.join();
+ }
+
+ private byte[] createClassBytecode(String className, int methodCount) throws Exception {
+ String methodBody = "";
+ for (int j = 0; j < COUNTER_INC_COUNT; j++) {
+ methodBody += "c+=1;";
+ }
+
+ String classSrc = "public class " + className + " { int c;";
+
+ for (int i = 0; i < methodCount; i++) {
+ classSrc += "\npublic void m" + i + "(){";
+ classSrc += methodBody;
+ classSrc += "\n}";
+ }
+ classSrc += "\n}";
+
+ return InMemoryJavaCompiler.compile(className, classSrc);
+ }
+
+ // We need a number of methods such that the linking time is greater than
+ // or equal to MIN_LINK_TIME_MS.
+ // We create a class having 5 methods and trigger the linking process.
+ // We measure the time taken and use it to calculate the needed number.
+ private int findMethodCount() throws Exception {
+ int methodCount = 5;
+ final String className = "BigClass" + methodCount;
+ final byte[] bytecode = createClassBytecode(className, methodCount);
+ ClassLoader classLoader = new ClassLoader() {
+ @Override
+ protected Class> findClass(String name) throws ClassNotFoundException {
+ if (name.equals(className)) {
+ return defineClass(name, bytecode, 0, bytecode.length);
+ }
+
+ return super.findClass(name);
+ }
+ };
+ var bigClass = Class.forName(className, false, classLoader);
+ long startTime = System.nanoTime();
+ assertTrue(bigClass.getConstructor().newInstance().hashCode() != 0);
+ double linkTimeMs = (System.nanoTime() - startTime) / 1000000.0;
+ System.out.println("Link time for a class with " + methodCount + " methods each having " + COUNTER_INC_COUNT + " counter increments: " + Math.round(linkTimeMs));
+ if (linkTimeMs < MIN_LINK_TIME_MS) {
+ methodCount = (int)Math.round((MIN_LINK_TIME_MS * methodCount) / linkTimeMs);
+ }
+ System.out.println("The number of methods to exceed " + MIN_LINK_TIME_MS + " ms linking time: " + methodCount);
+ return methodCount;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ bigClassBytecode = createClassBytecode("BigClass", findMethodCount());
+ fInst.addTransformer(new SimpleIdentityTransformer());
+ retransformThread = new Thread(() -> {
+ try {
+ synchronized (LOCK) {
+ while (bigClass == null) {
+ System.out.println("[retransformThread]: Waiting for bigClass");
+ LOCK.wait();
+ }
+ }
+ Thread.sleep(RETRANSFORM_CLASSES_DELAY_MS);
+ fInst.retransformClasses(bigClass);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+ retransformThread.start();
+ Thread.sleep(100);
+ }
+}
From 385c13298932f1de16e6161652be35d966d822ec Mon Sep 17 00:00:00 2001
From: Albert Mingkun Yang
Date: Wed, 10 Sep 2025 12:49:38 +0000
Subject: [PATCH 022/120] 8367240: Parallel: Refactor PSScavengeCLDClosure
Reviewed-by: stefank
---
.../share/gc/parallel/psClosure.inline.hpp | 57 ++++++++-----------
1 file changed, 24 insertions(+), 33 deletions(-)
diff --git a/src/hotspot/share/gc/parallel/psClosure.inline.hpp b/src/hotspot/share/gc/parallel/psClosure.inline.hpp
index 5cc059a09f5..914a16e77a6 100644
--- a/src/hotspot/share/gc/parallel/psClosure.inline.hpp
+++ b/src/hotspot/share/gc/parallel/psClosure.inline.hpp
@@ -78,16 +78,17 @@ typedef PSRootsClosure*promote_immediately=*/false> PSScavengeRootsClosure;
typedef PSRootsClosure*promote_immediately=*/true> PSPromoteRootsClosure;
// Scavenges a single oop in a ClassLoaderData.
-class PSScavengeFromCLDClosure: public OopClosure {
-private:
+class PSScavengeCLDOopClosure : public OopClosure {
PSPromotionManager* _pm;
- // Used to redirty a scanned cld if it has oops
- // pointing to the young generation after being scanned.
- ClassLoaderData* _scanned_cld;
+
public:
- PSScavengeFromCLDClosure(PSPromotionManager* pm) : _pm(pm), _scanned_cld(nullptr) { }
+ // Records whether this CLD contains oops pointing into young-gen after scavenging.
+ bool _has_oops_into_young_gen;
+
+ PSScavengeCLDOopClosure(PSPromotionManager* pm) : _pm(pm), _has_oops_into_young_gen(false) {}
+
void do_oop(narrowOop* p) { ShouldNotReachHere(); }
- void do_oop(oop* p) {
+ void do_oop(oop* p) {
ParallelScavengeHeap* psh = ParallelScavengeHeap::heap();
assert(!psh->is_in_reserved(p), "GC barrier needed");
if (PSScavenge::should_scavenge(p)) {
@@ -97,43 +98,33 @@ public:
oop new_obj = _pm->copy_to_survivor_space*promote_immediately=*/false>(o);
RawAccess::oop_store(p, new_obj);
- if (PSScavenge::is_obj_in_young(new_obj)) {
- do_cld_barrier();
+ if (PSScavenge::is_obj_in_young(new_obj) && !_has_oops_into_young_gen) {
+ _has_oops_into_young_gen = true;
}
}
}
-
- void set_scanned_cld(ClassLoaderData* cld) {
- assert(_scanned_cld == nullptr || cld == nullptr, "Should always only handling one cld at a time");
- _scanned_cld = cld;
- }
-
-private:
- void do_cld_barrier() {
- assert(_scanned_cld != nullptr, "Should not be called without having a scanned cld");
- _scanned_cld->record_modified_oops();
- }
};
// Scavenges the oop in a ClassLoaderData.
class PSScavengeCLDClosure: public CLDClosure {
-private:
- PSScavengeFromCLDClosure _oop_closure;
+ PSPromotionManager* _pm;
+
public:
- PSScavengeCLDClosure(PSPromotionManager* pm) : _oop_closure(pm) { }
+ PSScavengeCLDClosure(PSPromotionManager* pm) : _pm(pm) { }
+
void do_cld(ClassLoaderData* cld) {
- // If the cld has not been dirtied we know that there's
- // no references into the young gen and we can skip it.
+ // If the cld has not been dirtied we know that there are
+ // no references into the young gen, so we can skip it.
+ if (!cld->has_modified_oops()) {
+ return;
+ }
- if (cld->has_modified_oops()) {
- // Setup the promotion manager to redirty this cld
- // if references are left in the young gen.
- _oop_closure.set_scanned_cld(cld);
+ PSScavengeCLDOopClosure oop_closure{_pm};
+ // Clean the cld since we're going to scavenge all the metadata.
+ cld->oops_do(&oop_closure, ClassLoaderData::_claim_none, /*clear_modified_oops*/true);
- // Clean the cld since we're going to scavenge all the metadata.
- cld->oops_do(&_oop_closure, ClassLoaderData::_claim_none, /*clear_modified_oops*/true);
-
- _oop_closure.set_scanned_cld(nullptr);
+ if (oop_closure._has_oops_into_young_gen) {
+ cld->record_modified_oops();
}
}
};
From c968a672c034fe533ea5f4ac5efe37ffb76c93e2 Mon Sep 17 00:00:00 2001
From: Casper Norrbin
Date: Wed, 10 Sep 2025 13:45:06 +0000
Subject: [PATCH 023/120] 8362282: runtime/logging/StressAsyncUL.java failed
with exitValue = 134
Reviewed-by: jsjolen, dholmes
---
src/hotspot/share/logging/logAsyncWriter.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/hotspot/share/logging/logAsyncWriter.cpp b/src/hotspot/share/logging/logAsyncWriter.cpp
index cfb6a991c4c..d184827f582 100644
--- a/src/hotspot/share/logging/logAsyncWriter.cpp
+++ b/src/hotspot/share/logging/logAsyncWriter.cpp
@@ -318,13 +318,13 @@ void AsyncLogWriter::initialize() {
AsyncLogWriter* self = new AsyncLogWriter();
if (self->_initialized) {
- Atomic::release_store_fence(&AsyncLogWriter::_instance, self);
- // All readers of _instance after the fence see non-null.
// We use LogOutputList's RCU counters to ensure all synchronous logsites have completed.
- // After that, we start AsyncLog Thread and it exclusively takes over all logging I/O.
+ // After that, we publish the initalized _instance to readers.
+ // Then we start the AsyncLog Thread and it exclusively takes over all logging I/O.
for (LogTagSet* ts = LogTagSet::first(); ts != nullptr; ts = ts->next()) {
ts->wait_until_no_readers();
}
+ Atomic::release_store_fence(&AsyncLogWriter::_instance, self);
os::start_thread(self);
log_debug(logging, thread)("Async logging thread started.");
} else {
From 5cd7721ad448cc4bdac37b0456252335f6b9d9f5 Mon Sep 17 00:00:00 2001
From: Kerem Kat
Date: Wed, 10 Sep 2025 14:36:11 +0000
Subject: [PATCH 024/120] 8366154: Validate thread type requirements in debug
commands
Reviewed-by: dholmes, simonis, kevinw
---
src/hotspot/share/utilities/debug.cpp | 87 ++++++++++++++++++++-------
1 file changed, 64 insertions(+), 23 deletions(-)
diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp
index 2865dbc5991..bd82336020a 100644
--- a/src/hotspot/share/utilities/debug.cpp
+++ b/src/hotspot/share/utilities/debug.cpp
@@ -63,6 +63,7 @@
#include "utilities/unsigned5.hpp"
#include "utilities/vmError.hpp"
+#include
#include
#include
@@ -296,20 +297,40 @@ void report_java_out_of_memory(const char* message) {
class Command : public StackObj {
private:
- ResourceMark _rm;
- DebuggingContext _debugging;
- public:
static int level;
- Command(const char* str) {
- if (level++ > 0) return;
- tty->cr();
- tty->print_cr("\"Executing %s\"", str);
+ DebuggingContext _debugging;
+ bool _has_rm;
+ // Union members of class type are implicitly allocated but not constructed automatically.
+ // We therefore have to explicitly construct _rm with a placement new call (see 'onThread()') and
+ // clean it up afterwards with an explicit destructor call (see '~Command()').
+ union { ResourceMark _rm; };
+ public:
+ Command(const char* str) : _has_rm(false) {
+ if (level++ == 0) {
+ tty->cr();
+ tty->print_cr("\"Executing %s\"", str);
+ }
+ tty->flush();
}
-
~Command() {
+ if (_has_rm) _rm.~ResourceMark();
tty->flush();
level--;
}
+
+ bool onThread() {
+ Thread* thread = Thread::current_or_null();
+ if (thread == nullptr) {
+ tty->print_cr("Failed: Current thread is not attached");
+ return false;
+ }
+
+ if (!_has_rm) {
+ ::new (&_rm) ResourceMark();
+ _has_rm = true;
+ }
+ return true;
+ }
};
int Command::level = 0;
@@ -370,6 +391,7 @@ extern "C" DEBUGEXPORT void printnm(intptr_t p) {
extern "C" DEBUGEXPORT void universe() {
Command c("universe");
+ if (!c.onThread()) return;
Universe::print_on(tty);
}
@@ -379,6 +401,7 @@ extern "C" DEBUGEXPORT void verify() {
// note: this may not be safe if we're not at a safepoint; for debugging,
// this manipulates the safepoint settings to avoid assertion failures
Command c("universe verify");
+ if (!c.onThread()) return;
bool safe = SafepointSynchronize::is_at_safepoint();
if (!safe) {
tty->print_cr("warning: not at safepoint -- verify may fail");
@@ -393,6 +416,7 @@ extern "C" DEBUGEXPORT void verify() {
extern "C" DEBUGEXPORT void pp(void* p) {
Command c("pp");
+ if (!c.onThread()) return;
FlagSetting fl(DisplayVMOutput, true);
if (p == nullptr) {
tty->print_cr("null");
@@ -416,14 +440,15 @@ extern "C" DEBUGEXPORT void pp(void* p) {
}
-extern "C" DEBUGEXPORT void findpc(intptr_t x);
-
extern "C" DEBUGEXPORT void ps() { // print stack
- if (Thread::current_or_null() == nullptr) return;
- Command c("ps");
-
// Prints the stack of the current Java thread
+ Command c("ps");
+ if (!c.onThread()) return;
JavaThread* p = JavaThread::active();
+ if (p == nullptr) {
+ tty->print_cr("Failed: JavaThread::active is null");
+ return;
+ }
tty->print(" for thread: ");
p->print();
tty->cr();
@@ -450,7 +475,12 @@ extern "C" DEBUGEXPORT void ps() { // print stack
extern "C" DEBUGEXPORT void pfl() {
// print frame layout
Command c("pfl");
+ if (!c.onThread()) return;
JavaThread* p = JavaThread::active();
+ if (p == nullptr) {
+ tty->print_cr("Failed: JavaThread::active is null");
+ return;
+ }
tty->print(" for thread: ");
p->print();
tty->cr();
@@ -460,34 +490,39 @@ extern "C" DEBUGEXPORT void pfl() {
}
extern "C" DEBUGEXPORT void psf() { // print stack frames
- {
- Command c("psf");
- JavaThread* p = JavaThread::active();
- tty->print(" for thread: ");
- p->print();
- tty->cr();
- if (p->has_last_Java_frame()) {
- p->trace_frames();
- }
+ Command c("psf");
+ if (!c.onThread()) return;
+ JavaThread* p = JavaThread::active();
+ if (p == nullptr) {
+ tty->print_cr("Failed: JavaThread::active is null");
+ return;
+ }
+ tty->print(" for thread: ");
+ p->print();
+ tty->cr();
+ if (p->has_last_Java_frame()) {
+ p->trace_frames();
}
}
extern "C" DEBUGEXPORT void threads() {
Command c("threads");
+ if (!c.onThread()) return;
Threads::print(false, true);
}
extern "C" DEBUGEXPORT void psd() {
Command c("psd");
+ if (!c.onThread()) return;
SystemDictionary::print();
}
extern "C" DEBUGEXPORT void pss() { // print all stacks
- if (Thread::current_or_null() == nullptr) return;
Command c("pss");
+ if (!c.onThread()) return;
Threads::print(true, PRODUCT_ONLY(false) NOT_PRODUCT(true));
}
@@ -534,12 +569,14 @@ extern "C" DEBUGEXPORT nmethod* findnm(intptr_t addr) {
extern "C" DEBUGEXPORT void find(intptr_t x) {
Command c("find");
+ if (!c.onThread()) return;
os::print_location(tty, x, false);
}
extern "C" DEBUGEXPORT void findpc(intptr_t x) {
Command c("findpc");
+ if (!c.onThread()) return;
os::print_location(tty, x, true);
}
@@ -551,6 +588,7 @@ extern "C" DEBUGEXPORT void findpc(intptr_t x) {
// call findmethod("*ang/Object*", "wait:(*J*)V", 0x1) -> list all "wait" methods in j.l.Object that have a long parameter
extern "C" DEBUGEXPORT void findclass(const char* class_name_pattern, int flags) {
Command c("findclass");
+ if (!c.onThread()) return;
ClassPrinter::print_flags_help(tty);
ClassPrinter::print_classes(class_name_pattern, flags, tty);
}
@@ -558,6 +596,7 @@ extern "C" DEBUGEXPORT void findclass(const char* class_name_pattern, int flags)
extern "C" DEBUGEXPORT void findmethod(const char* class_name_pattern,
const char* method_pattern, int flags) {
Command c("findmethod");
+ if (!c.onThread()) return;
ClassPrinter::print_flags_help(tty);
ClassPrinter::print_methods(class_name_pattern, method_pattern, flags, tty);
}
@@ -644,6 +683,7 @@ void help() {
#ifndef PRODUCT
extern "C" DEBUGEXPORT void pns(void* sp, void* fp, void* pc) { // print native stack
Command c("pns");
+ if (!c.onThread()) return;
static char buf[O_BUFLEN];
// Call generic frame constructor (certain arguments may be ignored)
frame fr(sp, fp, pc);
@@ -662,6 +702,7 @@ extern "C" DEBUGEXPORT void pns(void* sp, void* fp, void* pc) { // print native
//
extern "C" DEBUGEXPORT void pns2() { // print native stack
Command c("pns2");
+ if (!c.onThread()) return;
static char buf[O_BUFLEN];
address lastpc = nullptr;
NativeStackPrinter nsp(Thread::current_or_null());
From 34c3ac0316dbd29ae670db51bd9230a1e77382d9 Mon Sep 17 00:00:00 2001
From: Prasanta Sadhukhan
Date: Wed, 10 Sep 2025 16:00:28 +0000
Subject: [PATCH 025/120] 8162380: [TEST_BUG]
MouseEvent/.../AltGraphModifierTest.java has only "Fail" button
Reviewed-by: azvegint, aivanov
---
test/jdk/ProblemList.txt | 1 -
.../AltGraphModifierTest.java | 256 +++---------------
2 files changed, 44 insertions(+), 213 deletions(-)
diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt
index 475704f7e95..70e912d7e93 100644
--- a/test/jdk/ProblemList.txt
+++ b/test/jdk/ProblemList.txt
@@ -794,7 +794,6 @@ java/awt/event/MouseEvent/SpuriousExitEnter/SpuriousExitEnter_2.java 7131438,802
java/awt/Modal/WsDisabledStyle/CloseBlocker/CloseBlocker.java 7187741 linux-all,macosx-all
java/awt/xembed/server/TestXEmbedServerJava.java 8001150,8004031 generic-all
java/awt/Modal/PrintDialogsTest/PrintDialogsTest.java 8068378 generic-all
-java/awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java 8162380 generic-all
java/awt/image/VolatileImage/VolatileImageConfigurationTest.java 8171069 macosx-all,linux-all
java/awt/Modal/InvisibleParentTest/InvisibleParentTest.java 8172245 linux-all
java/awt/Frame/FrameStateTest/FrameStateTest.java 8203920 macosx-all,linux-all
diff --git a/test/jdk/java/awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java b/test/jdk/java/awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java
index 47d808f336d..c8730b28964 100644
--- a/test/jdk/java/awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java
+++ b/test/jdk/java/awt/event/MouseEvent/AltGraphModifierTest/AltGraphModifierTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,51 +22,57 @@
*/
/*
- @test
- @bug 8041928 8158616
- @requires (os.family != "mac")
- @summary Confirm that the Alt-Gr Modifier bit is set correctly.
- @run main/manual AltGraphModifierTest
+ * @test
+ * @bug 8041928 8158616
+ * @requires (os.family != "mac")
+ * @summary Confirm that the Alt-Gr Modifier bit is set correctly.
+ * @library /java/awt/regtesthelpers
+ * @build PassFailJFrame
+ * @run main/manual AltGraphModifierTest
*/
-import java.awt.Button;
-import java.awt.Dialog;
import java.awt.Frame;
-import java.awt.Panel;
-import java.awt.TextArea;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class AltGraphModifierTest {
- private static void init() throws Exception {
- String[] instructions
- = {
- "This test is for verifying Alt-Gr modifier of an event.",
- "Linux :-",
- "1. Please check if Alt-Gr key is present on keyboard.",
- "2. If present, press the Alt-Gr key and perform",
- " mouse click on the TestWindow.",
- "3. Navigate to System Settings-> Keyboard-> Shortcuts->",
- " Typing.",
- "4. Select an option for the Alternative Characters Key",
- " For example. Right Alt",
- "5. Close the settings and navigate to test",
- "6. Press Right Alt Key & perform mouse click on the",
- " TestWindow",
- "7. Test will exit by itself with appropriate result.",
- " ",
- };
- Sysout.createDialog();
- Sysout.printInstructions(instructions);
+ public static void main(String[] args) throws Exception {
+ String INSTRUCTIONS = """
+ This test is for verifying Alt-Gr modifier of an event.
+ Please check if Alt-Gr key is present on keyboard.
+ If not present, press Pass.
+ On Windows:
+ Press Alt-Gr or Right Alt key and simultaneously
+ perform mouse click on the "TestWindow".
+ On Linux:
+ Navigate to
+ System Settings-> Keyboard-> Special Character Entry
+ Select "Right Alt" option for the "Alternate Characters Key"
+ Close the settings and navigate to test
+ Press Right Alt Key & simultaneously
+ perform mouse click on the "TestWindow".
+
+ If the system does not have such setting, press Pass.
+ After the test, change the Setting of "Alternate Characters Key"
+ back to "Layout default".
+
+ If "Alt-Gr Modifier bit is set" message is displayed in logArea,
+ press Pass else press Fail.
+ """;
+
+ PassFailJFrame.builder()
+ .instructions(INSTRUCTIONS)
+ .columns(35)
+ .testUI(AltGraphModifierTest::initTestWindow)
+ .logArea()
+ .build()
+ .awaitAndCheck();
}
- static Frame mainFrame;
- public static void initTestWindow() {
- mainFrame = new Frame();
+ public static Frame initTestWindow() {
+ Frame mainFrame = new Frame();
mainFrame.setTitle("TestWindow");
mainFrame.setBounds(700, 10, 300, 300);
mainFrame.addMouseListener(new MouseAdapter() {
@@ -74,186 +80,12 @@ public class AltGraphModifierTest {
public void mousePressed(MouseEvent e) {
int ex = e.getModifiersEx();
if ((ex & InputEvent.ALT_GRAPH_DOWN_MASK) == 0) {
- AltGraphModifierTest.fail("Alt-Gr Modifier bit is not set.");
+ PassFailJFrame.log("Alt-Gr Modifier bit is not set.");
} else {
- AltGraphModifierTest.pass();
+ PassFailJFrame.log("Alt-Gr Modifier bit is set");
}
}
});
- mainFrame.setVisible(true);
- }
-
- public static void dispose() {
- Sysout.dispose();
- mainFrame.dispose();
- }
-
- /**
- * ***************************************************
- * Standard Test Machinery Section DO NOT modify anything in this section --
- * it's a standard chunk of code which has all of the synchronisation
- * necessary for the test harness. By keeping it the same in all tests, it
- * is easier to read and understand someone else's test, as well as insuring
- * that all tests behave correctly with the test harness. There is a section
- * following this for test-defined classes
- * ****************************************************
- */
- private static boolean theTestPassed = false;
- private static boolean testGeneratedInterrupt = false;
- private static String failureMessage = "";
- private static Thread mainThread = null;
- final private static int sleepTime = 300000;
-
- public static void main(String args[]) throws Exception {
- mainThread = Thread.currentThread();
- try {
- init();
- initTestWindow();
- } catch (Exception e) {
- e.printStackTrace();
- }
- try {
- mainThread.sleep(sleepTime);
- } catch (InterruptedException e) {
- dispose();
- if (testGeneratedInterrupt && !theTestPassed) {
- throw new Exception(failureMessage);
- }
- }
- if (!testGeneratedInterrupt) {
- dispose();
- throw new RuntimeException("Timed out after " + sleepTime / 1000
- + " seconds");
- }
- }
-
- public static synchronized void pass() {
- theTestPassed = true;
- testGeneratedInterrupt = true;
- mainThread.interrupt();
- }
-
- public static synchronized void fail(String whyFailed) {
- theTestPassed = false;
- testGeneratedInterrupt = true;
- failureMessage = whyFailed;
- mainThread.interrupt();
- }
-}
-
-// *********** End Standard Test Machinery Section **********
-/**
- * **************************************************
- * Standard Test Machinery DO NOT modify anything below -- it's a standard chunk
- * of code whose purpose is to make user interaction uniform, and thereby make
- * it simpler to read and understand someone else's test.
- * **************************************************
- */
-/**
- * This is part of the standard test machinery. It creates a dialog (with the
- * instructions), and is the interface for sending text messages to the user. To
- * print the instructions, send an array of strings to Sysout.createDialog
- * WithInstructions method. Put one line of instructions per array entry. To
- * display a message for the tester to see, simply call Sysout.println with the
- * string to be displayed. This mimics System.out.println but works within the
- * test harness as well as standalone.
- */
-class Sysout {
- private static TestDialog dialog;
- private static Frame frame;
-
- public static void createDialog() {
- frame = new Frame();
- dialog = new TestDialog(frame, "Instructions");
- String[] defInstr = {"Instructions will appear here. ", ""};
- dialog.printInstructions(defInstr);
- dialog.show();
- println("Any messages for the tester will display here.");
- }
-
- public static void printInstructions(String[] instructions) {
- dialog.printInstructions(instructions);
- }
-
- public static void println(String messageIn) {
- dialog.displayMessage(messageIn);
- }
-
- public static void dispose() {
- dialog.dispose();
- frame.dispose();
- }
-}
-
-/**
- * This is part of the standard test machinery. It provides a place for the test
- * instructions to be displayed, and a place for interactive messages to the
- * user to be displayed. To have the test instructions displayed, see Sysout. To
- * have a message to the user be displayed, see Sysout. Do not call anything in
- * this dialog directly.
- */
-class TestDialog extends Dialog implements ActionListener {
- TextArea instructionsText;
- TextArea messageText;
- int maxStringLength = 80;
- Panel buttonP;
- Button failB;
-
- // DO NOT call this directly, go through Sysout
- public TestDialog(Frame frame, String name) {
- super(frame, name);
- int scrollBoth = TextArea.SCROLLBARS_BOTH;
- instructionsText = new TextArea("", 15, maxStringLength, scrollBoth);
- add("North", instructionsText);
-
- messageText = new TextArea("", 5, maxStringLength, scrollBoth);
- add("Center", messageText);
-
- buttonP = new Panel();
- failB = new Button("Fail");
- failB.setActionCommand("fail");
- failB.addActionListener(this);
- buttonP.add("Center", failB);
-
- add("South", buttonP);
- pack();
- setVisible(true);
- }
-
- // DO NOT call this directly, go through Sysout
- public void printInstructions(String[] instructions) {
- instructionsText.setText("");
- String printStr, remainingStr;
- for (int i = 0; i < instructions.length; i++) {
- remainingStr = instructions[i];
- while (remainingStr.length() > 0) {
- if (remainingStr.length() >= maxStringLength) {
- int posOfSpace = remainingStr.
- lastIndexOf(' ', maxStringLength - 1);
-
- if (posOfSpace <= 0) {
- posOfSpace = maxStringLength - 1;
- }
-
- printStr = remainingStr.substring(0, posOfSpace + 1);
- remainingStr = remainingStr.substring(posOfSpace + 1);
- }
- else {
- printStr = remainingStr;
- remainingStr = "";
- }
- instructionsText.append(printStr + "\n");
- }
- }
- }
-
- public void displayMessage(String messageIn) {
- messageText.append(messageIn + "\n");
- }
-
- public void actionPerformed(ActionEvent e) {
- if (e.getActionCommand() == "fail") {
- AltGraphModifierTest.fail("User Clicked Fail");
- }
+ return mainFrame;
}
}
From af18ff8d7c8fdd6437304839caa2e49eb34b6caa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?=
Date: Wed, 10 Sep 2025 16:43:40 +0000
Subject: [PATCH 026/120] 8367007: javadoc generation of JavaFX docs fails
after fix for JDK-8350920
Reviewed-by: liach, nbenalla
---
.../formats/html/AbstractMemberWriter.java | 2 +-
.../doclets/formats/html/ClassWriter.java | 2 +-
.../doclets/toolkit/PropertyUtils.java | 96 ++++++++++---------
.../javadoc/doclet/testJavaFX/TestJavaFX.java | 36 ++++++-
.../jdk/javadoc/doclet/testJavaFX/pkg1/B.java | 27 ++++++
5 files changed, 115 insertions(+), 48 deletions(-)
create mode 100644 test/langtools/jdk/javadoc/doclet/testJavaFX/pkg1/B.java
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java
index e2e286d5856..fabca0a894d 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java
@@ -319,7 +319,7 @@ public abstract class AbstractMemberWriter {
inheritedHeader.add(links);
if (utils.isIncluded(inheritedClass)) {
- var pHelper = writer.getPropertyHelper();
+ var pHelper = configuration.propertyUtils.getPropertyHelper(inheritedClass);
Table table = createInheritedSummaryTable(inheritedClass);
for (Element member : inheritedMembers) {
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriter.java
index d301dd6e283..f5a5a48222c 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriter.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriter.java
@@ -96,7 +96,7 @@ public class ClassWriter extends SubWriterHolderWriter {
this.typeElement = typeElement;
this.classTree = classTree;
- pHelper = new PropertyUtils.PropertyHelper(configuration, typeElement);
+ pHelper = configuration.propertyUtils.getPropertyHelper(typeElement);
switch (typeElement.getKind()) {
case ENUM -> setEnumDocumentation(typeElement);
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/PropertyUtils.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/PropertyUtils.java
index 823f172b360..8913be5b301 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/PropertyUtils.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/PropertyUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -54,6 +54,8 @@ import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.
*/
public class PropertyUtils {
+ final BaseConfiguration configuration;
+
final TypeMirror jbObservableType;
final Pattern fxMethodPatterns;
@@ -62,7 +64,10 @@ public class PropertyUtils {
final Types typeUtils;
+ final Map propertyHelpers = new HashMap<>();
+
PropertyUtils(BaseConfiguration configuration) {
+ this.configuration = configuration;
BaseOptions options = configuration.getOptions();
javafx = options.javafx();
@@ -82,30 +87,37 @@ public class PropertyUtils {
: null;
}
+ /**
+ * Returns a property helper for the given type element.
+ * @param typeElement a type element
+ * @return the property helper
+ */
+ public PropertyHelper getPropertyHelper(TypeElement typeElement) {
+ return propertyHelpers.computeIfAbsent(typeElement, te -> new PropertyHelper(configuration, te));
+ }
+
/**
* Returns a base name for a property method. Supposing we
* have {@code BooleanProperty acmeProperty()}, then "acme"
* will be returned.
- * @param propertyMethod
+ * @param propertyMethod a property method
* @return the base name of a property method.
*/
public String getBaseName(ExecutableElement propertyMethod) {
String name = propertyMethod.getSimpleName().toString();
- String baseName = name.substring(0, name.indexOf("Property"));
- return baseName;
+ return name.substring(0, name.indexOf("Property"));
}
/**
* Returns a property getter's name. Supposing we have a property
* method {@code DoubleProperty acmeProperty()}, then "getAcme"
* will be returned.
- * @param propertyMethod
+ * @param propertyMethod a property method
* @return the property getter's name.
*/
public String getGetName(ExecutableElement propertyMethod) {
String baseName = getBaseName(propertyMethod);
- String fnUppercased = "" +
- Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1);
+ String fnUppercased = Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1);
return "get" + fnUppercased;
}
@@ -113,20 +125,19 @@ public class PropertyUtils {
* Returns an "is" method's name for a property method. Supposing
* we have a property method {@code BooleanProperty acmeProperty()},
* then "isAcme" will be returned.
- * @param propertyMethod
+ * @param propertyMethod a property method
* @return the property is getter's name.
*/
public String getIsName(ExecutableElement propertyMethod) {
String baseName = getBaseName(propertyMethod);
- String fnUppercased = "" +
- Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1);
+ String fnUppercased = Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1);
return "is" + fnUppercased;
}
/**
* Returns true if a property method could have an "is" method, meaning
* {@code isAcme} could exist for a property method.
- * @param propertyMethod
+ * @param propertyMethod a property method
* @return true if the property could have an "is" method, false otherwise.
*/
public boolean hasIsMethod(ExecutableElement propertyMethod) {
@@ -139,20 +150,19 @@ public class PropertyUtils {
* Returns a property setter's name. Supposing we have a property
* method {@code DoubleProperty acmeProperty()}, then "setAcme"
* will be returned.
- * @param propertyMethod
+ * @param propertyMethod a property method
* @return the property setter's method name.
*/
public String getSetName(ExecutableElement propertyMethod) {
String baseName = getBaseName(propertyMethod);
- String fnUppercased = "" +
- Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1);
+ String fnUppercased = Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1);
return "set" + fnUppercased;
}
/**
* Returns true if the given setter method is a valid property setter
* method.
- * @param setterMethod
+ * @param setterMethod a setter method
* @return true if setter method, false otherwise.
*/
public boolean isValidSetterMethod(ExecutableElement setterMethod) {
@@ -161,28 +171,28 @@ public class PropertyUtils {
/**
* Returns true if the method is a property method.
- * @param propertyMethod
+ * @param method a method
* @return true if the method is a property method, false otherwise.
*/
- public boolean isPropertyMethod(ExecutableElement propertyMethod) {
+ public boolean isPropertyMethod(ExecutableElement method) {
if (!javafx ||
- !propertyMethod.getParameters().isEmpty() ||
- !propertyMethod.getTypeParameters().isEmpty()) {
+ !method.getParameters().isEmpty() ||
+ !method.getTypeParameters().isEmpty()) {
return false;
}
- String methodName = propertyMethod.getSimpleName().toString();
+ String methodName = method.getSimpleName().toString();
if (!methodName.endsWith("Property") ||
fxMethodPatterns.matcher(methodName).matches()) {
return false;
}
- TypeMirror returnType = propertyMethod.getReturnType();
+ TypeMirror returnType = method.getReturnType();
if (jbObservableType == null) {
// JavaFX references missing, make a lazy backward compatible check.
return returnType.getKind() != TypeKind.VOID;
} else {
// Apply strict checks since JavaFX references are available
- returnType = typeUtils.erasure(propertyMethod.getReturnType());
+ returnType = typeUtils.erasure(method.getReturnType());
return typeUtils.isAssignable(returnType, jbObservableType);
}
}
@@ -202,20 +212,13 @@ public class PropertyUtils {
* method. If any method does not have a comment, one will be provided.
*/
public static class PropertyHelper {
- private final BaseConfiguration configuration;
- private final Utils utils;
- private final TypeElement typeElement;
+ private Map classPropertiesMap = null;
- private final Map classPropertiesMap = new HashMap<>();
-
- public PropertyHelper(BaseConfiguration configuration, TypeElement typeElement) {
- this.configuration = configuration;
- this.utils = configuration.utils;
- this.typeElement = typeElement;
- computeProperties();
+ private PropertyHelper(BaseConfiguration configuration, TypeElement typeElement) {
+ computeProperties(configuration, typeElement);
}
- private void computeProperties() {
+ private void computeProperties(BaseConfiguration configuration, TypeElement typeElement) {
VisibleMemberTable vmt = configuration.getVisibleMemberTable(typeElement);
List props = ElementFilter.methodsIn(vmt.getVisibleMembers(PROPERTIES));
for (ExecutableElement propertyMethod : props) {
@@ -223,37 +226,42 @@ public class PropertyUtils {
ExecutableElement setter = vmt.getPropertySetter(propertyMethod);
VariableElement field = vmt.getPropertyField(propertyMethod);
- addToPropertiesMap(propertyMethod, field, getter, setter);
+ addToPropertiesMap(configuration, propertyMethod, field, getter, setter);
}
}
- private void addToPropertiesMap(ExecutableElement propertyMethod,
+ private void addToPropertiesMap(BaseConfiguration configuration,
+ ExecutableElement propertyMethod,
VariableElement field,
ExecutableElement getter,
ExecutableElement setter) {
// determine the preferred element from which to derive the property description
- Element e = field == null || !utils.hasDocCommentTree(field)
+ Element e = field == null || !configuration.utils.hasDocCommentTree(field)
? propertyMethod : field;
- if (e == field && utils.hasDocCommentTree(propertyMethod)) {
+ if (e == field && configuration.utils.hasDocCommentTree(propertyMethod)) {
configuration.getReporter().print(Diagnostic.Kind.WARNING,
propertyMethod, configuration.getDocResources().getText("doclet.duplicate.comment.for.property"));
}
- addToPropertiesMap(propertyMethod, e);
- addToPropertiesMap(getter, e);
- addToPropertiesMap(setter, e);
+ if (classPropertiesMap == null) {
+ classPropertiesMap = new HashMap<>();
+ }
+ addToPropertiesMap(configuration, propertyMethod, e);
+ addToPropertiesMap(configuration, getter, e);
+ addToPropertiesMap(configuration, setter, e);
}
- private void addToPropertiesMap(Element propertyMethod,
+ private void addToPropertiesMap(BaseConfiguration configuration,
+ Element propertyMethod,
Element commentSource) {
Objects.requireNonNull(commentSource);
if (propertyMethod == null) {
return;
}
- DocCommentTree docTree = utils.hasDocCommentTree(propertyMethod)
- ? utils.getDocCommentTree(propertyMethod)
+ DocCommentTree docTree = configuration.utils.hasDocCommentTree(propertyMethod)
+ ? configuration.utils.getDocCommentTree(propertyMethod)
: null;
/* The second condition is required for the property buckets. In
@@ -271,7 +279,7 @@ public class PropertyUtils {
* @return the element for the property documentation, null if there is none.
*/
public Element getPropertyElement(Element element) {
- return classPropertiesMap.get(element);
+ return classPropertiesMap == null ? null : classPropertiesMap.get(element);
}
}
}
diff --git a/test/langtools/jdk/javadoc/doclet/testJavaFX/TestJavaFX.java b/test/langtools/jdk/javadoc/doclet/testJavaFX/TestJavaFX.java
index 4fa89ee0ead..04e032dbe05 100644
--- a/test/langtools/jdk/javadoc/doclet/testJavaFX/TestJavaFX.java
+++ b/test/langtools/jdk/javadoc/doclet/testJavaFX/TestJavaFX.java
@@ -25,7 +25,7 @@
* @test
* @bug 7112427 8012295 8025633 8026567 8061305 8081854 8150130 8162363
* 8167967 8172528 8175200 8178830 8182257 8186332 8182765 8025091
- * 8203791 8184205 8249633 8261976 8350920
+ * 8203791 8184205 8249633 8261976 8350920 8367007
* @summary Test of the JavaFX doclet features.
* @library ../../lib
* @modules jdk.javadoc/jdk.javadoc.internal.tool
@@ -54,11 +54,18 @@ public class TestJavaFX extends JavadocTester {
"-sourcepath", testSrc,
"-javafx",
"--disable-javafx-strict-checks",
- "-Xdoclint:all,-missing",
"-package",
"pkg1");
checkExit(Exit.OK);
+ checkOutput(Output.OUT, true,
+ "C.java:78: warning: no comment");
+ checkOutput(Output.OUT, false,
+ "C.java:59: warning: no comment",
+ "C.java:61: warning: no comment",
+ "C.java:63: warning: no comment",
+ "C.java:67: warning: no comment");
+
checkOutput("pkg1/C.html", true,
"""
See Also:
@@ -266,6 +273,31 @@ public class TestJavaFX extends JavadocTester {
""");
checkOutput("pkg1/D.html", false, "shouldNotAppear");
+
+ // Test for inherited properties and property methods.
+ checkOrder("pkg1/B.html",
+ """
+ Properties inherited from class C""",
+ """
+
Defines if paused.
""",
+ """
+
Defines the direction/speed at which the Timeline is expected to
+ be played.
""",
+ """
+ Methods inherited from class C""",
+ """
+
Gets the value of the rate property.
""",
+ """
+
Gets the value of the paused property.
""",
+ """
+
Defines if paused.
""",
+ """
+
Defines the direction/speed at which the Timeline is expected to
+ be played.
""",
+ """
+
Sets the value of the paused property.
""",
+ """
+
Sets the value of the rate property.
""");
}
/*
diff --git a/test/langtools/jdk/javadoc/doclet/testJavaFX/pkg1/B.java b/test/langtools/jdk/javadoc/doclet/testJavaFX/pkg1/B.java
new file mode 100644
index 00000000000..2ec6f77833a
--- /dev/null
+++ b/test/langtools/jdk/javadoc/doclet/testJavaFX/pkg1/B.java
@@ -0,0 +1,27 @@
+/*
+ * 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 pkg1;
+
+public class B extends C {
+}
From 7a3025e3d7d33ed02db34c1485aa3c7b44b2d8ee Mon Sep 17 00:00:00 2001
From: Weijun Wang
Date: Wed, 10 Sep 2025 17:24:53 +0000
Subject: [PATCH 027/120] 8367348: Enhance PassFailJFrame to support links in
HTML
Reviewed-by: aivanov
---
.../awt/regtesthelpers/PassFailJFrame.java | 25 +++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java b/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java
index c0a483056df..49e56493488 100644
--- a/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java
+++ b/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java
@@ -72,6 +72,7 @@ import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.Timer;
import javax.swing.border.Border;
+import javax.swing.event.HyperlinkListener;
import javax.swing.text.JTextComponent;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;
@@ -98,7 +99,8 @@ import static javax.swing.SwingUtilities.isEventDispatchThread;
* tester. The instructions can be either plain text or HTML. If the
* text of the instructions starts with {@code ""}, the
* instructions are displayed as HTML, as supported by Swing, which
- * provides richer formatting options.
+ * provides richer formatting options. To handle navigating links in the
+ * instructions, call {@link Builder#hyperlinkListener} to install a listener.
*
* The instructions are displayed in a text component with word-wrapping
* so that there's no horizontal scroll bar. If the text doesn't fit, a
@@ -592,6 +594,7 @@ public final class PassFailJFrame {
frame.add(createInstructionUIPanel(instructions,
testTimeOut,
rows, columns,
+ null,
false,
false, 0),
BorderLayout.CENTER);
@@ -610,6 +613,7 @@ public final class PassFailJFrame {
createInstructionUIPanel(builder.instructions,
builder.testTimeOut,
builder.rows, builder.columns,
+ builder.hyperlinkListener,
builder.screenCapture,
builder.addLogArea,
builder.logAreaRows);
@@ -631,6 +635,7 @@ public final class PassFailJFrame {
private static JComponent createInstructionUIPanel(String instructions,
long testTimeOut,
int rows, int columns,
+ HyperlinkListener hyperlinkListener,
boolean enableScreenCapture,
boolean addLogArea,
int logAreaRows) {
@@ -643,6 +648,9 @@ public final class PassFailJFrame {
JTextComponent text = instructions.startsWith("")
? configureHTML(instructions, rows, columns)
: configurePlainText(instructions, rows, columns);
+ if (hyperlinkListener != null && text instanceof JEditorPane ep) {
+ ep.addHyperlinkListener(hyperlinkListener);
+ }
text.setEditable(false);
text.setBorder(createTextBorder());
text.setCaretPosition(0);
@@ -716,7 +724,7 @@ public final class PassFailJFrame {
// Reduce the list default margins
styles.addRule("ol, ul { margin-left-ltr: 30; margin-left-rtl: 30 }");
// Make the size of code (and other elements) the same as other text
- styles.addRule("code, kbd, samp, pre { font-size: inherit }");
+ styles.addRule("code, kbd, samp, pre { font-size: inherit; background: #DDD; }");
return text;
}
@@ -1398,6 +1406,7 @@ public final class PassFailJFrame {
private int rows;
private int columns;
private boolean screenCapture;
+ private HyperlinkListener hyperlinkListener;
private boolean addLogArea;
private int logAreaRows = 10;
@@ -1478,6 +1487,18 @@ public final class PassFailJFrame {
return this;
}
+ /**
+ * Sets a {@link HyperlinkListener} for navigating links inside
+ * the instructions pane.
+ *
+ * @param hyperlinkListener the listener
+ * @return this builder
+ */
+ public Builder hyperlinkListener(HyperlinkListener hyperlinkListener) {
+ this.hyperlinkListener = hyperlinkListener;
+ return this;
+ }
+
public Builder screenCapture() {
this.screenCapture = true;
return this;
From 4e2a85f7500876d65c36aeaf54f5361a1549e7f5 Mon Sep 17 00:00:00 2001
From: Man Cao
Date: Wed, 10 Sep 2025 17:42:15 +0000
Subject: [PATCH 028/120] 8366118: DontCompileHugeMethods is not respected with
-XX:-TieredCompilation
Co-authored-by: Chuck Rasbold
Co-authored-by: Justin King
Reviewed-by: rasbold, iveresov, jiangli
---
.../share/compiler/compilationPolicy.cpp | 39 +++---
.../runtime/TestDontCompileHugeMethods.java | 122 ++++++++++++++++++
2 files changed, 142 insertions(+), 19 deletions(-)
create mode 100644 test/hotspot/jtreg/compiler/runtime/TestDontCompileHugeMethods.java
diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp
index 5db6cb1b0cc..36b597b6e37 100644
--- a/src/hotspot/share/compiler/compilationPolicy.cpp
+++ b/src/hotspot/share/compiler/compilationPolicy.cpp
@@ -922,28 +922,29 @@ void CompilationPolicy::compile(const methodHandle& mh, int bci, CompLevel level
return;
}
- if (!CompilationModeFlag::disable_intermediate()) {
- // Check if the method can be compiled. If it cannot be compiled with C1, continue profiling
- // in the interpreter and then compile with C2 (the transition function will request that,
- // see common() ). If the method cannot be compiled with C2 but still can with C1, compile it with
- // pure C1.
- if ((bci == InvocationEntryBci && !can_be_compiled(mh, level))) {
- if (level == CompLevel_full_optimization && can_be_compiled(mh, CompLevel_simple)) {
- compile(mh, bci, CompLevel_simple, THREAD);
- }
- return;
+ // Check if the method can be compiled. Additional logic for TieredCompilation:
+ // If it cannot be compiled with C1, continue profiling in the interpreter
+ // and then compile with C2 (the transition function will request that,
+ // see common() ). If the method cannot be compiled with C2 but still can with C1, compile it with
+ // pure C1.
+ if ((bci == InvocationEntryBci && !can_be_compiled(mh, level))) {
+ if (!CompilationModeFlag::disable_intermediate() &&
+ level == CompLevel_full_optimization && can_be_compiled(mh, CompLevel_simple)) {
+ compile(mh, bci, CompLevel_simple, THREAD);
}
- if ((bci != InvocationEntryBci && !can_be_osr_compiled(mh, level))) {
- if (level == CompLevel_full_optimization && can_be_osr_compiled(mh, CompLevel_simple)) {
- nmethod* osr_nm = mh->lookup_osr_nmethod_for(bci, CompLevel_simple, false);
- if (osr_nm != nullptr && osr_nm->comp_level() > CompLevel_simple) {
- // Invalidate the existing OSR nmethod so that a compile at CompLevel_simple is permitted.
- osr_nm->make_not_entrant(nmethod::InvalidationReason::OSR_INVALIDATION_FOR_COMPILING_WITH_C1);
- }
- compile(mh, bci, CompLevel_simple, THREAD);
+ return;
+ }
+ if ((bci != InvocationEntryBci && !can_be_osr_compiled(mh, level))) {
+ if (!CompilationModeFlag::disable_intermediate() &&
+ level == CompLevel_full_optimization && can_be_osr_compiled(mh, CompLevel_simple)) {
+ nmethod* osr_nm = mh->lookup_osr_nmethod_for(bci, CompLevel_simple, false);
+ if (osr_nm != nullptr && osr_nm->comp_level() > CompLevel_simple) {
+ // Invalidate the existing OSR nmethod so that a compile at CompLevel_simple is permitted.
+ osr_nm->make_not_entrant(nmethod::InvalidationReason::OSR_INVALIDATION_FOR_COMPILING_WITH_C1);
}
- return;
+ compile(mh, bci, CompLevel_simple, THREAD);
}
+ return;
}
if (bci != InvocationEntryBci && mh->is_not_osr_compilable(level)) {
return;
diff --git a/test/hotspot/jtreg/compiler/runtime/TestDontCompileHugeMethods.java b/test/hotspot/jtreg/compiler/runtime/TestDontCompileHugeMethods.java
new file mode 100644
index 00000000000..c5e035edbd0
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/runtime/TestDontCompileHugeMethods.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2025, Google LLC. All rights reserved.
+ * 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 8366118
+ * @summary Check that a huge method is not compiled under -XX:+DontCompileHugeMethods.
+ * @library /test/lib
+ * @run main compiler.runtime.TestDontCompileHugeMethods
+ */
+package compiler.runtime;
+
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.Utils;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestDontCompileHugeMethods {
+
+ private static final String HUGE_SWITCH_CLASS_NAME = "HugeSwitch";
+
+ private static void generateClass(Writer writer) throws IOException {
+ writer.write("""
+ public class HugeSwitch {
+ private static int hugeSwitch(int x) {
+ switch (x) {
+ """);
+ for (int i = 0; i < 2000; i++) {
+ writer.write(" case " + i + ": return " + i + " + 1;\n");
+ }
+ writer.write("""
+ default:
+ return 0;
+ }
+ }
+ private static int shortMethod(int x) {
+ if (x % 3 == 0) {
+ return x - 1;
+ }
+ return x + 1;
+ }
+ public static void main(String[] args) {
+ int val = 0;
+ for (int i = 0; i < 100000; i++) {
+ val += hugeSwitch(shortMethod(i));
+ }
+ System.out.println(val);
+ }
+ }
+ """);
+ }
+
+ private static void compileClass(Path workDir, Path sourceFile) throws Exception {
+ JDKToolLauncher javac = JDKToolLauncher.create("javac").addToolArg("-d")
+ .addToolArg(workDir.toAbsolutePath().toString()).addToolArg("-cp")
+ .addToolArg(Utils.TEST_CLASS_PATH).addToolArg(sourceFile.toAbsolutePath().toString());
+
+ OutputAnalyzer output = ProcessTools.executeProcess(javac.getCommand());
+ output.shouldHaveExitValue(0);
+ }
+
+ private static void generateAndCompileClass(Path workDir) throws Exception {
+ Path sourceFile = workDir.resolve(HUGE_SWITCH_CLASS_NAME + ".java");
+ try (Writer writer = Files.newBufferedWriter(sourceFile)) {
+ generateClass(writer);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ compileClass(workDir, sourceFile);
+ }
+
+ private static void runTest(Path workDir, List jvmArgs) throws Exception {
+ ArrayList command = new ArrayList<>();
+ command.add("-XX:+PrintCompilation");
+ command.add("-Xbatch");
+ command.addAll(jvmArgs);
+ command.add("-cp");
+ command.add(workDir.toAbsolutePath().toString());
+ command.add(HUGE_SWITCH_CLASS_NAME);
+ ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(command);
+ OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
+ analyzer.shouldHaveExitValue(0);
+ analyzer.shouldContain(" HugeSwitch::shortMethod (");
+ analyzer.shouldNotContain(" HugeSwitch::hugeSwitch (");
+ }
+
+ public static void main(String[] args) throws Exception {
+ Path workDir = Paths.get("");
+ generateAndCompileClass(workDir);
+
+ runTest(workDir, List.of());
+ runTest(workDir, List.of("-XX:-TieredCompilation"));
+ }
+}
From fdc11a1569248c9b671b66d547b4616aeb953ecf Mon Sep 17 00:00:00 2001
From: Sergey Bylokhov
Date: Wed, 10 Sep 2025 18:41:42 +0000
Subject: [PATCH 029/120] 8367131: Test com/sun/jdi/ThreadMemoryLeakTest.java
fails on 32 bits
Reviewed-by: lmesnik, cjplummer, shade
---
test/jdk/com/sun/jdi/ThreadMemoryLeakTest.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/jdk/com/sun/jdi/ThreadMemoryLeakTest.java b/test/jdk/com/sun/jdi/ThreadMemoryLeakTest.java
index 59168a4de7e..5431b8835a2 100644
--- a/test/jdk/com/sun/jdi/ThreadMemoryLeakTest.java
+++ b/test/jdk/com/sun/jdi/ThreadMemoryLeakTest.java
@@ -28,7 +28,7 @@
*
* @comment Don't allow -Xcomp or -Xint as they impact memory useage and number of iterations.
* Require compressed oops because not doing so increases memory usage.
- * @requires (vm.compMode == "Xmixed") & vm.opt.final.UseCompressedOops
+ * @requires (vm.compMode == "Xmixed") & (vm.bits == 32 | vm.opt.final.UseCompressedOops)
* @run build TestScaffold VMConnection TargetListener TargetAdapter
* @run compile -g ThreadMemoryLeakTest.java
* @comment run with -Xmx7m so any leak will quickly produce OOME
From 85715e1050fa774c3267dbbe2f749717aeeec8ff Mon Sep 17 00:00:00 2001
From: Ioi Lam
Date: Wed, 10 Sep 2025 19:21:00 +0000
Subject: [PATCH 030/120] 8317269: Store old classes in linked state in AOT
cache
Reviewed-by: coleenp, matsaave
---
src/hotspot/share/cds/aotMetaspace.cpp | 20 +-
src/hotspot/share/cds/aotMetaspace.hpp | 1 +
src/hotspot/share/cds/archiveBuilder.cpp | 2 +-
src/hotspot/share/cds/cdsConfig.cpp | 30 ++
src/hotspot/share/cds/cdsConfig.hpp | 9 +
src/hotspot/share/cds/dumpTimeClassInfo.cpp | 13 +-
src/hotspot/share/cds/dumpTimeClassInfo.hpp | 15 +-
src/hotspot/share/cds/dynamicArchive.cpp | 6 +
.../share/cds/lambdaProxyClassDictionary.cpp | 6 +-
src/hotspot/share/cds/runTimeClassInfo.cpp | 10 +-
src/hotspot/share/cds/runTimeClassInfo.hpp | 4 +-
.../classfile/systemDictionaryShared.cpp | 316 +++++++++++++++---
.../classfile/systemDictionaryShared.hpp | 21 +-
src/hotspot/share/oops/instanceKlass.cpp | 14 +-
src/hotspot/share/oops/methodData.cpp | 5 +-
src/hotspot/share/oops/trainingData.cpp | 9 +-
src/hotspot/share/prims/jvm.cpp | 7 +
src/hotspot/share/runtime/mutexLocker.cpp | 4 +-
test/hotspot/jtreg/TEST.groups | 6 +-
.../cds/appcds/aotCache/ExcludedClasses.java | 12 +-
.../runtime/cds/appcds/aotCache/OldA.jasm | 38 +++
.../cds/appcds/aotCache/OldClassSupport.java | 162 +++++++++
...dClassWithExcludedVerifierConstraints.jasm | 50 +++
.../OldClassWithVerifierConstraints.jasm | 50 +++
.../AOTClassLinkingVerification.java | 294 ++++++++++++++++
.../appcds/aotClassLinking/BadNewClass.jasm | 52 +++
.../appcds/aotClassLinking/BadNewClass2.jasm | 52 +++
.../appcds/aotClassLinking/BadNewClass3.jasm | 53 +++
.../appcds/aotClassLinking/BadNewClass4.jasm | 53 +++
.../appcds/aotClassLinking/BadOldClass.jasm | 52 +++
.../appcds/aotClassLinking/BadOldClass2.jasm | 52 +++
.../appcds/aotClassLinking/BadOldClass3.jasm | 53 +++
.../appcds/aotClassLinking/BadOldClass4.jasm | 53 +++
.../aotClassLinking/BulkLoaderTest.java | 2 +-
.../appcds/aotClassLinking/GoodOldClass.jasm | 49 +++
35 files changed, 1485 insertions(+), 90 deletions(-)
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldA.jasm
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport.java
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassWithExcludedVerifierConstraints.jasm
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassWithVerifierConstraints.jasm
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVerification.java
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass.jasm
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass2.jasm
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass3.jasm
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass4.jasm
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass.jasm
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass2.jasm
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass3.jasm
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass4.jasm
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GoodOldClass.jasm
diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp
index c2d9d0193f5..cca82bed4f1 100644
--- a/src/hotspot/share/cds/aotMetaspace.cpp
+++ b/src/hotspot/share/cds/aotMetaspace.cpp
@@ -652,6 +652,8 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables(AOTClassLocationConfig*&
}
void VM_PopulateDumpSharedSpace::doit() {
+ CDSConfig::set_is_at_aot_safepoint(true);
+
if (!CDSConfig::is_dumping_final_static_archive()) {
guarantee(!CDSConfig::is_using_archive(), "We should not be using an archive when we dump");
}
@@ -717,6 +719,8 @@ void VM_PopulateDumpSharedSpace::doit() {
_map_info->set_serialized_data(serialized_data);
_map_info->set_cloned_vtables(CppVtables::vtables_serialized_base());
_map_info->header()->set_class_location_config(cl_config);
+
+ CDSConfig::set_is_at_aot_safepoint(false);
}
class CollectClassesForLinking : public KlassClosure {
@@ -773,12 +777,9 @@ bool AOTMetaspace::may_be_eagerly_linked(InstanceKlass* ik) {
return true;
}
-void AOTMetaspace::link_shared_classes(TRAPS) {
- AOTClassLinker::initialize();
- AOTClassInitializer::init_test_class(CHECK);
-
+void AOTMetaspace::link_all_loaded_classes(JavaThread* current) {
while (true) {
- ResourceMark rm(THREAD);
+ ResourceMark rm(current);
CollectClassesForLinking collect_classes;
bool has_linked = false;
const GrowableArray* mirrors = collect_classes.mirrors();
@@ -786,7 +787,7 @@ void AOTMetaspace::link_shared_classes(TRAPS) {
OopHandle mirror = mirrors->at(i);
InstanceKlass* ik = InstanceKlass::cast(java_lang_Class::as_Klass(mirror.resolve()));
if (may_be_eagerly_linked(ik)) {
- has_linked |= try_link_class(THREAD, ik);
+ has_linked |= try_link_class(current, ik);
}
}
@@ -796,6 +797,13 @@ void AOTMetaspace::link_shared_classes(TRAPS) {
// Class linking includes verification which may load more classes.
// Keep scanning until we have linked no more classes.
}
+}
+
+void AOTMetaspace::link_shared_classes(TRAPS) {
+ AOTClassLinker::initialize();
+ AOTClassInitializer::init_test_class(CHECK);
+
+ link_all_loaded_classes(THREAD);
// Eargerly resolve all string constants in constant pools
{
diff --git a/src/hotspot/share/cds/aotMetaspace.hpp b/src/hotspot/share/cds/aotMetaspace.hpp
index 6c0ad37dbf7..1803199766d 100644
--- a/src/hotspot/share/cds/aotMetaspace.hpp
+++ b/src/hotspot/share/cds/aotMetaspace.hpp
@@ -135,6 +135,7 @@ public:
}
static bool try_link_class(JavaThread* current, InstanceKlass* ik);
+ static void link_all_loaded_classes(JavaThread* current);
static void link_shared_classes(TRAPS) NOT_CDS_RETURN;
static bool may_be_eagerly_linked(InstanceKlass* ik) NOT_CDS_RETURN_(false);
diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp
index 42d575a012f..77f51443bb2 100644
--- a/src/hotspot/share/cds/archiveBuilder.cpp
+++ b/src/hotspot/share/cds/archiveBuilder.cpp
@@ -937,7 +937,7 @@ void ArchiveBuilder::make_klasses_shareable() {
ADD_COUNT(num_enum_klasses);
}
- if (!ik->can_be_verified_at_dumptime()) {
+ if (CDSConfig::is_old_class_for_verifier(ik)) {
ADD_COUNT(num_old_klasses);
old = " old";
}
diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp
index 90b802731f0..d3048b9ee7a 100644
--- a/src/hotspot/share/cds/cdsConfig.cpp
+++ b/src/hotspot/share/cds/cdsConfig.cpp
@@ -56,6 +56,7 @@ bool CDSConfig::_has_temp_aot_config_file = false;
bool CDSConfig::_old_cds_flags_used = false;
bool CDSConfig::_new_aot_flags_used = false;
bool CDSConfig::_disable_heap_dumping = false;
+bool CDSConfig::_is_at_aot_safepoint = false;
const char* CDSConfig::_default_archive_path = nullptr;
const char* CDSConfig::_input_static_archive_path = nullptr;
@@ -922,6 +923,35 @@ bool CDSConfig::is_dumping_lambdas_in_legacy_mode() {
return !is_dumping_method_handles();
}
+bool CDSConfig::is_preserving_verification_constraints() {
+ // Verification dependencies are classes used in assignability checks by the
+ // bytecode verifier. In the following example, the verification dependencies
+ // for X are A and B.
+ //
+ // class X {
+ // A getA() { return new B(); }
+ // }
+ //
+ // With the AOT cache, we can ensure that all the verification dependencies
+ // (A and B in the above example) are unconditionally loaded during the bootstrap
+ // of the production run. This means that if a class was successfully verified
+ // in the assembly phase, all of the verifier's assignability checks will remain
+ // valid in the production run, so we don't need to verify aot-linked classes again.
+
+ if (is_dumping_preimage_static_archive()) { // writing AOT config
+ return AOTClassLinking;
+ } else if (is_dumping_final_static_archive()) { // writing AOT cache
+ return is_dumping_aot_linked_classes();
+ } else {
+ // For simplicity, we don't support this optimization with the old CDS workflow.
+ return false;
+ }
+}
+
+bool CDSConfig::is_old_class_for_verifier(const InstanceKlass* ik) {
+ return ik->major_version() < 50 /*JAVA_6_VERSION*/;
+}
+
#if INCLUDE_CDS_JAVA_HEAP
bool CDSConfig::are_vm_options_incompatible_with_dumping_heap() {
return check_options_incompatible_with_dumping_heap() != nullptr;
diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp
index 1fd229ff34f..8361cf052db 100644
--- a/src/hotspot/share/cds/cdsConfig.hpp
+++ b/src/hotspot/share/cds/cdsConfig.hpp
@@ -30,6 +30,7 @@
#include "utilities/macros.hpp"
class JavaThread;
+class InstanceKlass;
class CDSConfig : public AllStatic {
#if INCLUDE_CDS
@@ -43,6 +44,7 @@ class CDSConfig : public AllStatic {
static bool _has_aot_linked_classes;
static bool _is_single_command_training;
static bool _has_temp_aot_config_file;
+ static bool _is_at_aot_safepoint;
const static char* _default_archive_path;
const static char* _input_static_archive_path;
@@ -99,6 +101,9 @@ public:
static const char* type_of_archive_being_written();
static void prepare_for_dumping();
+ static bool is_at_aot_safepoint() { return CDS_ONLY(_is_at_aot_safepoint) NOT_CDS(false); }
+ static void set_is_at_aot_safepoint(bool value) { CDS_ONLY(_is_at_aot_safepoint = value); }
+
// --- Basic CDS features
// archive(s) in general
@@ -161,6 +166,10 @@ public:
static bool is_using_aot_linked_classes() NOT_CDS_JAVA_HEAP_RETURN_(false);
static void set_has_aot_linked_classes(bool has_aot_linked_classes) NOT_CDS_JAVA_HEAP_RETURN;
+ // Bytecode verification
+ static bool is_preserving_verification_constraints();
+ static bool is_old_class_for_verifier(const InstanceKlass* ik);
+
// archive_path
// Points to the classes.jsa in $JAVA_HOME (could be input or output)
diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.cpp b/src/hotspot/share/cds/dumpTimeClassInfo.cpp
index 8af762dba4d..0f5773a2729 100644
--- a/src/hotspot/share/cds/dumpTimeClassInfo.cpp
+++ b/src/hotspot/share/cds/dumpTimeClassInfo.cpp
@@ -47,7 +47,7 @@ size_t DumpTimeClassInfo::runtime_info_bytesize() const {
num_enum_klass_static_fields());
}
-void DumpTimeClassInfo::add_verification_constraint(InstanceKlass* k, Symbol* name,
+void DumpTimeClassInfo::add_verification_constraint(Symbol* name,
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
if (_verifier_constraints == nullptr) {
_verifier_constraints = new (mtClass) GrowableArray(4, mtClass);
@@ -73,9 +73,14 @@ void DumpTimeClassInfo::add_verification_constraint(InstanceKlass* k, Symbol* na
if (log_is_enabled(Trace, aot, verification)) {
ResourceMark rm;
- log_trace(aot, verification)("add_verification_constraint: %s: %s must be subclass of %s [0x%x] array len %d flags len %d",
- k->external_name(), from_name->as_klass_external_name(),
- name->as_klass_external_name(), c, vc_array->length(), vcflags_array->length());
+ if (from_name != nullptr) {
+ log_trace(aot, verification)("add verification constraint: %s: %s must be subclass of %s [0x%x]",
+ _klass->external_name(), from_name->as_klass_external_name(),
+ name->as_klass_external_name(), c);
+ } else {
+ log_trace(aot, verification)("added old verification constraint: %s: %s", _klass->external_name(),
+ name->as_klass_external_name());
+ }
}
}
diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.hpp
index 0bc0f8bedda..c2f83b22337 100644
--- a/src/hotspot/share/cds/dumpTimeClassInfo.hpp
+++ b/src/hotspot/share/cds/dumpTimeClassInfo.hpp
@@ -88,7 +88,7 @@ class DumpTimeClassInfo: public CHeapObj {
Symbol* _from_name;
public:
DTVerifierConstraint() : _name(nullptr), _from_name(nullptr) {}
- DTVerifierConstraint(Symbol* n, Symbol* fn) : _name(n), _from_name(fn) {
+ DTVerifierConstraint(Symbol* n, Symbol* fn = nullptr) : _name(n), _from_name(fn) {
Symbol::maybe_increment_refcount(_name);
Symbol::maybe_increment_refcount(_from_name);
}
@@ -152,8 +152,9 @@ public:
DumpTimeClassInfo& operator=(const DumpTimeClassInfo&) = delete;
~DumpTimeClassInfo();
- void add_verification_constraint(InstanceKlass* k, Symbol* name,
- Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object);
+ // For old verifier: only name is saved; all other fields are null/false.
+ void add_verification_constraint(Symbol* name,
+ Symbol* from_name = nullptr, bool from_field_is_protected = false, bool from_is_array = false, bool from_is_object = false);
void record_linking_constraint(Symbol* name, Handle loader1, Handle loader2);
void add_enum_klass_static_field(int archived_heap_root_index);
int enum_klass_static_field(int which_field);
@@ -175,6 +176,14 @@ public:
return array_length_or_zero(_verifier_constraint_flags);
}
+ Symbol* verifier_constraint_name_at(int i) const {
+ return _verifier_constraints->at(i).name();
+ }
+
+ Symbol* verifier_constraint_from_name_at(int i) const {
+ return _verifier_constraints->at(i).from_name();
+ }
+
int num_loader_constraints() const {
return array_length_or_zero(_loader_constraints);
}
diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp
index d628a4e991f..58b354b9240 100644
--- a/src/hotspot/share/cds/dynamicArchive.cpp
+++ b/src/hotspot/share/cds/dynamicArchive.cpp
@@ -110,6 +110,12 @@ public:
}
void doit() {
+ CDSConfig::set_is_at_aot_safepoint(true);
+ doit_inner();
+ CDSConfig::set_is_at_aot_safepoint(false);
+ }
+
+ void doit_inner() {
verify_universe("Before CDS dynamic dump");
DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm);
diff --git a/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp b/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp
index c8281ef497c..62b1b8c05f1 100644
--- a/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp
+++ b/src/hotspot/share/cds/lambdaProxyClassDictionary.cpp
@@ -471,12 +471,12 @@ class LambdaProxyClassDictionary::CleanupDumpTimeLambdaProxyClassTable: StackObj
// If the caller class and/or nest_host are excluded, the associated lambda proxy
// must also be excluded.
- bool always_exclude = SystemDictionaryShared::check_for_exclusion(caller_ik, nullptr) ||
- SystemDictionaryShared::check_for_exclusion(nest_host, nullptr);
+ bool always_exclude = SystemDictionaryShared::should_be_excluded(caller_ik) ||
+ SystemDictionaryShared::should_be_excluded(nest_host);
for (int i = info._proxy_klasses->length() - 1; i >= 0; i--) {
InstanceKlass* ik = info._proxy_klasses->at(i);
- if (always_exclude || SystemDictionaryShared::check_for_exclusion(ik, nullptr)) {
+ if (always_exclude || SystemDictionaryShared::should_be_excluded(ik)) {
LambdaProxyClassDictionary::reset_registered_lambda_proxy_class(ik);
info._proxy_klasses->remove_at(i);
}
diff --git a/src/hotspot/share/cds/runTimeClassInfo.cpp b/src/hotspot/share/cds/runTimeClassInfo.cpp
index d93ef5e9c1d..832b0ce8932 100644
--- a/src/hotspot/share/cds/runTimeClassInfo.cpp
+++ b/src/hotspot/share/cds/runTimeClassInfo.cpp
@@ -40,12 +40,18 @@ void RunTimeClassInfo::init(DumpTimeClassInfo& info) {
_num_verifier_constraints = info.num_verifier_constraints();
_num_loader_constraints = info.num_loader_constraints();
int i;
+
+ if (CDSConfig::is_preserving_verification_constraints() && CDSConfig::is_dumping_final_static_archive()) {
+ // The production run doesn't need the verifier constraints, as we can guarantee that all classes checked by
+ // the verifier during AOT training/assembly phases cannot be replaced in the production run.
+ _num_verifier_constraints = 0;
+ }
if (_num_verifier_constraints > 0) {
RTVerifierConstraint* vf_constraints = verifier_constraints();
char* flags = verifier_constraint_flags();
for (i = 0; i < _num_verifier_constraints; i++) {
- vf_constraints[i]._name = builder->any_to_offset_u4(info._verifier_constraints->at(i).name());
- vf_constraints[i]._from_name = builder->any_to_offset_u4(info._verifier_constraints->at(i).from_name());
+ vf_constraints[i]._name = builder->any_to_offset_u4(info._verifier_constraints->at(i).name());
+ vf_constraints[i]._from_name = builder->any_or_null_to_offset_u4(info._verifier_constraints->at(i).from_name());
}
for (i = 0; i < _num_verifier_constraints; i++) {
flags[i] = info._verifier_constraint_flags->at(i);
diff --git a/src/hotspot/share/cds/runTimeClassInfo.hpp b/src/hotspot/share/cds/runTimeClassInfo.hpp
index 29670f5ec51..371924f9065 100644
--- a/src/hotspot/share/cds/runTimeClassInfo.hpp
+++ b/src/hotspot/share/cds/runTimeClassInfo.hpp
@@ -59,7 +59,9 @@ class RunTimeClassInfo {
u4 _name;
u4 _from_name;
Symbol* name() { return ArchiveUtils::offset_to_archived_address(_name); }
- Symbol* from_name() { return ArchiveUtils::offset_to_archived_address(_from_name); }
+ Symbol* from_name() {
+ return (_from_name == 0) ? nullptr : ArchiveUtils::offset_to_archived_address(_from_name);
+ }
};
struct RTLoaderConstraint {
diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp
index 45a5dc2328c..eda823704ca 100644
--- a/src/hotspot/share/classfile/systemDictionaryShared.cpp
+++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp
@@ -204,28 +204,156 @@ DumpTimeClassInfo* SystemDictionaryShared::get_info_locked(InstanceKlass* k) {
return info;
}
-bool SystemDictionaryShared::check_for_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) {
- if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(k)) {
- // We have reached a super type that's already in the base archive. Treat it
- // as "not excluded".
- return false;
- }
-
- if (info == nullptr) {
- info = _dumptime_table->get(k);
- assert(info != nullptr, "supertypes of any classes in _dumptime_table must either be shared, or must also be in _dumptime_table");
- }
+bool SystemDictionaryShared::should_be_excluded_impl(InstanceKlass* k, DumpTimeClassInfo* info) {
+ assert_lock_strong(DumpTimeTable_lock);
if (!info->has_checked_exclusion()) {
- if (check_for_exclusion_impl(k)) {
- info->set_excluded();
- }
- info->set_has_checked_exclusion();
+ check_exclusion_for_self_and_dependencies(k);
+ assert(info->has_checked_exclusion(), "must be");
}
return info->is_excluded();
}
+// returns bool and takes a single parameter of Symbol*
+// The return value indicates whether we want to keep on iterating or not.
+template
+void SystemDictionaryShared::iterate_verification_constraint_names(InstanceKlass* k, DumpTimeClassInfo* info, Function func) {
+ int n = info->num_verifier_constraints();
+ bool cont; // continue iterating?
+ for (int i = 0; i < n; i++) {
+ cont = func(info->verifier_constraint_name_at(i));
+ if (!cont) {
+ return; // early termination
+ }
+ Symbol* from_name = info->verifier_constraint_from_name_at(i);
+ if (from_name != nullptr) {
+ cont = func(from_name);
+ if (!cont) {
+ return; // early termination
+ }
+ }
+ }
+}
+
+// This is a table of classes that need to be checked for exclusion.
+class SystemDictionaryShared::ExclusionCheckCandidates
+ : public HashTable {
+ void add_candidate(InstanceKlass* k) {
+ if (contains(k)) {
+ return;
+ }
+ if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(k)) {
+ return;
+ }
+
+ DumpTimeClassInfo* info = SystemDictionaryShared::get_info_locked(k);
+ if (info->has_checked_exclusion()) {
+ // We have check exclusion of k and all of its dependencies, so there's no need to check again.
+ return;
+ }
+
+ put(k, info);
+
+ if (!k->is_loaded()) {
+ // super types are not yet initialized for k.
+ return;
+ }
+
+ InstanceKlass* super = k->java_super();
+ if (super != nullptr) {
+ add_candidate(super);
+ }
+
+ Array* interfaces = k->local_interfaces();
+ int len = interfaces->length();
+ for (int i = 0; i < len; i++) {
+ add_candidate(interfaces->at(i));
+ }
+
+ InstanceKlass* nest_host = k->nest_host_or_null();
+ if (nest_host != nullptr && nest_host != k) {
+ add_candidate(nest_host);
+ }
+
+ if (CDSConfig::is_preserving_verification_constraints()) {
+ SystemDictionaryShared::iterate_verification_constraint_names(k, info, [&] (Symbol* constraint_class_name) {
+ Klass* constraint_bottom_class = find_verification_constraint_bottom_class(k, constraint_class_name);
+ if (constraint_bottom_class != nullptr && constraint_bottom_class->is_instance_klass()) {
+ add_candidate(InstanceKlass::cast(constraint_bottom_class));
+ }
+ return true; // Keep iterating.
+ });
+ }
+ }
+
+public:
+ ExclusionCheckCandidates(InstanceKlass* k) {
+ add_candidate(k);
+ }
+};
+
+// A class X is excluded if check_self_exclusion() returns true for X or any of
+// X's "exclusion dependency" classes, which include:
+// - ik's super types
+// - ik's nest host (if any)
+//
+// plus, if CDSConfig::is_preserving_verification_constraints()==true:
+// - ik's verification constraints. These are the classes used in assignability checks
+// when verifying ik's bytecodes.
+//
+// This method ensure that exclusion check is performed on X and all of its exclusion dependencies.
+void SystemDictionaryShared::check_exclusion_for_self_and_dependencies(InstanceKlass* ik) {
+ assert_lock_strong(DumpTimeTable_lock);
+ ResourceMark rm;
+
+ // This will recursively find ik and all of its exclusion dependencies that have not yet been checked.
+ ExclusionCheckCandidates candidates(ik);
+
+ // (1) Check each class to see if it should be excluded due to its own problems
+ candidates.iterate_all([&] (InstanceKlass* k, DumpTimeClassInfo* info) {
+ if (check_self_exclusion(k)) {
+ info->set_excluded();
+ }
+ });
+
+ // (2) Check each class to see if it should be excluded because of problems in a depeendency class
+ while (true) {
+ bool found_new_exclusion = false;
+
+ candidates.iterate_all([&] (InstanceKlass* k, DumpTimeClassInfo* info) {
+ if (!info->is_excluded() && check_dependencies_exclusion(k, info)) {
+ info->set_excluded();
+ found_new_exclusion = true;
+ }
+ });
+
+ // Algorithm notes:
+ //
+ // The dependencies form a directed graph, possibly cyclic. Class X is excluded
+ // if it has at least one directed path that reaches class Y, where
+ // check_self_exclusion(Y) returns true.
+ //
+ // Because of the possibility of cycles in the graph, we cannot use simple
+ // recursion. Otherwise we will either never terminate, or will miss some paths.
+ //
+ // Hence, we keep doing a linear scan of the candidates until we stop finding
+ // new exclusions.
+ //
+ // In the worst case, we find one exclusion per iteration of the while loop,
+ // so the while loop gets executed O(N^2) times. However, in reality we have
+ // very few exclusions, so in most cases the while loop executes only once, and we
+ // walk each edge in the dependencies graph exactly once.
+ if (!found_new_exclusion) {
+ break;
+ }
+ }
+ candidates.iterate_all([&] (InstanceKlass* k, DumpTimeClassInfo* info) {
+ // All candidates have been fully checked, so we don't need to check them again.
+ info->set_has_checked_exclusion();
+ });
+}
+
// Returns true so the caller can do: return warn_excluded(".....");
bool SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) {
ResourceMark rm;
@@ -248,7 +376,8 @@ bool SystemDictionaryShared::is_early_klass(InstanceKlass* ik) {
return (info != nullptr) ? info->is_early_klass() : false;
}
-bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
+bool SystemDictionaryShared::check_self_exclusion(InstanceKlass* k) {
+ assert_lock_strong(DumpTimeTable_lock);
if (CDSConfig::is_dumping_final_static_archive() && k->defined_by_other_loaders()
&& k->in_aot_cache()) {
return false; // Do not exclude: unregistered classes are passed from preimage to final image.
@@ -301,9 +430,8 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
return warn_excluded(k, "Failed verification");
} else if (CDSConfig::is_dumping_aot_linked_classes()) {
// Most loaded classes should have been speculatively linked by AOTMetaspace::link_class_for_cds().
- // However, we do not speculatively link old classes, as they are not recorded by
- // SystemDictionaryShared::record_linking_constraint(). As a result, such an unlinked
- // class may fail to verify in AOTLinkedClassBulkLoader::init_required_classes_for_loader(),
+ // Old classes may not be linked if CDSConfig::is_preserving_verification_constraints()==false.
+ // An unlinked class may fail to verify in AOTLinkedClassBulkLoader::init_required_classes_for_loader(),
// causing the JVM to fail at bootstrap.
return warn_excluded(k, "Unlinked class not supported by AOTClassLinking");
} else if (CDSConfig::is_dumping_preimage_static_archive()) {
@@ -329,10 +457,13 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
return true;
}
- InstanceKlass* super = k->super();
- if (super != nullptr && check_for_exclusion(super, nullptr)) {
- ResourceMark rm;
- aot_log_warning(aot)("Skipping %s: super class %s is excluded", k->name()->as_C_string(), super->name()->as_C_string());
+ return false;
+}
+
+// Returns true if DumpTimeClassInfo::is_excluded() is true for at least one of k's exclusion dependencies.
+bool SystemDictionaryShared::check_dependencies_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) {
+ InstanceKlass* super = k->java_super();
+ if (super != nullptr && is_dependency_excluded(k, super, "super")) {
return true;
}
@@ -340,21 +471,87 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
int len = interfaces->length();
for (int i = 0; i < len; i++) {
InstanceKlass* intf = interfaces->at(i);
- if (check_for_exclusion(intf, nullptr)) {
- ResourceMark rm;
- aot_log_warning(aot)("Skipping %s: interface %s is excluded", k->name()->as_C_string(), intf->name()->as_C_string());
+ if (is_dependency_excluded(k, intf, "interface")) {
return true;
}
}
InstanceKlass* nest_host = k->nest_host_or_null();
- if (nest_host != nullptr && nest_host != k && check_for_exclusion(nest_host, nullptr)) {
- ResourceMark rm;
- aot_log_warning(aot)("Skipping %s: nest_host class %s is excluded", k->name()->as_C_string(), nest_host->name()->as_C_string());
+ if (nest_host != nullptr && nest_host != k && is_dependency_excluded(k, nest_host, "nest host class")) {
return true;
}
- return false; // false == k should NOT be excluded
+ if (CDSConfig::is_preserving_verification_constraints()) {
+ bool excluded = false;
+
+ iterate_verification_constraint_names(k, info, [&] (Symbol* constraint_class_name) {
+ if (check_verification_constraint_exclusion(k, constraint_class_name)) {
+ // If one of the verification constraint class has been excluded, the assignability checks
+ // by the verifier may no longer be valid in the production run. For safety, exclude this class.
+ excluded = true;
+ return false; // terminate iteration; k will be excluded
+ } else {
+ return true; // keep iterating
+ }
+ });
+
+ if (excluded) {
+ // At least one verification constraint class has been excluded
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool SystemDictionaryShared::is_dependency_excluded(InstanceKlass* k, InstanceKlass* dependency, const char* type) {
+ if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(dependency)) {
+ return false;
+ }
+ DumpTimeClassInfo* dependency_info = get_info_locked(dependency);
+ if (dependency_info->is_excluded()) {
+ ResourceMark rm;
+ aot_log_warning(aot)("Skipping %s: %s %s is excluded", k->name()->as_C_string(), type, dependency->name()->as_C_string());
+ return true;
+ }
+ return false;
+}
+
+bool SystemDictionaryShared::check_verification_constraint_exclusion(InstanceKlass* k, Symbol* constraint_class_name) {
+ Klass* constraint_bottom_class = find_verification_constraint_bottom_class(k, constraint_class_name);
+ if (constraint_bottom_class == nullptr) {
+ // We don't have a bottom class (constraint_class_name is a type array), or constraint_class_name
+ // has not been loaded. The latter case happens when the new verifier was checking
+ // if constraint_class_name is assignable to an interface, and found the answer without resolving
+ // constraint_class_name.
+ //
+ // Since this class is not even loaded, it surely cannot be excluded.
+ return false;
+ } else if (constraint_bottom_class->is_instance_klass()) {
+ if (is_dependency_excluded(k, InstanceKlass::cast(constraint_bottom_class), "verification constraint")) {
+ return true;
+ }
+ } else {
+ assert(constraint_bottom_class->is_typeArray_klass(), "must be");
+ }
+
+ return false;
+}
+
+Klass* SystemDictionaryShared::find_verification_constraint_bottom_class(InstanceKlass* k, Symbol* constraint_class_name) {
+ Thread* current = Thread::current();
+ Handle loader(current, k->class_loader());
+ Klass* constraint_class = SystemDictionary::find_instance_or_array_klass(current, constraint_class_name, loader);
+ if (constraint_class == nullptr) {
+ return nullptr;
+ }
+
+ if (constraint_class->is_objArray_klass()) {
+ constraint_class = ObjArrayKlass::cast(constraint_class)->bottom_klass();
+ }
+
+ precond(constraint_class->is_typeArray_klass() || constraint_class->is_instance_klass());
+ return constraint_class;
}
bool SystemDictionaryShared::is_builtin_loader(ClassLoaderData* loader_data) {
@@ -556,7 +753,7 @@ void SystemDictionaryShared::handle_class_unloading(InstanceKlass* klass) {
void SystemDictionaryShared::init_dumptime_info_from_preimage(InstanceKlass* k) {
init_dumptime_info(k);
- copy_verification_constraints_from_preimage(k);
+ copy_verification_info_from_preimage(k);
copy_linking_constraints_from_preimage(k);
if (SystemDictionary::is_platform_class_loader(k->class_loader())) {
@@ -651,16 +848,21 @@ public:
// Returns true if the class should be excluded. This can be called by
// AOTConstantPoolResolver before or after we enter the CDS safepoint.
// When called before the safepoint, we need to link the class so that
-// it can be checked by check_for_exclusion().
+// it can be checked by should_be_excluded_impl().
bool SystemDictionaryShared::should_be_excluded(Klass* k) {
assert(CDSConfig::is_dumping_archive(), "sanity");
assert(CDSConfig::current_thread_is_vm_or_dumper(), "sanity");
- if (k->is_objArray_klass()) {
- return should_be_excluded(ObjArrayKlass::cast(k)->bottom_klass());
+ if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(k)) {
+ // We have reached a super type that's already in the base archive. Treat it
+ // as "not excluded".
+ return false;
}
- if (!k->is_instance_klass()) {
+ if (k->is_objArray_klass()) {
+ return should_be_excluded(ObjArrayKlass::cast(k)->bottom_klass());
+ } else if (!k->is_instance_klass()) {
+ assert(k->is_typeArray_klass(), "must be");
return false;
} else {
InstanceKlass* ik = InstanceKlass::cast(k);
@@ -672,7 +874,7 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) {
if (!SafepointSynchronize::is_at_safepoint()) {
if (!ik->is_linked()) {
- // check_for_exclusion() below doesn't link unlinked classes. We come
+ // should_be_excluded_impl() below doesn't link unlinked classes. We come
// here only when we are trying to aot-link constant pool entries, so
// we'd better link the class.
JavaThread* THREAD = JavaThread::current();
@@ -681,6 +883,10 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) {
CLEAR_PENDING_EXCEPTION;
return true; // linking failed -- let's exclude it
}
+
+ // Also link any classes that were loaded for the verification of ik or its supertypes.
+ // Otherwise we might miss the verification constraints of those classes.
+ AOTMetaspace::link_all_loaded_classes(THREAD);
}
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
@@ -688,8 +894,17 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) {
if (p->is_excluded()) {
return true;
}
- return check_for_exclusion(ik, p);
+ return should_be_excluded_impl(ik, p);
} else {
+ // When called within the CDS safepoint, the correctness of this function
+ // relies on the call to AOTMetaspace::link_all_loaded_classes()
+ // that happened right before we enter the CDS safepoint.
+ //
+ // Do not call this function in other types of safepoints. For example, if this
+ // is called in a GC safepoint, a klass may be improperly excluded because some
+ // of its verification constraints have not yet been linked.
+ assert(CDSConfig::is_at_aot_safepoint(), "Do not call this function in any other safepoint");
+
// No need to check for is_linked() as all eligible classes should have
// already been linked in AOTMetaspace::link_class_for_cds().
// Can't take the lock as we are in safepoint.
@@ -697,12 +912,13 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) {
if (p->is_excluded()) {
return true;
}
- return check_for_exclusion(ik, p);
+ return should_be_excluded_impl(ik, p);
}
}
}
void SystemDictionaryShared::finish_exclusion_checks() {
+ assert_at_safepoint();
if (CDSConfig::is_dumping_dynamic_archive() || CDSConfig::is_dumping_preimage_static_archive()) {
// Do this first -- if a base class is excluded due to duplication,
// all of its subclasses will also be excluded.
@@ -713,7 +929,7 @@ void SystemDictionaryShared::finish_exclusion_checks() {
}
_dumptime_table->iterate_all_live_classes([&] (InstanceKlass* k, DumpTimeClassInfo& info) {
- SystemDictionaryShared::check_for_exclusion(k, &info);
+ SystemDictionaryShared::should_be_excluded_impl(k, &info);
});
_dumptime_table->update_counts();
@@ -793,7 +1009,7 @@ void SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbo
bool* skip_assignability_check) {
assert(CDSConfig::is_dumping_archive(), "sanity");
DumpTimeClassInfo* info = get_info(k);
- info->add_verification_constraint(k, name, from_name, from_field_is_protected,
+ info->add_verification_constraint(name, from_name, from_field_is_protected,
from_is_array, from_is_object);
if (CDSConfig::is_dumping_classic_static_archive() && !is_builtin(k)) {
@@ -818,6 +1034,15 @@ void SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbo
}
}
+// When the old verifier is verifying the class at dump time, it tries to resolve a
+// class with the given . For the verification result to be valid at run time, we must
+// ensure that resolves to the exact same Klass as in dump time.
+void SystemDictionaryShared::add_old_verification_constraint(Thread* current, InstanceKlass* ik, Symbol* name) {
+ precond(CDSConfig::is_preserving_verification_constraints());
+ DumpTimeClassInfo* info = get_info(ik);
+ info->add_verification_constraint(name);
+}
+
void SystemDictionaryShared::add_enum_klass_static_field(InstanceKlass* ik, int root_index) {
assert(CDSConfig::is_dumping_heap(), "sanity");
DumpTimeClassInfo* info = get_info_locked(ik);
@@ -836,6 +1061,13 @@ void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass
Symbol* name = vc->name();
Symbol* from_name = vc->from_name();
+ if (from_name == nullptr) {
+ // This is for old verifier. No need to check, as we can guarantee that all classes checked by
+ // the old verifier during AOT training phase cannot be replaced in the asembly phase.
+ precond(CDSConfig::is_dumping_final_static_archive());
+ continue;
+ }
+
if (log_is_enabled(Trace, aot, verification)) {
ResourceMark rm(THREAD);
log_trace(aot, verification)("check_verification_constraint: %s: %s must be subclass of %s [0x%x]",
@@ -860,7 +1092,7 @@ void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass
}
}
-void SystemDictionaryShared::copy_verification_constraints_from_preimage(InstanceKlass* klass) {
+void SystemDictionaryShared::copy_verification_info_from_preimage(InstanceKlass* klass) {
assert(CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
DumpTimeClassInfo* dt_info = get_info(klass);
RunTimeClassInfo* rt_info = RunTimeClassInfo::get_for(klass); // from preimage
@@ -872,7 +1104,7 @@ void SystemDictionaryShared::copy_verification_constraints_from_preimage(Instanc
Symbol* name = vc->name();
Symbol* from_name = vc->from_name();
- dt_info->add_verification_constraint(klass, name, from_name,
+ dt_info->add_verification_constraint(name, from_name,
rt_info->from_field_is_protected(i), rt_info->from_is_array(i), rt_info->from_is_object(i));
}
}
diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp
index 30b38a5aa59..baad020cb61 100644
--- a/src/hotspot/share/classfile/systemDictionaryShared.hpp
+++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp
@@ -146,7 +146,7 @@ class SystemDictionaryShared: public SystemDictionary {
};
private:
-
+ class ExclusionCheckCandidates;
static DumpTimeSharedClassTable* _dumptime_table;
static ArchiveInfo _static_archive;
@@ -175,14 +175,27 @@ private:
static void write_dictionary(RunTimeSharedDictionary* dictionary,
bool is_builtin);
static bool is_jfr_event_class(InstanceKlass *k);
- static bool check_for_exclusion_impl(InstanceKlass* k);
+ static bool should_be_excluded_impl(InstanceKlass* k, DumpTimeClassInfo* info);
+
+ // exclusion checks
+ static void check_exclusion_for_self_and_dependencies(InstanceKlass *k);
+ static bool check_self_exclusion(InstanceKlass* k);
+ static bool check_dependencies_exclusion(InstanceKlass* k, DumpTimeClassInfo* info);
+ static bool check_verification_constraint_exclusion(InstanceKlass* k, Symbol* constraint_class_name);
+ static bool is_dependency_excluded(InstanceKlass* k, InstanceKlass* dependency, const char* type);
+ static bool is_excluded_verification_constraint(InstanceKlass* k, Symbol* constraint_class_name);
+ static Klass* find_verification_constraint_bottom_class(InstanceKlass* k, Symbol* constraint_class_name);
+
static void remove_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN;
static bool has_been_redefined(InstanceKlass* k);
DEBUG_ONLY(static bool _class_loading_may_happen;)
- static void copy_verification_constraints_from_preimage(InstanceKlass* klass);
+ static void copy_verification_info_from_preimage(InstanceKlass* klass);
static void copy_linking_constraints_from_preimage(InstanceKlass* klass);
+ template
+ static void iterate_verification_constraint_names(InstanceKlass* k, DumpTimeClassInfo* info, Function func);
+
public:
static bool is_early_klass(InstanceKlass* k); // Was k loaded while JvmtiExport::is_early_phase()==true
static bool has_archived_enum_objs(InstanceKlass* ik);
@@ -239,6 +252,7 @@ public:
Symbol* from_name, bool from_field_is_protected,
bool from_is_array, bool from_is_object,
bool* skip_assignability_check);
+ static void add_old_verification_constraint(Thread* current, InstanceKlass* k, Symbol* name);
static void check_verification_constraints(InstanceKlass* klass,
TRAPS) NOT_CDS_RETURN;
static void add_enum_klass_static_field(InstanceKlass* ik, int root_index);
@@ -258,7 +272,6 @@ public:
static DumpTimeSharedClassTable* dumptime_table() { return _dumptime_table; }
static bool should_be_excluded(Klass* k);
- static bool check_for_exclusion(InstanceKlass* k, DumpTimeClassInfo* info);
static void validate_before_archiving(InstanceKlass* k);
static bool is_excluded_class(InstanceKlass* k);
static void set_excluded(InstanceKlass* k);
diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
index 1afc59d8da1..e0ebb92c7ae 100644
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -2839,18 +2839,20 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl
DEBUG_ONLY(FieldInfoStream::validate_search_table(_constants, _fieldinfo_stream, _fieldinfo_search_table));
}
-// Check if a class or any of its supertypes has a version older than 50.
-// CDS will not perform verification of old classes during dump time because
-// without changing the old verifier, the verification constraint cannot be
-// retrieved during dump time.
-// Verification of archived old classes will be performed during run time.
bool InstanceKlass::can_be_verified_at_dumptime() const {
if (AOTMetaspace::in_aot_cache(this)) {
// This is a class that was dumped into the base archive, so we know
// it was verified at dump time.
return true;
}
- if (major_version() < 50 /*JAVA_6_VERSION*/) {
+
+ if (CDSConfig::is_preserving_verification_constraints()) {
+ return true;
+ }
+
+ if (CDSConfig::is_old_class_for_verifier(this)) {
+ // The old verifier does not save verification constraints, so at run time
+ // SystemDictionaryShared::check_verification_constraints() will not work for this class.
return false;
}
if (super() != nullptr && !super()->can_be_verified_at_dumptime()) {
diff --git a/src/hotspot/share/oops/methodData.cpp b/src/hotspot/share/oops/methodData.cpp
index 4c027e0839a..0463d8d9a81 100644
--- a/src/hotspot/share/oops/methodData.cpp
+++ b/src/hotspot/share/oops/methodData.cpp
@@ -323,9 +323,8 @@ void VirtualCallTypeData::post_initialize(BytecodeStream* stream, MethodData* md
static bool is_excluded(Klass* k) {
#if INCLUDE_CDS
- if (SafepointSynchronize::is_at_safepoint() &&
- CDSConfig::is_dumping_archive() &&
- CDSConfig::current_thread_is_vm_or_dumper()) {
+ if (CDSConfig::is_at_aot_safepoint()) {
+ // Check for CDS exclusion only at CDS safe point.
if (k->is_instance_klass() && !InstanceKlass::cast(k)->is_loaded()) {
log_debug(aot, training)("Purged %s from MDO: unloaded class", k->name()->as_C_string());
return true;
diff --git a/src/hotspot/share/oops/trainingData.cpp b/src/hotspot/share/oops/trainingData.cpp
index 845dc20c0d0..8f906ae3d37 100644
--- a/src/hotspot/share/oops/trainingData.cpp
+++ b/src/hotspot/share/oops/trainingData.cpp
@@ -554,7 +554,11 @@ void KlassTrainingData::cleanup(Visitor& visitor) {
}
visitor.visit(this);
if (has_holder()) {
- bool is_excluded = !holder()->is_loaded() || SystemDictionaryShared::check_for_exclusion(holder(), nullptr);
+ bool is_excluded = !holder()->is_loaded();
+ if (CDSConfig::is_at_aot_safepoint()) {
+ // Check for AOT exclusion only at AOT safe point.
+ is_excluded |= SystemDictionaryShared::should_be_excluded(holder());
+ }
if (is_excluded) {
ResourceMark rm;
log_debug(aot, training)("Cleanup KTD %s", name()->as_klass_external_name());
@@ -573,7 +577,8 @@ void MethodTrainingData::cleanup(Visitor& visitor) {
}
visitor.visit(this);
if (has_holder()) {
- if (SystemDictionaryShared::check_for_exclusion(holder()->method_holder(), nullptr)) {
+ if (CDSConfig::is_at_aot_safepoint() && SystemDictionaryShared::should_be_excluded(holder()->method_holder())) {
+ // Check for AOT exclusion only at AOT safe point.
log_debug(aot, training)("Cleanup MTD %s::%s", name()->as_klass_external_name(), signature()->as_utf8());
if (_final_profile != nullptr && _final_profile->method() != _holder) {
log_warning(aot, training)("Stale MDO for %s::%s", name()->as_klass_external_name(), signature()->as_utf8());
diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
index 99c8a56c727..0651c173e7b 100644
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -850,6 +850,13 @@ JVM_ENTRY(jclass, JVM_FindClassFromClass(JNIEnv *env, const char *name,
log_debug(class, resolve)("%s %s (verification)", from_name, to);
}
+#if INCLUDE_CDS
+ if (CDSConfig::is_preserving_verification_constraints() && from_class->is_instance_klass()) {
+ InstanceKlass* ik = InstanceKlass::cast(from_class);
+ SystemDictionaryShared::add_old_verification_constraint(THREAD, ik, h_name);
+ }
+#endif
+
return result;
JVM_END
diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp
index 0c604205939..e0eafbc416b 100644
--- a/src/hotspot/share/runtime/mutexLocker.cpp
+++ b/src/hotspot/share/runtime/mutexLocker.cpp
@@ -303,11 +303,11 @@ void mutex_init() {
#endif
MUTEX_DEFN(DumpTimeTable_lock , PaddedMutex , nosafepoint);
MUTEX_DEFN(CDSLambda_lock , PaddedMutex , nosafepoint);
- MUTEX_DEFN(DumpRegion_lock , PaddedMutex , nosafepoint);
+ MUTEX_DEFL(DumpRegion_lock , PaddedMutex , DumpTimeTable_lock);
MUTEX_DEFN(ClassListFile_lock , PaddedMutex , nosafepoint);
MUTEX_DEFN(UnregisteredClassesTable_lock , PaddedMutex , nosafepoint-1);
MUTEX_DEFN(LambdaFormInvokers_lock , PaddedMutex , safepoint);
- MUTEX_DEFN(ScratchObjects_lock , PaddedMutex , nosafepoint-1); // Holds DumpTimeTable_lock
+ MUTEX_DEFL(ScratchObjects_lock , PaddedMutex , DumpTimeTable_lock);
MUTEX_DEFN(FinalImageRecipes_lock , PaddedMutex , nosafepoint);
#endif // INCLUDE_CDS
MUTEX_DEFN(Bootclasspath_lock , PaddedMutex , nosafepoint);
diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups
index eeb5110b077..3af6548fe33 100644
--- a/test/hotspot/jtreg/TEST.groups
+++ b/test/hotspot/jtreg/TEST.groups
@@ -526,6 +526,7 @@ hotspot_aot_classlinking = \
-runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java \
-runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java \
-runtime/cds/appcds/CDSandJFR.java \
+ -runtime/cds/appcds/LambdaContainsOldInf.java \
-runtime/cds/appcds/customLoader/CustomClassListDump.java \
-runtime/cds/appcds/customLoader/HelloCustom_JFR.java \
-runtime/cds/appcds/customLoader/OldClassAndInf.java \
@@ -533,14 +534,17 @@ hotspot_aot_classlinking = \
-runtime/cds/appcds/customLoader/ParallelTestSingleFP.java \
-runtime/cds/appcds/customLoader/SameNameInTwoLoadersTest.java \
-runtime/cds/appcds/DumpClassListWithLF.java \
- -runtime/cds/appcds/dynamicArchive/ModulePath.java \
+ -runtime/cds/appcds/dynamicArchive/LambdaContainsOldInf.java \
-runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java \
-runtime/cds/appcds/dynamicArchive/LambdaForOldInfInBaseArchive.java \
-runtime/cds/appcds/dynamicArchive/LambdaInBaseArchive.java \
-runtime/cds/appcds/dynamicArchive/LambdasInTwoArchives.java \
+ -runtime/cds/appcds/dynamicArchive/ModulePath.java \
+ -runtime/cds/appcds/dynamicArchive/NestHostOldInf.java \
-runtime/cds/appcds/dynamicArchive/OldClassAndInf.java \
-runtime/cds/appcds/dynamicArchive/OldClassInBaseArchive.java \
-runtime/cds/appcds/dynamicArchive/OldClassVerifierTrouble.java \
+ -runtime/cds/appcds/dynamicArchive/RedefineCallerClassTest.java \
-runtime/cds/appcds/HelloExtTest.java \
-runtime/cds/appcds/javaldr/ExceptionDuringDumpAtObjectsInitPhase.java \
-runtime/cds/appcds/javaldr/GCDuringDump.java \
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java
index f50a2d1f905..9a9524eb2f1 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java
@@ -99,7 +99,6 @@ public class ExcludedClasses {
if (runMode == RunMode.ASSEMBLY) {
out.shouldNotMatch("aot,resolve.*archived field.*TestApp.Foo => TestApp.Foo.ShouldBeExcluded.f:I");
} else if (runMode == RunMode.PRODUCTION) {
- out.shouldContain("check_verification_constraint: TestApp$Foo$Taz: TestApp$Foo$ShouldBeExcludedChild must be subclass of TestApp$Foo$ShouldBeExcluded");
out.shouldContain("jdk.jfr.Event source: jrt:/jdk.jfr");
out.shouldMatch("TestApp[$]Foo[$]ShouldBeExcluded source: .*/app.jar");
out.shouldMatch("TestApp[$]Foo[$]ShouldBeExcludedChild source: .*/app.jar");
@@ -259,14 +258,9 @@ class TestApp {
static class Taz {
static ShouldBeExcluded m() {
- // When verifying this method, we need to check the constraint that
- // ShouldBeExcluded must be a supertype of ShouldBeExcludedChild. This information
- // is checked by SystemDictionaryShared::check_verification_constraints() when the Taz
- // class is linked during the production run.
- //
- // Because ShouldBeExcluded is excluded from the AOT archive, it must be loaded
- // dynamically from app.jar inside SystemDictionaryShared::check_verification_constraints().
- // This must happen after the app class loader has been fully restored from the AOT cache.
+ // Taz should be excluded from the AOT cache because it has a verification constraint that
+ // "ShouldBeExcludedChild must be a subtype of ShouldBeExcluded", but ShouldBeExcluded is
+ // excluded from the AOT cache.
return new ShouldBeExcludedChild();
}
static void hotSpot4() {
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldA.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldA.jasm
new file mode 100644
index 00000000000..e0362eb0649
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldA.jasm
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ *
+ */
+
+super public class OldA
+ version 49:0
+{
+
+
+public Method "":"()V"
+ stack 1 locals 1
+{
+ aload_0;
+ invokespecial Method java/lang/Object."":"()V";
+ return;
+}
+
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport.java
new file mode 100644
index 00000000000..42161b469bf
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport.java
@@ -0,0 +1,162 @@
+/*
+ * 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 Store old classes linked state in AOT cache as long as their verification constraints are not excluded.
+ * @bug 8317269
+ * @requires vm.cds.supports.aot.class.linking
+ * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
+ * @build OldClass OldA OldClassWithVerifierConstraints OldClassWithExcludedVerifierConstraints
+ * @build OldClassSupport
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
+ * AppUsesOldClass MyIntf OldClass OldA NewB MyEvent MyEvent2
+ * OldClassWithVerifierConstraints
+ * OldClassWithExcludedVerifierConstraints
+ * NewClassWithExcludedVerifierConstraints
+ * @run driver OldClassSupport
+ */
+
+import jdk.jfr.Event;
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class OldClassSupport {
+ static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+ static final String mainClass = "AppUsesOldClass";
+
+ public static void main(String[] args) throws Exception {
+ Tester tester = new Tester();
+ tester.run(new String[] {"AOT", "--two-step-training"} );
+ }
+
+ static class Tester extends CDSAppTester {
+ public Tester() {
+ super(mainClass);
+ }
+
+ @Override
+ public String classpath(RunMode runMode) {
+ return appJar;
+ }
+
+ @Override
+ public String[] vmArgs(RunMode runMode) {
+ return new String[] {
+ "-Xlog:aot+class=debug",
+ "-Xlog:aot+resolve=trace",
+ };
+ }
+
+ @Override
+ public String[] appCommandLine(RunMode runMode) {
+ return new String[] {"-Xlog:cds+class=debug", mainClass};
+ }
+
+ @Override
+ public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+ Class[] included = {
+ OldClass.class,
+ OldA.class,
+ NewB.class,
+ OldClassWithVerifierConstraints.class,
+ };
+
+ Class[] excluded = {
+ OldClassWithExcludedVerifierConstraints.class,
+ NewClassWithExcludedVerifierConstraints.class,
+ };
+
+
+ if (runMode == RunMode.TRAINING) {
+ shouldInclude(out, false, included);
+ shouldNotInclude(out, excluded);
+ shouldSkip(out, excluded);
+ } else if (runMode == RunMode.ASSEMBLY) {
+ shouldInclude(out, true, included);
+ shouldNotInclude(out, excluded);
+ }
+ }
+ }
+
+ static void shouldInclude(OutputAnalyzer out, boolean linked, Class[] classes) {
+ for (Class c : classes) {
+ out.shouldMatch("aot,class.* = 0x.* app *" + c.getName() + (linked ? " .*aot-linked" : ""));
+ }
+ }
+
+ static void shouldNotInclude(OutputAnalyzer out, Class[] classes) {
+ for (Class c : classes) {
+ out.shouldNotMatch("aot,class.* = 0x.* app *" + c.getName());
+ }
+ }
+
+ static void shouldSkip(OutputAnalyzer out, Class[] classes) {
+ for (Class c : classes) {
+ out.shouldMatch("Skipping " + c.getName() + ": verification constraint .* is excluded");
+ }
+ }
+}
+
+class AppUsesOldClass {
+ public static void main(String args[]) {
+ System.out.println("Old Class Instance: " + new OldClass());
+
+ System.out.println(get_OldA_from_NewB());
+ System.out.println(OldClassWithVerifierConstraints.get_OldA_from_NewB());
+ System.out.println(OldClassWithExcludedVerifierConstraints.get_Event_from_MyEvent());
+ System.out.println(NewClassWithExcludedVerifierConstraints.get_MyEvent_from_MyEvent2());
+ System.out.println(new MyEvent());
+
+ // OldClassWithExcludedVerifierConstraints should still be excluded even it has been used
+ // in a lambda expression during the training run.
+ run((OldClassWithExcludedVerifierConstraints x) -> {
+ System.out.println(x);
+ });
+ }
+
+ static OldA get_OldA_from_NewB() {
+ return new NewB();
+ }
+
+ static void run(MyIntf intf) {
+ intf.function(new OldClassWithExcludedVerifierConstraints());
+ }
+}
+
+interface MyIntf {
+ public void function(OldClassWithExcludedVerifierConstraints x);
+}
+
+class NewB extends OldA {}
+
+class MyEvent extends Event {}
+class MyEvent2 extends MyEvent {}
+
+class NewClassWithExcludedVerifierConstraints {
+ static MyEvent get_MyEvent_from_MyEvent2() {
+ return new MyEvent2();
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassWithExcludedVerifierConstraints.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassWithExcludedVerifierConstraints.jasm
new file mode 100644
index 00000000000..0c0556bf122
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassWithExcludedVerifierConstraints.jasm
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ *
+ */
+
+// This old class has a verification constraint that "MyEvent must be a subtype of Event". However,
+// Event and all of its subtypes are excluded from the AOT cache, so this class must also be excluded.
+
+super public class OldClassWithExcludedVerifierConstraints
+ version 49:0
+{
+
+
+public Method "":"()V"
+ stack 1 locals 1
+{
+ aload_0;
+ invokespecial Method java/lang/Object."":"()V";
+ return;
+}
+
+static Method get_Event_from_MyEvent:"()Ljdk/jfr/Event;"
+ stack 2 locals 0
+{
+ new class MyEvent;
+ dup;
+ invokespecial Method MyEvent."":"()V";
+ areturn;
+}
+
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassWithVerifierConstraints.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassWithVerifierConstraints.jasm
new file mode 100644
index 00000000000..946c51050a3
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassWithVerifierConstraints.jasm
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ *
+ */
+
+// This old class as a verification constraint that "NewB must be a subtype of OldA". Since both
+// OldA and NewB are not excluded, then this class should be cached in aot-linked state.
+
+super public class OldClassWithVerifierConstraints
+ version 49:0
+{
+
+
+public Method "":"()V"
+ stack 1 locals 1
+{
+ aload_0;
+ invokespecial Method java/lang/Object."":"()V";
+ return;
+}
+
+static Method get_OldA_from_NewB:"()LOldA;"
+ stack 2 locals 0
+{
+ new class NewB;
+ dup;
+ invokespecial Method NewB."":"()V";
+ areturn;
+}
+
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVerification.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVerification.java
new file mode 100644
index 00000000000..050f7d28585
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVerification.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * 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 8317269
+ * @requires vm.cds
+ * @requires vm.cds.supports.aot.class.linking
+ * @summary Test for verification of classes that are aot-linked
+ * @library /test/jdk/lib/testlibrary
+ * /test/lib
+ * /test/hotspot/jtreg/runtime/cds/appcds
+ * /test/hotspot/jtreg/runtime/cds/appcds/test-classes
+ * @build GoodOldClass
+ * BadOldClass BadOldClass2 BadOldClass3 BadOldClass4
+ * BadNewClass BadNewClass2 BadNewClass3 BadNewClass4
+ * @build AOTClassLinkingVerification
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app1.jar
+ * AOTClassLinkingVerificationApp
+ * Unlinked UnlinkedSuper
+ * BadOldClass
+ * BadOldClass2
+ * BadOldClass3
+ * BadOldClass4
+ * BadNewClass
+ * BadNewClass2
+ * BadNewClass3
+ * BadNewClass4
+ * GoodOldClass Vehicle Car
+ * Util
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app2.jar
+ * Foo NotFoo
+ * UnlinkedSub
+ * @run driver AOTClassLinkingVerification
+ */
+
+import java.io.File;
+import java.lang.invoke.MethodHandles;
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.whitebox.WhiteBox;
+
+public class AOTClassLinkingVerification {
+ static final String app1Jar = ClassFileInstaller.getJarPath("app1.jar");
+ static final String app2Jar = ClassFileInstaller.getJarPath("app2.jar");
+ static final String wbJar = TestCommon.getTestJar("WhiteBox.jar");
+ static final String bootAppendWhiteBox = "-Xbootclasspath/a:" + wbJar;
+ static final String mainClass = AOTClassLinkingVerificationApp.class.getName();
+
+ static class Tester extends CDSAppTester {
+ public Tester(String testName) {
+ super(testName);
+ }
+
+ @Override
+ public String[] vmArgs(RunMode runMode) {
+ if (runMode == RunMode.TRAINING ||
+ runMode == RunMode.ASSEMBLY) {
+ return new String[] {
+ "-XX:+AOTClassLinking", "-Xlog:cds+class=debug", bootAppendWhiteBox,
+ };
+ } else {
+ return new String[] {
+ "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", bootAppendWhiteBox,
+ };
+ }
+ }
+
+ @Override
+ public String classpath(RunMode runMode) {
+ if (runMode == RunMode.TRAINING ||
+ runMode == RunMode.ASSEMBLY) {
+ return app1Jar;
+ } else {
+ return app1Jar + File.pathSeparator + app2Jar;
+ }
+ }
+
+ @Override
+ public String[] appCommandLine(RunMode runMode) {
+ if (runMode == RunMode.TRAINING ||
+ runMode == RunMode.ASSEMBLY) {
+ return new String[] {
+ "AOTClassLinkingVerificationApp", app1Jar, "ASSEMBLY"
+ };
+ } else {
+ return new String[] {
+ "AOTClassLinkingVerificationApp", app1Jar, "PRODUCTION"
+ };
+ }
+ }
+
+ @Override
+ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
+ if (runMode == RunMode.TRAINING) {
+ out.shouldContain("Preload Warning: Verification failed for BadNewClass");
+ out.shouldContain("Preload Warning: Verification failed for BadNewClass2");
+ out.shouldContain("Preload Warning: Verification failed for BadNewClass3");
+ out.shouldContain("Preload Warning: Verification failed for BadNewClass4");
+ out.shouldContain("Preload Warning: Verification failed for BadOldClass");
+ out.shouldContain("Preload Warning: Verification failed for BadOldClass2");
+ out.shouldContain("Preload Warning: Verification failed for BadOldClass3");
+ out.shouldContain("Preload Warning: Verification failed for BadOldClass4");
+ out.shouldContain("Preload Warning: Verification failed for Unlinked");
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ // Dump without app2.jar so:
+ // - Unlinked can be resolved, but UnlinkedSuper UnlinkedSub cannot be resolved,
+ // so Unlinked cannot be verified at dump time.
+ // - BadOldClass2 can be resolved, but Foo and NotFoo cannot be resolved,
+ // so BadOldClass2 cannot be verified at dump time.
+ // - BadNewClass2 can be resolved, but Foo and NotFoo cannot be resolved,
+ // so BadNewClass2 cannot be verified at dump time.
+ Tester t1 = new Tester("verification-aot-linked-classes");
+ t1.run("AOT");
+ }
+}
+
+class AOTClassLinkingVerificationApp {
+ static WhiteBox wb = WhiteBox.getWhiteBox();
+ static ClassLoader classLoader = AOTClassLinkingVerificationApp.class.getClassLoader();
+ static File app1Jar;
+ static boolean isProduction;
+ public static void main(String[] args) throws Exception {
+ app1Jar = new File(args[0]);
+ isProduction = args[1].equals("PRODUCTION");
+ if (isProduction) {
+ assertNotShared(UnlinkedSub.class);
+ assertShared(UnlinkedSuper.class);
+ assertNotShared(Unlinked.class); // failed verification during dump time
+ assertNotShared(Foo.class);
+ assertNotShared(NotFoo.class);
+ }
+ String s = null;
+ try {
+ s = Unlinked.doit();
+ } catch (NoClassDefFoundError ncdfe) {
+ // UnlinkedSub is in app2Jar but only app1Jar is used during training
+ // and assembly phases. So NoClassDefFoundError is expected during
+ // during training and assembly phases.
+ if (isProduction) {
+ throw ncdfe;
+ }
+ }
+ if (isProduction && !s.equals("heyhey")) {
+ throw new RuntimeException("Unlinked.doit() returns wrong result: " + s);
+ }
+
+ // ===============================================================================
+
+ checkSimpleBadClass("BadOldClass");
+
+ Class cls_BadOldClass2 = Class.forName("BadOldClass2", false, classLoader);
+ if (isProduction) {
+ assertNotShared(cls_BadOldClass2); // failed verification during dump time
+ }
+ try {
+ cls_BadOldClass2.newInstance();
+ throw new RuntimeException("BadOldClass2 cannot be verified");
+ } catch (NoClassDefFoundError ncdfe) {
+ // BadOldClass2 loads Foo and NotFoo which is in app2Jar which is used
+ // only in production run.
+ if (isProduction) {
+ throw ncdfe;
+ }
+ } catch (VerifyError expected) {}
+
+ checkSimpleBadClass("BadOldClass3");
+ checkSimpleBadClass("BadOldClass4");
+
+ // ===============================================================================
+
+ checkSimpleBadClass("BadNewClass");
+
+ Class cls_BadNewClass2 = Class.forName("BadNewClass2", false, classLoader);
+ if (isProduction) {
+ assertNotShared(cls_BadNewClass2); // failed verification during dump time
+ }
+ try {
+ cls_BadNewClass2.newInstance();
+ throw new RuntimeException("BadNewClass2 cannot be verified");
+ } catch (NoClassDefFoundError ncdfe) {
+ // BadNewClass2 loads Foo and NotFoo which is in app2Jar which is used
+ // only in production run.
+ if (isProduction) {
+ throw ncdfe;
+ }
+ } catch (VerifyError expected) {}
+
+ checkSimpleBadClass("BadNewClass3");
+ checkSimpleBadClass("BadNewClass4");
+
+ // ===============================================================================
+
+ if (isProduction) {
+ assertAlreadyLoaded("Vehicle");
+ assertAlreadyLoaded("Car");
+ assertAlreadyLoaded("GoodOldClass");
+
+ assertShared(GoodOldClass.class);
+ assertShared(Vehicle.class);
+ assertShared(Car.class);
+ }
+
+ GoodOldClass.doit(); // Should not fail
+ }
+
+ static void checkSimpleBadClass(String className) throws Exception {
+ Class cls = Class.forName(className, false, classLoader);
+ if (isProduction) {
+ assertNotShared(cls); // failed verification during dump time
+ }
+ try {
+ cls.newInstance();
+ throw new RuntimeException(className + " should not pass verification");
+ } catch (VerifyError expected) {}
+ }
+
+ static void assertShared(Class c) {
+ if (!wb.isSharedClass(c)) {
+ throw new RuntimeException("wb.isSharedClass(" + c.getName() + ") should be true");
+ }
+ }
+
+ static void assertNotShared(Class c) {
+ if (wb.isSharedClass(c)) {
+ throw new RuntimeException("wb.isSharedClass(" + c.getName() + ") should be false");
+ }
+ }
+
+ static void assertAlreadyLoaded(String className) throws Exception {
+ byte[] data = Util.getClassFileFromJar(app1Jar, className);
+ try {
+ MethodHandles.lookup().defineClass(data);
+ } catch (LinkageError e) {
+ if (e.getMessage().contains("duplicate class definition for " + className)) {
+ return;
+ } else {
+ throw e;
+ }
+ }
+ throw new RuntimeException(className + " must have already been loaded");
+ }
+}
+
+
+class Unlinked {
+ static String doit() {
+ UnlinkedSuper sup = new UnlinkedSub();
+ return sup.doit();
+ }
+}
+
+abstract class UnlinkedSuper {
+ abstract String doit();
+}
+
+class UnlinkedSub extends UnlinkedSuper {
+ String doit() {
+ return "heyhey";
+ }
+}
+
+class Foo {}
+class NotFoo {}
+
+class Vehicle {}
+class Car extends Vehicle {}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass.jasm
new file mode 100644
index 00000000000..cf71d209819
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass.jasm
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ */
+
+super public class BadNewClass
+ version 52:0
+{
+
+public Method "":"()V"
+ stack 1 locals 1
+{
+ aload_0;
+ invokespecial Method java/lang/Object."":"()V";
+ return;
+}
+
+ /*
+ * The following method tries to return an Object as a String.
+ * Verifier should fail.
+ */
+public Method doit:"()Ljava/lang/String;"
+ stack 2 locals 1
+{
+ new class java/lang/Object;
+ dup;
+ invokespecial Method java/lang/Object."":"()V";
+ astore_0;
+ aload_0;
+ areturn; // tries to return an Object as a String
+}
+
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass2.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass2.jasm
new file mode 100644
index 00000000000..c243d583484
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass2.jasm
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ */
+
+super public class BadNewClass2
+ version 52:0
+{
+
+public Method "":"()V"
+ stack 1 locals 1
+{
+ aload_0;
+ invokespecial Method java/lang/Object."":"()V";
+ return;
+}
+
+ /*
+ * The following method tries to return a NotFoo as a Foo.
+ * Verifier should fail.
+ */
+public Method doit:"()LFoo;"
+ stack 2 locals 1
+{
+ new class NotFoo;
+ dup;
+ invokespecial Method NotFoo."":"()V";
+ astore_0;
+ aload_0;
+ areturn; // tries to return a NotFoo as a Foo
+}
+
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass3.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass3.jasm
new file mode 100644
index 00000000000..afce8f76ed8
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass3.jasm
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ *
+ */
+
+super public class BadNewClass3
+ version 52:0
+{
+
+public Method "":"()V"
+ stack 1 locals 1
+{
+ aload_0;
+ invokespecial Method java/lang/Object."":"()V";
+ return;
+}
+
+ /*
+ * The following method tries to return a String[][] as an Integer[].
+ * Verifier should fail.
+ *
+ * Note: the arrays must have different number of dimensions, or else
+ * the new verifier will just check the "bottom" classes. I.e., String and Integer
+ */
+public Method doit:"()[Ljava/lang/Integer;"
+ stack 2 locals 1
+{
+ iconst_1;
+ iconst_1;
+ multianewarray class "[[Ljava/lang/String;", 2;
+ areturn;
+}
+
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass4.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass4.jasm
new file mode 100644
index 00000000000..afebe3f1f8e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass4.jasm
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ *
+ */
+
+super public class BadNewClass4
+ version 52:0
+{
+
+public Method "":"()V"
+ stack 1 locals 1
+{
+ aload_0;
+ invokespecial Method java/lang/Object."":"()V";
+ return;
+}
+
+ /*
+ * The following method tries to return a String[][] as an Integer[][].
+ * Verifier should fail.
+ *
+ * Note: the new verifier looks up the Integer and String types,
+ * not the array types.
+ */
+public Method doit:"()[[Ljava/lang/Integer;"
+ stack 2 locals 1
+{
+ iconst_1;
+ iconst_1;
+ multianewarray class "[[Ljava/lang/String;", 2;
+ areturn;
+}
+
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass.jasm
new file mode 100644
index 00000000000..adc6a50d4ba
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass.jasm
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ */
+
+super public class BadOldClass
+ version 49:0
+{
+
+public Method "":"()V"
+ stack 1 locals 1
+{
+ aload_0;
+ invokespecial Method java/lang/Object."":"()V";
+ return;
+}
+
+ /*
+ * The following method tries to return an Object as a String.
+ * Verifier should fail.
+ */
+public Method doit:"()Ljava/lang/String;"
+ stack 2 locals 1
+{
+ new class java/lang/Object;
+ dup;
+ invokespecial Method java/lang/Object."":"()V";
+ astore_0;
+ aload_0;
+ areturn; // tries to return an Object as a String
+}
+
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass2.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass2.jasm
new file mode 100644
index 00000000000..1808a019ace
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass2.jasm
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ */
+
+super public class BadOldClass2
+ version 49:0
+{
+
+public Method "":"()V"
+ stack 1 locals 1
+{
+ aload_0;
+ invokespecial Method java/lang/Object."":"()V";
+ return;
+}
+
+ /*
+ * The following method tries to return a NotFoo as a Foo.
+ * Verifier should fail.
+ */
+public Method doit:"()LFoo;"
+ stack 2 locals 1
+{
+ new class NotFoo;
+ dup;
+ invokespecial Method NotFoo."":"()V";
+ astore_0;
+ aload_0;
+ areturn; // tries to return a NotFoo as a Foo
+}
+
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass3.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass3.jasm
new file mode 100644
index 00000000000..6e943cf5afc
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass3.jasm
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ *
+ */
+
+super public class BadOldClass3
+ version 49:0
+{
+
+public Method "":"()V"
+ stack 1 locals 1
+{
+ aload_0;
+ invokespecial Method java/lang/Object."":"()V";
+ return;
+}
+
+ /*
+ * The following method tries to return a String[][] as an Integer[].
+ * Verifier should fail.
+ *
+ * Note: the arrays have different number of dimensions. The old verifier
+ * rejects this immediately without looking up the String/Integer types.
+ */
+public Method doit:"()[Ljava/lang/Integer;"
+ stack 2 locals 1
+{
+ iconst_1;
+ iconst_1;
+ multianewarray class "[[Ljava/lang/String;", 2;
+ areturn;
+}
+
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass4.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass4.jasm
new file mode 100644
index 00000000000..56f2a8d299a
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass4.jasm
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ *
+ */
+
+super public class BadOldClass4
+ version 49:0
+{
+
+public Method "":"()V"
+ stack 1 locals 1
+{
+ aload_0;
+ invokespecial Method java/lang/Object."":"()V";
+ return;
+}
+
+ /*
+ * The following method tries to return a String[][] as an Integer[][].
+ * Verifier should fail.
+ *
+ * Note: the old verifier looks up the Integer and String types,
+ * not the array types.
+ */
+public Method doit:"()[[Ljava/lang/Integer;"
+ stack 2 locals 1
+{
+ iconst_1;
+ iconst_1;
+ multianewarray class "[[Ljava/lang/String;", 2;
+ areturn;
+}
+
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java
index 0f7707edae3..e1f5f548593 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java
@@ -147,7 +147,7 @@ public class BulkLoaderTest {
@Override
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
if (isAOTWorkflow() && runMode == RunMode.TRAINING) {
- out.shouldContain("Skipping BadOldClassA: Unlinked class not supported by AOTConfiguration");
+ out.shouldContain("Skipping BadOldClassA: Failed verification");
out.shouldContain("Skipping SimpleCusty: Duplicated unregistered class");
}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GoodOldClass.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GoodOldClass.jasm
new file mode 100644
index 00000000000..92a79380d93
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GoodOldClass.jasm
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+super public class GoodOldClass
+ version 49:0
+{
+
+public Method "":"()V"
+ stack 1 locals 1
+{
+ aload_0;
+ invokespecial Method java/lang/Object."":"()V";
+ return;
+}
+
+
+public static Method doit:"()LVehicle;"
+ stack 2 locals 1
+{
+ new class Car;
+ dup;
+ invokespecial Method Car."":"()V";
+ astore_0;
+ aload_0;
+ areturn; // tries to return a Car as a Vehicle
+}
+
+}
From 85996572b61e789d7e45bd26b23d233a0a41e158 Mon Sep 17 00:00:00 2001
From: Chen Liang
Date: Wed, 10 Sep 2025 21:23:45 +0000
Subject: [PATCH 031/120] 8365676: javac incorrectly allows calling interface
static method via type variable
Co-authored-by: Maurizio Cimadamore
Reviewed-by: vromero
---
.../com/sun/tools/javac/comp/Attr.java | 16 ++++++++++++---
.../generics/typevars/8365676/T8365676.java | 20 +++++++++++++++++++
.../generics/typevars/8365676/T8365676.out | 4 ++++
3 files changed, 37 insertions(+), 3 deletions(-)
create mode 100644 test/langtools/tools/javac/generics/typevars/8365676/T8365676.java
create mode 100644 test/langtools/tools/javac/generics/typevars/8365676/T8365676.out
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
index 9f32e7f6186..f780df025bd 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -4569,9 +4569,19 @@ public class Attr extends JCTree.Visitor {
log.error(pos, Errors.TypeVarCantBeDeref);
return syms.errSymbol;
} else {
- Symbol sym2 = (sym.flags() & Flags.PRIVATE) != 0 ?
- rs.new AccessError(env, site, sym) :
- sym;
+ // JLS 4.9 specifies the members are derived by inheritance.
+ // We skip inducing a whole class by filtering members that
+ // can never be inherited:
+ Symbol sym2;
+ if (sym.isPrivate()) {
+ // Private members
+ sym2 = rs.new AccessError(env, site, sym);
+ } else if (sym.owner.isInterface() && sym.kind == MTH && (sym.flags() & STATIC) != 0) {
+ // Interface static methods
+ sym2 = rs.new SymbolNotFoundError(ABSENT_MTH);
+ } else {
+ sym2 = sym;
+ }
rs.accessBase(sym2, pos, location, site, name, true);
return sym;
}
diff --git a/test/langtools/tools/javac/generics/typevars/8365676/T8365676.java b/test/langtools/tools/javac/generics/typevars/8365676/T8365676.java
new file mode 100644
index 00000000000..f6b992cb47b
--- /dev/null
+++ b/test/langtools/tools/javac/generics/typevars/8365676/T8365676.java
@@ -0,0 +1,20 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8365676
+ * @summary Interface static methods should not be inherited by type variables
+ * @compile/fail/ref=T8365676.out -XDrawDiagnostics T8365676.java
+ */
+
+import java.text.Collator;
+import java.util.Comparator;
+
+class T8365676 {
+ // T and P should have equivalent members
+ , P extends Object & Comparator