From 3a93daf189213d4c8af5d57d7af1e832401331b6 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 18 Mar 2026 09:07:14 +0000 Subject: [PATCH 001/160] 8373369: [REDO] Remove ThreadLocalAllocBuffer::_reserve_for_allocation_prefetch Reviewed-by: mdoerr, kvn, tschatzl --- src/hotspot/cpu/ppc/ppc.ad | 29 ------- .../gc/shared/threadLocalAllocBuffer.cpp | 28 +------ .../gc/shared/threadLocalAllocBuffer.hpp | 1 - src/hotspot/share/opto/macro.cpp | 3 +- src/hotspot/share/runtime/vmStructs.cpp | 1 - .../runtime/ThreadLocalAllocBuffer.java | 3 +- .../classes/sun/jvm/hotspot/runtime/VM.java | 7 -- test/hotspot/jtreg/ProblemList.txt | 3 +- .../TestAllocatePrefetchStyleLargeFlags.java | 79 +++++++++++++++++++ 9 files changed, 84 insertions(+), 70 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyleLargeFlags.java diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 21fc12742ef..01c290f9b04 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -6327,36 +6327,8 @@ instruct loadConD_Ex(regD dst, immD src) %{ // Prefetch instructions. // Must be safe to execute with invalid address (cannot fault). -// Special prefetch versions which use the dcbz instruction. -instruct prefetch_alloc_zero(indirectMemory mem, iRegLsrc src) %{ - match(PrefetchAllocation (AddP mem src)); - predicate(AllocatePrefetchStyle == 3); - ins_cost(MEMORY_REF_COST); - - format %{ "PREFETCH $mem, 2, $src \t// Prefetch write-many with zero" %} - size(4); - ins_encode %{ - __ dcbz($src$$Register, $mem$$base$$Register); - %} - ins_pipe(pipe_class_memory); -%} - -instruct prefetch_alloc_zero_no_offset(indirectMemory mem) %{ - match(PrefetchAllocation mem); - predicate(AllocatePrefetchStyle == 3); - ins_cost(MEMORY_REF_COST); - - format %{ "PREFETCH $mem, 2 \t// Prefetch write-many with zero" %} - size(4); - ins_encode %{ - __ dcbz($mem$$base$$Register); - %} - ins_pipe(pipe_class_memory); -%} - instruct prefetch_alloc(indirectMemory mem, iRegLsrc src) %{ match(PrefetchAllocation (AddP mem src)); - predicate(AllocatePrefetchStyle != 3); ins_cost(MEMORY_REF_COST); format %{ "PREFETCH $mem, 2, $src \t// Prefetch write-many" %} @@ -6369,7 +6341,6 @@ instruct prefetch_alloc(indirectMemory mem, iRegLsrc src) %{ instruct prefetch_alloc_no_offset(indirectMemory mem) %{ match(PrefetchAllocation mem); - predicate(AllocatePrefetchStyle != 3); ins_cost(MEMORY_REF_COST); format %{ "PREFETCH $mem, 2 \t// Prefetch write-many" %} diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp index 61cf73fe04a..4c160929f5a 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp @@ -37,7 +37,6 @@ #include "utilities/copy.hpp" size_t ThreadLocalAllocBuffer::_max_size = 0; -int ThreadLocalAllocBuffer::_reserve_for_allocation_prefetch = 0; unsigned int ThreadLocalAllocBuffer::_target_refills = 0; ThreadLocalAllocBuffer::ThreadLocalAllocBuffer() : @@ -225,30 +224,6 @@ void ThreadLocalAllocBuffer::startup_initialization() { // abort during VM initialization. _target_refills = MAX2(_target_refills, 2U); -#ifdef COMPILER2 - // If the C2 compiler is present, extra space is needed at the end of - // TLABs, otherwise prefetching instructions generated by the C2 - // compiler will fault (due to accessing memory outside of heap). - // The amount of space is the max of the number of lines to - // prefetch for array and for instance allocations. (Extra space must be - // reserved to accommodate both types of allocations.) - // - // Only SPARC-specific BIS instructions are known to fault. (Those - // instructions are generated if AllocatePrefetchStyle==3 and - // AllocatePrefetchInstr==1). To be on the safe side, however, - // extra space is reserved for all combinations of - // AllocatePrefetchStyle and AllocatePrefetchInstr. - // - // If the C2 compiler is not present, no space is reserved. - - // +1 for rounding up to next cache line, +1 to be safe - if (CompilerConfig::is_c2_or_jvmci_compiler_enabled()) { - int lines = MAX2(AllocatePrefetchLines, AllocateInstancePrefetchLines) + 2; - _reserve_for_allocation_prefetch = (AllocatePrefetchDistance + AllocatePrefetchStepSize * lines) / - (int)HeapWordSize; - } -#endif - // During jvm startup, the main thread is initialized // before the heap is initialized. So reinitialize it now. guarantee(Thread::current()->is_Java_thread(), "tlab initialization thread not Java thread"); @@ -454,8 +429,7 @@ void ThreadLocalAllocStats::publish() { } size_t ThreadLocalAllocBuffer::end_reserve() { - size_t reserve_size = CollectedHeap::lab_alignment_reserve(); - return MAX2(reserve_size, (size_t)_reserve_for_allocation_prefetch); + return CollectedHeap::lab_alignment_reserve(); } size_t ThreadLocalAllocBuffer::estimated_used_bytes() const { diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp index 25d9bf00eac..eb664d13961 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp @@ -57,7 +57,6 @@ private: uint64_t _allocated_before_last_gc; // total bytes allocated up until the last gc static size_t _max_size; // maximum size of any TLAB - static int _reserve_for_allocation_prefetch; // Reserve at the end of the TLAB static unsigned _target_refills; // expected number of refills between GCs unsigned _number_of_refills; diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index b9045ddcf4c..6995cacd1b0 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -1917,8 +1917,7 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, transform_later(cache_adr); cache_adr = new CastP2XNode(needgc_false, cache_adr); transform_later(cache_adr); - // Address is aligned to execute prefetch to the beginning of cache line size - // (it is important when BIS instruction is used on SPARC as prefetch). + // Address is aligned to execute prefetch to the beginning of cache line size. Node* mask = _igvn.MakeConX(~(intptr_t)(step_size-1)); cache_adr = new AndXNode(cache_adr, mask); transform_later(cache_adr); diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 9796b990b2a..93e0ff2f3b6 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -335,7 +335,6 @@ nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \ - static_field(ThreadLocalAllocBuffer, _reserve_for_allocation_prefetch, int) \ static_field(ThreadLocalAllocBuffer, _target_refills, unsigned) \ nonstatic_field(ThreadLocalAllocBuffer, _number_of_refills, unsigned) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste, unsigned) \ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java index 11f03a6003e..683e4b67935 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadLocalAllocBuffer.java @@ -76,10 +76,9 @@ public class ThreadLocalAllocBuffer extends VMObject { private long endReserve() { long labAlignmentReserve = VM.getVM().getLabAlignmentReserve(); - long reserveForAllocationPrefetch = VM.getVM().getReserveForAllocationPrefetch(); long heapWordSize = VM.getVM().getHeapWordSize(); - return Math.max(labAlignmentReserve, reserveForAllocationPrefetch) * heapWordSize; + return labAlignmentReserve * heapWordSize; } /** Support for iteration over heap -- not sure how this will diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java index dc27a4fc59e..1607563150a 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java @@ -123,7 +123,6 @@ public class VM { private int invocationEntryBCI; private ReversePtrs revPtrs; private VMRegImpl vmregImpl; - private int reserveForAllocationPrefetch; private int labAlignmentReserve; // System.getProperties from debuggee VM @@ -447,8 +446,6 @@ public class VM { boolType = (CIntegerType) db.lookupType("bool"); Type threadLocalAllocBuffer = db.lookupType("ThreadLocalAllocBuffer"); - CIntegerField reserveForAllocationPrefetchField = threadLocalAllocBuffer.getCIntegerField("_reserve_for_allocation_prefetch"); - reserveForAllocationPrefetch = (int)reserveForAllocationPrefetchField.getCInteger(intType); Type collectedHeap = db.lookupType("CollectedHeap"); CIntegerField labAlignmentReserveField = collectedHeap.getCIntegerField("_lab_alignment_reserve"); @@ -915,10 +912,6 @@ public class VM { return vmInternalInfo; } - public int getReserveForAllocationPrefetch() { - return reserveForAllocationPrefetch; - } - public int getLabAlignmentReserve() { return labAlignmentReserve; } diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index ba63f775223..60d2ffa722f 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -83,6 +83,8 @@ compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 compiler/longcountedloops/TestLoopNestTooManyTraps.java 8376591 generic-all +compiler/unsafe/AlignmentGapAccess.java 8373487 generic-all + ############################################################################# # :hotspot_gc @@ -196,4 +198,3 @@ vmTestbase/nsk/monitoring/ThreadMXBean/findMonitorDeadlockedThreads/find006/Test # in either implementation or test code. ############################################################################# - diff --git a/test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyleLargeFlags.java b/test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyleLargeFlags.java new file mode 100644 index 00000000000..b455107d5f6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestAllocatePrefetchStyleLargeFlags.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 Stress allocation prefetch with large legal AllocatePrefetch* values + * @requires vm.compiler2.enabled + * + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+UseTLAB + * -XX:AllocatePrefetchStyle=1 + * -XX:AllocatePrefetchDistance=512 + * -XX:AllocatePrefetchStepSize=512 + * -XX:AllocatePrefetchLines=64 + * -XX:AllocateInstancePrefetchLines=64 + * compiler.c2.TestAllocatePrefetchStyleLargeFlags + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+UseTLAB + * -XX:AllocatePrefetchStyle=2 + * -XX:AllocatePrefetchDistance=512 + * -XX:AllocatePrefetchStepSize=512 + * -XX:AllocatePrefetchLines=64 + * -XX:AllocateInstancePrefetchLines=64 + * compiler.c2.TestAllocatePrefetchStyleLargeFlags + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+UseTLAB + * -XX:AllocatePrefetchStyle=3 + * -XX:AllocatePrefetchDistance=512 + * -XX:AllocatePrefetchStepSize=512 + * -XX:AllocatePrefetchLines=64 + * -XX:AllocateInstancePrefetchLines=64 + * compiler.c2.TestAllocatePrefetchStyleLargeFlags + */ + +package compiler.c2; + +public class TestAllocatePrefetchStyleLargeFlags { + private static volatile Object sink; + + private static final class Payload { + private final int value; + + private Payload(int value) { + this.value = value; + } + } + + private static Object allocateInstance(int value) { + return new Payload(value); + } + + private static Object allocateArray(int value) { + return new int[value & 31]; + } + + public static void main(String[] args) { + for (int i = 0; i < 50_000; i++) { + sink = allocateInstance(i); + sink = allocateArray(i); + } + } +} From 9ef2e8dc1c993a875eb7e47525df277d96066fe1 Mon Sep 17 00:00:00 2001 From: Ivan Bereziuk Date: Wed, 18 Mar 2026 10:18:19 +0000 Subject: [PATCH 002/160] 8278102: containers/docker/TestJcmd.java failed with "RuntimeException: Could not find specified process" Reviewed-by: kevinw, sgehwolf --- test/hotspot/jtreg/ProblemList.txt | 1 - test/hotspot/jtreg/containers/docker/TestJcmd.java | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 60d2ffa722f..5bb5bf54fba 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -117,7 +117,6 @@ runtime/NMT/VirtualAllocCommitMerge.java 8309698 linux-s390x applications/jcstress/copy.java 8229852 linux-all -containers/docker/TestJcmd.java 8278102 linux-all containers/docker/TestJFREvents.java 8327723 linux-x64 containers/docker/TestJcmdWithSideCar.java 8341518 linux-x64 diff --git a/test/hotspot/jtreg/containers/docker/TestJcmd.java b/test/hotspot/jtreg/containers/docker/TestJcmd.java index fcd5c665f2b..4f6fda20c86 100644 --- a/test/hotspot/jtreg/containers/docker/TestJcmd.java +++ b/test/hotspot/jtreg/containers/docker/TestJcmd.java @@ -170,14 +170,10 @@ public class TestJcmd { opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z") .addJavaOpts("-cp", "/test-classes/") .addDockerOpts("--cap-add=SYS_PTRACE") + .addDockerOpts("--pull=never") .addDockerOpts("--name", CONTAINER_NAME) .addClassOptions("" + TIME_TO_RUN_CONTAINER_PROCESS); - if (IS_PODMAN && !ROOT_UID.equals(getId("-u"))) { - // map the current userid to the one in the target namespace - opts.addDockerOpts("--userns=keep-id"); - } - // avoid large Xmx opts.appendTestJavaOptions = false; @@ -186,7 +182,7 @@ public class TestJcmd { return ProcessTools.startProcess("main-container-process", pb, line -> line.contains(EventGeneratorLoop.MAIN_METHOD_STARTED), - 5, TimeUnit.SECONDS); + 15, TimeUnit.SECONDS); } From e99ed13691369346386cc22df053c16f96aa6f69 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 18 Mar 2026 11:05:10 +0000 Subject: [PATCH 003/160] 8379671: C2: Fix usage of PhaseGVN::transform in some intrinsics Reviewed-by: bmaillard, dfenacci, roland --- src/hotspot/share/opto/library_call.cpp | 75 ++++++++++++------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index ffc798019b4..743612a1051 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -3006,7 +3006,7 @@ bool LibraryCallKit::inline_native_time_funcs(address funcAddr, const char* func // slow path: runtime call // } bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, const char* funcName, bool is_final_transition) { - Node* vt_oop = _gvn.transform(must_be_not_null(argument(0), true)); // VirtualThread this argument + Node* vt_oop = must_be_not_null(argument(0), true); // VirtualThread this argument IdealKit ideal(this); Node* thread = ideal.thread(); @@ -3026,7 +3026,7 @@ bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, co ideal.if_then(disabled, BoolTest::ne, ideal.ConI(0)); { sync_kit(ideal); - Node* is_mount = is_final_transition ? ideal.ConI(0) : _gvn.transform(argument(1)); + Node* is_mount = is_final_transition ? ideal.ConI(0) : argument(1); const TypeFunc* tf = OptoRuntime::vthread_transition_Type(); make_runtime_call(RC_NO_LEAF, tf, funcAddr, funcName, TypePtr::BOTTOM, vt_oop, is_mount); ideal.sync_kit(this); @@ -3038,7 +3038,7 @@ bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, co } bool LibraryCallKit::inline_native_vthread_end_transition(address funcAddr, const char* funcName, bool is_first_transition) { - Node* vt_oop = _gvn.transform(must_be_not_null(argument(0), true)); // VirtualThread this argument + Node* vt_oop = must_be_not_null(argument(0), true); // VirtualThread this argument IdealKit ideal(this); Node* _notify_jvmti_addr = makecon(TypeRawPtr::make((address)MountUnmountDisabler::notify_jvmti_events_address())); @@ -3046,7 +3046,7 @@ bool LibraryCallKit::inline_native_vthread_end_transition(address funcAddr, cons ideal.if_then(_notify_jvmti, BoolTest::eq, ideal.ConI(1)); { sync_kit(ideal); - Node* is_mount = is_first_transition ? ideal.ConI(1) : _gvn.transform(argument(1)); + Node* is_mount = is_first_transition ? ideal.ConI(1) : argument(1); const TypeFunc* tf = OptoRuntime::vthread_transition_Type(); make_runtime_call(RC_NO_LEAF, tf, funcAddr, funcName, TypePtr::BOTTOM, vt_oop, is_mount); ideal.sync_kit(this); @@ -3077,7 +3077,7 @@ bool LibraryCallKit::inline_native_notify_jvmti_sync() { { // unconditionally update the is_disable_suspend bit in current JavaThread Node* thread = ideal.thread(); - Node* arg = _gvn.transform(argument(0)); // argument for notification + Node* arg = argument(0); // argument for notification Node* addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::is_disable_suspend_offset())); const TypePtr *addr_type = _gvn.type(addr)->isa_ptr(); @@ -3157,7 +3157,7 @@ bool LibraryCallKit::inline_native_classID() { ideal.set(result, _gvn.transform(new AddLNode(array_kls_trace_id, longcon(1)))); } __ else_(); { // void class case - ideal.set(result, _gvn.transform(longcon(LAST_TYPE_ID + 1))); + ideal.set(result, longcon(LAST_TYPE_ID + 1)); } __ end_if(); Node* signaled_flag_address = makecon(TypeRawPtr::make(JfrIntrinsicSupport::signal_address())); @@ -3185,9 +3185,9 @@ bool LibraryCallKit::inline_native_jvm_commit() { // TLS. Node* tls_ptr = _gvn.transform(new ThreadLocalNode()); // Jfr java buffer. - Node* java_buffer_offset = _gvn.transform(new AddPNode(top(), tls_ptr, _gvn.transform(MakeConX(in_bytes(JAVA_BUFFER_OFFSET_JFR))))); + Node* java_buffer_offset = _gvn.transform(new AddPNode(top(), tls_ptr, MakeConX(in_bytes(JAVA_BUFFER_OFFSET_JFR)))); Node* java_buffer = _gvn.transform(new LoadPNode(control(), input_memory_state, java_buffer_offset, TypePtr::BOTTOM, TypeRawPtr::NOTNULL, MemNode::unordered)); - Node* java_buffer_pos_offset = _gvn.transform(new AddPNode(top(), java_buffer, _gvn.transform(MakeConX(in_bytes(JFR_BUFFER_POS_OFFSET))))); + Node* java_buffer_pos_offset = _gvn.transform(new AddPNode(top(), java_buffer, MakeConX(in_bytes(JFR_BUFFER_POS_OFFSET)))); // Load the current value of the notified field in the JfrThreadLocal. Node* notified_offset = basic_plus_adr(top(), tls_ptr, in_bytes(NOTIFY_OFFSET_JFR)); @@ -3209,7 +3209,7 @@ bool LibraryCallKit::inline_native_jvm_commit() { // Iff notified, the return address of the commit method is the current position of the backing java buffer. This is used to reset the event writer. Node* current_pos_X = _gvn.transform(new LoadXNode(control(), input_memory_state, java_buffer_pos_offset, TypeRawPtr::NOTNULL, TypeX_X, MemNode::unordered)); // Convert the machine-word to a long. - Node* current_pos = _gvn.transform(ConvX2L(current_pos_X)); + Node* current_pos = ConvX2L(current_pos_X); // False branch, not notified. Node* not_notified = _gvn.transform(new IfFalseNode(iff_notified)); @@ -3219,7 +3219,7 @@ bool LibraryCallKit::inline_native_jvm_commit() { // Arg is the next position as a long. Node* arg = argument(0); // Convert long to machine-word. - Node* next_pos_X = _gvn.transform(ConvL2X(arg)); + Node* next_pos_X = ConvL2X(arg); // Store the next_position to the underlying jfr java buffer. store_to_memory(control(), java_buffer_pos_offset, next_pos_X, LP64_ONLY(T_LONG) NOT_LP64(T_INT), MemNode::release); @@ -3228,9 +3228,9 @@ bool LibraryCallKit::inline_native_jvm_commit() { set_all_memory(commit_memory); // Now load the flags from off the java buffer and decide if the buffer is a lease. If so, it needs to be returned post-commit. - Node* java_buffer_flags_offset = _gvn.transform(new AddPNode(top(), java_buffer, _gvn.transform(MakeConX(in_bytes(JFR_BUFFER_FLAGS_OFFSET))))); + Node* java_buffer_flags_offset = _gvn.transform(new AddPNode(top(), java_buffer, MakeConX(in_bytes(JFR_BUFFER_FLAGS_OFFSET)))); Node* flags = make_load(control(), java_buffer_flags_offset, TypeInt::UBYTE, T_BYTE, MemNode::unordered); - Node* lease_constant = _gvn.transform(_gvn.intcon(4)); + Node* lease_constant = _gvn.intcon(4); // And flags with lease constant. Node* lease = _gvn.transform(new AndINode(flags, lease_constant)); @@ -3267,7 +3267,7 @@ bool LibraryCallKit::inline_native_jvm_commit() { lease_compare_rgn->init_req(_true_path, call_return_lease_control); lease_compare_rgn->init_req(_false_path, not_lease); - lease_compare_mem->init_req(_true_path, _gvn.transform(reset_memory())); + lease_compare_mem->init_req(_true_path, reset_memory()); lease_compare_mem->init_req(_false_path, commit_memory); lease_compare_io->init_req(_true_path, i_o()); @@ -3425,10 +3425,10 @@ bool LibraryCallKit::inline_native_getEventWriter() { IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); // Mask off the excluded information from the epoch. - Node * vthread_is_excluded = _gvn.transform(new AndINode(vthread_epoch_raw, _gvn.transform(excluded_mask))); + Node * vthread_is_excluded = _gvn.transform(new AndINode(vthread_epoch_raw, excluded_mask)); // Branch on excluded to conditionalize updating the epoch for the virtual thread. - Node* is_excluded_cmp = _gvn.transform(new CmpINode(vthread_is_excluded, _gvn.transform(excluded_mask))); + Node* is_excluded_cmp = _gvn.transform(new CmpINode(vthread_is_excluded, excluded_mask)); Node* test_not_excluded = _gvn.transform(new BoolNode(is_excluded_cmp, BoolTest::ne)); IfNode* iff_not_excluded = create_and_map_if(control(), test_not_excluded, PROB_MAX, COUNT_UNKNOWN); @@ -3440,7 +3440,7 @@ bool LibraryCallKit::inline_native_getEventWriter() { set_control(included); // Get epoch value. - Node* epoch = _gvn.transform(new AndINode(vthread_epoch_raw, _gvn.transform(epoch_mask))); + Node* epoch = _gvn.transform(new AndINode(vthread_epoch_raw, epoch_mask)); // Load the current epoch generation. The value is unsigned 16-bit, so we type it as T_CHAR. Node* epoch_generation_address = makecon(TypeRawPtr::make(JfrIntrinsicSupport::epoch_generation_address())); @@ -3478,7 +3478,7 @@ bool LibraryCallKit::inline_native_getEventWriter() { // Update control and phi nodes. epoch_compare_rgn->init_req(_true_path, call_write_checkpoint_control); epoch_compare_rgn->init_req(_false_path, epoch_is_equal); - epoch_compare_mem->init_req(_true_path, _gvn.transform(reset_memory())); + epoch_compare_mem->init_req(_true_path, reset_memory()); epoch_compare_mem->init_req(_false_path, input_memory_state); epoch_compare_io->init_req(_true_path, i_o()); epoch_compare_io->init_req(_false_path, input_io_state); @@ -3519,11 +3519,11 @@ bool LibraryCallKit::inline_native_getEventWriter() { vthread_compare_mem->init_req(_false_path, input_memory_state); vthread_compare_io->init_req(_true_path, _gvn.transform(exclude_compare_io)); vthread_compare_io->init_req(_false_path, input_io_state); - tid->init_req(_true_path, _gvn.transform(vthread_tid)); - tid->init_req(_false_path, _gvn.transform(thread_obj_tid)); - exclusion->init_req(_true_path, _gvn.transform(vthread_is_excluded)); - exclusion->init_req(_false_path, _gvn.transform(threadObj_is_excluded)); - pinVirtualThread->init_req(_true_path, _gvn.transform(continuation_support)); + tid->init_req(_true_path, vthread_tid); + tid->init_req(_false_path, thread_obj_tid); + exclusion->init_req(_true_path, vthread_is_excluded); + exclusion->init_req(_false_path, threadObj_is_excluded); + pinVirtualThread->init_req(_true_path, continuation_support); pinVirtualThread->init_req(_false_path, _gvn.intcon(0)); // Update branch state. @@ -3581,9 +3581,9 @@ bool LibraryCallKit::inline_native_getEventWriter() { // Update control and phi nodes. event_writer_tid_compare_rgn->init_req(_true_path, tid_is_not_equal); event_writer_tid_compare_rgn->init_req(_false_path, tid_is_equal); - event_writer_tid_compare_mem->init_req(_true_path, _gvn.transform(reset_memory())); + event_writer_tid_compare_mem->init_req(_true_path, reset_memory()); event_writer_tid_compare_mem->init_req(_false_path, _gvn.transform(vthread_compare_mem)); - event_writer_tid_compare_io->init_req(_true_path, _gvn.transform(i_o())); + event_writer_tid_compare_io->init_req(_true_path, i_o()); event_writer_tid_compare_io->init_req(_false_path, _gvn.transform(vthread_compare_io)); // Result of top level CFG, Memory, IO and Value. @@ -3598,14 +3598,14 @@ bool LibraryCallKit::inline_native_getEventWriter() { // Result memory. result_mem->init_req(_true_path, _gvn.transform(event_writer_tid_compare_mem)); - result_mem->init_req(_false_path, _gvn.transform(input_memory_state)); + result_mem->init_req(_false_path, input_memory_state); // Result IO. result_io->init_req(_true_path, _gvn.transform(event_writer_tid_compare_io)); - result_io->init_req(_false_path, _gvn.transform(input_io_state)); + result_io->init_req(_false_path, input_io_state); // Result value. - result_value->init_req(_true_path, _gvn.transform(event_writer)); // return event writer oop + result_value->init_req(_true_path, event_writer); // return event writer oop result_value->init_req(_false_path, null()); // return null // Set output state. @@ -3671,7 +3671,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); // Mask off the excluded information from the epoch. - Node * const is_excluded = _gvn.transform(new AndINode(epoch_raw, _gvn.transform(excluded_mask))); + Node * const is_excluded = _gvn.transform(new AndINode(epoch_raw, excluded_mask)); // Load the tid field from the thread. Node* tid = load_field_from_object(thread, "tid", "J"); @@ -3681,7 +3681,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { Node* tid_memory = store_to_memory(control(), thread_id_offset, tid, T_LONG, MemNode::unordered, true); // Branch is_excluded to conditionalize updating the epoch . - Node* excluded_cmp = _gvn.transform(new CmpINode(is_excluded, _gvn.transform(excluded_mask))); + Node* excluded_cmp = _gvn.transform(new CmpINode(is_excluded, excluded_mask)); Node* test_excluded = _gvn.transform(new BoolNode(excluded_cmp, BoolTest::eq)); IfNode* iff_excluded = create_and_map_if(control(), test_excluded, PROB_MIN, COUNT_UNKNOWN); @@ -3696,7 +3696,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { Node* vthread_is_included = _gvn.intcon(0); // Get epoch value. - Node* epoch = _gvn.transform(new AndINode(epoch_raw, _gvn.transform(epoch_mask))); + Node* epoch = _gvn.transform(new AndINode(epoch_raw, epoch_mask)); // Store the vthread epoch to the jfr thread local. Node* vthread_epoch_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EPOCH_OFFSET_JFR)); @@ -3714,8 +3714,8 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { excluded_rgn->init_req(_false_path, included); excluded_mem->init_req(_true_path, tid_memory); excluded_mem->init_req(_false_path, included_memory); - exclusion->init_req(_true_path, _gvn.transform(vthread_is_excluded)); - exclusion->init_req(_false_path, _gvn.transform(vthread_is_included)); + exclusion->init_req(_true_path, vthread_is_excluded); + exclusion->init_req(_false_path, vthread_is_included); // Set intermediate state. set_control(_gvn.transform(excluded_rgn)); @@ -3771,7 +3771,6 @@ bool LibraryCallKit::inline_native_setCurrentThread() { Node* p = basic_plus_adr(top()/*!oop*/, thread, in_bytes(JavaThread::vthread_offset())); Node* thread_obj_handle = make_load(nullptr, p, p->bottom_type()->is_ptr(), T_OBJECT, MemNode::unordered); - thread_obj_handle = _gvn.transform(thread_obj_handle); const TypePtr *adr_type = _gvn.type(thread_obj_handle)->isa_ptr(); access_store_at(nullptr, thread_obj_handle, adr_type, arr, _gvn.type(arr), T_OBJECT, IN_NATIVE | MO_UNORDERED); @@ -3867,7 +3866,7 @@ bool LibraryCallKit::inline_native_Continuation_pinning(bool unpin) { } else { pin_count_rhs = _gvn.intcon(UINT32_MAX); } - Node* pin_count_cmp = _gvn.transform(new CmpUNode(_gvn.transform(pin_count), pin_count_rhs)); + Node* pin_count_cmp = _gvn.transform(new CmpUNode(pin_count, pin_count_rhs)); Node* test_pin_count_over_underflow = _gvn.transform(new BoolNode(pin_count_cmp, BoolTest::eq)); IfNode* iff_pin_count_over_underflow = create_and_map_if(control(), test_pin_count_over_underflow, PROB_MIN, COUNT_UNKNOWN); @@ -3904,10 +3903,10 @@ bool LibraryCallKit::inline_native_Continuation_pinning(bool unpin) { PhiNode* result_mem = new PhiNode(result_rgn, Type::MEMORY, TypePtr::BOTTOM); record_for_igvn(result_mem); - result_rgn->init_req(_true_path, _gvn.transform(valid_pin_count)); - result_rgn->init_req(_false_path, _gvn.transform(continuation_is_null)); - result_mem->init_req(_true_path, _gvn.transform(reset_memory())); - result_mem->init_req(_false_path, _gvn.transform(input_memory_state)); + result_rgn->init_req(_true_path, valid_pin_count); + result_rgn->init_req(_false_path, continuation_is_null); + result_mem->init_req(_true_path, reset_memory()); + result_mem->init_req(_false_path, input_memory_state); // Set output state. set_control(_gvn.transform(result_rgn)); From 262b31be3de42012bf9460e5deb0b43bec6a3fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Wed, 18 Mar 2026 12:33:41 +0000 Subject: [PATCH 004/160] 8359335: Template-Framework Library: Primitive Types subtyping Reviewed-by: chagedorn, epeter --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 5 ++- .../library/Expression.java | 31 +++++++++++-- .../library/Operations.java | 13 +++++- .../library/PrimitiveType.java | 20 ++++++++- .../examples/TestPrimitiveTypes.java | 20 ++++++++- .../tests/TestExpression.java | 45 ++++++++++++++++--- 6 files changed, 116 insertions(+), 18 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 875ef57c865..33be24a0367 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -52,6 +52,7 @@ import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; import compiler.lib.template_framework.library.CodeGenerationDataNameType; import compiler.lib.template_framework.library.Expression; +import compiler.lib.template_framework.library.Expression.Nesting; import compiler.lib.template_framework.library.Operations; import compiler.lib.template_framework.library.PrimitiveType; import compiler.lib.template_framework.library.TestFrameworkClass; @@ -335,7 +336,7 @@ public class ExpressionFuzzer { for (int i = 0; i < 10; i++) { // The depth determines roughly how many operations are going to be used in the expression. int depth = RANDOM.nextInt(1, 20); - Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, depth); + Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, depth, Nesting.EXACT); tests.add(testTemplate.asToken(expression)); } } @@ -350,7 +351,7 @@ public class ExpressionFuzzer { for (int i = 0; i < 2; i++) { // The depth determines roughly how many operations are going to be used in the expression. int depth = RANDOM.nextInt(1, 20); - Expression expression = Expression.nestRandomly(type, Operations.SCALAR_NUMERIC_OPERATIONS, depth); + Expression expression = Expression.nestRandomly(type, Operations.SCALAR_NUMERIC_OPERATIONS, depth, Nesting.EXACT); tests.add(testTemplate.asToken(expression)); } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index 43ab16af415..37ad4debde6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -382,6 +382,23 @@ public class Expression { return sb.toString(); } + /** + * {@link Nesting} defines the different ways of selecting {@link Expression}s + * to nest based on their types. + */ + public enum Nesting { + /** + * Only nest {@Expression}s where the argument and return types match exactly + * based on the implementation of {@link CodeGenerateionDataNameType#isSubtypeOf}. + */ + EXACT, + /** + * Only nest {@Expression}s where the return type is a subtype of the argument + * type based on the implemetation of {@link CodeGenerateionDataNameType#isSubtypeOf}. + */ + SUBTYPE + } + /** * Create a nested {@link Expression} with a specified {@code returnType} from a * set of {@code expressions}. @@ -391,11 +408,13 @@ public class Expression { * the nested {@link Expression}. * @param maxNumberOfUsedExpressions the maximal number of {@link Expression}s from the * {@code expressions} are nested. + * @param nesting control the {@link Nesting} of the sampled {@link Expression}s. * @return a new randomly nested {@link Expression}. */ public static Expression nestRandomly(CodeGenerationDataNameType returnType, List expressions, - int maxNumberOfUsedExpressions) { + int maxNumberOfUsedExpressions, + Nesting nesting) { List filtered = expressions.stream().filter(e -> e.returnType.isSubtypeOf(returnType)).toList(); if (filtered.isEmpty()) { @@ -406,7 +425,7 @@ public class Expression { Expression expression = filtered.get(r); for (int i = 1; i < maxNumberOfUsedExpressions; i++) { - expression = expression.nestRandomly(expressions); + expression = expression.nestRandomly(expressions, nesting); } return expression; } @@ -416,12 +435,16 @@ public class Expression { * {@code this} {@link Expression}, ensuring compatibility of argument and return type. * * @param nestingExpressions list of expressions we sample from for the inner {@link Expression}. + * @param nesting control the {@link Nesting} of the sampled {@link Expression}s. * @return a new nested {@link Expression}. */ - public Expression nestRandomly(List nestingExpressions) { + public Expression nestRandomly(List nestingExpressions, Nesting nesting) { int argumentIndex = RANDOM.nextInt(this.argumentTypes.size()); CodeGenerationDataNameType argumentType = this.argumentTypes.get(argumentIndex); - List filtered = nestingExpressions.stream().filter(e -> e.returnType.isSubtypeOf(argumentType)).toList(); + List filtered = nestingExpressions.stream() + .filter(e -> e.returnType.isSubtypeOf(argumentType) && + (nesting == Nesting.EXACT ? argumentType.isSubtypeOf(e.returnType) : true)) + .toList(); if (filtered.isEmpty()) { // Found no expression that has a matching returnType. diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index 1fe05cc2b6c..2ea251cc5e5 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -129,8 +129,17 @@ public final class Operations { ops.add(Expression.make(type, "(", type, " + ", type, ")")); ops.add(Expression.make(type, "(", type, " - ", type, ")")); ops.add(Expression.make(type, "(", type, " * ", type, ")")); - ops.add(Expression.make(type, "(", type, " / ", type, ")")); - ops.add(Expression.make(type, "(", type, " % ", type, ")")); + // Because of subtyping, we can sample an expression like `(float)((int)(3) / (int)(0))`. Floating point + // division and modulo do not throw an ArithmeticException on division by zero, integer division and modulo + // do. In the expression above, the division has an integer on both sides, so it is executed as an integer + // division and throws an ArithmeticException even though we would expect the float division not to do so. + // To prevent this issue, we provide two versions of floating point division operations: one that casts + // its operands and one that expects that an ArithmeticException might be thrown when we get unlucky when + // sampling subtypes. + ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") / (" + type.name() +")(", type, "))")); + ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") % (" + type.name() +")(", type, "))")); + ops.add(Expression.make(type, "(", type, " / ", type, ")", WITH_ARITHMETIC_EXCEPTION)); + ops.add(Expression.make(type, "(", type, " % ", type, ")", WITH_ARITHMETIC_EXCEPTION)); // Relational / Comparison Operators ops.add(Expression.make(BOOLEANS, "(", type, " == ", type, ")")); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java index c8541ac1fa6..b20dbb28d22 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java @@ -71,7 +71,25 @@ public final class PrimitiveType implements CodeGenerationDataNameType { @Override public boolean isSubtypeOf(DataName.Type other) { - return (other instanceof PrimitiveType pt) && pt.kind == kind; + // Implement other >: this according to JLS ยง4.10.1. + if (other instanceof PrimitiveType superType) { + if (superType.kind == Kind.BOOLEAN || kind == Kind.BOOLEAN) { + // Boolean does not have a supertype and only itself as a subtype. + return superType.kind == this.kind; + } + if (superType.kind == Kind.CHAR || kind == Kind.CHAR) { + // Char does not have a subtype, but it is itself a subtype of any primitive type with + // a larger byte size. The following is correct for the subtype relation to floats, + // since chars are 16 bits wide and floats 32 bits or more. + return superType.kind == this.kind || (superType.byteSize() > this.byteSize() && this.kind != Kind.BYTE); + } + // Due to float >: long, all integers are subtypes of floating point types. + return (superType.isFloating() && !this.isFloating()) || + // Generally, narrower types are subtypes of wider types. + (superType.isFloating() == this.isFloating() && superType.byteSize() >= this.byteSize()); + } + + return false; } @Override diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java index 6193b6aad0c..facaefaa8f3 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java @@ -48,6 +48,7 @@ import static compiler.lib.template_framework.Template.let; import static compiler.lib.template_framework.Template.$; import static compiler.lib.template_framework.Template.addDataName; import static compiler.lib.template_framework.DataName.Mutability.MUTABLE; +import static compiler.lib.template_framework.DataName.Mutability.MUTABLE_OR_IMMUTABLE; import compiler.lib.template_framework.library.Hooks; import compiler.lib.template_framework.library.CodeGenerationDataNameType; @@ -129,7 +130,8 @@ public class TestPrimitiveTypes { } // Finally, test the type by creating some DataNames (variables), and sampling - // from them. There should be no cross-over between the types. + // from them. Sampling exactly should not lead to any conversion and sampling + // subtypes should only lead to widening conversions. // IMPORTANT: since we are adding the DataName via an inserted Template, we // must chose a "transparentScope", so that the DataName escapes. If we // instead chose "scope", the test would fail, because it later @@ -150,6 +152,14 @@ public class TestPrimitiveTypes { """ )); + var assignmentTemplate = Template.make("lhsType", (PrimitiveType lhsType) -> scope( + dataNames(MUTABLE).exactOf(lhsType).sampleAndLetAs("lhs"), + dataNames(MUTABLE_OR_IMMUTABLE).subtypeOf(lhsType).sampleAndLetAs("rhs"), + """ + #lhs = #rhs; + """ + )); + var namesTemplate = Template.make(() -> scope( """ public static void test_names() { @@ -161,10 +171,16 @@ public class TestPrimitiveTypes { ).toList() ), """ - // Now sample: + // Sample exactly: """, Collections.nCopies(10, CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(sampleTemplate::asToken).toList() + ), + """ + // Sample subtypes: + """, + Collections.nCopies(10, + CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(assignmentTemplate::asToken).toList() ) )), """ diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java index b34538c39c1..609b0936079 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java @@ -41,6 +41,7 @@ import compiler.lib.template_framework.TemplateToken; import static compiler.lib.template_framework.Template.scope; import compiler.lib.template_framework.library.CodeGenerationDataNameType; import compiler.lib.template_framework.library.Expression; +import compiler.lib.template_framework.library.Expression.Nesting; /** * This tests the use of the {@link Expression} from the template library. This is @@ -174,44 +175,74 @@ public class TestExpression { Expression e4 = Expression.make(myTypeA1, "<", myTypeA, ">"); Expression e5 = Expression.make(myTypeA, "[", myTypeB, "]"); - Expression e1e2 = e1.nestRandomly(List.of(e2)); - Expression e1ex = e1.nestRandomly(List.of(e3, e2, e3)); - Expression e1e4 = e1.nestRandomly(List.of(e3, e4, e3)); - Expression e1ey = e1.nestRandomly(List.of(e3, e3)); + Expression e1e2 = e1.nestRandomly(List.of(e2), Nesting.SUBTYPE); + Expression e1e2Exact = e1.nestRandomly(List.of(e2), Nesting.EXACT); + Expression e1ex = e1.nestRandomly(List.of(e3, e2, e3), Nesting.SUBTYPE); + Expression e1exExact = e1.nestRandomly(List.of(e3, e2, e3), Nesting.EXACT); + Expression e1e4 = e1.nestRandomly(List.of(e3, e4, e3), Nesting.SUBTYPE); + Expression e1e4Exact = e1.nestRandomly(List.of(e3, e4, e3), Nesting.EXACT); + Expression e1ey = e1.nestRandomly(List.of(e3, e3), Nesting.SUBTYPE); + Expression e1eyExact = e1.nestRandomly(List.of(e3, e3), Nesting.EXACT); // 5-deep nesting of e1 - Expression deep1 = Expression.nestRandomly(myTypeA, List.of(e1, e3), 5); + Expression deep1 = Expression.nestRandomly(myTypeA, List.of(e1, e3), 5, Nesting.SUBTYPE); + Expression deep1Exact = Expression.nestRandomly(myTypeA, List.of(e1, e3), 5, Nesting.EXACT); // Alternating pattern - Expression deep2 = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5); + Expression deep2 = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5, Nesting.SUBTYPE); + Expression deep2Exact = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5, Nesting.SUBTYPE); var template = Template.make(() -> scope( "xx", e1e2.toString(), "yy\n", + "xx", e1e2Exact.toString(), "yy\n", "xx", e1ex.toString(), "yy\n", + "xx", e1exExact.toString(), "yy\n", "xx", e1e4.toString(), "yy\n", + "xx", e1e4Exact.toString(), "yy\n", "xx", e1ey.toString(), "yy\n", + "xx", e1eyExact.toString(), "yy\n", "xx", deep1.toString(), "yy\n", + "xx", deep1Exact.toString(), "yy\n", "xx", deep2.toString(), "yy\n", + "xx", deep2Exact.toString(), "yy\n", "xx", e1e2.asToken(List.of("a")), "yy\n", + "xx", e1e2Exact.asToken(List.of("a")), "yy\n", "xx", e1ex.asToken(List.of("a")), "yy\n", + "xx", e1exExact.asToken(List.of("a")), "yy\n", "xx", e1e4.asToken(List.of("a")), "yy\n", + "xx", e1e4Exact.asToken(List.of("a")), "yy\n", "xx", e1ey.asToken(List.of("a")), "yy\n", + "xx", e1eyExact.asToken(List.of("a")), "yy\n", "xx", deep1.asToken(List.of("a")), "yy\n", - "xx", deep2.asToken(List.of("a")), "yy\n" + "xx", deep1Exact.asToken(List.of("a")), "yy\n", + "xx", deep2.asToken(List.of("a")), "yy\n", + "xx", deep2Exact.asToken(List.of("a")), "yy\n" )); String expected = """ xxExpression["[(", MyTypeA, ")]"]yy xxExpression["[(", MyTypeA, ")]"]yy + xxExpression["[(", MyTypeA, ")]"]yy + xxExpression["[(", MyTypeA, ")]"]yy xxExpression["[<", MyTypeA, ">]"]yy xxExpression["[", MyTypeA, "]"]yy + xxExpression["[", MyTypeA, "]"]yy + xxExpression["[", MyTypeA, "]"]yy + xxExpression["[[[[[", MyTypeA, "]]]]]"]yy xxExpression["[[[[[", MyTypeA, "]]]]]"]yy xxExpression["[{[{[", MyTypeB, "]}]}]"]yy + xxExpression["[{[{[", MyTypeB, "]}]}]"]yy + xx[(a)]yy + xx[(a)]yy xx[(a)]yy xx[(a)]yy xx[]yy xx[a]yy + xx[a]yy + xx[a]yy xx[[[[[a]]]]]yy + xx[[[[[a]]]]]yy + xx[{[{[a]}]}]yy xx[{[{[a]}]}]yy """; String code = template.render(); From 08ff2bf4d0541f7b3fd7c4a9c7f386cb710510af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= Date: Wed, 18 Mar 2026 14:12:43 +0000 Subject: [PATCH 005/160] 8376398: [TESTBUG] Testing of Unsafe native (re)allocation is sensitive to current JDK native memory use Reviewed-by: azafari --- test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java | 4 ++-- test/hotspot/jtreg/runtime/Unsafe/Reallocate.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java b/test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java index 7d8d33b1225..b18bcb8cc13 100644 --- a/test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java +++ b/test/hotspot/jtreg/runtime/Unsafe/AllocateMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,7 +61,7 @@ public class AllocateMemory { // allocateMemory() should throw an OutOfMemoryError when the underlying malloc fails, // since we start with -XX:MallocLimit try { - address = unsafe.allocateMemory(100 * 1024 * 1024); + address = unsafe.allocateMemory(101 * 1024 * 1024); throw new RuntimeException("Did not get expected OutOfMemoryError"); } catch (OutOfMemoryError e) { // Expected diff --git a/test/hotspot/jtreg/runtime/Unsafe/Reallocate.java b/test/hotspot/jtreg/runtime/Unsafe/Reallocate.java index c15b931449d..dc0ce86fa1f 100644 --- a/test/hotspot/jtreg/runtime/Unsafe/Reallocate.java +++ b/test/hotspot/jtreg/runtime/Unsafe/Reallocate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * 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 @@ public class Reallocate { // Make sure we can throw an OOME when we fail to reallocate due to OOM try { - unsafe.reallocateMemory(address, 100 * 1024 * 1024); + unsafe.reallocateMemory(address, 101 * 1024 * 1024); } catch (OutOfMemoryError e) { // Expected return; From 00a77704b00eb4a4d4356695329fb0e247bb3028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Arias=20de=20Reyna=20Dom=C3=ADnguez?= Date: Wed, 18 Mar 2026 14:26:48 +0000 Subject: [PATCH 006/160] 8380292: Confusing "reverted *" messages during training Reviewed-by: adinn, asmehra --- src/hotspot/share/oops/constantPool.cpp | 2 +- src/hotspot/share/oops/cpCache.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 456333efad0..8d817178d1a 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -564,7 +564,7 @@ void ConstantPool::remove_resolved_klass_if_non_deterministic(int cp_index) { } LogStreamHandle(Trace, aot, resolve) log; - if (log.is_enabled()) { + if (log.is_enabled() && !CDSConfig::is_dumping_preimage_static_archive()) { ResourceMark rm; log.print("%s klass CP entry [%3d]: %s %s", (can_archive ? "archived" : "reverted"), diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index edb5f6714c0..4177e4601ef 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -450,7 +450,7 @@ void ConstantPoolCache::remove_resolved_field_entries_if_non_deterministic() { Symbol* klass_name = cp->klass_name_at(klass_cp_index); Symbol* name = cp->uncached_name_ref_at(cp_index); Symbol* signature = cp->uncached_signature_ref_at(cp_index); - if (resolved) { + if (resolved && !CDSConfig::is_dumping_preimage_static_archive()) { log.print("%s field CP entry [%3d]: %s => %s.%s:%s%s%s", (archived ? "archived" : "reverted"), cp_index, @@ -493,7 +493,7 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() { Symbol* name = cp->uncached_name_ref_at(cp_index); Symbol* signature = cp->uncached_signature_ref_at(cp_index); LogStream ls(lt); - if (resolved) { + if (resolved && !CDSConfig::is_dumping_preimage_static_archive()) { ls.print("%s%s method CP entry [%3d]: %s %s.%s:%s", (archived ? "archived" : "reverted"), (rme->is_resolved(Bytecodes::_invokeinterface) ? " interface" : ""), @@ -539,7 +539,7 @@ void ConstantPoolCache::remove_resolved_indy_entries_if_non_deterministic() { Symbol* bsm_name = cp->uncached_name_ref_at(bsm_ref); Symbol* bsm_signature = cp->uncached_signature_ref_at(bsm_ref); Symbol* bsm_klass = cp->klass_name_at(cp->uncached_klass_ref_index_at(bsm_ref)); - if (resolved) { + if (resolved && !CDSConfig::is_dumping_preimage_static_archive()) { log.print("%s indy CP entry [%3d]: %s (%d)", (archived ? "archived" : "reverted"), cp_index, cp->pool_holder()->name()->as_C_string(), i); From 0379c0b005f4e99cb0f22ef6c43608697e805422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= Date: Wed, 18 Mar 2026 14:36:58 +0000 Subject: [PATCH 007/160] 8379557: Further optimize URL.toExternalForm Reviewed-by: vyazici --- .../classes/java/net/URLStreamHandler.java | 35 +++++++-- test/jdk/java/net/URL/Constructor.java | 19 ++++- .../openjdk/bench/java/net/URLToString.java | 77 +++++++++++++++++++ 3 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/net/URLToString.java diff --git a/src/java.base/share/classes/java/net/URLStreamHandler.java b/src/java.base/share/classes/java/net/URLStreamHandler.java index f66902a451e..76807d27cee 100644 --- a/src/java.base/share/classes/java/net/URLStreamHandler.java +++ b/src/java.base/share/classes/java/net/URLStreamHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -481,14 +481,33 @@ public abstract class URLStreamHandler { * @return a string representation of the {@code URL} argument. */ protected String toExternalForm(URL u) { - String s; + // The fast paths and branch-free concatenations in this method are here for + // a reason and should not be updated without checking performance figures. + + // Optionality, subtly different for authority + boolean emptyAuth = u.getAuthority() == null || u.getAuthority().isEmpty(); + boolean emptyPath = u.getPath() == null; + boolean emptyQuery = u.getQuery() == null; + boolean emptyRef = u.getRef() == null; + var path = emptyPath ? "" : u.getPath(); + // Fast paths for empty components + if (emptyQuery && emptyRef) { + return emptyAuth + ? (u.getProtocol() + ":" + path) + : (u.getProtocol() + "://" + u.getAuthority() + path); + } + // Prefer locals for efficient concatenation + var authSep = emptyAuth ? ":" : "://"; + var auth = emptyAuth ? "" : u.getAuthority(); + var querySep = emptyQuery ? "" : "?"; + var query = emptyQuery ? "" : u.getQuery(); + var refSep = emptyRef ? "" : "#"; + var ref = emptyRef ? "" : u.getRef(); return u.getProtocol() - + ':' - + ((s = u.getAuthority()) != null && !s.isEmpty() - ? "//" + s : "") - + ((s = u.getPath()) != null ? s : "") - + ((s = u.getQuery()) != null ? '?' + s : "") - + ((s = u.getRef()) != null ? '#' + s : ""); + + authSep + auth + + path + + querySep + query + + refSep + ref; } /** diff --git a/test/jdk/java/net/URL/Constructor.java b/test/jdk/java/net/URL/Constructor.java index 13eae40d6d4..b1d6dc91bc4 100644 --- a/test/jdk/java/net/URL/Constructor.java +++ b/test/jdk/java/net/URL/Constructor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,6 +48,7 @@ public class Constructor { entries.addAll(Arrays.asList(jarURLs)); entries.addAll(Arrays.asList(normalHttpURLs)); entries.addAll(Arrays.asList(abnormalHttpURLs)); + entries.addAll(Arrays.asList(blankComponents)); if (hasFtp()) entries.addAll(Arrays.asList(ftpURLs)); URL url; @@ -252,4 +253,20 @@ public class Constructor { "/dir1/entry.txt", "ftp://br:pwd@ftp.foo.com/dir1/entry.txt") }; + + static Entry[] blankComponents = new Entry[] { + new Entry(null, "http://host/path#", "http://host/path#"), + new Entry(null, "http://host/path?", "http://host/path?"), + new Entry(null, "http://host/path?#", "http://host/path?#"), + new Entry(null, "http://host/path#?", "http://host/path#?"), + new Entry(null, "file:/path#", "file:/path#"), + new Entry(null, "file:/path?", "file:/path?"), + new Entry(null, "file:/path?#", "file:/path?#"), + new Entry(null, "file:///path#", "file:/path#"), + new Entry(null, "file:///path?", "file:/path?"), + new Entry(null, "file:/path#?", "file:/path#?"), + new Entry("file:/path", "path?#", "file:/path?#"), + new Entry(null, "file:", "file:"), + new Entry(null, "file:#", "file:#") + }; } diff --git a/test/micro/org/openjdk/bench/java/net/URLToString.java b/test/micro/org/openjdk/bench/java/net/URLToString.java new file mode 100644 index 00000000000..38f6500c573 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/net/URLToString.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.net; + +import org.openjdk.jmh.annotations.*; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.concurrent.TimeUnit; + +/** + * Tests java.net.URL.toString performance + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 3) +public class URLToString { + + @Param({"false", "true"}) + boolean auth; + + @Param({"false", "true"}) + boolean query; + + @Param({"false", "true"}) + boolean ref; + + private URL url; + + @Setup() + public void setup() throws MalformedURLException { + StringBuilder sb = new StringBuilder(); + if (auth) { + sb.append("http://hostname"); + } else { + sb.append("file:"); + } + sb.append("/some/long/path/to/jar/app-1.0.jar!/org/summerframework/samples/horseclinic/HorseClinicApplication.class"); + if (query) { + sb.append("?param=value"); + } + if (ref) { + sb.append("#fragment"); + } + + url = URI.create(sb.toString()).toURL(); + } + + @Benchmark + public String urlToString() { + return url.toString(); + } +} From 766959f884be63e7f65175c6e685a10445c8a182 Mon Sep 17 00:00:00 2001 From: Ashay Rane <253344819+raneashay@users.noreply.github.com> Date: Wed, 18 Mar 2026 15:15:12 +0000 Subject: [PATCH 008/160] 8371685: C2: Add flag to disable Loop Peeling Reviewed-by: chagedorn, snatarajan, roland --- src/hotspot/share/opto/c2_globals.hpp | 9 ++ src/hotspot/share/opto/loopTransform.cpp | 22 +++++ src/hotspot/share/opto/loopnode.cpp | 8 +- .../compiler/loopopts/TestLoopPeeling.java | 37 +++++++- .../loopopts/TestLoopPeelingDisabled.java | 89 +++++++++++++++++++ 5 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestLoopPeelingDisabled.java diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 1662f808286..66a6f2d0c3c 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -334,6 +334,15 @@ product(bool, PartialPeelLoop, true, \ "Partial peel (rotate) loops") \ \ + product(uint, LoopPeeling, 1, DIAGNOSTIC, \ + "Control loop peeling optimization: " \ + "0 = always disable loop peeling, " \ + "1 = enable loop peeling (default), " \ + "2 = disable loop peeling as a standalone optimization but " \ + "allow it as a helper to other loop optimizations like removing " \ + "empty loops") \ + range(0, 2) \ + \ product(intx, PartialPeelNewPhiDelta, 0, \ "Additional phis that can be created by partial peeling") \ range(0, max_jint) \ diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 4e221a9a0ef..1801841bad3 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -524,6 +524,9 @@ bool IdealLoopTree::policy_peeling(PhaseIdealLoop *phase) { // return the estimated loop size if peeling is applicable, otherwise return // zero. No node budget is allocated. uint IdealLoopTree::estimate_peeling(PhaseIdealLoop *phase) { + if (LoopPeeling != 1) { + return 0; + } // If nodes are depleted, some transform has miscalculated its needs. assert(!phase->exceeding_node_budget(), "sanity"); @@ -775,6 +778,7 @@ void PhaseIdealLoop::peeled_dom_test_elim(IdealLoopTree* loop, Node_List& old_ne // exit // void PhaseIdealLoop::do_peeling(IdealLoopTree *loop, Node_List &old_new) { + assert(LoopPeeling != 0, "do_peeling called with loop peeling always disabled"); C->set_major_progress(); // Peeling a 'main' loop in a pre/main/post situation obfuscates the @@ -2201,6 +2205,15 @@ void PhaseIdealLoop::do_maximally_unroll(IdealLoopTree *loop, Node_List &old_new // If loop is tripping an odd number of times, peel odd iteration if ((cl->trip_count() & 1) == 1) { + if (LoopPeeling == 0) { +#ifndef PRODUCT + if (TraceLoopOpts) { + tty->print("MaxUnroll cancelled since LoopPeeling is always disabled"); + loop->dump_head(); + } +#endif + return; + } do_peeling(loop, old_new); } @@ -3243,6 +3256,15 @@ bool IdealLoopTree::do_remove_empty_loop(PhaseIdealLoop *phase) { #endif if (needs_guard) { + if (LoopPeeling == 0) { +#ifndef PRODUCT + if (TraceLoopOpts) { + tty->print("Empty loop not removed since LoopPeeling is always disabled"); + this->dump_head(); + } +#endif + return false; + } // Peel the loop to ensure there's a zero trip guard Node_List old_new; phase->do_peeling(this, old_new); diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 1523372e64c..b2eb0c47458 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -896,11 +896,11 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { // Loop is strip mined: use the safepoint of the outer strip mined loop OuterStripMinedLoopNode* outer_loop = head->as_CountedLoop()->outer_loop(); assert(outer_loop != nullptr, "no outer loop"); - safepoint = outer_loop->outer_safepoint(); + safepoint = LoopPeeling == 0 ? nullptr : outer_loop->outer_safepoint(); outer_loop->transform_to_counted_loop(&_igvn, this); exit_test = head->loopexit(); } else { - safepoint = find_safepoint(back_control, x, loop); + safepoint = LoopPeeling == 0 ? nullptr : find_safepoint(back_control, x, loop); } IfFalseNode* exit_branch = exit_test->false_proj(); @@ -1074,8 +1074,8 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { // Peel one iteration of the loop and use the safepoint at the end // of the peeled iteration to insert Parse Predicates. If no well // positioned safepoint peel to guarantee a safepoint in the outer - // loop. - if (safepoint != nullptr || !loop->_has_call) { + // loop. When loop peeling is disabled, skip the peeling step altogether. + if (LoopPeeling != 0 && (safepoint != nullptr || !loop->_has_call)) { old_new.clear(); do_peeling(loop, old_new); } else { diff --git a/test/hotspot/jtreg/compiler/loopopts/TestLoopPeeling.java b/test/hotspot/jtreg/compiler/loopopts/TestLoopPeeling.java index a32f3cb851a..aab6e4f2aaa 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestLoopPeeling.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestLoopPeeling.java @@ -23,12 +23,27 @@ /* * @test - * @bug 8078262 8177095 - * @summary Tests correct dominator information after loop peeling. + * @bug 8078262 8177095 8371685 + * @summary Tests correct dominator information after loop peeling and + * tests that LoopPeeling flag correctly disables loop peeling. * * @run main/othervm -Xcomp * -XX:CompileCommand=compileonly,compiler.loopopts.TestLoopPeeling::test* * compiler.loopopts.TestLoopPeeling + * @run main/othervm -Xcomp + * -XX:+UnlockDiagnosticVMOptions -XX:LoopPeeling=0 + * -XX:CompileCommand=compileonly,compiler.loopopts.TestLoopPeeling::test* + * compiler.loopopts.TestLoopPeeling + * @run main/othervm -Xbatch + * -XX:+UnlockDiagnosticVMOptions -XX:LoopPeeling=0 + * compiler.loopopts.TestLoopPeeling + * @run main/othervm -Xcomp + * -XX:+UnlockDiagnosticVMOptions -XX:LoopPeeling=2 + * -XX:CompileCommand=compileonly,compiler.loopopts.TestLoopPeeling::test* + * compiler.loopopts.TestLoopPeeling + * @run main/othervm -Xbatch + * -XX:+UnlockDiagnosticVMOptions -XX:LoopPeeling=2 + * compiler.loopopts.TestLoopPeeling */ package compiler.loopopts; @@ -44,6 +59,8 @@ public class TestLoopPeeling { test.testArrayAccess2(0); test.testArrayAccess3(0, false); test.testArrayAllocation(0, 1); + test.testEmptyLoop(100); + test.testMaxUnrollOddTrip(); } catch (Exception e) { // Ignore exceptions } @@ -142,5 +159,19 @@ public class TestLoopPeeling { } return null; } -} + public void testEmptyLoop(int limit) { + for (int i = 0; i < limit; i++) { + // Empty body - candidate for empty loop removal + } + } + + public int testMaxUnrollOddTrip() { + int sum = 0; + // Trip count of 5 (odd) - maximal unroll needs to peel one iteration + for (int i = 0; i < 5; i++) { + sum += array[i]; + } + return sum; + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/TestLoopPeelingDisabled.java b/test/hotspot/jtreg/compiler/loopopts/TestLoopPeelingDisabled.java new file mode 100644 index 00000000000..e7bbe24d1de --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestLoopPeelingDisabled.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.loopopts; + +import compiler.lib.ir_framework.*; +import compiler.lib.ir_framework.driver.irmatching.IRViolationException; +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8371685 + * @requires vm.flagless & vm.debug + * @summary Verifies that the LoopPeeling flag correctly disables loop peeling + * by checking whether the "After Loop Peeling" compile phase is + * emitted. When loop peeling is disabled, no peeling should occur and + * the phase must be absent from the compilation output. + * @library /test/lib / + * @run driver compiler.loopopts.TestLoopPeelingDisabled + */ +public class TestLoopPeelingDisabled { + static int[] array = new int[100]; + + public static void main(String[] args) { + // First, run the test with loop peeling enabled, which is the default. + // The IR framework should catch if the number of counted loops does not + // match the annotations. + TestFramework.run(); + + // Then, run the same test with loop peeling disabled, which should + // elide the {BEFORE,AFTER}_LOOP_PEELING compilation phases, causing the + // test to throw an IRViolationException. We then check whether the + // exception message matches our expectation (that the loop peeling + // phase was not found). + try { + TestFramework.runWithFlags("-XX:+UnlockDiagnosticVMOptions", + "-XX:LoopPeeling=0"); + Asserts.fail("Expected IRViolationException"); + } catch (IRViolationException e) { + String info = e.getExceptionInfo(); + if (!info.contains("NO compilation output found for this phase")) { + Asserts.fail("Unexpected IR violation: " + info); + } + System.out.println("Loop peeling correctly disabled"); + } + + // Finally, run the same test with loop peeling disabled only when + // splitting iterations. Since the function being tested does not hit + // this case, we expect that the loop will be peeled, which is ensured + // by the IR annotations. + TestFramework.runWithFlags("-XX:+UnlockDiagnosticVMOptions", + "-XX:LoopPeeling=2"); + } + + @Test + @IR(counts = {IRNode.COUNTED_LOOP, "1"}, phase = CompilePhase.BEFORE_LOOP_PEELING) + @IR(counts = {IRNode.COUNTED_LOOP, "2"}, phase = CompilePhase.AFTER_LOOP_PEELING) + public static int test() { + int sum = 0; + + // Use an odd trip count so that `do_maximally_unroll()` tries to peel + // the odd iteration. + for (int i = 0; i < 5; i++) { + sum += array[i]; + } + + return sum; + } +} From 446fb203b2e27807f0cd2ec78447478a1de19773 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 18 Mar 2026 16:07:41 +0000 Subject: [PATCH 009/160] 8379015: Convert TraceNewOopMapGeneration to unified logging Reviewed-by: dholmes, matsaave --- src/hotspot/share/classfile/classPrinter.cpp | 3 +- src/hotspot/share/classfile/classPrinter.hpp | 3 +- .../share/interpreter/bytecodeTracer.cpp | 8 +- src/hotspot/share/logging/logTag.hpp | 3 +- src/hotspot/share/oops/generateOopMap.cpp | 111 +++++++----------- src/hotspot/share/runtime/globals.hpp | 12 -- .../runtime/logging/GenerateOopMapTest.java | 84 +++++++++++++ 7 files changed, 139 insertions(+), 85 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java diff --git a/src/hotspot/share/classfile/classPrinter.cpp b/src/hotspot/share/classfile/classPrinter.cpp index 3ed0a5e9840..6cf89f7357f 100644 --- a/src/hotspot/share/classfile/classPrinter.cpp +++ b/src/hotspot/share/classfile/classPrinter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -224,6 +224,7 @@ void ClassPrinter::print_flags_help(outputStream* os) { os->print_cr(" 0x%02x - print info for invokehandle", PRINT_METHOD_HANDLE); os->print_cr(" 0x%02x - print details of the C++ and Java objects that represent classes", PRINT_CLASS_DETAILS); os->print_cr(" 0x%02x - print details of the C++ objects that represent methods", PRINT_METHOD_DETAILS); + os->print_cr(" 0x%02x - print MethodData", PRINT_METHOD_DATA); os->cr(); } diff --git a/src/hotspot/share/classfile/classPrinter.hpp b/src/hotspot/share/classfile/classPrinter.hpp index 470e82ddc0e..b09a1a1ef3b 100644 --- a/src/hotspot/share/classfile/classPrinter.hpp +++ b/src/hotspot/share/classfile/classPrinter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * 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,7 @@ public: PRINT_METHOD_HANDLE = 1 << 4, // extra information for invokehandle PRINT_CLASS_DETAILS = 1 << 5, // print details of the C++ and Java objects that represent classes PRINT_METHOD_DETAILS = 1 << 6, // print details of the C++ objects that represent methods + PRINT_METHOD_DATA = 1 << 7, // print MethodData - requires MDO lock }; static bool has_mode(int flags, Mode mode) { return (flags & static_cast(mode)) != 0; diff --git a/src/hotspot/share/interpreter/bytecodeTracer.cpp b/src/hotspot/share/interpreter/bytecodeTracer.cpp index 21974218957..2610a8a2bd6 100644 --- a/src/hotspot/share/interpreter/bytecodeTracer.cpp +++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp @@ -78,7 +78,7 @@ class BytecodePrinter { void print_field_or_method(int cp_index, outputStream* st); void print_dynamic(int cp_index, outputStream* st); void print_attributes(int bci, outputStream* st); - void bytecode_epilog(int bci, outputStream* st); + void print_method_data_at(int bci, outputStream* st); public: BytecodePrinter(int flags = 0) : _is_wide(false), _code(Bytecodes::_illegal), _flags(flags) {} @@ -171,7 +171,9 @@ class BytecodePrinter { } _next_pc = is_wide() ? bcp+2 : bcp+1; print_attributes(bci, st); - bytecode_epilog(bci, st); + if (ClassPrinter::has_mode(_flags, ClassPrinter::PRINT_METHOD_DATA)) { + print_method_data_at(bci, st); + } } }; @@ -598,7 +600,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { } -void BytecodePrinter::bytecode_epilog(int bci, outputStream* st) { +void BytecodePrinter::print_method_data_at(int bci, outputStream* st) { MethodData* mdo = method()->method_data(); if (mdo != nullptr) { diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index 3ad6a197d07..20d61b542b0 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -90,6 +90,7 @@ class outputStream; LOG_TAG(freelist) \ LOG_TAG(gc) \ NOT_PRODUCT(LOG_TAG(generate)) \ + LOG_TAG(generateoopmap) \ LOG_TAG(handshake) \ LOG_TAG(hashtables) \ LOG_TAG(heap) \ diff --git a/src/hotspot/share/oops/generateOopMap.cpp b/src/hotspot/share/oops/generateOopMap.cpp index 5e12c57676d..09912aeaf63 100644 --- a/src/hotspot/share/oops/generateOopMap.cpp +++ b/src/hotspot/share/oops/generateOopMap.cpp @@ -400,9 +400,7 @@ void GenerateOopMap::bb_mark_fct(GenerateOopMap *c, int bci, int *data) { if (c->is_bb_header(bci)) return; - if (TraceNewOopMapGeneration) { - tty->print_cr("Basicblock#%d begins at: %d", c->_bb_count, bci); - } + log_debug(generateoopmap)( "Basicblock#%d begins at: %d", c->_bb_count, bci); c->set_bbmark_bit(bci); c->_bb_count++; } @@ -913,13 +911,12 @@ void GenerateOopMap::do_interpretation() // iterated more than once. int i = 0; do { -#ifndef PRODUCT - if (TraceNewOopMapGeneration) { - tty->print("\n\nIteration #%d of do_interpretation loop, method:\n", i); - method()->print_name(tty); - tty->print("\n\n"); + if (log_is_enabled(Trace, generateoopmap)) { + LogStream st(Log(generateoopmap)::trace()); + st.print("Iteration #%d of do_interpretation loop, method:", i); + method()->print_name(&st); + st.print("\n\n"); } -#endif _conflict = false; _monitor_safe = true; // init_state is now called from init_basic_blocks. The length of a @@ -1084,8 +1081,7 @@ void GenerateOopMap::initialize_vars() { void GenerateOopMap::add_to_ref_init_set(int localNo) { - if (TraceNewOopMapGeneration) - tty->print_cr("Added init vars: %d", localNo); + log_debug(generateoopmap)("Added init vars: %d", localNo); // Is it already in the set? if (_init_vars->contains(localNo) ) @@ -1284,13 +1280,13 @@ void GenerateOopMap::report_monitor_mismatch(const char *msg) { void GenerateOopMap::print_states(outputStream *os, CellTypeState* vec, int num) { for (int i = 0; i < num; i++) { - vec[i].print(tty); + vec[i].print(os); } } // Print the state values at the current bytecode. -void GenerateOopMap::print_current_state(outputStream *os, - BytecodeStream *currentBC, +void GenerateOopMap::print_current_state(outputStream* os, + BytecodeStream* currentBC, bool detailed) { if (detailed) { os->print(" %4d vars = ", currentBC->bci()); @@ -1312,6 +1308,7 @@ void GenerateOopMap::print_current_state(outputStream *os, case Bytecodes::_invokestatic: case Bytecodes::_invokedynamic: case Bytecodes::_invokeinterface: { + ResourceMark rm; int idx = currentBC->has_index_u4() ? currentBC->get_index_u4() : currentBC->get_index_u2(); ConstantPool* cp = method()->constants(); int nameAndTypeIdx = cp->name_and_type_ref_index_at(idx, currentBC->code()); @@ -1342,8 +1339,9 @@ void GenerateOopMap::print_current_state(outputStream *os, // Sets the current state to be the state after executing the // current instruction, starting in the current state. void GenerateOopMap::interp1(BytecodeStream *itr) { - if (TraceNewOopMapGeneration) { - print_current_state(tty, itr, TraceNewOopMapGenerationDetailed); + if (log_is_enabled(Trace, generateoopmap)) { + LogStream st(Log(generateoopmap)::trace()); + print_current_state(&st, itr, Verbose); } // Should we report the results? Result is reported *before* the instruction at the current bci is executed. @@ -2026,9 +2024,7 @@ void GenerateOopMap::ret_jump_targets_do(BytecodeStream *bcs, jmpFct_t jmpFct, i DEBUG_ONLY(BasicBlock* target_bb = &jsr_bb[1];) assert(target_bb == get_basic_block_at(target_bci), "wrong calc. of successor basicblock"); bool alive = jsr_bb->is_alive(); - if (TraceNewOopMapGeneration) { - tty->print("pc = %d, ret -> %d alive: %s\n", bci, target_bci, alive ? "true" : "false"); - } + log_debug(generateoopmap)("pc = %d, ret -> %d alive: %s", bci, target_bci, alive ? "true" : "false"); if (alive) jmpFct(this, target_bci, data); } } @@ -2046,6 +2042,7 @@ char* GenerateOopMap::state_vec_to_string(CellTypeState* vec, int len) { return _state_vec_buf; } +#ifndef PRODUCT void GenerateOopMap::print_time() { tty->print_cr ("Accumulated oopmap times:"); tty->print_cr ("---------------------------"); @@ -2053,6 +2050,7 @@ void GenerateOopMap::print_time() { tty->print_cr (" (%3.0f bytecodes per sec) ", (double)GenerateOopMap::_total_byte_count / GenerateOopMap::_total_oopmap_time.seconds()); } +#endif // // ============ Main Entry Point =========== @@ -2062,27 +2060,16 @@ GenerateOopMap::GenerateOopMap(const methodHandle& method) { _method = method; _max_locals=0; _init_vars = nullptr; - -#ifndef PRODUCT - // If we are doing a detailed trace, include the regular trace information. - if (TraceNewOopMapGenerationDetailed) { - TraceNewOopMapGeneration = true; - } -#endif } bool GenerateOopMap::compute_map(Thread* current) { #ifndef PRODUCT - if (TimeOopMap2) { - method()->print_short_name(tty); - tty->print(" "); - } if (TimeOopMap) { _total_byte_count += method()->code_size(); + TraceTime t_all(nullptr, &_total_oopmap_time, TimeOopMap); } #endif - TraceTime t_single("oopmap time", TimeOopMap2); - TraceTime t_all(nullptr, &_total_oopmap_time, TimeOopMap); + TraceTime t_single("oopmap time", TRACETIME_LOG(Debug, generateoopmap)); // Initialize values _got_error = false; @@ -2099,16 +2086,16 @@ bool GenerateOopMap::compute_map(Thread* current) { _did_rewriting = false; _did_relocation = false; - if (TraceNewOopMapGeneration) { - tty->print("Method name: %s\n", method()->name()->as_C_string()); - if (Verbose) { - _method->print_codes(); - tty->print_cr("Exception table:"); - ExceptionTable excps(method()); - for(int i = 0; i < excps.length(); i ++) { - tty->print_cr("[%d - %d] -> %d", - excps.start_pc(i), excps.end_pc(i), excps.handler_pc(i)); - } + if (log_is_enabled(Debug, generateoopmap)) { + ResourceMark rm; + LogStream st(Log(generateoopmap)::debug()); + st.print_cr("Method name: %s\n", method()->name()->as_C_string()); + _method->print_codes_on(&st); + st.print_cr("Exception table:"); + ExceptionTable excps(method()); + for (int i = 0; i < excps.length(); i ++) { + st.print_cr("[%d - %d] -> %d", + excps.start_pc(i), excps.end_pc(i), excps.handler_pc(i)); } } @@ -2175,7 +2162,7 @@ void GenerateOopMap::verify_error(const char *format, ...) { // void GenerateOopMap::report_result() { - if (TraceNewOopMapGeneration) tty->print_cr("Report result pass"); + log_debug(generateoopmap)("Report result pass"); // We now want to report the result of the parse _report_result = true; @@ -2192,7 +2179,7 @@ void GenerateOopMap::report_result() { } void GenerateOopMap::result_for_basicblock(int bci) { - if (TraceNewOopMapGeneration) tty->print_cr("Report result pass for basicblock"); + log_debug(generateoopmap)("Report result pass for basicblock"); // We now want to report the result of the parse _report_result = true; @@ -2200,7 +2187,7 @@ void GenerateOopMap::result_for_basicblock(int bci) { // Find basicblock and report results BasicBlock* bb = get_basic_block_containing(bci); guarantee(bb != nullptr, "no basic block for bci"); - assert(bb->is_reachable(), "getting result from unreachable basicblock"); + assert(bb->is_reachable(), "getting result from unreachable basicblock %d", bci); bb->set_changed(true); interp_bb(bb); } @@ -2212,9 +2199,7 @@ void GenerateOopMap::result_for_basicblock(int bci) { void GenerateOopMap::record_refval_conflict(int varNo) { assert(varNo>=0 && varNo< _max_locals, "index out of range"); - if (TraceOopMapRewrites) { - tty->print("### Conflict detected (local no: %d)\n", varNo); - } + log_trace(generateoopmap)("### Conflict detected (local no: %d)", varNo); if (!_new_var_map) { _new_var_map = NEW_RESOURCE_ARRAY(int, _max_locals); @@ -2253,10 +2238,12 @@ void GenerateOopMap::rewrite_refval_conflicts() // Tracing flag _did_rewriting = true; - if (TraceOopMapRewrites) { - tty->print_cr("ref/value conflict for method %s - bytecodes are getting rewritten", method()->name()->as_C_string()); - method()->print(); - method()->print_codes(); + if (log_is_enabled(Trace, generateoopmap)) { + ResourceMark rm; + LogStream st(Log(generateoopmap)::trace()); + st.print_cr("ref/value conflict for method %s - bytecodes are getting rewritten", method()->name()->as_C_string()); + method()->print_on(&st); + method()->print_codes_on(&st); } assert(_new_var_map!=nullptr, "nothing to rewrite"); @@ -2266,9 +2253,7 @@ void GenerateOopMap::rewrite_refval_conflicts() if (!_got_error) { for (int k = 0; k < _max_locals && !_got_error; k++) { if (_new_var_map[k] != k) { - if (TraceOopMapRewrites) { - tty->print_cr("Rewriting: %d -> %d", k, _new_var_map[k]); - } + log_trace(generateoopmap)("Rewriting: %d -> %d", k, _new_var_map[k]); rewrite_refval_conflict(k, _new_var_map[k]); if (_got_error) return; nof_conflicts++; @@ -2319,22 +2304,16 @@ bool GenerateOopMap::rewrite_refval_conflict_inst(BytecodeStream *itr, int from, int bci = itr->bci(); if (is_aload(itr, &index) && index == from) { - if (TraceOopMapRewrites) { - tty->print_cr("Rewriting aload at bci: %d", bci); - } + log_trace(generateoopmap)("Rewriting aload at bci: %d", bci); return rewrite_load_or_store(itr, Bytecodes::_aload, Bytecodes::_aload_0, to); } if (is_astore(itr, &index) && index == from) { if (!stack_top_holds_ret_addr(bci)) { - if (TraceOopMapRewrites) { - tty->print_cr("Rewriting astore at bci: %d", bci); - } + log_trace(generateoopmap)("Rewriting astore at bci: %d", bci); return rewrite_load_or_store(itr, Bytecodes::_astore, Bytecodes::_astore_0, to); } else { - if (TraceOopMapRewrites) { - tty->print_cr("Suppress rewriting of astore at bci: %d", bci); - } + log_trace(generateoopmap)("Suppress rewriting of astore at bci: %d", bci); } } @@ -2502,9 +2481,7 @@ void GenerateOopMap::compute_ret_adr_at_TOS() { // TDT: should this be is_good_address() ? if (_stack_top > 0 && stack()[_stack_top-1].is_address()) { _ret_adr_tos->append(bcs.bci()); - if (TraceNewOopMapGeneration) { - tty->print_cr("Ret_adr TOS at bci: %d", bcs.bci()); - } + log_debug(generateoopmap)("Ret_adr TOS at bci: %d", bcs.bci()); } interp1(&bcs); } diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 559071cae68..0c627e27209 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -871,21 +871,9 @@ const int ObjectAlignmentInBytes = 8; develop(bool, VerifyDependencies, trueInDebug, \ "Exercise and verify the compilation dependency mechanism") \ \ - develop(bool, TraceNewOopMapGeneration, false, \ - "Trace OopMapGeneration") \ - \ - develop(bool, TraceNewOopMapGenerationDetailed, false, \ - "Trace OopMapGeneration: print detailed cell states") \ - \ develop(bool, TimeOopMap, false, \ "Time calls to GenerateOopMap::compute_map() in sum") \ \ - develop(bool, TimeOopMap2, false, \ - "Time calls to GenerateOopMap::compute_map() individually") \ - \ - develop(bool, TraceOopMapRewrites, false, \ - "Trace rewriting of methods during oop map generation") \ - \ develop(bool, TraceFinalizerRegistration, false, \ "Trace registration of final references") \ \ diff --git a/test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java b/test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java new file mode 100644 index 00000000000..6e114ff6b1d --- /dev/null +++ b/test/hotspot/jtreg/runtime/logging/GenerateOopMapTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 GenerateOopMap + * @bug 8379015 + * @requires vm.flagless + * @modules java.base/jdk.internal.misc + * @library /test/lib + * @run driver GenerateOopMapTest + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Platform; + +public class GenerateOopMapTest { + + static String infoPattern = "[generateoopmap]"; + static String debugPattern = "[generateoopmap] Basicblock#0 begins at:"; + static String tracePattern = "[trace][generateoopmap] 5 vars = 'r' stack = 'v' monitors = '' \tifne"; + static String traceDetailPattern = "[generateoopmap] 0 vars = ( r |slot0) invokestatic()V"; + + static void test() throws Exception { + // Don't print much with info level. + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:generateoopmap=info", + "GenerateOopMapTest", "test"); + OutputAnalyzer o = new OutputAnalyzer(pb.start()); + o.shouldNotContain(infoPattern).shouldHaveExitValue(0); + + // Prints bytecodes and BasicBlock information in debug. + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:generateoopmap=debug", + "GenerateOopMapTest", "test"); + o = new OutputAnalyzer(pb.start()); + o.shouldContain(debugPattern).shouldHaveExitValue(0); + + // Prints ref/val for each bytecode in trace. + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:generateoopmap=trace", + "GenerateOopMapTest", "test"); + o = new OutputAnalyzer(pb.start()); + o.shouldContain(tracePattern).shouldHaveExitValue(0); + + // Prints extra stuff with detailed. Not sure how useful this is but keep it for now. + if (Platform.isDebugBuild()) { + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:generateoopmap=trace", + "-XX:+Verbose", + "GenerateOopMapTest", "test"); + o = new OutputAnalyzer(pb.start()); + o.shouldContain(traceDetailPattern).shouldHaveExitValue(0); + } + }; + + public static void main(String... args) throws Exception { + System.gc(); + if (args.length == 0) { + test(); + } + } +} From 20567e82412b0ea8976a029728b400ca788178ba Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Wed, 18 Mar 2026 16:08:20 +0000 Subject: [PATCH 010/160] 8380218: Refactor test/jdk/java/nio/charset TestNG tests to JUnit Reviewed-by: bpb, naoto --- .../charset/Charset/DefaultCharsetTest.java | 50 +++++++-------- .../jdk/java/nio/charset/Charset/ForName.java | 62 ++++++++++--------- .../CoderMalfunctionErrorTest.java | 18 +++--- .../CoderMalfunctionErrorTest.java | 17 ++--- .../charset/spi/CharsetProviderBasicTest.java | 26 ++++---- test/jdk/sun/nio/cs/StreamEncoderOut.java | 25 ++++---- .../sun/nio/cs/TestUnicodeReversedBOM.java | 47 +++++++------- 7 files changed, 124 insertions(+), 121 deletions(-) diff --git a/test/jdk/java/nio/charset/Charset/DefaultCharsetTest.java b/test/jdk/java/nio/charset/Charset/DefaultCharsetTest.java index be33ecb70f2..f3caf94775e 100644 --- a/test/jdk/java/nio/charset/Charset/DefaultCharsetTest.java +++ b/test/jdk/java/nio/charset/Charset/DefaultCharsetTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,22 +34,19 @@ * jdk.test.lib.Platform * jdk.test.lib.process.* * Default - * @run testng DefaultCharsetTest + * @run junit DefaultCharsetTest */ -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; import java.util.Map; +import java.util.stream.Stream; -import jdk.test.lib.Platform; import jdk.test.lib.process.ProcessTools; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; public class DefaultCharsetTest { @@ -58,28 +55,27 @@ public class DefaultCharsetTest { private static final Map env = pb.environment(); private static String UNSUPPORTED = null; - @BeforeClass + @BeforeAll public static void checkSupports() throws Exception { UNSUPPORTED = runWithLocale("nonexist"); } - @DataProvider - public static Iterator locales() { - List data = new ArrayList<>(); - data.add(new String[]{"en_US", "iso-8859-1"}); - data.add(new String[]{"ja_JP.utf8", "utf-8"}); - data.add(new String[]{"tr_TR", "iso-8859-9"}); - data.add(new String[]{"C", "us-ascii"}); - data.add(new String[]{"ja_JP", "x-euc-jp-linux"}); - data.add(new String[]{"ja_JP.eucjp", "x-euc-jp-linux"}); - data.add(new String[]{"ja_JP.ujis", "x-euc-jp-linux"}); - data.add(new String[]{"ja_JP.utf8", "utf-8"}); - return data.iterator(); + public static Stream locales() { + return Stream.of( + Arguments.of("en_US", "iso-8859-1"), + Arguments.of("ja_JP.utf8", "utf-8"), + Arguments.of("tr_TR", "iso-8859-9"), + Arguments.of("C", "us-ascii"), + Arguments.of("ja_JP", "x-euc-jp-linux"), + Arguments.of("ja_JP.eucjp", "x-euc-jp-linux"), + Arguments.of("ja_JP.ujis", "x-euc-jp-linux"), + Arguments.of("ja_JP.utf8", "utf-8") + ); } - @Test(dataProvider = "locales") - public void testDefaultCharset(String locale, String expectedCharset) - throws Exception { + @ParameterizedTest + @MethodSource("locales") + public void testDefaultCharset(String locale, String expectedCharset) throws Exception { String actual = runWithLocale(locale); if (UNSUPPORTED.equals(actual)) { System.out.println(locale + ": Locale not supported, skipping..."); diff --git a/test/jdk/java/nio/charset/Charset/ForName.java b/test/jdk/java/nio/charset/Charset/ForName.java index d8a1e2f72d3..6af903a3c8a 100644 --- a/test/jdk/java/nio/charset/Charset/ForName.java +++ b/test/jdk/java/nio/charset/Charset/ForName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,49 +25,51 @@ * @summary Unit test for forName(String, Charset) * @bug 8270490 * @modules jdk.charsets - * @run testng ForName + * @run junit ForName */ import java.nio.charset.Charset; -import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.StandardCharsets; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -@Test public class ForName { - @DataProvider - Object[][] params() { - return new Object[][] { - {"UTF-8", null, StandardCharsets.UTF_8}, - {"UTF-8", StandardCharsets.US_ASCII, StandardCharsets.UTF_8}, - {"windows-31j", StandardCharsets.US_ASCII, Charset.forName("windows-31j")}, - {"foo", StandardCharsets.US_ASCII, StandardCharsets.US_ASCII}, - {"foo", null, null}, - {"\u3042", null, null}, - {"\u3042", StandardCharsets.UTF_8, StandardCharsets.UTF_8}, - }; + static Stream params() { + return Stream.of( + Arguments.of("UTF-8", null, StandardCharsets.UTF_8), + Arguments.of("UTF-8", StandardCharsets.US_ASCII, StandardCharsets.UTF_8), + Arguments.of("windows-31j", StandardCharsets.US_ASCII, Charset.forName("windows-31j")), + Arguments.of("foo", StandardCharsets.US_ASCII, StandardCharsets.US_ASCII), + Arguments.of("foo", null, null), + Arguments.of("\u3042", null, null), + Arguments.of("\u3042", StandardCharsets.UTF_8, StandardCharsets.UTF_8) + ); } - @DataProvider - Object[][] paramsIAE() { - return new Object[][] { - {null, null}, - {null, StandardCharsets.UTF_8}, - }; + static Stream paramsIAE() { + return Stream.of( + Arguments.of(null, null), + Arguments.of(null, StandardCharsets.UTF_8) + ); } - @Test(dataProvider="params") - public void testForName_2arg(String name, Charset fallback, Charset expected) throws Exception { + @ParameterizedTest + @MethodSource("params") + public void testForName_2arg(String name, Charset fallback, Charset expected) { var cs = Charset.forName(name, fallback); - assertEquals(cs, expected); + assertEquals(expected, cs); } - @Test(dataProvider="paramsIAE", expectedExceptions=IllegalArgumentException.class) - public void testForName_2arg_IAE(String name, Charset fallback) throws Exception { - Charset.forName(name, fallback); + @ParameterizedTest + @MethodSource("paramsIAE") + public void testForName_2arg_IAE(String name, Charset fallback) { + assertThrows(IllegalArgumentException.class, () -> Charset.forName(name, fallback)); } } diff --git a/test/jdk/java/nio/charset/CharsetDecoder/CoderMalfunctionErrorTest.java b/test/jdk/java/nio/charset/CharsetDecoder/CoderMalfunctionErrorTest.java index 79091071ea3..5e830b955b6 100644 --- a/test/jdk/java/nio/charset/CharsetDecoder/CoderMalfunctionErrorTest.java +++ b/test/jdk/java/nio/charset/CharsetDecoder/CoderMalfunctionErrorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,26 +23,30 @@ /* @test * @bug 8253832 - * @run testng CoderMalfunctionErrorTest * @summary Check CoderMalfunctionError is thrown for any RuntimeException * on CharsetDecoder.decodeLoop() invocation. + * @run junit CoderMalfunctionErrorTest */ -import org.testng.annotations.Test; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.*; -@Test +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + public class CoderMalfunctionErrorTest { - @Test (expectedExceptions = CoderMalfunctionError.class) + + @Test public void testDecodeLoop() { - new CharsetDecoder(StandardCharsets.US_ASCII, 1, 1) { + assertThrows(CoderMalfunctionError.class, + () -> new CharsetDecoder(StandardCharsets.US_ASCII, 1, 1) { @Override protected CoderResult decodeLoop(ByteBuffer byteBuffer, CharBuffer charBuffer) { throw new RuntimeException("This exception should be wrapped in CoderMalfunctionError"); } - }.decode(null, null, true); + }.decode(null, null, true)); } } diff --git a/test/jdk/java/nio/charset/CharsetEncoder/CoderMalfunctionErrorTest.java b/test/jdk/java/nio/charset/CharsetEncoder/CoderMalfunctionErrorTest.java index ffd73164d9f..e917126d461 100644 --- a/test/jdk/java/nio/charset/CharsetEncoder/CoderMalfunctionErrorTest.java +++ b/test/jdk/java/nio/charset/CharsetEncoder/CoderMalfunctionErrorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,26 +23,29 @@ /* @test * @bug 8253832 - * @run testng CoderMalfunctionErrorTest + * @run junit CoderMalfunctionErrorTest * @summary Check CoderMalfunctionError is thrown for any RuntimeException * on CharsetEncoder.encodeLoop() invocation. */ -import org.testng.annotations.Test; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.*; -@Test +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + public class CoderMalfunctionErrorTest { - @Test (expectedExceptions = CoderMalfunctionError.class) + @Test public void testEncodeLoop() { - new CharsetEncoder(StandardCharsets.US_ASCII, 1, 1) { + assertThrows(CoderMalfunctionError.class, + () -> new CharsetEncoder(StandardCharsets.US_ASCII, 1, 1) { @Override protected CoderResult encodeLoop(CharBuffer charBuffer, ByteBuffer byteBuffer) { throw new RuntimeException("This exception should be wrapped in CoderMalfunctionError"); } - }.encode(null, null, true); + }.encode(null, null, true)); } } diff --git a/test/jdk/java/nio/charset/spi/CharsetProviderBasicTest.java b/test/jdk/java/nio/charset/spi/CharsetProviderBasicTest.java index abf46d81b1c..8b0e74d4360 100644 --- a/test/jdk/java/nio/charset/spi/CharsetProviderBasicTest.java +++ b/test/jdk/java/nio/charset/spi/CharsetProviderBasicTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,28 +35,25 @@ * jdk.test.lib.util.JarUtils * FooCharset FooProvider CharsetTest * @run driver SetupJar - * @run testng CharsetProviderBasicTest + * @run junit CharsetProviderBasicTest */ import java.io.File; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.stream.Stream; import jdk.test.lib.JDKToolFinder; import jdk.test.lib.Utils; import jdk.test.lib.process.ProcessTools; - -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static java.util.Arrays.asList; public class CharsetProviderBasicTest { - private static final String TEST_SRC = System.getProperty("test.src"); - private static final List DEFAULT_CSS = List.of( "US-ASCII", "8859_1", "iso-ir-6", "UTF-16", "windows-1252", "!BAR", "cp1252" ); @@ -69,14 +66,15 @@ public class CharsetProviderBasicTest { .equals(locale); } - @DataProvider - public static Iterator testCases() { - return Stream.of("", "ja_JP.eucJP", "tr_TR") - .map(locale -> new Object[]{locale, "FOO"}) - .iterator(); + public static Stream testCases() { + return Stream.of("", + "ja_JP.eucJP", + "tr_TR") + .map(locale -> Arguments.of(locale, "FOO")); } - @Test(dataProvider = "testCases") + @ParameterizedTest + @MethodSource("testCases") public void testDefaultCharset(String locale, String css) throws Throwable { if ((System.getProperty("os.name").startsWith("Windows") || !checkSupports(locale)) && (!locale.isEmpty())) { diff --git a/test/jdk/sun/nio/cs/StreamEncoderOut.java b/test/jdk/sun/nio/cs/StreamEncoderOut.java index 90a22512311..90b4aee838d 100644 --- a/test/jdk/sun/nio/cs/StreamEncoderOut.java +++ b/test/jdk/sun/nio/cs/StreamEncoderOut.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,10 +24,8 @@ /* @test @bug 8030179 @summary test if the charset encoder deails with surrogate correctly - * @run testng/othervm -esa StreamEncoderOut + * @run junit/othervm -esa StreamEncoderOut */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.io.OutputStream; @@ -35,9 +33,12 @@ import java.io.OutputStreamWriter; import java.nio.charset.Charset; import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import static java.util.stream.Collectors.joining; -@Test public class StreamEncoderOut { enum Input { @@ -57,14 +58,11 @@ public class StreamEncoderOut { } } - @DataProvider(name = "CharsetAndString") - // [Charset, Input] - public static Object[][] makeStreamTestData() { + public static Stream makeStreamTestData() { // Cross product of supported charsets and inputs - return Charset.availableCharsets().values().stream(). - filter(Charset::canEncode). - flatMap(cs -> Stream.of(Input.values()).map(i -> new Object[]{cs, i})). - toArray(Object[][]::new); + return Charset.availableCharsets().values().stream() + .filter(Charset::canEncode) + .flatMap(cs -> Stream.of(Input.values()).map(i -> Arguments.of(cs, i))); } private static String generate(String s, int n) { @@ -79,7 +77,8 @@ public class StreamEncoderOut { public void write(int b) throws IOException {} }; - @Test(dataProvider = "CharsetAndString") + @ParameterizedTest + @MethodSource("makeStreamTestData") public void test(Charset cs, Input input) throws IOException { OutputStreamWriter w = new OutputStreamWriter(DEV_NULL, cs); String t = generate(input.value, 8193); diff --git a/test/jdk/sun/nio/cs/TestUnicodeReversedBOM.java b/test/jdk/sun/nio/cs/TestUnicodeReversedBOM.java index d76bf2f6034..ca0798c13e5 100644 --- a/test/jdk/sun/nio/cs/TestUnicodeReversedBOM.java +++ b/test/jdk/sun/nio/cs/TestUnicodeReversedBOM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,16 +26,18 @@ * @bug 8216140 * @summary Test reversed BOM (U+FFFE) in the middle of a byte buffer * passes through during decoding with UnicodeDecoder. - * @run testng TestUnicodeReversedBOM + * @run junit TestUnicodeReversedBOM */ + import java.nio.charset.*; import java.nio.*; import java.util.*; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -@Test public class TestUnicodeReversedBOM { private static byte[] A_REVERSED_BE = {(byte)0x0, (byte)'A', (byte)0xff, (byte)0xfe}; @@ -46,26 +48,25 @@ public class TestUnicodeReversedBOM { private static byte[] BOM_REVERSED_LE = {(byte)0xff, (byte)0xfe, (byte)0xfe, (byte)0xff}; - @DataProvider - // [(byte[])byte array, (Charset)cs] - public static Object[][] ReversedBOM() { - return new Object[][] { - {A_REVERSED_BE, StandardCharsets.UTF_16}, - {A_REVERSED_LE, StandardCharsets.UTF_16}, - {A_REVERSED_BE, StandardCharsets.UTF_16BE}, - {A_REVERSED_LE, StandardCharsets.UTF_16BE}, - {A_REVERSED_BE, StandardCharsets.UTF_16LE}, - {A_REVERSED_LE, StandardCharsets.UTF_16LE}, - {BOM_REVERSED_BE, StandardCharsets.UTF_16}, - {BOM_REVERSED_LE, StandardCharsets.UTF_16}, - {BOM_REVERSED_BE, StandardCharsets.UTF_16BE}, - {BOM_REVERSED_LE, StandardCharsets.UTF_16BE}, - {BOM_REVERSED_BE, StandardCharsets.UTF_16LE}, - {BOM_REVERSED_LE, StandardCharsets.UTF_16LE}, - }; + public static Stream ReversedBOM() { + return Stream.of( + Arguments.of(A_REVERSED_BE, StandardCharsets.UTF_16), + Arguments.of(A_REVERSED_LE, StandardCharsets.UTF_16), + Arguments.of(A_REVERSED_BE, StandardCharsets.UTF_16BE), + Arguments.of(A_REVERSED_LE, StandardCharsets.UTF_16BE), + Arguments.of(A_REVERSED_BE, StandardCharsets.UTF_16LE), + Arguments.of(A_REVERSED_LE, StandardCharsets.UTF_16LE), + Arguments.of(BOM_REVERSED_BE, StandardCharsets.UTF_16), + Arguments.of(BOM_REVERSED_LE, StandardCharsets.UTF_16), + Arguments.of(BOM_REVERSED_BE, StandardCharsets.UTF_16BE), + Arguments.of(BOM_REVERSED_LE, StandardCharsets.UTF_16BE), + Arguments.of(BOM_REVERSED_BE, StandardCharsets.UTF_16LE), + Arguments.of(BOM_REVERSED_LE, StandardCharsets.UTF_16LE) + ); } - @Test(dataProvider = "ReversedBOM") + @ParameterizedTest + @MethodSource("ReversedBOM") public void testReversedBOM(byte[] ba, Charset cs) throws CharacterCodingException { cs.newDecoder() .onMalformedInput(CodingErrorAction.REPORT) From b6de5aed2c2a5493c089b7f8493e953b13e4c2fa Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Wed, 18 Mar 2026 16:45:53 +0000 Subject: [PATCH 011/160] 8379699: H3ConnectionPoolTest::testH2H3WithTwoAltSVC fails intermittently Reviewed-by: syan, jpai --- .../http3/H3ConnectionPoolTest.java | 56 +++++++------------ 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java b/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java index 614d564005b..12956604484 100644 --- a/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java +++ b/test/jdk/java/net/httpclient/http3/H3ConnectionPoolTest.java @@ -25,11 +25,13 @@ * @test * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.http2.Http2TestServer - * jdk.test.lib.Asserts * jdk.test.lib.Utils * jdk.test.lib.net.SimpleSSLContext * @run junit/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors,http3,quic:hs * -Djdk.internal.httpclient.debug=false + * -Djdk.httpclient.keepalive.timeout.h3=480 + * -Djdk.httpclient.quic.idleTimeout=480 + * -Djdk.test.server.quic.idleTimeout=480 * H3ConnectionPoolTest */ @@ -44,7 +46,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.function.Supplier; import javax.net.ssl.SSLContext; @@ -60,11 +61,11 @@ import static java.net.http.HttpOption.H3_DISCOVERY; import static java.net.http.HttpOption.Http3DiscoveryMode.ALT_SVC; import static java.net.http.HttpOption.Http3DiscoveryMode.ANY; import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; -import static jdk.test.lib.Asserts.assertEquals; -import static jdk.test.lib.Asserts.assertNotEquals; -import static jdk.test.lib.Asserts.assertTrue; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class H3ConnectionPoolTest implements HttpServerAdapters { @@ -257,7 +258,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { HttpResponse response2 = client.send(request2, BodyHandlers.ofString()); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), response1.connectionLabel().get()); + assertNotEquals(response1.connectionLabel().get(), response2.connectionLabel().get()); // second request with HTTP3_URI_ONLY should reuse a created connection // It should reuse the advertised connection (from response2) if same @@ -295,8 +296,8 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { HttpResponse response2 = client.send(request2, BodyHandlers.ofString()); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), h2resp2.connectionLabel().get()); - assertNotEquals(response2.connectionLabel().get(), response1.connectionLabel().get()); + assertNotEquals(h2resp2.connectionLabel().get(), response2.connectionLabel().get()); + assertNotEquals(response1.connectionLabel().get(), response2.connectionLabel().get()); // third request with ALT_SVC should reuse the same advertised // connection (from response2), regardless of same origin... @@ -304,16 +305,16 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { HttpResponse response3 = client.send(request3, BodyHandlers.ofString()); assertEquals(HTTP_3, response3.version()); checkStatus(200, response3.statusCode()); - assertEquals(response3.connectionLabel().get(), response2.connectionLabel().get()); - assertNotEquals(response3.connectionLabel().get(), response1.connectionLabel().get()); + assertEquals(response2.connectionLabel().get(), response3.connectionLabel().get()); + assertNotEquals(response1.connectionLabel().get(), response3.connectionLabel().get()); // fourth request with HTTP_3_URI_ONLY should reuse the first connection, // and not reuse the second. HttpRequest request4 = req1Builder.copy().build(); HttpResponse response4 = client.send(request4, BodyHandlers.ofString()); assertEquals(HTTP_3, response4.version()); - assertEquals(response4.connectionLabel().get(), response1.connectionLabel().get()); - assertNotEquals(response4.connectionLabel().get(), response3.connectionLabel().get()); + assertEquals(response1.connectionLabel().get(), response4.connectionLabel().get()); + assertNotEquals(response3.connectionLabel().get(), response4.connectionLabel().get()); checkStatus(200, response1.statusCode()); } else { System.out.println("WARNING: Couldn't create HTTP/3 server on same port! Can't test all..."); @@ -329,7 +330,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { HttpResponse response2 = client.send(request2, BodyHandlers.ofString()); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), h2resp2.connectionLabel().get()); + assertNotEquals(h2resp2.connectionLabel().get(), response2.connectionLabel().get()); // third request with ALT_SVC should reuse the same advertised // connection (from response2), regardless of same origin... @@ -337,7 +338,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { HttpResponse response3 = client.send(request3, BodyHandlers.ofString()); assertEquals(HTTP_3, response3.version()); checkStatus(200, response3.statusCode()); - assertEquals(response3.connectionLabel().get(), response2.connectionLabel().get()); + assertEquals(response2.connectionLabel().get(), response3.connectionLabel().get()); } } finally { http3OnlyServer.stop(); @@ -417,7 +418,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { response2); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), c1Label); + assertNotEquals(c1Label, response2.connectionLabel().get()); if (i == 0) { c2Label = response2.connectionLabel().get(); } @@ -494,8 +495,8 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { if (i == 0) { c2Label = response2.connectionLabel().get(); } - assertNotEquals(response2.connectionLabel().get(), h2resp2.connectionLabel().get()); - assertNotEquals(response2.connectionLabel().get(), c1Label); + assertNotEquals(h2resp2.connectionLabel().get(), response2.connectionLabel().get()); + assertNotEquals(c1Label, response2.connectionLabel().get()); assertEquals(c2Label, response2.connectionLabel().orElse(null)); } var expectedLabels = Set.of(c1Label, c2Label); @@ -507,7 +508,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { response3); assertEquals(HTTP_3, response3.version()); checkStatus(200, response3.statusCode()); - assertNotEquals(response3.connectionLabel().get(), h2resp2.connectionLabel().get()); + assertNotEquals(h2resp2.connectionLabel().get(), response3.connectionLabel().get()); var label = response3.connectionLabel().orElse(""); assertTrue(expectedLabels.contains(label), "Unexpected label: %s not in %s" .formatted(label, expectedLabels)); @@ -526,7 +527,7 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { HttpResponse response2 = client.send(request2, BodyHandlers.ofString()); assertEquals(HTTP_3, response2.version()); checkStatus(200, response2.statusCode()); - assertNotEquals(response2.connectionLabel().get(), h2resp2.connectionLabel().get()); + assertNotEquals(h2resp2.connectionLabel().get(), response2.connectionLabel().get()); // third request with ALT_SVC should reuse the same advertised // connection (from response2), regardless of same origin... @@ -560,21 +561,4 @@ public class H3ConnectionPoolTest implements HttpServerAdapters { } } - static void checkStrings(String expected, String found) throws Exception { - if (!expected.equals(found)) { - System.err.printf("Test failed: wrong string %s/%s\n", - expected, found); - throw new RuntimeException("Test failed"); - } - } - - - static T logExceptionally(String desc, Throwable t) { - System.out.println(desc + " failed: " + t); - System.err.println(desc + " failed: " + t); - if (t instanceof RuntimeException r) throw r; - if (t instanceof Error e) throw e; - throw new CompletionException(t); - } - } From 7d805e113948196ac4e45ac05e09413e63b9bb46 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Wed, 18 Mar 2026 16:57:52 +0000 Subject: [PATCH 012/160] 8380222: Refactor test/jdk/java/lang/Character TestNG tests to JUnit Reviewed-by: jlu, bpb, liach, iris --- .../lang/Character/Latin1CaseConversion.java | 47 +++++++++---------- .../UnicodeBlock/NumberEntities.java | 17 +++---- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/test/jdk/java/lang/Character/Latin1CaseConversion.java b/test/jdk/java/lang/Character/Latin1CaseConversion.java index a176bd4b002..436193c5f16 100644 --- a/test/jdk/java/lang/Character/Latin1CaseConversion.java +++ b/test/jdk/java/lang/Character/Latin1CaseConversion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,17 +21,16 @@ * questions. */ -import org.testng.annotations.Test; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; /** * @test * @bug 8302877 * @summary Provides exhaustive verification of Character.toUpperCase and Character.toLowerCase * for all code points in the latin1 range 0-255. - * @run testng Latin1CaseConversion + * @run junit Latin1CaseConversion */ public class Latin1CaseConversion { @@ -44,41 +43,41 @@ public class Latin1CaseConversion { if (c < 0x41) { // Before A assertUnchanged(upper, lower, c); } else if (c <= 0x5A) { // A-Z - assertEquals(upper, c); - assertEquals(lower, c + 32); + assertEquals(c, upper); + assertEquals(c + 32, lower); } else if (c < 0x61) { // Between Z and a assertUnchanged(upper, lower, c); } else if (c <= 0x7A) { // a-z - assertEquals(upper, c - 32); - assertEquals(lower, c); + assertEquals(c - 32, upper); + assertEquals(c, lower); } else if (c < 0xB5) { // Between z and Micro Sign assertUnchanged(upper, lower, c); } else if (c == 0xB5) { // Special case for Micro Sign - assertEquals(upper, 0x39C); - assertEquals(lower, c); + assertEquals(0x39C, upper); + assertEquals(c, lower); } else if (c < 0xC0) { // Between my and A-grave assertUnchanged(upper, lower, c); } else if (c < 0xD7) { // A-grave - O with Diaeresis - assertEquals(upper, c); - assertEquals(lower, c + 32); + assertEquals(c, upper); + assertEquals(c + 32, lower); } else if (c == 0xD7) { // Multiplication assertUnchanged(upper, lower, c); } else if (c <= 0xDE) { // O with slash - Thorn - assertEquals(upper, c); - assertEquals(lower, c + 32); + assertEquals(c, upper); + assertEquals(c + 32, lower); } else if (c == 0xDF) { // Sharp s assertUnchanged(upper, lower, c); } else if (c < 0xF7) { // a-grave - divsion - assertEquals(upper, c - 32); - assertEquals(lower, c); + assertEquals(c - 32, upper); + assertEquals(c, lower); } else if (c == 0xF7) { // Division assertUnchanged(upper, lower, c); } else if (c < 0xFF) { // o with slash - thorn - assertEquals(upper, c - 32); - assertEquals(lower, c); + assertEquals(c - 32, upper); + assertEquals(c, lower); } else if (c == 0XFF) { // Special case for y with Diaeresis - assertEquals(upper, 0x178); - assertEquals(lower, c); + assertEquals(0x178, upper); + assertEquals(c, lower); } else { fail("Uncovered code point: " + Integer.toHexString(c)); } @@ -86,7 +85,7 @@ public class Latin1CaseConversion { } private static void assertUnchanged(int upper, int lower, int c) { - assertEquals(upper, c); - assertEquals(lower, c); + assertEquals(c, upper); + assertEquals(c, lower); } } diff --git a/test/jdk/java/lang/Character/UnicodeBlock/NumberEntities.java b/test/jdk/java/lang/Character/UnicodeBlock/NumberEntities.java index 30382c3cfe3..86b9fe6ea69 100644 --- a/test/jdk/java/lang/Character/UnicodeBlock/NumberEntities.java +++ b/test/jdk/java/lang/Character/UnicodeBlock/NumberEntities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,17 +28,17 @@ * of Character.UnicodeBlock constants. Also checks the size of * Character.UnicodeScript's "aliases" map. * @modules java.base/java.lang:open - * @run testng NumberEntities + * @run junit NumberEntities */ -import static org.testng.Assert.assertEquals; -import org.testng.annotations.Test; - import java.lang.reflect.Field; import java.util.Map; -@Test +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class NumberEntities { + @Test public void test_UnicodeBlock_NumberEntities() throws Throwable { // The number of entries in Character.UnicodeBlock.map. // See src/java.base/share/classes/java/lang/Character.java @@ -46,13 +46,14 @@ public class NumberEntities { Field m = Character.UnicodeBlock.class.getDeclaredField("map"); n.setAccessible(true); m.setAccessible(true); - assertEquals(((Map)m.get(null)).size(), n.getInt(null)); + assertEquals(n.getInt(null), ((Map)m.get(null)).size()); } + @Test public void test_UnicodeScript_aliases() throws Throwable { // The number of entries in Character.UnicodeScript.aliases. // See src/java.base/share/classes/java/lang/Character.java Field aliases = Character.UnicodeScript.class.getDeclaredField("aliases"); aliases.setAccessible(true); - assertEquals(((Map)aliases.get(null)).size(), Character.UnicodeScript.UNKNOWN.ordinal() + 1); + assertEquals(Character.UnicodeScript.UNKNOWN.ordinal() + 1, ((Map)aliases.get(null)).size()); } } From f3069680e48712a7cacc4208550816300f68fbf4 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 18 Mar 2026 17:55:00 +0000 Subject: [PATCH 013/160] 8380303: Obsolete unused flag UseXMMForArrayCopy Reviewed-by: kvn, adinn, ayang --- src/hotspot/cpu/x86/vm_version_x86.cpp | 13 ------------- src/hotspot/share/runtime/arguments.cpp | 2 +- src/hotspot/share/runtime/globals.hpp | 3 --- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index a800feea0a8..747d662f668 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1508,9 +1508,6 @@ void VM_Version::get_processor_features() { MaxLoopPad = 11; } #endif // COMPILER2 - if (FLAG_IS_DEFAULT(UseXMMForArrayCopy)) { - UseXMMForArrayCopy = true; // use SSE2 movq on new ZX cpus - } if (supports_sse4_2()) { // new ZX cpus if (FLAG_IS_DEFAULT(UseUnalignedLoadStores)) { UseUnalignedLoadStores = true; // use movdqu on newest ZX cpus @@ -1571,10 +1568,6 @@ void VM_Version::get_processor_features() { if (FLAG_IS_DEFAULT(AllocatePrefetchInstr)) { FLAG_SET_DEFAULT(AllocatePrefetchInstr, 3); } - // On family 15h processors use XMM and UnalignedLoadStores for Array Copy - if (supports_sse2() && FLAG_IS_DEFAULT(UseXMMForArrayCopy)) { - FLAG_SET_DEFAULT(UseXMMForArrayCopy, true); - } if (supports_sse2() && FLAG_IS_DEFAULT(UseUnalignedLoadStores)) { FLAG_SET_DEFAULT(UseUnalignedLoadStores, true); } @@ -1591,9 +1584,6 @@ void VM_Version::get_processor_features() { if (cpu_family() >= 0x17) { // On family >=17h processors use XMM and UnalignedLoadStores // for Array Copy - if (supports_sse2() && FLAG_IS_DEFAULT(UseXMMForArrayCopy)) { - FLAG_SET_DEFAULT(UseXMMForArrayCopy, true); - } if (supports_sse2() && FLAG_IS_DEFAULT(UseUnalignedLoadStores)) { FLAG_SET_DEFAULT(UseUnalignedLoadStores, true); } @@ -1640,9 +1630,6 @@ void VM_Version::get_processor_features() { } #endif // COMPILER2 - if (FLAG_IS_DEFAULT(UseXMMForArrayCopy)) { - UseXMMForArrayCopy = true; // use SSE2 movq on new Intel cpus - } if ((supports_sse4_2() && supports_ht()) || supports_avx()) { // Newest Intel cpus if (FLAG_IS_DEFAULT(UseUnalignedLoadStores)) { UseUnalignedLoadStores = true; // use movdqu on newest Intel cpus diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 449ac8e826c..778e9c1fc6d 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -554,7 +554,7 @@ static SpecialFlag const special_jvm_flags[] = { { "NewSizeThreadIncrease", JDK_Version::undefined(), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "NeverActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "AlwaysActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, - + { "UseXMMForArrayCopy", JDK_Version::undefined(), JDK_Version::jdk(27), JDK_Version::jdk(28) }, #ifdef ASSERT { "DummyObsoleteTestFlag", JDK_Version::undefined(), JDK_Version::jdk(18), JDK_Version::undefined() }, #endif diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 0c627e27209..17e10e2f87c 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -798,9 +798,6 @@ const int ObjectAlignmentInBytes = 8; "Number of OutOfMemoryErrors preallocated with backtrace") \ range(0, 1024) \ \ - product(bool, UseXMMForArrayCopy, false, \ - "Use SSE2 MOVQ instruction for Arraycopy") \ - \ develop(bool, PrintFieldLayout, false, \ "Print field layout for each class") \ \ From 4f2debe697a1ba35ee480fc66d5d937aa4ac2f30 Mon Sep 17 00:00:00 2001 From: Christoph Langer Date: Wed, 18 Mar 2026 18:58:55 +0000 Subject: [PATCH 014/160] 8380033: Test java/util/jar/Manifest/IncludeInExceptionsTest.java could be more agnostic to VM default Reviewed-by: mbaesken --- .../jar/Manifest/IncludeInExceptionsTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java b/test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java index bb3cd6e23ef..52d3783b4cf 100644 --- a/test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java +++ b/test/jdk/java/util/jar/Manifest/IncludeInExceptionsTest.java @@ -21,9 +21,6 @@ * questions. */ -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; @@ -34,17 +31,23 @@ import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; +import static sun.security.util.SecurityProperties.INCLUDE_JAR_NAME_IN_EXCEPTIONS; /* * @test * @bug 8216362 - * @run junit/othervm -Djdk.includeInExceptions=jar IncludeInExceptionsTest - * @run junit/othervm IncludeInExceptionsTest * @summary Verify that the property jdk.includeInExceptions works as expected - * when an error occurs while reading an invalid Manifest file. + * when an error occurs while reading an invalid Manifest file. + * @modules java.base/sun.security.util + * @run junit/othervm -Djdk.includeInExceptions=jar IncludeInExceptionsTest + * @run junit/othervm -Djdk.includeInExceptions= IncludeInExceptionsTest + * @run junit/othervm IncludeInExceptionsTest */ /* @@ -53,8 +56,6 @@ import static org.junit.jupiter.api.Assertions.fail; */ public class IncludeInExceptionsTest { - private static final boolean includeInExceptions = System.getProperty("jdk.includeInExceptions") != null; - static final String FILENAME = "Unique-Filename-Expected-In_Msg.jar"; static final byte[] INVALID_MANIFEST = ( @@ -79,9 +80,9 @@ public class IncludeInExceptionsTest { void testInvalidManifest(Callable attempt) { var ioException = assertThrows(IOException.class, attempt::call); boolean foundFileName = ioException.getMessage().contains(FILENAME); - if (includeInExceptions && !foundFileName) { + if (INCLUDE_JAR_NAME_IN_EXCEPTIONS && !foundFileName) { fail("JAR file name expected but not found in error message"); - } else if (foundFileName && !includeInExceptions) { + } else if (foundFileName && !INCLUDE_JAR_NAME_IN_EXCEPTIONS) { fail("JAR file name found but should not be in error message"); } } @@ -96,4 +97,3 @@ public class IncludeInExceptionsTest { ); } } - From 30145a4bd7919d5e616251056903826d3ab01702 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Wed, 18 Mar 2026 20:40:42 +0000 Subject: [PATCH 015/160] 8379952: Revert JDK-8365711: Restore protected visibility of menuBarHeight and hotTrackingOn Reviewed-by: azvegint --- .../com/sun/java/swing/plaf/windows/WindowsMenuUI.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java index a7aca0c5ccf..259c32c74f4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java @@ -51,8 +51,8 @@ import com.sun.java.swing.plaf.windows.TMSchema.State; * Windows rendition of the component. */ public final class WindowsMenuUI extends BasicMenuUI { - private Integer menuBarHeight; - private boolean hotTrackingOn; + protected Integer menuBarHeight; + protected boolean hotTrackingOn; final WindowsMenuItemUIAccessor accessor = new WindowsMenuItemUIAccessor() { From 37cdc7977fc346c4b66d10fa149d6f28553f7002 Mon Sep 17 00:00:00 2001 From: Alexander Zvegintsev Date: Wed, 18 Mar 2026 20:47:48 +0000 Subject: [PATCH 016/160] 8380228: JNI primitive type mismatch in Java_com_sun_java_swing_plaf_gtk_GTKLookAndFeel_applyThemeIfNeeded of swing_GTKEngine.c:403 (ID: 52423) Reviewed-by: psadhukhan, serb --- .../unix/native/libawt_xawt/awt/swing_GTKEngine.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c index 3b7b2880316..1c29e5168ba 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -400,5 +400,5 @@ Java_com_sun_java_swing_plaf_gtk_GTKLookAndFeel_applyThemeIfNeeded(JNIEnv *env, const gboolean result = gtk->apply_theme_if_needed(); gtk->gdk_threads_leave(); - return result; + return result ? JNI_TRUE : JNI_FALSE; } From 27a4ed5b7adbf6652b0ff82158f80d90d3f9ba65 Mon Sep 17 00:00:00 2001 From: Rui Li Date: Wed, 18 Mar 2026 23:11:48 +0000 Subject: [PATCH 017/160] 8375568: Shenandoah: Abbreviate thread names in display when length constraints apply Reviewed-by: wkemper, kdnilsen --- src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp | 2 +- .../share/gc/shenandoah/shenandoahGenerationalControlThread.cpp | 2 +- src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp | 2 +- src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp | 2 +- src/hotspot/share/gc/shenandoah/shenandoahUncommitThread.cpp | 2 +- .../hotspot/jtreg/gc/ergonomics/TestInitialGCThreadLogging.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index c5607421265..48183507124 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -45,7 +45,7 @@ ShenandoahControlThread::ShenandoahControlThread() : _requested_gc_cause(GCCause::_no_gc), _degen_point(ShenandoahGC::_degenerated_outside_cycle), _control_lock(CONTROL_LOCK_RANK, "ShenandoahControl_lock", true) { - set_name("Shenandoah Control Thread"); + set_name("ShenControl"); create_and_start(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 5f201a404d8..1edff443ded 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -55,7 +55,7 @@ ShenandoahGenerationalControlThread::ShenandoahGenerationalControlThread() : _heap(ShenandoahGenerationalHeap::heap()), _age_period(0) { shenandoah_assert_generational(); - set_name("Shenandoah Control Thread"); + set_name("ShenControl"); create_and_start(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index c6889351161..c0a4cfc34fb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -261,7 +261,7 @@ jint ShenandoahHeap::initialize() { // // Worker threads must be initialized after the barrier is configured // - _workers = new ShenandoahWorkerThreads("Shenandoah GC Threads", _max_workers); + _workers = new ShenandoahWorkerThreads("ShenWorker", _max_workers); if (_workers == nullptr) { vm_exit_during_initialization("Failed necessary allocation."); } else { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp index fe92a3a3e08..28094a1d57d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp @@ -43,7 +43,7 @@ ShenandoahRegulatorThread::ShenandoahRegulatorThread(ShenandoahGenerationalContr _young_heuristics = _heap->young_generation()->heuristics(); _global_heuristics = _heap->global_generation()->heuristics(); - set_name("Shenandoah Regulator Thread"); + set_name("ShenRegulator"); create_and_start(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUncommitThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUncommitThread.cpp index ec708b198e7..f2fcd39daf5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUncommitThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUncommitThread.cpp @@ -32,7 +32,7 @@ ShenandoahUncommitThread::ShenandoahUncommitThread(ShenandoahHeap* heap) : _heap(heap), _uncommit_lock(Mutex::safepoint - 2, "ShenandoahUncommit_lock", true) { - set_name("Shenandoah Uncommit Thread"); + set_name("ShenUncommit"); create_and_start(); // Allow uncommits. This is managed by the control thread during a GC. diff --git a/test/hotspot/jtreg/gc/ergonomics/TestInitialGCThreadLogging.java b/test/hotspot/jtreg/gc/ergonomics/TestInitialGCThreadLogging.java index 69a3e7b8742..52aa1bc26e5 100644 --- a/test/hotspot/jtreg/gc/ergonomics/TestInitialGCThreadLogging.java +++ b/test/hotspot/jtreg/gc/ergonomics/TestInitialGCThreadLogging.java @@ -55,7 +55,7 @@ public class TestInitialGCThreadLogging { if (GC.Shenandoah.isSupported()) { noneGCSupported = false; - testInitialGCThreadLogging("UseShenandoahGC", "Shenandoah GC Thread"); + testInitialGCThreadLogging("UseShenandoahGC", "ShenWorker"); } if (noneGCSupported) { From deccad2e95549611631543211d69ca7fff59c5e7 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Thu, 19 Mar 2026 05:04:00 +0000 Subject: [PATCH 018/160] 8377907: (process) Race in ProcessBuilder can cause JVM hangs Reviewed-by: simonis, rriggs --- make/test/JtregNativeJdk.gmk | 8 +- .../unix/native/jspawnhelper/jspawnhelper.c | 72 ++++--- .../unix/native/libjava/ProcessImpl_md.c | 159 +++++++++++--- src/java.base/unix/native/libjava/childproc.c | 121 +++++++---- src/java.base/unix/native/libjava/childproc.h | 17 +- test/jdk/java/lang/ProcessBuilder/Basic.java | 39 +++- .../ConcNativeForkTest.java | 129 ++++++++++++ .../ConcNativeForkTest/libConcNativeFork.c | 147 +++++++++++++ .../ProcessBuilder/FDLeakTest/FDLeakTest.java | 8 +- .../ProcessBuilder/JspawnhelperProtocol.java | 18 +- .../ProcessBuilder/JspawnhelperWarnings.java | 9 +- .../ProcessBuilder/NonPipelineLeaksFD.java | 182 ++++++++++++++++ .../lang/ProcessBuilder/PipelineLeaksFD.java | 18 +- .../PipesCloseOnExecTest.java | 115 ++++++++++ .../libPipesCloseOnExec.c | 198 ++++++++++++++++++ test/lib/native/testlib_thread_barriers.h | 83 ++++++++ 16 files changed, 1190 insertions(+), 133 deletions(-) create mode 100644 test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/ConcNativeForkTest.java create mode 100644 test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/libConcNativeFork.c create mode 100644 test/jdk/java/lang/ProcessBuilder/NonPipelineLeaksFD.java create mode 100644 test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/PipesCloseOnExecTest.java create mode 100644 test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/libPipesCloseOnExec.c create mode 100644 test/lib/native/testlib_thread_barriers.h diff --git a/make/test/JtregNativeJdk.gmk b/make/test/JtregNativeJdk.gmk index 0482011f561..6774e708f99 100644 --- a/make/test/JtregNativeJdk.gmk +++ b/make/test/JtregNativeJdk.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -63,7 +63,8 @@ ifeq ($(call isTargetOs, windows), true) BUILD_JDK_JTREG_EXCLUDE += libDirectIO.c libInheritedChannel.c \ libExplicitAttach.c libImplicitAttach.c \ exelauncher.c libFDLeaker.c exeFDLeakTester.c \ - libChangeSignalDisposition.c exePrintSignalDisposition.c + libChangeSignalDisposition.c exePrintSignalDisposition.c \ + libConcNativeFork.c libPipesCloseOnExec.c BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerTest := $(LIBCXX) BUILD_JDK_JTREG_EXECUTABLES_LIBS_exerevokeall := advapi32.lib @@ -77,6 +78,9 @@ else BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libLinkerInvokerUnnamed := -pthread BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libLinkerInvokerModule := -pthread BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libLoaderLookupInvoker := -pthread + BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libConcNativeFork := -pthread + BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libPipesCloseOnExec := -pthread + BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libLoaderLookupInvoker := -pthread BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libExplicitAttach := -pthread BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libImplicitAttach := -pthread diff --git a/src/java.base/unix/native/jspawnhelper/jspawnhelper.c b/src/java.base/unix/native/jspawnhelper/jspawnhelper.c index f26d0d618e3..2523fb30acc 100644 --- a/src/java.base/unix/native/jspawnhelper/jspawnhelper.c +++ b/src/java.base/unix/native/jspawnhelper/jspawnhelper.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,12 +26,13 @@ #include #include #include +#include +#include #include #include #include #include #include -#include #include "childproc.h" #include "childproc_errorcodes.h" @@ -148,17 +149,37 @@ void initChildStuff (int fdin, int fdout, ChildStuff *c) { offset += sp.parentPathvBytes; } +#ifdef DEBUG +static void checkIsValid(int fd) { + if (!fdIsValid(fd)) { + puts(usageErrorText); + sendErrorCodeAndExit(-1, ESTEP_JSPAWN_INVALID_FD, fd, errno); + } +} +static void checkIsPipe(int fd) { + checkIsValid(fd); + if (!fdIsPipe(fd)) { + puts(usageErrorText); + sendErrorCodeAndExit(-1, ESTEP_JSPAWN_NOT_A_PIPE, fd, errno); + } +} +static void checkFileDescriptorSetup() { + checkIsValid(STDIN_FILENO); + checkIsValid(STDOUT_FILENO); + checkIsValid(STDERR_FILENO); + checkIsPipe(FAIL_FILENO); + checkIsPipe(CHILDENV_FILENO); +} +#endif // DEBUG + int main(int argc, char *argv[]) { ChildStuff c; - struct stat buf; - /* argv[1] contains the fd number to read all the child info */ - int r, fdinr, fdinw, fdout; #ifdef DEBUG jtregSimulateCrash(0, 4); #endif - if (argc != 3) { + if (argc != 2) { printf("Incorrect number of arguments: %d\n", argc); puts(usageErrorText); sendErrorCodeAndExit(-1, ESTEP_JSPAWN_ARG_ERROR, 0, 0); @@ -170,32 +191,21 @@ int main(int argc, char *argv[]) { sendErrorCodeAndExit(-1, ESTEP_JSPAWN_VERSION_ERROR, 0, 0); } - r = sscanf (argv[2], "%d:%d:%d", &fdinr, &fdinw, &fdout); - if (r == 3 && fcntl(fdinr, F_GETFD) != -1 && fcntl(fdinw, F_GETFD) != -1) { - fstat(fdinr, &buf); - if (!S_ISFIFO(buf.st_mode)) { - printf("Incorrect input pipe\n"); - puts(usageErrorText); - sendErrorCodeAndExit(-1, ESTEP_JSPAWN_NOT_A_PIPE, fdinr, errno); - } - } else { - printf("Incorrect FD array data: %s\n", argv[2]); - puts(usageErrorText); - sendErrorCodeAndExit(-1, ESTEP_JSPAWN_NOT_A_PIPE, fdinr, errno); - } +#ifdef DEBUG + /* Check expected file descriptors */ + checkFileDescriptorSetup(); +#endif - // Close the writing end of the pipe we use for reading from the parent. - // We have to do this before we start reading from the parent to avoid - // blocking in the case the parent exits before we finished reading from it. - close(fdinw); // Deliberately ignore errors (see https://lwn.net/Articles/576478/). - initChildStuff (fdinr, fdout, &c); - // Now set the file descriptor for the pipe's writing end to -1 - // for the case that somebody tries to close it again. - assert(c.childenv[1] == fdinw); - c.childenv[1] = -1; - // The file descriptor for reporting errors back to our parent we got on the command - // line should be the same like the one in the ChildStuff struct we've just read. - assert(c.fail[1] == fdout); + initChildStuff(CHILDENV_FILENO, FAIL_FILENO, &c); + +#ifdef DEBUG + /* Not needed in spawn mode */ + assert(c.in[0] == -1 && c.in[1] == -1 && + c.out[0] == -1 && c.out[1] == -1 && + c.err[0] == -1 && c.err[1] == -1 && + c.fail[0] == -1 && c.fail[1] == -1 && + c.fds[0] == -1 && c.fds[1] == -1 && c.fds[2] == -1); +#endif childProcess (&c); return 0; /* NOT REACHED */ diff --git a/src/java.base/unix/native/libjava/ProcessImpl_md.c b/src/java.base/unix/native/libjava/ProcessImpl_md.c index 29522f3c822..f7d91166e76 100644 --- a/src/java.base/unix/native/libjava/ProcessImpl_md.c +++ b/src/java.base/unix/native/libjava/ProcessImpl_md.c @@ -43,7 +43,9 @@ #include #include #include - +#include +#include +#include #include #include "childproc.h" @@ -528,27 +530,46 @@ forkChild(ChildStuff *c) { return resultPid; } +/* Given two fds, one of which has to be -1, the other one has to be valid, + * return the valid one. */ +static int eitherOneOf(int fd1, int fd2) { + if (fd2 == -1) { + assert(fdIsValid(fd1)); + return fd1; + } + assert(fd1 == -1); + assert(fdIsValid(fd2)); + return fd2; +} + +static int call_posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions, int filedes, int newfiledes) { +#ifdef __APPLE__ + /* MacOS is not POSIX-compliant: dup2 file actions specifying the same fd as source and destination + * should be handled as no-op according to spec, but they cause EBADF. */ + if (filedes == newfiledes) { + return 0; + } +#endif + return posix_spawn_file_actions_adddup2(file_actions, filedes, newfiledes); +} + static pid_t spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) { pid_t resultPid; - int i, offset, rval, bufsize, magic; - char *buf, buf1[(3 * 11) + 3]; // "%d:%d:%d\0" - char *hlpargs[4]; + int offset, rval, bufsize, magic; + char* buf; + char* hlpargs[3]; SpawnInfo sp; + posix_spawn_file_actions_t file_actions; + int child_stdin, child_stdout, child_stderr, child_childenv, child_fail = -1; - /* need to tell helper which fd is for receiving the childstuff - * and which fd to send response back on - */ - snprintf(buf1, sizeof(buf1), "%d:%d:%d", c->childenv[0], c->childenv[1], c->fail[1]); /* NULL-terminated argv array. * argv[0] contains path to jspawnhelper, to follow conventions. * argv[1] contains the version string as argument to jspawnhelper - * argv[2] contains the fd string as argument to jspawnhelper */ hlpargs[0] = (char*)helperpath; hlpargs[1] = VERSION_STRING; - hlpargs[2] = buf1; - hlpargs[3] = NULL; + hlpargs[2] = NULL; /* Following items are sent down the pipe to the helper * after it is spawned. @@ -571,19 +592,79 @@ spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) bufsize += sp.dirlen; arraysize(parentPathv, &sp.nparentPathv, &sp.parentPathvBytes); bufsize += sp.parentPathvBytes; - /* We need to clear FD_CLOEXEC if set in the fds[]. - * Files are created FD_CLOEXEC in Java. - * Otherwise, they will be closed when the target gets exec'd */ - for (i=0; i<3; i++) { - if (c->fds[i] != -1) { - int flags = fcntl(c->fds[i], F_GETFD); - if (flags & FD_CLOEXEC) { - fcntl(c->fds[i], F_SETFD, flags & (~FD_CLOEXEC)); - } - } + + /* Prepare file descriptors for jspawnhelper and the target binary. */ + + /* 0: copy of either "in" pipe read fd or the stdin redirect fd */ + child_stdin = eitherOneOf(c->fds[0], c->in[0]); + + /* 1: copy of either "out" pipe write fd or the stdout redirect fd */ + child_stdout = eitherOneOf(c->fds[1], c->out[1]); + + /* 2: redirectErrorStream=1: redirected to child's stdout (Order matters!) + * redirectErrorStream=0: copy of either "err" pipe write fd or stderr redirect fd. */ + if (c->redirectErrorStream) { + child_stderr = STDOUT_FILENO; /* Note: this refers to the future stdout in the child process */ + } else { + child_stderr = eitherOneOf(c->fds[2], c->err[1]); } - rval = posix_spawn(&resultPid, helperpath, 0, 0, (char * const *) hlpargs, environ); + /* 3: copy of the "fail" pipe write fd */ + child_fail = c->fail[1]; + + /* 4: copy of the "childenv" pipe read end */ + child_childenv = c->childenv[0]; + + assert(fdIsValid(child_stdin)); + assert(fdIsValid(child_stdout)); + assert(fdIsValid(child_stderr)); + assert(fdIsPipe(child_fail)); + assert(fdIsPipe(child_childenv)); + /* This must always hold true, unless someone deliberately closed 0, 1, or 2 in the parent JVM. */ + assert(child_fail > STDERR_FILENO); + assert(child_childenv > STDERR_FILENO); + + /* Slot in dup2 file actions. */ + posix_spawn_file_actions_init(&file_actions); + +#ifdef __APPLE__ + /* On MacOS, posix_spawn does not behave in a POSIX-conform way in that the + * kernel closes CLOEXEC file descriptors too early for dup2 file actions to + * copy them after the fork. We have to explicitly prevent that by calling a + * propietary API. */ + posix_spawn_file_actions_addinherit_np(&file_actions, child_stdin); + posix_spawn_file_actions_addinherit_np(&file_actions, child_stdout); + posix_spawn_file_actions_addinherit_np(&file_actions, child_stderr); + posix_spawn_file_actions_addinherit_np(&file_actions, child_fail); + posix_spawn_file_actions_addinherit_np(&file_actions, child_childenv); +#endif + + /* First dup2 stdin/out/err to 0,1,2. After this, we can safely dup2 over the + * original stdin/out/err. */ + if (call_posix_spawn_file_actions_adddup2(&file_actions, child_stdin, STDIN_FILENO) != 0 || + call_posix_spawn_file_actions_adddup2(&file_actions, child_stdout, STDOUT_FILENO) != 0 || + /* Order matters: stderr may be redirected to stdout, so this dup2 must happen after the stdout one. */ + call_posix_spawn_file_actions_adddup2(&file_actions, child_stderr, STDERR_FILENO) != 0) + { + return -1; + } + + /* We dup2 with one intermediary step to prevent accidentally dup2'ing over child_childenv. */ + const int tmp_child_childenv = child_fail < 10 ? 10 : child_fail - 1; + if (call_posix_spawn_file_actions_adddup2(&file_actions, child_childenv, tmp_child_childenv) != 0 || + call_posix_spawn_file_actions_adddup2(&file_actions, child_fail, FAIL_FILENO) != 0 || + call_posix_spawn_file_actions_adddup2(&file_actions, tmp_child_childenv, CHILDENV_FILENO) != 0) + { + return -1; + } + + /* Since we won't use these in jspawnhelper, reset them all */ + c->in[0] = c->in[1] = c->out[0] = c->out[1] = + c->err[0] = c->err[1] = c->fail[0] = c->fail[1] = + c->fds[0] = c->fds[1] = c->fds[2] = -1; + c->redirectErrorStream = false; + + rval = posix_spawn(&resultPid, helperpath, &file_actions, 0, (char * const *) hlpargs, environ); if (rval != 0) { return -1; @@ -667,6 +748,30 @@ startChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) } } +static int pipeSafely(int fd[2]) { + /* Pipe filedescriptors must be CLOEXEC as early as possible - ideally from the point of + * creation on - since at any moment a concurrent (third-party) fork() could inherit copies + * of these descriptors and accidentally keep the pipes open. That could cause the parent + * process to hang (see e.g. JDK-8377907). + * We use pipe2(2), if we have it. If we don't, we use pipe(2) + fcntl(2) immediately. + * The latter is still racy and can therefore still cause hangs as described in JDK-8377907, + * but at least the dangerous time window is as short as we can make it. + */ + int rc = -1; +#ifdef HAVE_PIPE2 + rc = pipe2(fd, O_CLOEXEC); +#else + rc = pipe(fd); + if (rc == 0) { + fcntl(fd[0], F_SETFD, FD_CLOEXEC); + fcntl(fd[1], F_SETFD, FD_CLOEXEC); + } +#endif /* HAVE_PIPE2 */ + assert(fdIsCloexec(fd[0])); + assert(fdIsCloexec(fd[1])); + return rc; +} + JNIEXPORT jint JNICALL Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, jobject process, @@ -727,11 +832,11 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, fds = (*env)->GetIntArrayElements(env, std_fds, NULL); if (fds == NULL) goto Catch; - if ((fds[0] == -1 && pipe(in) < 0) || - (fds[1] == -1 && pipe(out) < 0) || - (fds[2] == -1 && !redirectErrorStream && pipe(err) < 0) || // if not redirecting create the pipe - (pipe(childenv) < 0) || - (pipe(fail) < 0)) { + if ((fds[0] == -1 && pipeSafely(in) < 0) || + (fds[1] == -1 && pipeSafely(out) < 0) || + (fds[2] == -1 && !redirectErrorStream && pipeSafely(err) < 0) || + (pipeSafely(childenv) < 0) || + (pipeSafely(fail) < 0)) { throwInternalIOException(env, errno, "Bad file descriptor", mode); goto Catch; } diff --git a/src/java.base/unix/native/libjava/childproc.c b/src/java.base/unix/native/libjava/childproc.c index a45674e3f82..83ee782482f 100644 --- a/src/java.base/unix/native/libjava/childproc.c +++ b/src/java.base/unix/native/libjava/childproc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,13 +23,16 @@ * questions. */ +#include #include #include #include #include +#include #include #include #include +#include #include #include @@ -39,12 +42,27 @@ const char * const *parentPathv; +#ifdef DEBUG +bool fdIsValid(int fd) { + return fcntl(fd, F_GETFD) != -1; +} +bool fdIsPipe(int fd) { + struct stat buf; + errno = 0; + return fstat(fd, &buf) != -1 && S_ISFIFO(buf.st_mode); +} +bool fdIsCloexec(int fd) { + errno = 0; + const int flags = fcntl(fd, F_GETFD); + return flags != -1 && (flags & FD_CLOEXEC); +} +#endif // DEBUG + +static int +restartableDup2(int fd_from, int fd_to, errcode_t* errcode) /* All functions taking an errcode_t* as output behave the same: upon error, they populate * errcode_t::hint and errcode_t::errno, but leave errcode_t::step as ESTEP_UNKNOWN since * this information will be provided by the outer caller */ - -static bool -restartableDup2(int fd_from, int fd_to, errcode_t* errcode) { int err; RESTARTABLE(dup2(fd_from, fd_to), err); @@ -393,7 +411,10 @@ childProcess(void *arg) { const ChildStuff* p = (const ChildStuff*) arg; - int fail_pipe_fd = p->fail[1]; + int fail_pipe_fd = (p->mode == MODE_POSIX_SPAWN) ? + FAIL_FILENO : /* file descriptors already set up by posix_spawn(). */ + p->fail[1]; + /* error information for WhyCantJohnnyExec */ errcode_t errcode; @@ -407,55 +428,63 @@ childProcess(void *arg) #ifdef DEBUG jtregSimulateCrash(0, 6); #endif - /* Close the parent sides of the pipes. - Closing pipe fds here is redundant, since markDescriptorsCloseOnExec() - would do it anyways, but a little paranoia is a good thing. */ - if (!closeSafely2(p->in[1], &errcode) || - !closeSafely2(p->out[0], &errcode) || - !closeSafely2(p->err[0], &errcode) || - !closeSafely2(p->childenv[0], &errcode) || - !closeSafely2(p->childenv[1], &errcode) || - !closeSafely2(p->fail[0], &errcode)) - { - errcode.step = ESTEP_PIPECLOSE_FAIL; - goto WhyCantJohnnyExec; - } - /* Give the child sides of the pipes the right fileno's. */ - /* Note: it is possible for in[0] == 0 */ - if (!moveDescriptor(p->in[0] != -1 ? p->in[0] : p->fds[0], - STDIN_FILENO, &errcode)) { - errcode.step = ESTEP_DUP2_STDIN_FAIL; - goto WhyCantJohnnyExec; - } + /* File descriptor setup for non-Posix-spawn mode */ + if (p->mode != MODE_POSIX_SPAWN) { - if (!moveDescriptor(p->out[1] != -1 ? p->out[1] : p->fds[1], - STDOUT_FILENO, &errcode)) { - errcode.step = ESTEP_DUP2_STDOUT_FAIL; - goto WhyCantJohnnyExec; - } - - if (p->redirectErrorStream) { - if (!closeSafely2(p->err[1], &errcode) || - !restartableDup2(STDOUT_FILENO, STDERR_FILENO, &errcode)) { - errcode.step = ESTEP_DUP2_STDERR_REDIRECT_FAIL; + /* Close the parent sides of the pipes. + Closing pipe fds here is redundant, since markDescriptorsCloseOnExec() + would do it anyways, but a little paranoia is a good thing. */ + if (!closeSafely2(p->in[1], &errcode) || + !closeSafely2(p->out[0], &errcode) || + !closeSafely2(p->err[0], &errcode) || + !closeSafely2(p->childenv[0], &errcode) || + !closeSafely2(p->childenv[1], &errcode) || + !closeSafely2(p->fail[0], &errcode)) + { + errcode.step = ESTEP_PIPECLOSE_FAIL; goto WhyCantJohnnyExec; } - } else { - if (!moveDescriptor(p->err[1] != -1 ? p->err[1] : p->fds[2], - STDERR_FILENO, &errcode)) { - errcode.step = ESTEP_DUP2_STDERR_REDIRECT_FAIL; + + /* Give the child sides of the pipes the right fileno's. */ + /* Note: it is possible for in[0] == 0 */ + if (!moveDescriptor(p->in[0] != -1 ? p->in[0] : p->fds[0], + STDIN_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_STDIN_FAIL; goto WhyCantJohnnyExec; } - } - if (!moveDescriptor(fail_pipe_fd, FAIL_FILENO, &errcode)) { - errcode.step = ESTEP_DUP2_FAILPIPE_FAIL; - goto WhyCantJohnnyExec; - } + if (!moveDescriptor(p->out[1] != -1 ? p->out[1] : p->fds[1], + STDOUT_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_STDOUT_FAIL; + goto WhyCantJohnnyExec; + } - /* We moved the fail pipe fd */ - fail_pipe_fd = FAIL_FILENO; + if (p->redirectErrorStream) { + if (!closeSafely2(p->err[1], &errcode) || + !restartableDup2(STDOUT_FILENO, STDERR_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_STDERR_REDIRECT_FAIL; + goto WhyCantJohnnyExec; + } + } else { + if (!moveDescriptor(p->err[1] != -1 ? p->err[1] : p->fds[2], + STDERR_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_STDERR_REDIRECT_FAIL; + goto WhyCantJohnnyExec; + } + } + + if (!moveDescriptor(fail_pipe_fd, FAIL_FILENO, &errcode)) { + errcode.step = ESTEP_DUP2_FAILPIPE_FAIL; + goto WhyCantJohnnyExec; + } + + /* We moved the fail pipe fd */ + fail_pipe_fd = FAIL_FILENO; + + } /* end: FORK/VFORK mode */ + + assert(fail_pipe_fd == FAIL_FILENO); /* For AIX: The code in markDescriptorsCloseOnExec() relies on the current * semantic of this function. When this point here is reached only the diff --git a/src/java.base/unix/native/libjava/childproc.h b/src/java.base/unix/native/libjava/childproc.h index 4271c786635..27414e60137 100644 --- a/src/java.base/unix/native/libjava/childproc.h +++ b/src/java.base/unix/native/libjava/childproc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #define CHILDPROC_MD_H #include +#include #ifdef __APPLE__ #include @@ -72,6 +73,9 @@ extern char **environ; #define FAIL_FILENO (STDERR_FILENO + 1) +/* For POSIX_SPAWN mode */ +#define CHILDENV_FILENO (FAIL_FILENO + 1) + /* These numbers must be the same as the Enum in ProcessImpl.java * Must be a better way of doing this. */ @@ -129,6 +133,17 @@ int childProcess(void *arg); * See: test/jdk/java/lang/ProcessBuilder/JspawnhelperProtocol.java */ void jtregSimulateCrash(pid_t child, int stage); +/* Helper functions to check the state of fds */ +bool fdIsValid(int fd); +bool fdIsPipe(int fd); +bool fdIsCloexec(int fd); #endif +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#define HAVE_PIPE2 +#else +// Neither MacOS nor AIX support pipe2, unfortunately +#undef HAVE_PIPE2 #endif + +#endif /* CHILDPROC_MD_H */ diff --git a/test/jdk/java/lang/ProcessBuilder/Basic.java b/test/jdk/java/lang/ProcessBuilder/Basic.java index dd97de8fad8..401a1e2bd8a 100644 --- a/test/jdk/java/lang/ProcessBuilder/Basic.java +++ b/test/jdk/java/lang/ProcessBuilder/Basic.java @@ -22,32 +22,43 @@ */ /* - * @test + * @test id=POSIX_SPAWN * @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689 * 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313 * 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958 * 4947220 7018606 7034570 4244896 5049299 8003488 8054494 8058464 * 8067796 8224905 8263729 8265173 8272600 8231297 8282219 8285517 - * 8352533 8368192 + * 8352533 8368192 8377907 * @key intermittent * @summary Basic tests for Process and Environment Variable code * @modules java.base/java.lang:open * java.base/java.io:open * @requires vm.flagless * @library /test/lib - * @run main/othervm/native/timeout=360 Basic - * @run main/othervm/native/timeout=360 -Djdk.lang.Process.launchMechanism=fork Basic + * @run main/othervm/native/timeout=360 -Djdk.lang.Process.launchMechanism=posix_spawn Basic * @author Martin Buchholz */ /* - * @test + * @test id=FORK + * @key intermittent + * @summary Basic tests for Process and Environment Variable code + * @modules java.base/java.lang:open + * java.base/java.io:open + * @requires !vm.musl + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/native/timeout=360 -Djdk.lang.Process.launchMechanism=fork Basic + */ + +/* + * @test id=VFORK * @modules java.base/java.lang:open * java.base/java.io:open * java.base/jdk.internal.misc * @requires (os.family == "linux") * @library /test/lib - * @run main/othervm/timeout=300 -Djdk.lang.Process.launchMechanism=posix_spawn Basic + * @run main/othervm/timeout=300 -Djdk.lang.Process.launchMechanism=vfork Basic */ import java.lang.ProcessBuilder.Redirect; @@ -1229,6 +1240,20 @@ public class Basic { equal(r.out(), "standard output"); equal(r.err(), "standard error"); } + + //---------------------------------------------------------------- + // Default: should go to pipes (use a fresh ProcessBuilder) + //---------------------------------------------------------------- + { + ProcessBuilder pb2 = new ProcessBuilder(childArgs); + Process p = pb2.start(); + new PrintStream(p.getOutputStream()).print("standard input"); + p.getOutputStream().close(); + ProcessResults r = run(p); + equal(r.exitValue(), 0); + equal(r.out, "standard output"); + equal(r.err, "standard error"); + } } static void checkProcessPid() { @@ -1267,6 +1292,8 @@ public class Basic { if (UnicodeOS.is()) System.out.println("This appears to be a Unicode-based OS."); + System.out.println("Using:" + System.getProperty("jdk.lang.Process.launchMechanism")); + try { testIORedirection(); } catch (Throwable t) { unexpected(t); } diff --git a/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/ConcNativeForkTest.java b/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/ConcNativeForkTest.java new file mode 100644 index 00000000000..f02e4991302 --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/ConcNativeForkTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=POSIX_SPAWN + * @bug 8377907 + * @summary Test that demonstrates the hanging-parent-on-native-concurrent-forks problem + * @requires os.family != "windows" + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/manual -Djdk.lang.Process.launchMechanism=POSIX_SPAWN ConcNativeForkTest + */ + +/* + * @test id=FORK + * @bug 8377907 + * @summary Test that demonstrates the hanging-parent-on-native-concurrent-forks problem + * @requires os.family != "windows" + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/manual -Djdk.lang.Process.launchMechanism=FORK ConcNativeForkTest + */ + +/* + * @test id=VFORK + * @bug 8377907 + * @summary Test that demonstrates the hanging-parent-on-native-concurrent-forks problem + * @requires os.family == "linux" + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/manual -Djdk.lang.Process.launchMechanism=VFORK ConcNativeForkTest + */ + +public class ConcNativeForkTest { + + // How this works: + // - We start a child process via ProcessBuilder. Does not matter what, we just call "/bin/true". + // - Concurrently, we continuously (up to a limit) fork natively; these forks will all exec "sleep 30". + // - If the natively forked child process forks off at the right (wrong) moment, it will catch the open pipe from + // the "/bin/true" child process, and forcing the parent process (this test) to wait in ProcessBuilder.start() + // (inside forkAndExec()) until the natively forked child releases the pipe file descriptors it inherited. + + // Notes: + // + // Obviously, this is racy and depends on scheduler timings of the underlying OS. The test succeeding is + // no proof the bug does not exist (see PipesCloseOnExecTest as a complimentary test that is more reliable, but + // only works on Linux). + // That said, in tests it reliably reproduces the bug on Linux x64 and MacOS Arm. + // + // This test is not well suited for automatic test execution, since the test essentially + // fork-bombs itself, and that may run into issues in containerized CI/CD environments. + + native static boolean prepareNativeForkerThread(int numForks); + native static void releaseNativeForkerThread(); + native static void stopNativeForkerThread(); + + private static final int numIterations = 20; + + public static void main(String[] args) throws Exception { + + System.out.println("jdk.lang.Process.launchMechanism=" + + System.getProperty("jdk.lang.Process.launchMechanism")); + + System.loadLibrary("ConcNativeFork"); + + // A very simple program returning immediately (/bin/true) + ProcessBuilder pb = new ProcessBuilder("true").inheritIO(); + final int numJavaProcesses = 10; + final int numNativeProcesses = 250; + Process[] processes = new Process[numJavaProcesses]; + + for (int iteration = 0; iteration < numIterations; iteration ++) { + + if (!prepareNativeForkerThread(numNativeProcesses)) { + throw new RuntimeException("Failed to start native forker thread (see stdout)"); + } + + long[] durations = new long[numJavaProcesses]; + + releaseNativeForkerThread(); + + for (int np = 0; np < numJavaProcesses; np ++) { + long t1 = System.currentTimeMillis(); + try (Process p = pb.start()) { + durations[np] = System.currentTimeMillis() - t1; + processes[np] = p; + } + } + + stopNativeForkerThread(); + + long longestDuration = 0; + for (int np = 0; np < numJavaProcesses; np ++) { + processes[np].waitFor(); + System.out.printf("Duration: %dms%n", durations[np]); + longestDuration = Math.max(durations[np], longestDuration); + } + + System.out.printf("Longest startup time: %dms%n", longestDuration); + + if (longestDuration >= 30000) { + throw new RuntimeException("Looks like we blocked on native fork"); + } + } + + } + +} diff --git a/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/libConcNativeFork.c b/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/libConcNativeFork.c new file mode 100644 index 00000000000..59729308558 --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/libConcNativeFork.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "testlib_thread_barriers.h" + +static void trc(const char* fmt, ...) { + char buf [1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + printf("(pid: %d): %s\n", (int)getpid(), buf); + fflush(stdout); +} + +static pthread_t tid_forker; + +static pthread_barrier_t start_barrier; +static atomic_bool stop_now = false; + +static void* forkerLoop(void* info) { + + const int numForks = (int)(intptr_t)info; + pid_t* pids = calloc(numForks, sizeof(pid_t)); + + trc("Forker: Waiting for Go."); + + pthread_barrier_wait(&start_barrier); + + for (int i = 0; i < numForks; i++) { + const pid_t pid = fork(); + if (pid == 0) { + /* Exec sleep. Properly opened file descriptors in parents (tagged CLOEXEC) should be released now. + * Note that we use bash to not have to deal with path resolution. For our case, it does not matter if + * sleep is a builtin or not. */ + char* env[] = { "PATH=/usr/bin:/bin", NULL }; + char* argv[] = { "sh", "-c", "sleep 30", NULL }; + execve("/bin/sh", argv, env); + trc("Native child: sleep exec failed? %d", errno); + /* The simplest way to handle this is to just wait here; this *will* cause the test to fail. */ + sleep(120); + trc("Native child: exiting"); + exit(0); + } else { + pids[i] = pid; + sched_yield(); + } + } + + trc("Forker: All native child processes started."); + + /* Wait for test to signal end */ + while (!atomic_load(&stop_now)) { + sleep(1); + } + + trc("Forker: Cleaning up."); + + /* Reap children */ + for (int i = 0; i < numForks; i ++) { + if (pids[i] != 0) { + kill(pids[i], SIGKILL); /* if still running */ + waitpid(pids[i], NULL, 0); + } + } + + trc("Forker: Done."); + + return NULL; +} + +JNIEXPORT jboolean JNICALL +Java_ConcNativeForkTest_prepareNativeForkerThread(JNIEnv* env, jclass cls, jint numForks) +{ + pthread_attr_t attr; + int rc = 0; + + const int cap = 1000; + const int numForksCapped = numForks > cap ? cap : numForks; + if (numForks > numForksCapped) { + trc("Main: Capping max. number of forks at %d", numForksCapped); /* don't forkbomb me */ + } + + if (pthread_barrier_init(&start_barrier, NULL, 2) != 0) { + trc("Main: pthread_barrier_init failed (%d)", errno); + return false; + } + + pthread_attr_init(&attr); + if (pthread_create(&tid_forker, &attr, forkerLoop, (void*)(intptr_t)numForksCapped) != 0) { + trc("Main: pthread_create failed (%d)", errno); + return JNI_FALSE; + } + + trc("Main: Prepared native forker thread"); + + return JNI_TRUE; +} + +JNIEXPORT void JNICALL +Java_ConcNativeForkTest_releaseNativeForkerThread(JNIEnv* env, jclass cls) +{ + pthread_barrier_wait(&start_barrier); + trc("Main: signaled GO"); +} + +JNIEXPORT void JNICALL +Java_ConcNativeForkTest_stopNativeForkerThread(JNIEnv* env, jclass cls) +{ + atomic_store(&stop_now, true); + pthread_join(tid_forker, NULL); + pthread_barrier_destroy(&start_barrier); +} diff --git a/test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java b/test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java index 146c2be563f..aee85e2335f 100644 --- a/test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java +++ b/test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /** * @test id=posix_spawn - * @summary Check that we don't leak FDs + * @summary Check that we don't leak FDs to child processes * @requires os.family != "windows" * @library /test/lib * @run main/othervm/native -Djdk.lang.Process.launchMechanism=posix_spawn -agentlib:FDLeaker FDLeakTest @@ -32,7 +32,7 @@ /** * @test id=fork - * @summary Check that we don't leak FDs + * @summary Check that we don't leak FDs to child processes * @requires os.family != "windows" * @library /test/lib * @run main/othervm/native -Djdk.lang.Process.launchMechanism=fork -agentlib:FDLeaker FDLeakTest @@ -40,7 +40,7 @@ /** * @test id=vfork - * @summary Check that we don't leak FDs + * @summary Check that we don't leak FDs to child processes * @requires os.family == "linux" * @library /test/lib * @run main/othervm/native -Djdk.lang.Process.launchMechanism=vfork -agentlib:FDLeaker FDLeakTest diff --git a/test/jdk/java/lang/ProcessBuilder/JspawnhelperProtocol.java b/test/jdk/java/lang/ProcessBuilder/JspawnhelperProtocol.java index 0098bc531fd..59ce9450cb9 100644 --- a/test/jdk/java/lang/ProcessBuilder/JspawnhelperProtocol.java +++ b/test/jdk/java/lang/ProcessBuilder/JspawnhelperProtocol.java @@ -39,6 +39,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; public class JspawnhelperProtocol { @@ -55,7 +56,9 @@ public class JspawnhelperProtocol { System.out.println("Recursively executing 'JspawnhelperProtocol " + arg + "'"); Process p = null; try { - p = Runtime.getRuntime().exec(CMD); + // Route any stdout from the child process - be it jspawnhelper error messages or the output of "/bin/pwd" - + // through to the parent process. + p = new ProcessBuilder(CMD).inheritIO().start(); } catch (Exception e) { // Check that exception contains rich message on failure. e.printStackTrace(System.out); @@ -70,12 +73,6 @@ public class JspawnhelperProtocol { System.exit(ERROR + 1); } if (p.exitValue() == 0) { - String pwd = p.inputReader().readLine(); - String realPwd = Path.of("").toAbsolutePath().toString(); - if (!realPwd.equals(pwd)) { - System.out.println("Child process returned '" + pwd + "' (expected '" + realPwd + "')"); - System.exit(ERROR + 2); - } System.out.println(" Successfully executed '" + CMD[0] + "'"); System.exit(0); } else { @@ -89,7 +86,6 @@ public class JspawnhelperProtocol { pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Djdk.lang.Process.launchMechanism=posix_spawn", "JspawnhelperProtocol", "normalExec"); - pb.inheritIO(); Process p = pb.start(); if (!p.waitFor(TIMEOUT, TimeUnit.SECONDS)) { throw new Exception("Parent process timed out"); @@ -97,6 +93,10 @@ public class JspawnhelperProtocol { if (p.exitValue() != 0) { throw new Exception("Parent process exited with " + p.exitValue()); } + OutputAnalyzer output = new OutputAnalyzer(p); + output.shouldContain("Recursively executing 'JspawnhelperProtocol normalExec'"); + String realPwd = Path.of("").toAbsolutePath().toString(); + output.shouldContain(realPwd); } private static void simulateCrashInChild(int stage) throws Exception { @@ -144,7 +144,7 @@ public class JspawnhelperProtocol { try (BufferedReader br = p.inputReader()) { line = br.readLine(); while (line != null && !line.startsWith("posix_spawn:")) { - System.out.println(line); + System.out.println("parent stdout:" + line); line = br.readLine(); } } diff --git a/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java b/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java index cb2d504951d..fec0b4f3efd 100644 --- a/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java +++ b/test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,7 +61,7 @@ public class JspawnhelperWarnings { oa.shouldHaveExitValue(EXITCODE_OFFSET + ESTEP_JSPAWN_ARG_ERROR); oa.shouldContain("jspawnhelper fail: (1-0-0)"); oa.shouldContain("This command is not for general use"); - if (nArgs != 2) { + if (nArgs != 1) { oa.shouldContain("Incorrect number of arguments"); } else { oa.shouldContain("Incorrect Java version"); @@ -69,10 +69,9 @@ public class JspawnhelperWarnings { } private static void testVersion() throws Exception { - String[] args = new String[3]; + String[] args = new String[2]; args[0] = Paths.get(System.getProperty("java.home"), "lib", "jspawnhelper").toString(); args[1] = "wrongVersion"; - args[2] = "1:1:1"; Process p = ProcessTools.startProcess("jspawnhelper", new ProcessBuilder(args)); OutputAnalyzer oa = new OutputAnalyzer(p); oa.shouldHaveExitValue(EXITCODE_OFFSET + ESTEP_JSPAWN_VERSION_ERROR); @@ -88,7 +87,7 @@ public class JspawnhelperWarnings { switch (args[0]) { case "badargs" -> { for (int nArgs = 0; nArgs < 10; nArgs++) { - if (nArgs != 2) { + if (nArgs != 1) { tryWithNArgs(nArgs); } } diff --git a/test/jdk/java/lang/ProcessBuilder/NonPipelineLeaksFD.java b/test/jdk/java/lang/ProcessBuilder/NonPipelineLeaksFD.java new file mode 100644 index 00000000000..be2c4af3bbe --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/NonPipelineLeaksFD.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.*; + +/* + * @test id=FORK + * @summary Check that we don't accumulate leaked FDs in the parent process + * @requires os.family == "linux" + * @library /test/lib + * @run main/othervm -Djdk.lang.Process.launchMechanism=fork NonPipelineLeaksFD + */ + +/* + * @test id=VFORK + * @summary Check that we don't accumulate leaked FDs in the parent process + * @requires os.family == "linux" + * @library /test/lib + * @run main/othervm -Djdk.lang.Process.launchMechanism=vfork NonPipelineLeaksFD + */ + +/* + * @test id=POSIX_SPAWN + * @summary Check that we don't accumulate leaked FDs in the parent process + * @requires os.family == "linux" + * @library /test/lib + * @run main/othervm -Djdk.lang.Process.launchMechanism=posix_spawn NonPipelineLeaksFD + */ + +public class NonPipelineLeaksFD { + + final static int repeatCount = 50; + + // Similar to PilelineLeaksFD, but where PilelineLeaksFD checks that the parent process + // does not leak file descriptors when invoking a pipeline, here we check that we don't + // leak FDs when executing simple (non-pipelined) programs but we test a wider span of + // redirection modes in both successful and failing variants. + + // How this works: + // + // We execute a mix of failing and succeeding child process starts with various + // flavors of IO redirections many times; we observe the open file descriptors + // before and afterwards. Test fails if we have significantly more file descriptors + // open afterwards than before. + + static int countNumberOfOpenFileDescriptors() { + return new File("/proc/self/fd").list().length; + } + + static void printOpenFileDescriptors() { + long mypid = ProcessHandle.current().pid(); + try(Process p = new ProcessBuilder("lsof", "-p", Long.toString(mypid)) + .inheritIO().start()) { + p.waitFor(); + } catch (InterruptedException | IOException ignored) { + // Quietly swallow; it was just an attempt. + } + } + + static String readFirstLineOf(File f) { + String result; + try (BufferedReader b = new BufferedReader(new FileReader(f))){ + result = b.readLine(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return result; + } + + static void runThisExpectSuccess(ProcessBuilder bld) { + try(Process p = bld.start()) { + p.waitFor(); + if (p.exitValue() != 0) { + throw new RuntimeException("Unexpected exitcode"); + } + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + static void runThisExpectError(ProcessBuilder bld) { + boolean failed = false; + try(Process p = bld.start()) { + p.waitFor(); + } catch (IOException | InterruptedException e) { + failed = true; + } + if (!failed) { + throw new RuntimeException("Expected Error"); + } + } + + static void runPosWithPipes() { + ProcessBuilder bld = new ProcessBuilder("sh", "-c", "echo hallo"); + runThisExpectSuccess(bld); + } + + static void runPosWithInheritIO() { + ProcessBuilder bld = new ProcessBuilder("sh", "-c", "echo hallo").inheritIO(); + runThisExpectSuccess(bld); + } + + static void runPosWithRedirectToFile() { + File fo = new File("test.out"); + ProcessBuilder bld = new ProcessBuilder("sh", "-c", "echo hallo"); + bld.redirectOutput(ProcessBuilder.Redirect.to(fo)); + runThisExpectSuccess(bld); + if (!readFirstLineOf(fo).equals("hallo")) { + throw new RuntimeException("mismatch"); + } + } + + static void runNegWithPipes() { + ProcessBuilder bld = new ProcessBuilder("doesnotexist"); + runThisExpectError(bld); + } + + static void runNegWithInheritIO() { + ProcessBuilder bld = new ProcessBuilder("doesnotexist").inheritIO(); + runThisExpectError(bld); + } + + static void runNegWithRedirectToFile() { + File fo = new File("test.out"); + ProcessBuilder bld = new ProcessBuilder("doesnotexist"); + bld.redirectOutput(ProcessBuilder.Redirect.to(fo)); + runThisExpectError(bld); + } + + static void doTestNTimesAndCountFDs(Runnable runnable, String name) { + System.out.println(name); + int c1 = countNumberOfOpenFileDescriptors(); + for (int i = 0; i < repeatCount; i++) { + runnable.run(); + } + int c2 = countNumberOfOpenFileDescriptors(); + System.out.printf("%d->%d", c1, c2); + } + + public static void main(String[] args) throws Exception { + System.out.println("jdk.lang.Process.launchMechanism=" + + System.getProperty("jdk.lang.Process.launchMechanism")); + int c1 = countNumberOfOpenFileDescriptors(); + doTestNTimesAndCountFDs(NonPipelineLeaksFD::runPosWithPipes, "runPosWithPipes"); + doTestNTimesAndCountFDs(NonPipelineLeaksFD::runPosWithInheritIO, "runPosWithInheritIO"); + doTestNTimesAndCountFDs(NonPipelineLeaksFD::runPosWithRedirectToFile, "runPosWithRedirectToFile"); + doTestNTimesAndCountFDs(NonPipelineLeaksFD::runNegWithPipes, "runNegWithPipes"); + doTestNTimesAndCountFDs(NonPipelineLeaksFD::runNegWithInheritIO, "runNegWithInheritIO"); + doTestNTimesAndCountFDs(NonPipelineLeaksFD::runNegWithRedirectToFile, "runNegWithRedirectToFile"); + int c2 = countNumberOfOpenFileDescriptors(); + + System.out.printf("All tests: %d->%d", c1, c2); + printOpenFileDescriptors(); + + final int fudge = 10; + if (c2 > (c1 + fudge)) { + throw new RuntimeException( + String.format("Leak suspected (%d->%d) - see lsof output", c1, c2)); + } + } +} diff --git a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java index 92cca736503..18dde6e95d7 100644 --- a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java +++ b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java @@ -46,12 +46,21 @@ import java.util.StringJoiner; import java.util.stream.Collectors; /* - * @test + * @test id=FORK * @bug 8289643 8291760 8291986 * @requires os.family == "mac" | (os.family == "linux" & !vm.musl) * @summary File descriptor leak detection with ProcessBuilder.startPipeline * @library /test/lib - * @run junit/othervm/timeout=240 PipelineLeaksFD + * @run junit/othervm/timeout=240 -Djdk.lang.Process.launchMechanism=FORK PipelineLeaksFD + */ + +/* + * @test id=POSIX_SPAWN + * @bug 8289643 8291760 8291986 + * @requires os.family == "mac" | (os.family == "linux" & !vm.musl) + * @summary File descriptor leak detection with ProcessBuilder.startPipeline + * @library /test/lib + * @run junit/othervm/timeout=240 -Djdk.lang.Process.launchMechanism=POSIX_SPAWN PipelineLeaksFD */ public class PipelineLeaksFD { @@ -97,6 +106,8 @@ public class PipelineLeaksFD { @MethodSource("builders") void checkForLeaks(List builders) throws IOException { + System.out.println("Using:" + System.getProperty("jdk.lang.Process.launchMechanism")); + List lsofLines = lsofForPids(MY_PID); Set pipesBefore = pipesFromLSOF(lsofLines, MY_PID); if (pipesBefore.size() < 3) { @@ -162,6 +173,9 @@ public class PipelineLeaksFD { @ParameterizedTest() @MethodSource("redirectCases") void checkRedirectErrorStream(boolean redirectError) { + + System.out.println("Using:" + System.getProperty("jdk.lang.Process.launchMechanism")); + try (Process p = new ProcessBuilder("cat") .redirectErrorStream(redirectError) .start()) { diff --git a/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/PipesCloseOnExecTest.java b/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/PipesCloseOnExecTest.java new file mode 100644 index 00000000000..1e988f0496c --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/PipesCloseOnExecTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test id=FORK + * @bug 8377907 + * @summary Check that we don't open pipes without CLOEXCEC + * @requires os.family == "linux" + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/native -Djdk.lang.Process.launchMechanism=FORK PipesCloseOnExecTest + */ + +/* + * @test id=VFORK + * @bug 8377907 + * @summary Check that we don't open pipes without CLOEXCEC + * @requires os.family == "linux" + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/native -Djdk.lang.Process.launchMechanism=VFORK PipesCloseOnExecTest + */ + +/* + * @test id=POSIX_SPAWN + * @bug 8377907 + * @summary Check that we don't open pipes without CLOEXCEC + * @requires os.family == "linux" + * @requires vm.flagless + * @library /test/lib + * @run main/othervm/native -Djdk.lang.Process.launchMechanism=POSIX_SPAWN PipesCloseOnExecTest + */ + +import jdk.test.lib.process.OutputAnalyzer; + +import java.io.IOException; +import java.time.LocalTime; + +public class PipesCloseOnExecTest { + + // How this works: + // - We start a child process A. Does not matter what, we just call "/bin/date". + // - Concurrently, we (natively, continuously) iterate all pipe file descriptors in + // the process and count all those that are not tagged with CLOEXEC. Finding one + // counts as error. + + // Note that this test can only reliably succeed with Linux and the xxxBSDs, where + // we have pipe2(2). + // + // On MacOs and AIX, we emulate pipe2(2) with pipe(2) and fcntl(2); therefore we + // have a tiny time window in which a concurrent thread can can observe pipe + // filedescriptors without CLOEXEC. Furthermore, on MacOS, we also have to employ + // the double-dup-trick to workaround a the buggy MacOS implementation of posix_spawn. + // Therefore, on these platforms, the test would (correctly) spot "bad" file descriptors. + + native static boolean startTester(); + native static boolean stopTester(); + + static final int num_tries = 100; + + static void printOpenFileDescriptors() { + long mypid = ProcessHandle.current().pid(); + try(Process p = new ProcessBuilder("lsof", "-p", Long.toString(mypid)) + .inheritIO().start()) { + p.waitFor(); + } catch (InterruptedException | IOException ignored) { + // Quietly swallow; it was just an attempt. + } + } + + public static void main(String[] args) throws Exception { + + System.out.println("jdk.lang.Process.launchMechanism=" + + System.getProperty("jdk.lang.Process.launchMechanism")); + + System.loadLibrary("PipesCloseOnExec"); + + if (!startTester()) { + throw new RuntimeException("Failed to start testers (see stdout)"); + } + + System.out.println(LocalTime.now() + ": Call ProcessBuilder.start..."); + + for (int i = 0; i < num_tries; i ++) { + ProcessBuilder pb = new ProcessBuilder("true").inheritIO(); + new OutputAnalyzer(pb.start()).shouldHaveExitValue(0); + } + + if (!stopTester()) { + printOpenFileDescriptors(); + throw new RuntimeException("Catched FDs without CLOEXEC? Check output."); + } + } +} diff --git a/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/libPipesCloseOnExec.c b/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/libPipesCloseOnExec.c new file mode 100644 index 00000000000..9a090f3bbd5 --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/libPipesCloseOnExec.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "testlib_thread_barriers.h" + +static void trc(const char* fmt, ...) { + char buf [1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + printf("%s\n", buf); + fflush(stdout); +} + +/* Set 1 to restrict this test to pipes, 0 to test all file descriptors. + * (for now, we ignore regular files opened with CLOEXEC since loaded jars seem not tagged as CLOEXEc. + * We should probably fix that eventually. */ +#define TEST_PIPES_ONLY 1 + +/* stdin/out/err file descriptors are usually not CLOEXEC */ +#define IGNORE_BELOW 4 + +/* Only query file descriptors up to this point */ +#define MAX_FD 1024 + +static pthread_t tid_tester; + +static pthread_barrier_t start_barrier; +static atomic_bool stop_now = false; + +/* Mainly to prevent tracing the same fd over and over again: + * 1 - present, 2 - present, cloexec */ +static unsigned fd_state[MAX_FD]; + +static bool is_pipe(int fd) { + struct stat mystat; + if (fstat(fd, &mystat) == -1) { + return false; + } + return mystat.st_mode & S_IFIFO; +} + +static void print_fd_details(int fd, char* out, size_t outlen) { + const char* type = "unknown"; + char link[1024] = { 0 }; + char procfd[129]; + struct stat mystat; + + out[0] = '\0'; + + if (fstat(fd, &mystat) == -1) { + snprintf(out, outlen, "%s", errno == EBADF ? "EBADF" : "???"); + return; + } + + switch (mystat.st_mode & S_IFMT) { + case S_IFBLK: type = "blk"; break; + case S_IFCHR: type = "char"; break; + case S_IFDIR: type = "dir"; break; + case S_IFIFO: type = "fifo"; break; + case S_IFLNK: type = "lnk"; break; + case S_IFREG: type = "reg"; break; + case S_IFSOCK: type = "sock"; break; + } + + snprintf(procfd, sizeof(procfd) - 1, "/proc/self/fd/%d", fd); + int linklen = readlink(procfd, link, sizeof(link) - 1); + if (linklen > 0) { + link[linklen] = '\0'; + snprintf(out, outlen, "%s (%s)", type, link); + } else { + snprintf(out, outlen, "%s", type); + } +} + +/* Returns true for error */ +static bool testFD(int fd) { + + int rc = fcntl(fd, F_GETFD); + if (rc == -1) { + return false; + } + + const bool has_cloexec = (rc & FD_CLOEXEC); + const bool is_a_pipe = is_pipe(fd); + const unsigned state = has_cloexec ? 2 : 1; + bool had_error = false; + + if (fd_state[fd] != state) { + fd_state[fd] = state; + char buf[1024]; + print_fd_details(fd, buf, sizeof(buf)); + if (has_cloexec) { + trc("%d: %s", fd, buf); + } else { + if (fd < IGNORE_BELOW) { + trc("%d: %s ** CLOEXEC MISSING ** (ignored - below scanned range)", fd, buf); + } else if (TEST_PIPES_ONLY && !is_a_pipe) { + trc("%d: %s ** CLOEXEC MISSING ** (ignored - not a pipe)", fd, buf); + } else { + trc("%d: %s ** CLOEXEC MISSING ** (ERROR)", fd, buf); + had_error = true; + } + } + } + + return had_error; +} + +static void* testerLoop(void* dummy) { + + pthread_barrier_wait(&start_barrier); + + trc("Tester is alive"); + + bool had_error = false; + + while (!atomic_load(&stop_now)) { + for (int fd = 0; fd < MAX_FD; fd++) { + bool rc = testFD(fd); + had_error = had_error || rc; + } + } + + trc("Tester dies"); + + return had_error ? (void*)1 : NULL; +} + +JNIEXPORT jboolean JNICALL +Java_PipesCloseOnExecTest_startTester(JNIEnv* env, jclass cls) +{ + pthread_attr_t attr; + int rc = 0; + + if (pthread_barrier_init(&start_barrier, NULL, 2) != 0) { + trc("pthread_barrier_init failed (%d)", errno); + return false; + } + + pthread_attr_init(&attr); + if (pthread_create(&tid_tester, &attr, testerLoop, NULL) != 0) { + trc("pthread_create failed (%d)", errno); + return JNI_FALSE; + } + + pthread_barrier_wait(&start_barrier); + + trc("Started tester"); + + return JNI_TRUE; +} + +JNIEXPORT jboolean JNICALL +Java_PipesCloseOnExecTest_stopTester(JNIEnv* env, jclass cls) +{ + atomic_store(&stop_now, true); + + void* retval = NULL; + pthread_join(tid_tester, &retval); + pthread_barrier_destroy(&start_barrier); + + return retval == NULL ? JNI_TRUE : JNI_FALSE; +} diff --git a/test/lib/native/testlib_thread_barriers.h b/test/lib/native/testlib_thread_barriers.h new file mode 100644 index 00000000000..8e0b717b57a --- /dev/null +++ b/test/lib/native/testlib_thread_barriers.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef TESTLIB_THREAD_BARRIERS_H +#define TESTLIB_THREAD_BARRIERS_H + +/* MacOS does not have pthread barriers; implement a fallback using condvars. */ + +#ifndef _WIN32 +#if !defined _POSIX_BARRIERS || _POSIX_BARRIERS < 0 + +#include + +#define PTHREAD_BARRIER_SERIAL_THREAD 1 + +#define pthread_barrier_t barr_t +#define pthread_barrier_init(barr, attr, need) barr_init(barr, attr, need) +#define pthread_barrier_destroy(barr) barr_destroy(barr) +#define pthread_barrier_wait(barr) barr_wait(barr) + +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + int have, need, trigger_count; +} barr_t; + +int barr_init(barr_t* b, void* ignored, int need) { + b->have = b->trigger_count = 0; + b->need = need; + pthread_mutex_init(&b->mutex, NULL); + pthread_cond_init(&b->cond, NULL); + return 0; +} + +int barr_destroy(barr_t* b) { + pthread_mutex_destroy(&b->mutex); + pthread_cond_destroy(&b->cond); + return 0; +} + +int barr_wait(barr_t* b) { + pthread_mutex_lock(&b->mutex); + int my_trigger_count = b->trigger_count; + b->have++; + if (b->have == b->need) { + b->have = 0; + b->trigger_count++; + pthread_cond_broadcast(&b->cond); + pthread_mutex_unlock(&b->mutex); + return PTHREAD_BARRIER_SERIAL_THREAD; + } + while (my_trigger_count == b->trigger_count) { // no spurious wakeups + pthread_cond_wait(&b->cond, &b->mutex); + } + pthread_mutex_unlock(&b->mutex); + return 0; +} + +#endif // !_POSIX_BARRIERS +#endif // !_WIN32 + +#endif // TESTLIB_THREAD_BARRIERS_H From 7c0da6702ffeed4fb5d991241010a133a7225321 Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Thu, 19 Mar 2026 05:20:45 +0000 Subject: [PATCH 019/160] 8380041: PPC: remove POWER6 remnants Reviewed-by: dbriemann, mdoerr --- src/hotspot/cpu/ppc/assembler_ppc.hpp | 7 ------- src/hotspot/cpu/ppc/assembler_ppc.inline.hpp | 2 -- src/hotspot/cpu/ppc/disassembler_ppc.cpp | 3 --- src/hotspot/cpu/ppc/macroAssembler_ppc.cpp | 4 ++-- src/hotspot/cpu/ppc/macroAssembler_ppc.hpp | 8 -------- src/hotspot/cpu/ppc/macroAssembler_ppc.inline.hpp | 13 ++++--------- src/hotspot/cpu/ppc/ppc.ad | 14 -------------- 7 files changed, 6 insertions(+), 45 deletions(-) diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index da2daffd579..378e01fc1cc 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -1580,10 +1580,6 @@ class Assembler : public AbstractAssembler { static bool is_nop(int x) { return x == 0x60000000; } - // endgroup opcode for Power6 - static bool is_endgroup(int x) { - return is_ori(x) && inv_ra_field(x) == 1 && inv_rs_field(x) == 1 && inv_d1_field(x) == 0; - } private: @@ -1659,9 +1655,6 @@ class Assembler : public AbstractAssembler { inline void ori_opt( Register d, int ui16); inline void oris_opt(Register d, int ui16); - // endgroup opcode for Power6 - inline void endgroup(); - // count instructions inline void cntlzw( Register a, Register s); inline void cntlzw_( Register a, Register s); diff --git a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp index bd6f3300606..d349bbc6f87 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp @@ -253,8 +253,6 @@ inline void Assembler::mr( Register d, Register s) { Assembler::orr(d, s, inline void Assembler::ori_opt( Register d, int ui16) { if (ui16!=0) Assembler::ori( d, d, ui16); } inline void Assembler::oris_opt(Register d, int ui16) { if (ui16!=0) Assembler::oris(d, d, ui16); } -inline void Assembler::endgroup() { Assembler::ori(R1, R1, 0); } - // count instructions inline void Assembler::cntlzw( Register a, Register s) { emit_int32(CNTLZW_OPCODE | rta(a) | rs(s) | rc(0)); } inline void Assembler::cntlzw_( Register a, Register s) { emit_int32(CNTLZW_OPCODE | rta(a) | rs(s) | rc(1)); } diff --git a/src/hotspot/cpu/ppc/disassembler_ppc.cpp b/src/hotspot/cpu/ppc/disassembler_ppc.cpp index fb3cb50cdec..2e16e1a301f 100644 --- a/src/hotspot/cpu/ppc/disassembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/disassembler_ppc.cpp @@ -119,9 +119,6 @@ address Disassembler::decode_instruction0(address here, outputStream * st, addre } else if (instruction == 0xbadbabe) { st->print(".data 0xbadbabe"); next = here + Assembler::instr_len(here); - } else if (Assembler::is_endgroup(instruction)) { - st->print("endgroup"); - next = here + Assembler::instr_len(here); } else { next = here; } diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index f3050cbabb4..14e90ddf185 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -483,7 +483,7 @@ void MacroAssembler::set_dest_of_bc_far_at(address instruction_addr, address des // variant 3, far cond branch to the next instruction, already patched to nops: // // nop - // endgroup + // nop // SKIP/DEST: // return; @@ -500,7 +500,7 @@ void MacroAssembler::set_dest_of_bc_far_at(address instruction_addr, address des if (is_bc_far_variant2_at(instruction_addr) && dest == instruction_addr + 8) { // Far branch to next instruction: Optimize it by patching nops (produce variant 3). masm.nop(); - masm.endgroup(); + masm.nop(); } else { if (is_bc_far_variant1_at(instruction_addr)) { // variant 1, the 1st instruction contains the destination address: diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp index 58dec702d7a..4be62098bdf 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp @@ -70,14 +70,6 @@ class MacroAssembler: public Assembler { // Move register if destination register and target register are different inline void mr_if_needed(Register rd, Register rs, bool allow_invalid = false); inline void fmr_if_needed(FloatRegister rd, FloatRegister rs); - // This is dedicated for emitting scheduled mach nodes. For better - // readability of the ad file I put it here. - // Endgroups are not needed if - // - the scheduler is off - // - the scheduler found that there is a natural group end, in that - // case it reduced the size of the instruction used in the test - // yielding 'needed'. - inline void endgroup_if_needed(bool needed); // Memory barriers. inline void membar(int bits); diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.inline.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.inline.hpp index 2b19d84c69c..cdeb8527bea 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.inline.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -72,11 +72,6 @@ inline void MacroAssembler::mr_if_needed(Register rd, Register rs, bool allow_no inline void MacroAssembler::fmr_if_needed(FloatRegister rd, FloatRegister rs) { if (rs != rd) fmr(rd, rs); } -inline void MacroAssembler::endgroup_if_needed(bool needed) { - if (needed) { - endgroup(); - } -} inline void MacroAssembler::membar(int bits) { // Comment: Usage of elemental_membar(bits) is not recommended for Power 8. @@ -240,13 +235,13 @@ inline bool MacroAssembler::is_bc_far_variant3_at(address instruction_addr) { // Variant 3, far cond branch to the next instruction, already patched to nops: // // nop - // endgroup + // nop // SKIP/DEST: // const int instruction_1 = *(int*)(instruction_addr); const int instruction_2 = *(int*)(instruction_addr + 4); return is_nop(instruction_1) && - is_endgroup(instruction_2); + is_nop(instruction_2); } // set dst to -1, 0, +1 as follows: if CR0bi is "greater than", dst is set to 1, diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 01c290f9b04..3b25604471f 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -14362,20 +14362,6 @@ instruct tlsLoadP(threadRegP dst) %{ //---Some PPC specific nodes--------------------------------------------------- -// Stop a group. -instruct endGroup() %{ - ins_cost(0); - - ins_is_nop(true); - - format %{ "End Bundle (ori r1, r1, 0)" %} - size(4); - ins_encode %{ - __ endgroup(); - %} - ins_pipe(pipe_class_default); -%} - // Nop instructions instruct fxNop() %{ From d0e62e114ce8a40b30300ae023b78488a350f879 Mon Sep 17 00:00:00 2001 From: Michael Reeves Date: Thu, 19 Mar 2026 07:44:30 +0000 Subject: [PATCH 020/160] 8351847: C2: Add "TraceSplitIf" flag to get more information about the optimization in Split-If Reviewed-by: chagedorn, dfenacci --- src/hotspot/share/opto/c2_globals.hpp | 3 + src/hotspot/share/opto/loopopts.cpp | 11 +++- src/hotspot/share/opto/split_if.cpp | 36 ++++++++++++ .../compiler/splitif/TestTraceSplitIf.java | 58 +++++++++++++++++++ 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/splitif/TestTraceSplitIf.java diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 66a6f2d0c3c..e0833afa29d 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -249,6 +249,9 @@ develop(bool, TraceLoopOpts, false, \ "Trace executed loop optimizations") \ \ + develop(bool, TraceSplitIf, false, \ + "Trace Split-If optimization") \ + \ develop(bool, TraceLoopLimitCheck, false, \ "Trace generation of loop limits checks") \ \ diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index a21fa43b30c..2a0178530e8 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1478,9 +1478,11 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n) { // Now split the IF C->print_method(PHASE_BEFORE_SPLIT_IF, 4, iff); - if (TraceLoopOpts) { - tty->print_cr("Split-If"); +#ifndef PRODUCT + if (TraceLoopOpts || TraceSplitIf) { + tty->print_cr("Split-If: %d %s", iff->_idx, iff->Name()); } +#endif do_split_if(iff); C->print_method(PHASE_AFTER_SPLIT_IF, 4, iff); return; @@ -1590,6 +1592,11 @@ bool PhaseIdealLoop::try_merge_identical_ifs(Node* n) { // Now split the IF RegionNode* new_false_region; RegionNode* new_true_region; +#ifndef PRODUCT + if (TraceLoopOpts || TraceSplitIf) { + tty->print_cr("Split-If Merging Identical Ifs: Dom-If: %d %s, If: %d %s", dom_if->_idx, dom_if->Name(), n->_idx, n->Name()); + } +#endif do_split_if(n, &new_false_region, &new_true_region); assert(new_false_region->req() == new_true_region->req(), ""); #ifdef ASSERT diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index dff8bf86606..91595c57a10 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -38,6 +38,11 @@ RegionNode* PhaseIdealLoop::split_thru_region(Node* n, RegionNode* region) { assert(n->is_CFG(), ""); RegionNode* r = new RegionNode(region->req()); IdealLoopTree* loop = get_loop(n); +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Splitting %d %s through %d %s", n->_idx, n->Name(), region->_idx, region->Name()); + } +#endif for (uint i = 1; i < region->req(); i++) { Node* x = n->clone(); Node* in0 = n->in(0); @@ -145,6 +150,11 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { } // Now actually split-up this guy. One copy per control path merging. +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Splitting up: %d %s", n->_idx, n->Name()); + } +#endif Node *phi = PhiNode::make_blank(blk1, n); for( uint j = 1; j < blk1->req(); j++ ) { Node *x = n->clone(); @@ -185,6 +195,11 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { // AddP and CheckCastPP have the same obj input after split if. bool PhaseIdealLoop::clone_cmp_loadklass_down(Node* n, const Node* blk1, const Node* blk2) { if (n->Opcode() == Op_AddP && at_relevant_ctrl(n, blk1, blk2)) { +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Cloning down (LoadKlass): %d %s", n->_idx, n->Name()); + } +#endif Node_List cmp_nodes; uint old = C->unique(); for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { @@ -301,6 +316,11 @@ bool PhaseIdealLoop::clone_cmp_down(Node* n, const Node* blk1, const Node* blk2) at_relevant_ctrl(cmov, blk1, blk2)))) { // Must clone down +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Cloning down (Cmp): %d %s", n->_idx, n->Name()); + } +#endif if (!n->is_FastLock()) { // Clone down any block-local BoolNode uses of this CmpNode for (DUIterator i = n->outs(); n->has_out(i); i++) { @@ -401,6 +421,12 @@ void PhaseIdealLoop::clone_template_assertion_expression_down(Node* node) { return; } +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Cloning down (Template Assertion Expression): %d %s", node->_idx, node->Name()); + } +#endif + TemplateAssertionExpressionNode template_assertion_expression_node(node); auto clone_expression = [&](IfNode* template_assertion_predicate) { OpaqueTemplateAssertionPredicateNode* opaque_node = @@ -459,6 +485,11 @@ Node *PhaseIdealLoop::spinup( Node *iff_dom, Node *new_false, Node *new_true, No Node *phi_post; if( prior_n == new_false || prior_n == new_true ) { phi_post = def->clone(); +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Spinup: cloning def to sink: %d %s -> %d %s", def->_idx, def->Name(), phi_post->_idx, phi_post->Name()); + } +#endif phi_post->set_req(0, prior_n ); register_new_node(phi_post, prior_n); } else { @@ -472,6 +503,11 @@ Node *PhaseIdealLoop::spinup( Node *iff_dom, Node *new_false, Node *new_true, No } else { assert( def->is_Phi(), "" ); assert( prior_n->is_Region(), "must be a post-dominating merge point" ); +#ifndef PRODUCT + if (TraceSplitIf) { + tty->print_cr(" Spinup: creating new Phi for merge: %d %s", def->_idx, def->Name()); + } +#endif // Need a Phi here phi_post = PhiNode::make_blank(prior_n, def); diff --git a/test/hotspot/jtreg/compiler/splitif/TestTraceSplitIf.java b/test/hotspot/jtreg/compiler/splitif/TestTraceSplitIf.java new file mode 100644 index 00000000000..32f3a213215 --- /dev/null +++ b/test/hotspot/jtreg/compiler/splitif/TestTraceSplitIf.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 8351847 + * @summary Sanity test for -XX:+TraceSplitIf to ensure it doesn't crash and produces output. + * @library /test/lib + * @requires vm.compiler2.enabled & vm.debug + * @run driver ${test.main.class} + */ + +package compiler.splitif; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestTraceSplitIf { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-XX:+TraceSplitIf", + "-Xcomp", + HelloWorld.class.getName() + ); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + output.shouldHaveExitValue(0); + output.shouldContain("Merging Identical Ifs"); + output.shouldContain("Split-If"); + } + + public static class HelloWorld { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } + } +} From 02001567f0d8ba83d28b014b9e8564f1e594ff74 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 19 Mar 2026 08:19:41 +0000 Subject: [PATCH 021/160] 8379425: Windows and macOS should not allow unsupported headless-only build Reviewed-by: erikj, aivanov, clanger --- make/autoconf/jdk-options.m4 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index dafac618c59..89fcbc88521 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -102,6 +102,13 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_JDK_OPTIONS], CHECKING_MSG: [if we should build headless-only (no GUI)]) AC_SUBST(ENABLE_HEADLESS_ONLY) + # Avoid headless-only on macOS and Windows, it is not supported there + if test "x$ENABLE_HEADLESS_ONLY" = xtrue; then + if test "x$OPENJDK_TARGET_OS" = xwindows || test "x$OPENJDK_TARGET_OS" = xmacosx; then + AC_MSG_ERROR([headless-only is not supported on macOS and Windows]) + fi + fi + # should we linktime gc unused code sections in the JDK build ? if test "x$OPENJDK_TARGET_OS" = "xlinux"; then if test "x$OPENJDK_TARGET_CPU" = "xs390x" || test "x$OPENJDK_TARGET_CPU" = "xppc64le"; then From a0c8fce9d76ee809d5f67b83b07c6829e44aed07 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Thu, 19 Mar 2026 09:57:28 +0000 Subject: [PATCH 022/160] 8379511: G1: G1CollectorState should derive concurrent cycle state from G1ConcurrentMark Reviewed-by: iwalulya, ayang --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 2 +- src/hotspot/share/gc/g1/g1CollectorState.cpp | 25 +++++++- src/hotspot/share/gc/g1/g1CollectorState.hpp | 26 ++------ src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 57 +++++++++-------- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 11 +++- .../share/gc/g1/g1ConcurrentMarkThread.cpp | 16 ++--- .../share/gc/g1/g1ConcurrentMarkThread.hpp | 46 ++++++++------ .../gc/g1/g1ConcurrentMarkThread.inline.hpp | 61 +++++++++++++++---- src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp | 6 +- src/hotspot/share/gc/g1/g1Policy.cpp | 22 ++----- src/hotspot/share/gc/g1/g1Policy.hpp | 2 +- src/hotspot/share/gc/g1/g1RemSet.cpp | 2 +- src/hotspot/share/gc/g1/g1VMOperations.cpp | 15 ++--- src/hotspot/share/gc/g1/g1YoungCollector.cpp | 2 +- .../g1/g1YoungGCAllocationFailureInjector.cpp | 10 +-- .../gc/g1/g1YoungGCPostEvacuateTasks.cpp | 2 +- src/hotspot/share/prims/whitebox.cpp | 2 +- 17 files changed, 185 insertions(+), 122 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index abc45254782..8b41a3b2079 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -2554,7 +2554,7 @@ HeapWord* G1CollectedHeap::do_collection_pause(size_t word_size, void G1CollectedHeap::start_concurrent_cycle(bool concurrent_operation_is_full_mark) { assert(_cm->is_fully_initialized(), "sanity"); - assert(!_cm->in_progress(), "Can not start concurrent operation while in progress"); + assert(!collector_state()->is_in_concurrent_cycle(), "Can not start concurrent cycle when already running"); MutexLocker x(G1CGC_lock, Mutex::_no_safepoint_check_flag); if (concurrent_operation_is_full_mark) { _cm->start_full_concurrent_cycle(); diff --git a/src/hotspot/share/gc/g1/g1CollectorState.cpp b/src/hotspot/share/gc/g1/g1CollectorState.cpp index d41ee22fdce..b64e9d45674 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.cpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,9 @@ * */ +#include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectorState.hpp" +#include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1GCPauseType.hpp" G1GCPauseType G1CollectorState::young_gc_pause_type(bool concurrent_operation_is_full_mark) const { @@ -44,3 +46,24 @@ G1GCPauseType G1CollectorState::young_gc_pause_type(bool concurrent_operation_is return G1GCPauseType::YoungGC; } } + +bool G1CollectorState::is_in_concurrent_cycle() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return cm->is_in_concurrent_cycle(); +} + +bool G1CollectorState::is_in_marking() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return cm->is_in_marking(); +} + +bool G1CollectorState::is_in_mark_or_rebuild() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return is_in_marking() || cm->is_in_rebuild_or_scrub(); +} + +bool G1CollectorState::is_in_reset_for_next_cycle() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return cm->is_in_reset_for_next_cycle(); +} + diff --git a/src/hotspot/share/gc/g1/g1CollectorState.hpp b/src/hotspot/share/gc/g1/g1CollectorState.hpp index fca30792344..ddf9d7fd181 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.hpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,16 +60,6 @@ class G1CollectorState { // do the concurrent start phase work. volatile bool _initiate_conc_mark_if_possible; - // Marking is in progress. Set from start of the concurrent start pause to the - // end of the Remark pause. - bool _mark_in_progress; - // Marking or rebuilding remembered set work is in progress. Set from the end - // of the concurrent start pause to the end of the Cleanup pause. - bool _mark_or_rebuild_in_progress; - - // The marking bitmap is currently being cleared or about to be cleared. - bool _clear_bitmap_in_progress; - // Set during a full gc pause. bool _in_full_gc; @@ -81,9 +71,6 @@ public: _in_concurrent_start_gc(false), _initiate_conc_mark_if_possible(false), - _mark_in_progress(false), - _mark_or_rebuild_in_progress(false), - _clear_bitmap_in_progress(false), _in_full_gc(false) { } // Phase setters @@ -96,10 +83,6 @@ public: void set_initiate_conc_mark_if_possible(bool v) { _initiate_conc_mark_if_possible = v; } - void set_mark_in_progress(bool v) { _mark_in_progress = v; } - void set_mark_or_rebuild_in_progress(bool v) { _mark_or_rebuild_in_progress = v; } - void set_clear_bitmap_in_progress(bool v) { _clear_bitmap_in_progress = v; } - // Phase getters bool in_young_only_phase() const { return _in_young_only_phase && !_in_full_gc; } bool in_mixed_phase() const { return !_in_young_only_phase && !_in_full_gc; } @@ -111,9 +94,10 @@ public: bool initiate_conc_mark_if_possible() const { return _initiate_conc_mark_if_possible; } - bool mark_in_progress() const { return _mark_in_progress; } - bool mark_or_rebuild_in_progress() const { return _mark_or_rebuild_in_progress; } - bool clear_bitmap_in_progress() const { return _clear_bitmap_in_progress; } + bool is_in_concurrent_cycle() const; + bool is_in_marking() const; + bool is_in_mark_or_rebuild() const; + bool is_in_reset_for_next_cycle() const; // Calculate GC Pause Type from internal state. G1GCPauseType young_gc_pause_type(bool concurrent_operation_is_full_mark) const; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 33b21d8348d..ab28feb520f 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -573,8 +573,20 @@ void G1ConcurrentMark::fully_initialize() { reset_at_marking_complete(); } -bool G1ConcurrentMark::in_progress() const { - return is_fully_initialized() ? _cm_thread->in_progress() : false; +bool G1ConcurrentMark::is_in_concurrent_cycle() const { + return is_fully_initialized() ? _cm_thread->is_in_progress() : false; +} + +bool G1ConcurrentMark::is_in_marking() const { + return is_fully_initialized() ? cm_thread()->is_in_marking() : false; +} + +bool G1ConcurrentMark::is_in_rebuild_or_scrub() const { + return cm_thread()->is_in_rebuild_or_scrub(); +} + +bool G1ConcurrentMark::is_in_reset_for_next_cycle() const { + return cm_thread()->is_in_reset_for_next_cycle(); } PartialArrayStateManager* G1ConcurrentMark::partial_array_state_manager() const { @@ -622,7 +634,7 @@ void G1ConcurrentMark::humongous_object_eagerly_reclaimed(G1HeapRegion* r) { // Need to clear mark bit of the humongous object. Doing this unconditionally is fine. mark_bitmap()->clear(r->bottom()); - if (!_g1h->collector_state()->mark_or_rebuild_in_progress()) { + if (!_g1h->collector_state()->is_in_mark_or_rebuild()) { return; } @@ -729,7 +741,7 @@ private: } bool is_clear_concurrent_undo() { - return suspendible() && _cm->cm_thread()->in_undo_mark(); + return suspendible() && _cm->cm_thread()->is_in_undo_cycle(); } bool has_aborted() { @@ -785,8 +797,7 @@ private: // as asserts here to minimize their overhead on the product. However, we // will have them as guarantees at the beginning / end of the bitmap // clearing to get some checking in the product. - assert(!suspendible() || _cm->in_progress(), "invariant"); - assert(!suspendible() || !G1CollectedHeap::heap()->collector_state()->mark_or_rebuild_in_progress(), "invariant"); + assert(!suspendible() || _cm->is_in_reset_for_next_cycle(), "invariant"); // Abort iteration if necessary. if (has_aborted()) { @@ -836,23 +847,14 @@ void G1ConcurrentMark::clear_bitmap(WorkerThreads* workers, bool may_yield) { void G1ConcurrentMark::cleanup_for_next_mark() { // Make sure that the concurrent mark thread looks to still be in // the current cycle. - guarantee(is_fully_initialized(), "should be initializd"); - guarantee(in_progress(), "invariant"); - - // We are finishing up the current cycle by clearing the next - // marking bitmap and getting it ready for the next cycle. During - // this time no other cycle can start. So, let's make sure that this - // is the case. - guarantee(!_g1h->collector_state()->mark_or_rebuild_in_progress(), "invariant"); + guarantee(is_in_reset_for_next_cycle(), "invariant"); clear_bitmap(_concurrent_workers, true); reset_partial_array_state_manager(); - // Repeat the asserts from above. - guarantee(is_fully_initialized(), "should be initializd"); - guarantee(in_progress(), "invariant"); - guarantee(!_g1h->collector_state()->mark_or_rebuild_in_progress(), "invariant"); + // Should not have changed state yet (even if a Full GC interrupted us). + guarantee(is_in_reset_for_next_cycle(), "invariant"); } void G1ConcurrentMark::reset_partial_array_state_manager() { @@ -977,14 +979,14 @@ void G1ConcurrentMark::start_full_concurrent_cycle() { // during it. No need to call it here. // Signal the thread to start work. - cm_thread()->start_full_mark(); + cm_thread()->start_full_cycle(); } void G1ConcurrentMark::start_undo_concurrent_cycle() { root_regions()->cancel_scan(); // Signal the thread to start work. - cm_thread()->start_undo_mark(); + cm_thread()->start_undo_cycle(); } void G1ConcurrentMark::notify_concurrent_cycle_completed() { @@ -1186,8 +1188,6 @@ uint G1ConcurrentMark::completed_mark_cycles() const { } void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) { - _g1h->collector_state()->set_clear_bitmap_in_progress(false); - _g1h->trace_heap_after_gc(_gc_tracer_cm); if (mark_cycle_completed) { @@ -1372,6 +1372,9 @@ void G1ConcurrentMark::remark() { G1ObjectCountIsAliveClosure is_alive(_g1h); _gc_tracer_cm->report_object_count_after_gc(&is_alive, _g1h->workers()); } + + // Successfully completed marking, advance state. + cm_thread()->set_full_cycle_rebuild_and_scrub(); } else { // We overflowed. Restart concurrent marking. _restart_for_overflow.store_relaxed(true); @@ -1392,6 +1395,8 @@ void G1ConcurrentMark::remark() { _g1h->update_perf_counter_cpu_time(); policy->record_concurrent_mark_remark_end(); + + return; } void G1ConcurrentMark::compute_new_sizes() { @@ -1454,6 +1459,10 @@ void G1ConcurrentMark::cleanup() { GCTraceTime(Debug, gc, phases) debug("Finalize Concurrent Mark Cleanup", _gc_timer_cm); policy->record_concurrent_mark_cleanup_end(needs_remembered_set_rebuild()); } + + // Advance state. + cm_thread()->set_full_cycle_reset_for_next_cycle(); + return; } // 'Keep Alive' oop closure used by both serial parallel reference processing. @@ -1880,7 +1889,7 @@ public: void G1ConcurrentMark::verify_no_collection_set_oops() { assert(SafepointSynchronize::is_at_safepoint() || !is_init_completed(), "should be at a safepoint or initializing"); - if (!_g1h->collector_state()->mark_or_rebuild_in_progress()) { + if (!is_fully_initialized() || !_g1h->collector_state()->is_in_mark_or_rebuild()) { return; } @@ -1958,7 +1967,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() { // has been signalled is already rare), and this work should be negligible compared // to actual full gc work. - if (!is_fully_initialized() || (!cm_thread()->in_progress() && !cm_thread()->should_terminate())) { + if (!is_fully_initialized() || (!cm_thread()->is_in_progress() && !cm_thread()->should_terminate())) { return false; } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 8bd04437097..de97179d210 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -353,6 +353,7 @@ class G1ConcurrentMark : public CHeapObj { friend class G1CMRootRegionScanTask; friend class G1CMTask; friend class G1ClearBitMapTask; + friend class G1CollectorState; friend class G1ConcurrentMarkThread; G1ConcurrentMarkThread* _cm_thread; // The thread doing the work @@ -528,6 +529,12 @@ class G1ConcurrentMark : public CHeapObj { G1ConcurrentMarkThread* cm_thread() const; + // Concurrent cycle state queries. + bool is_in_concurrent_cycle() const; + bool is_in_marking() const; + bool is_in_rebuild_or_scrub() const; + bool is_in_reset_for_next_cycle() const; + public: // To be called when an object is marked the first time, e.g. after a successful // mark_in_bitmap call. Updates various statistics data. @@ -561,7 +568,7 @@ public: void fully_initialize(); bool is_fully_initialized() const { return _cm_thread != nullptr; } - bool in_progress() const; + uint max_num_tasks() const {return _max_num_tasks; } // Clear statistics gathered during the concurrent cycle for the given region after @@ -666,8 +673,10 @@ public: // Do concurrent preclean work. void preclean(); + // Executes the Remark pause. void remark(); + // Executes the Cleanup pause. void cleanup(); // Mark in the marking bitmap. Used during evacuation failure to diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp index 629cbae935e..31d61b8b388 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -112,22 +112,22 @@ class G1ConcPhaseTimer : public GCTraceConcTimeImpl title("Concurrent %s Cycle", _state == FullMark ? "Mark" : "Undo"); + FormatBuffer<128> title("Concurrent %s Cycle", is_in_full_concurrent_cycle() ? "Mark" : "Undo"); GCTraceConcTime(Info, gc) tt(title); concurrent_cycle_start(); - if (_state == FullMark) { + if (_state == FullCycleMarking) { concurrent_mark_cycle_do(); } else { - assert(_state == UndoMark, "Must do undo mark but is %d", _state); + assert(_state == UndoCycleResetForNextCycle, "Must do undo mark but is %d", _state); concurrent_undo_cycle_do(); } - concurrent_cycle_end(_state == FullMark && !_cm->has_aborted()); + concurrent_cycle_end(is_in_full_concurrent_cycle() && !_cm->has_aborted()); update_perf_counter_cpu_time(); } @@ -135,7 +135,7 @@ void G1ConcurrentMarkThread::run_service() { } void G1ConcurrentMarkThread::stop_service() { - if (in_progress()) { + if (is_in_progress()) { // We are not allowed to abort the marking threads during root region scan. // Needs to be done separately. _cm->root_region_scan_abort_and_wait(); @@ -149,7 +149,7 @@ void G1ConcurrentMarkThread::stop_service() { bool G1ConcurrentMarkThread::wait_for_next_cycle() { MonitorLocker ml(G1CGC_lock, Mutex::_no_safepoint_check_flag); - while (!in_progress() && !should_terminate()) { + while (!is_in_progress() && !should_terminate()) { ml.wait(); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp index 22be7d9ffbb..e75298fdcb4 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,25 +32,34 @@ class G1Policy; // The concurrent mark thread triggers the various steps of the concurrent marking // cycle, including various marking cleanup. +// +// The concurrent cycle may either be "Full" (i.e. include marking, rebuilding and +// scrubbing, resetting for the next cycle) or "Undo", i.e. shortened to just the +// reset part. class G1ConcurrentMarkThread: public ConcurrentGCThread { G1ConcurrentMark* _cm; enum ServiceState : uint { Idle, - FullMark, - UndoMark + FullCycleMarking, + FullCycleRebuildOrScrub, + FullCycleResetForNextCycle, + UndoCycleResetForNextCycle }; volatile ServiceState _state; + // Returns whether we are in a "Full" cycle. + bool is_in_full_concurrent_cycle() const; + // Wait for next cycle. Returns the command passed over. bool wait_for_next_cycle(); bool mark_loop_needs_restart() const; - // Phases and subphases for the full concurrent marking cycle in order. + // Phases and subphases for the full concurrent cycle in order. // - // All these methods return true if the marking should be aborted. + // All these methods return true if the cycle should be aborted. bool phase_clear_cld_claimed_marks(); bool phase_scan_root_regions(); @@ -88,22 +97,25 @@ class G1ConcurrentMarkThread: public ConcurrentGCThread { double total_mark_cpu_time_s(); // Cpu time used by all marking worker threads in seconds. double worker_threads_cpu_time_s(); - - G1ConcurrentMark* cm() { return _cm; } - + // State management. void set_idle(); - void start_full_mark(); - void start_undo_mark(); + void start_full_cycle(); + void start_undo_cycle(); - bool idle() const; + void set_full_cycle_rebuild_and_scrub(); + void set_full_cycle_reset_for_next_cycle(); + + bool is_idle() const; // Returns true from the moment a concurrent cycle is - // initiated (during the concurrent start pause when started() is set) - // to the moment when the cycle completes (just after the next - // marking bitmap has been cleared and in_progress() is - // cleared). - bool in_progress() const; + // initiated (during the concurrent start pause when calling one of the + // start_*_cycle() methods) to the moment when the cycle completes. + bool is_in_progress() const; - bool in_undo_mark() const; + bool is_in_marking() const; + bool is_in_rebuild_or_scrub() const; + bool is_in_reset_for_next_cycle() const; + + bool is_in_undo_cycle() const; // Update the perf data counter for concurrent mark. void update_perf_counter_cpu_time(); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp index e80c5023405..8cb7881e000 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp @@ -40,29 +40,64 @@ inline double G1ConcurrentMarkThread::worker_threads_cpu_time_s() { return _cm->worker_threads_cpu_time_s(); } +inline bool G1ConcurrentMarkThread::is_in_full_concurrent_cycle() const { + ServiceState state = _state; + return (state == FullCycleMarking || state == FullCycleRebuildOrScrub || state == FullCycleResetForNextCycle); +} + inline void G1ConcurrentMarkThread::set_idle() { - assert(_state == FullMark || _state == UndoMark, "must not be starting a new cycle"); + // Concurrent cycle may be aborted any time. + assert(!is_idle(), "must not be idle"); _state = Idle; } -inline void G1ConcurrentMarkThread::start_full_mark() { - assert(_state == Idle, "cycle in progress"); - _state = FullMark; +inline void G1ConcurrentMarkThread::start_full_cycle() { + assert(SafepointSynchronize::is_at_safepoint(), "must be"); + assert(is_idle(), "cycle in progress"); + _state = FullCycleMarking; } -inline void G1ConcurrentMarkThread::start_undo_mark() { - assert(_state == Idle, "cycle in progress"); - _state = UndoMark; +inline void G1ConcurrentMarkThread::start_undo_cycle() { + assert(SafepointSynchronize::is_at_safepoint(), "must be"); + assert(is_idle(), "cycle in progress"); + _state = UndoCycleResetForNextCycle; } -inline bool G1ConcurrentMarkThread::idle() const { return _state == Idle; } - -inline bool G1ConcurrentMarkThread::in_progress() const { - return !idle(); +inline void G1ConcurrentMarkThread::set_full_cycle_rebuild_and_scrub() { + assert(SafepointSynchronize::is_at_safepoint(), "must be"); + assert(_state == FullCycleMarking, "must be"); + _state = FullCycleRebuildOrScrub; } -inline bool G1ConcurrentMarkThread::in_undo_mark() const { - return _state == UndoMark; +inline void G1ConcurrentMarkThread::set_full_cycle_reset_for_next_cycle() { + assert(SafepointSynchronize::is_at_safepoint(), "must be"); + assert(_state == FullCycleRebuildOrScrub, "must be"); + _state = FullCycleResetForNextCycle; +} + +inline bool G1ConcurrentMarkThread::is_in_marking() const { + return _state == FullCycleMarking; +} + +inline bool G1ConcurrentMarkThread::is_in_rebuild_or_scrub() const { + return _state == FullCycleRebuildOrScrub; +} + +inline bool G1ConcurrentMarkThread::is_in_reset_for_next_cycle() const { + ServiceState state = _state; + return state == FullCycleResetForNextCycle || state == UndoCycleResetForNextCycle; +} + +inline bool G1ConcurrentMarkThread::is_idle() const { + return _state == Idle; +} + +inline bool G1ConcurrentMarkThread::is_in_progress() const { + return !is_idle(); +} + +inline bool G1ConcurrentMarkThread::is_in_undo_cycle() const { + return _state == UndoCycleResetForNextCycle; } #endif // SHARE_GC_G1_G1CONCURRENTMARKTHREAD_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp b/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp index f280d76f3c7..ee11bbd961f 100644 --- a/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp +++ b/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,8 +38,8 @@ bool G1PeriodicGCTask::should_start_periodic_gc(G1CollectedHeap* g1h, // Ensure no GC safepoints while we're doing the checks, to avoid data races. SuspendibleThreadSetJoiner sts; - // If we are currently in a concurrent mark we are going to uncommit memory soon. - if (g1h->concurrent_mark()->in_progress()) { + // If we are currently in a concurrent cycle we are going to uncommit memory soon. + if (g1h->collector_state()->is_in_concurrent_cycle()) { log_debug(gc, periodic)("Concurrent cycle in progress. Skipping."); return false; } diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 98e6acc1d77..3c8b81bdb8f 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -579,9 +579,6 @@ void G1Policy::record_full_collection_end(size_t allocation_word_size) { collector_state()->set_in_young_gc_before_mixed(false); collector_state()->set_initiate_conc_mark_if_possible(need_to_start_conc_mark("end of Full GC", allocation_word_size)); collector_state()->set_in_concurrent_start_gc(false); - collector_state()->set_mark_in_progress(false); - collector_state()->set_mark_or_rebuild_in_progress(false); - collector_state()->set_clear_bitmap_in_progress(false); _eden_surv_rate_group->start_adding_regions(); // also call this on any additional surv rate groups @@ -709,7 +706,6 @@ void G1Policy::record_concurrent_mark_remark_end() { double elapsed_time_ms = (end_time_sec - start_time_sec) * 1000.0; _analytics->report_concurrent_mark_remark_times_ms(elapsed_time_ms); record_pause(G1GCPauseType::Remark, start_time_sec, end_time_sec); - collector_state()->set_mark_in_progress(false); } G1CollectionSetCandidates* G1Policy::candidates() const { @@ -739,7 +735,7 @@ double G1Policy::constant_other_time_ms(double pause_time_ms) const { } bool G1Policy::about_to_start_mixed_phase() const { - return _g1h->concurrent_mark()->in_progress() || collector_state()->in_young_gc_before_mixed(); + return collector_state()->is_in_concurrent_cycle() || collector_state()->in_young_gc_before_mixed(); } bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_word_size) { @@ -971,12 +967,8 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar _eden_surv_rate_group->start_adding_regions(); - assert(!(G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause) && collector_state()->mark_or_rebuild_in_progress()), - "If the last pause has been concurrent start, we should not have been in the marking window"); - if (G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause)) { - collector_state()->set_mark_in_progress(concurrent_operation_is_full_mark); - collector_state()->set_mark_or_rebuild_in_progress(concurrent_operation_is_full_mark); - } + assert(!(G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause) && collector_state()->is_in_concurrent_cycle()), + "If the last pause has been concurrent start, we should not have been in the marking cycle"); _free_regions_at_end_of_collection = _g1h->num_free_regions(); @@ -1235,7 +1227,7 @@ bool G1Policy::force_concurrent_start_if_outside_cycle(GCCause::Cause gc_cause) // We actually check whether we are marking here and not if we are in a // reclamation phase. This means that we will schedule a concurrent mark // even while we are still in the process of reclaiming memory. - bool during_cycle = _g1h->concurrent_mark()->in_progress(); + bool during_cycle = collector_state()->is_in_concurrent_cycle(); if (!during_cycle) { log_debug(gc, ergo)("Request concurrent cycle initiation (requested by GC cause). " "GC cause: %s", @@ -1328,8 +1320,8 @@ void G1Policy::decide_on_concurrent_start_pause() { // We do not allow concurrent start to be piggy-backed on a mixed GC. assert(!collector_state()->in_concurrent_start_gc() || collector_state()->in_young_only_phase(), "sanity"); - // We also do not allow mixed GCs during marking. - assert(!collector_state()->mark_or_rebuild_in_progress() || collector_state()->in_young_only_phase(), "sanity"); + // We also do not allow mixed GCs during marking/rebuilding. + assert(!collector_state()->is_in_mark_or_rebuild() || collector_state()->in_young_only_phase(), "sanity %d %d", collector_state()->is_in_concurrent_cycle(), collector_state()->in_young_only_phase()); } void G1Policy::record_concurrent_mark_cleanup_end(bool has_rebuilt_remembered_sets) { @@ -1349,8 +1341,6 @@ void G1Policy::record_concurrent_mark_cleanup_end(bool has_rebuilt_remembered_se log_debug(gc, ergo)("request young-only gcs (candidate old regions not available)"); } collector_state()->set_in_young_gc_before_mixed(mixed_gc_pending); - collector_state()->set_mark_or_rebuild_in_progress(false); - collector_state()->set_clear_bitmap_in_progress(true); double end_sec = os::elapsedTime(); double start_sec = cur_pause_start_sec(); diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 9513c79869e..ccbde31a465 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -115,7 +115,7 @@ class G1Policy: public CHeapObj { G1ConcurrentStartToMixedTimeTracker _concurrent_start_to_mixed; bool should_update_surv_rate_group_predictors() { - return collector_state()->in_young_only_phase() && !collector_state()->mark_or_rebuild_in_progress(); + return collector_state()->in_young_only_phase() && !collector_state()->is_in_mark_or_rebuild(); } double pending_cards_processing_time() const; diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index 0c9a0fad8f2..4b4a8a68c30 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -1026,7 +1026,7 @@ class G1MergeHeapRootsTask : public WorkerTask { // the pause occurs during the Concurrent Cleanup for Next Mark phase. // Only at that point the region's bitmap may contain marks while being in the collection // set at the same time. - return _g1h->collector_state()->clear_bitmap_in_progress() && + return _g1h->collector_state()->is_in_reset_for_next_cycle() && hr->is_old(); } diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index 56ab3a4b0fe..f98f0b078f3 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -84,8 +84,9 @@ void VM_G1TryInitiateConcMark::doit() { GCCauseSetter x(g1h, _gc_cause); - _mark_in_progress = g1h->collector_state()->mark_in_progress(); - _cycle_already_in_progress = g1h->concurrent_mark()->in_progress(); + G1CollectorState* state = g1h->collector_state(); + _mark_in_progress = state->is_in_marking(); + _cycle_already_in_progress = state->is_in_concurrent_cycle(); if (!g1h->policy()->force_concurrent_start_if_outside_cycle(_gc_cause)) { // Failure to force the next GC pause to be a concurrent start indicates @@ -166,11 +167,11 @@ void VM_G1PauseConcurrent::doit_epilogue() { } void VM_G1PauseRemark::work() { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - g1h->concurrent_mark()->remark(); + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + cm->remark(); } void VM_G1PauseCleanup::work() { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - g1h->concurrent_mark()->cleanup(); + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + cm->cleanup(); } diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 71a76a2e48b..3fceb42b6d7 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -391,7 +391,7 @@ class G1PrepareEvacuationTask : public WorkerTask { if (!obj->is_typeArray()) { // All regions that were allocated before marking have a TAMS != bottom. bool allocated_before_mark_start = region->bottom() != _g1h->concurrent_mark()->top_at_mark_start(region); - bool mark_in_progress = _g1h->collector_state()->mark_in_progress(); + bool mark_in_progress = _g1h->collector_state()->is_in_marking(); if (allocated_before_mark_start && mark_in_progress) { return false; diff --git a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp index 75cff2b339b..6ba72f6cde0 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,9 +61,9 @@ void G1YoungGCAllocationFailureInjector::select_allocation_failure_regions() { bool G1YoungGCAllocationFailureInjector::arm_if_needed_for_gc_type(bool for_young_only_phase, bool during_concurrent_start, - bool mark_or_rebuild_in_progress) { + bool in_concurrent_cycle) { bool res = false; - if (mark_or_rebuild_in_progress) { + if (in_concurrent_cycle) { res |= G1GCAllocationFailureALotDuringConcMark; } if (during_concurrent_start) { @@ -91,12 +91,12 @@ void G1YoungGCAllocationFailureInjector::arm_if_needed() { G1CollectorState* collector_state = g1h->collector_state(); const bool in_young_only_phase = collector_state->in_young_only_phase(); const bool in_concurrent_start_gc = collector_state->in_concurrent_start_gc(); - const bool mark_or_rebuild_in_progress = collector_state->mark_or_rebuild_in_progress(); + const bool in_concurrent_cycle = collector_state->is_in_concurrent_cycle(); _inject_allocation_failure_for_current_gc &= arm_if_needed_for_gc_type(in_young_only_phase, in_concurrent_start_gc, - mark_or_rebuild_in_progress); + in_concurrent_cycle); if (_inject_allocation_failure_for_current_gc) { select_allocation_failure_regions(); diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index a0013d27172..769cc9a077c 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -395,7 +395,7 @@ public: { ResourceMark rm; bool allocated_after_mark_start = r->bottom() == _g1h->concurrent_mark()->top_at_mark_start(r); - bool mark_in_progress = _g1h->collector_state()->mark_in_progress(); + bool mark_in_progress = _g1h->collector_state()->is_in_marking(); guarantee(obj->is_typeArray() || (allocated_after_mark_start || !mark_in_progress), "Only eagerly reclaiming primitive arrays is supported, other humongous objects only if allocated after mark start, but the object " PTR_FORMAT " (%s) is not (mark %d allocated after mark: %d).", diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 1a440584fe1..5df1461c0fd 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -578,7 +578,7 @@ WB_END WB_ENTRY(jboolean, WB_G1InConcurrentMark(JNIEnv* env, jobject o)) if (UseG1GC) { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - return g1h->concurrent_mark()->in_progress(); + return g1h->collector_state()->is_in_concurrent_cycle(); } THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1InConcurrentMark: G1 GC is not enabled"); WB_END From 75b2ee26804cf449ca3ab0580eb090817ab98eff Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Thu, 19 Mar 2026 10:11:57 +0000 Subject: [PATCH 023/160] 8376591: TestLoopNestTooManyTraps fails with various stress flags Reviewed-by: aseoane, qamai, dfenacci, syan --- test/hotspot/jtreg/ProblemList.txt | 2 -- .../compiler/longcountedloops/TestLoopNestTooManyTraps.java | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 5bb5bf54fba..a9211bed62e 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -81,8 +81,6 @@ compiler/interpreter/Test6833129.java 8335266 generic-i586 compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 -compiler/longcountedloops/TestLoopNestTooManyTraps.java 8376591 generic-all - compiler/unsafe/AlignmentGapAccess.java 8373487 generic-all ############################################################################# diff --git a/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java b/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java index 502afc56548..c2008a2cccb 100644 --- a/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java +++ b/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java @@ -25,6 +25,7 @@ * @test * @bug 8350330 * @summary C2: PhaseIdealLoop::add_parse_predicate() should mirror GraphKit::add_parse_predicate() + * @requires vm.flagless * @library /test/lib / * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox From 1de03224b8d4924f052ef074d251621d58bf98e4 Mon Sep 17 00:00:00 2001 From: Ralf Schmelter Date: Thu, 19 Mar 2026 12:28:17 +0000 Subject: [PATCH 024/160] 8378764: fileStream::fileSize() fails for >2GB files on Windows Reviewed-by: stuefe, dholmes, amenkov --- src/hotspot/os/posix/os_posix.cpp | 8 ++++++++ src/hotspot/os/windows/os_windows.cpp | 7 +++++++ src/hotspot/share/runtime/os.hpp | 2 ++ src/hotspot/share/utilities/ostream.cpp | 12 ++++++------ src/hotspot/share/utilities/ostream.hpp | 2 +- 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index f147ed4be93..1fb2a248bec 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -888,6 +888,14 @@ void* os::lookup_function(const char* name) { return dlsym(RTLD_DEFAULT, name); } +int64_t os::ftell(FILE* file) { + return ::ftell(file); +} + +int os::fseek(FILE* file, int64_t offset, int whence) { + return ::fseek(file, offset, whence); +} + jlong os::lseek(int fd, jlong offset, int whence) { return (jlong) ::lseek(fd, offset, whence); } diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 01162ef930d..18d047348cb 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -5114,6 +5114,13 @@ jlong os::seek_to_file_offset(int fd, jlong offset) { return (jlong)::_lseeki64(fd, (__int64)offset, SEEK_SET); } +int64_t os::ftell(FILE* file) { + return ::_ftelli64(file); +} + +int os::fseek(FILE* file, int64_t offset, int whence) { + return ::_fseeki64(file,offset, whence); +} jlong os::lseek(int fd, jlong offset, int whence) { return (jlong) ::_lseeki64(fd, offset, whence); diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 76041f256a9..c883b828456 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -721,6 +721,8 @@ class os: AllStatic { static int open(const char *path, int oflag, int mode); static FILE* fdopen(int fd, const char* mode); static FILE* fopen(const char* path, const char* mode); + static int64_t ftell(FILE* file); + static int fseek(FILE* file, int64_t offset, int whence); static jlong lseek(int fd, jlong offset, int whence); static bool file_exists(const char* file); diff --git a/src/hotspot/share/utilities/ostream.cpp b/src/hotspot/share/utilities/ostream.cpp index 5e339a700cb..ded233d48bf 100644 --- a/src/hotspot/share/utilities/ostream.cpp +++ b/src/hotspot/share/utilities/ostream.cpp @@ -611,15 +611,15 @@ void fileStream::write(const char* s, size_t len) { } } -long fileStream::fileSize() { - long size = -1; +int64_t fileStream::fileSize() { + int64_t size = -1; if (_file != nullptr) { - long pos = ::ftell(_file); + int64_t pos = os::ftell(_file); if (pos < 0) return pos; - if (::fseek(_file, 0, SEEK_END) == 0) { - size = ::ftell(_file); + if (os::fseek(_file, 0, SEEK_END) == 0) { + size = os::ftell(_file); } - ::fseek(_file, pos, SEEK_SET); + os::fseek(_file, pos, SEEK_SET); } return size; } diff --git a/src/hotspot/share/utilities/ostream.hpp b/src/hotspot/share/utilities/ostream.hpp index e971ac4d125..c3a4026f4d9 100644 --- a/src/hotspot/share/utilities/ostream.hpp +++ b/src/hotspot/share/utilities/ostream.hpp @@ -312,7 +312,7 @@ class fileStream : public outputStream { fclose(_file); _need_close = false; } - long fileSize(); + int64_t fileSize(); void flush(); }; From 4e9b35f9e8771e18352c7dfd3e3bc85f1811f617 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Thu, 19 Mar 2026 12:38:08 +0000 Subject: [PATCH 025/160] 8323792: ThreadSnapshot::initialize can cause assert in Thread::check_for_dangling_thread_pointer (possibility of dangling Thread pointer) Co-authored-by: Kevin Walls Reviewed-by: dholmes --- src/hotspot/share/runtime/threadSMR.cpp | 7 +- src/hotspot/share/runtime/threads.cpp | 2 +- src/hotspot/share/services/management.cpp | 3 +- src/hotspot/share/services/threadIdTable.cpp | 24 +-- src/hotspot/share/services/threadIdTable.hpp | 9 +- .../threads/ThreadInfoTest.java | 187 ++++++++++++++++++ 6 files changed, 213 insertions(+), 19 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/threads/ThreadInfoTest.java diff --git a/src/hotspot/share/runtime/threadSMR.cpp b/src/hotspot/share/runtime/threadSMR.cpp index 418f7707118..4c68648fec8 100644 --- a/src/hotspot/share/runtime/threadSMR.cpp +++ b/src/hotspot/share/runtime/threadSMR.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -726,7 +726,8 @@ JavaThread* ThreadsList::find_JavaThread_from_java_tid(jlong java_tid) const { } } } - } else if (!thread->is_exiting()) { + } else if (includes(thread) && !thread->is_exiting()) { + // The thread is protected by this list and has not yet exited return thread; } return nullptr; @@ -883,7 +884,7 @@ void ThreadsSMRSupport::add_thread(JavaThread *thread){ ThreadsList *old_list = xchg_java_thread_list(new_list); free_list(old_list); - if (ThreadIdTable::is_initialized()) { + if (ThreadIdTable::is_initialized_acquire()) { jlong tid = SharedRuntime::get_java_tid(thread); ThreadIdTable::add_thread(tid, thread); } diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index f7f755a37b3..442b68e596a 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -1127,7 +1127,7 @@ void Threads::remove(JavaThread* p, bool is_daemon) { ConditionalMutexLocker throttle_ml(ThreadsLockThrottle_lock, UseThreadsLockThrottleLock); MonitorLocker ml(Threads_lock); - if (ThreadIdTable::is_initialized()) { + if (ThreadIdTable::is_initialized_acquire()) { // This cleanup must be done before the current thread's GC barrier // is detached since we need to touch the threadObj oop. jlong tid = SharedRuntime::get_java_tid(p); diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp index 09277e16479..664fb5a8ef3 100644 --- a/src/hotspot/share/services/management.cpp +++ b/src/hotspot/share/services/management.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1146,6 +1146,7 @@ JVM_ENTRY(jint, jmm_GetThreadInfo(JNIEnv *env, jlongArray ids, jint maxDepth, jo // create dummy snapshot dump_result.add_thread_snapshot(); } else { + assert(dump_result.t_list()->includes(jt), "Must be protected"); dump_result.add_thread_snapshot(jt); } } diff --git a/src/hotspot/share/services/threadIdTable.cpp b/src/hotspot/share/services/threadIdTable.cpp index 1604927a0ac..d5141441ccd 100644 --- a/src/hotspot/share/services/threadIdTable.cpp +++ b/src/hotspot/share/services/threadIdTable.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ */ #include "classfile/javaClasses.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.inline.hpp" #include "runtime/threadSMR.hpp" @@ -44,7 +43,7 @@ static ThreadIdTableHash* volatile _local_table = nullptr; static volatile size_t _current_size = 0; static volatile size_t _items_count = 0; -volatile bool ThreadIdTable::_is_initialized = false; +Atomic ThreadIdTable::_is_initialized {false}; volatile bool ThreadIdTable::_has_work = false; class ThreadIdTableEntry : public CHeapObj { @@ -81,24 +80,25 @@ class ThreadIdTableConfig : public AllStatic { // Lazily creates the table and populates it with the given // thread list void ThreadIdTable::lazy_initialize(const ThreadsList *threads) { - if (!_is_initialized) { + if (!_is_initialized.load_acquire()) { { // There is no obvious benefit in allowing the thread table // to be concurrently populated during initialization. MutexLocker ml(ThreadIdTableCreate_lock); - if (_is_initialized) { + if (_is_initialized.load_relaxed()) { return; } create_table(threads->length()); - _is_initialized = true; + _is_initialized.release_store(true); } + for (uint i = 0; i < threads->length(); i++) { JavaThread* thread = threads->thread_at(i); oop tobj = thread->threadObj(); if (tobj != nullptr) { - jlong java_tid = java_lang_Thread::thread_id(tobj); MutexLocker ml(Threads_lock); if (!thread->is_exiting()) { + jlong java_tid = java_lang_Thread::thread_id(tobj); // Must be inside the lock to ensure that we don't add a thread to the table // that has just passed the removal point in Threads::remove(). add_thread(java_tid, thread); @@ -211,7 +211,7 @@ public: }; void ThreadIdTable::do_concurrent_work(JavaThread* jt) { - assert(_is_initialized, "Thread table is not initialized"); + assert(_is_initialized.load_relaxed(), "Thread table is not initialized"); _has_work = false; double load_factor = get_load_factor(); log_debug(thread, table)("Concurrent work, load factor: %g", load_factor); @@ -221,7 +221,8 @@ void ThreadIdTable::do_concurrent_work(JavaThread* jt) { } JavaThread* ThreadIdTable::add_thread(jlong tid, JavaThread* java_thread) { - assert(_is_initialized, "Thread table is not initialized"); + assert(Threads_lock->owned_by_self(), "Must hold Threads_lock"); + assert(_is_initialized.load_relaxed(), "Thread table is not initialized"); Thread* thread = Thread::current(); ThreadIdTableLookup lookup(tid); ThreadGet tg; @@ -240,7 +241,7 @@ JavaThread* ThreadIdTable::add_thread(jlong tid, JavaThread* java_thread) { } JavaThread* ThreadIdTable::find_thread_by_tid(jlong tid) { - assert(_is_initialized, "Thread table is not initialized"); + assert(_is_initialized.load_relaxed(), "Thread table is not initialized"); Thread* thread = Thread::current(); ThreadIdTableLookup lookup(tid); ThreadGet tg; @@ -249,7 +250,8 @@ JavaThread* ThreadIdTable::find_thread_by_tid(jlong tid) { } bool ThreadIdTable::remove_thread(jlong tid) { - assert(_is_initialized, "Thread table is not initialized"); + assert(Threads_lock->owned_by_self(), "Must hold Threads_lock"); + assert(_is_initialized.load_relaxed(), "Thread table is not initialized"); Thread* thread = Thread::current(); ThreadIdTableLookup lookup(tid); return _local_table->remove(thread, lookup); diff --git a/src/hotspot/share/services/threadIdTable.hpp b/src/hotspot/share/services/threadIdTable.hpp index 15dfb89d670..256484cce2d 100644 --- a/src/hotspot/share/services/threadIdTable.hpp +++ b/src/hotspot/share/services/threadIdTable.hpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ #define SHARE_SERVICES_THREADIDTABLE_HPP #include "memory/allStatic.hpp" +#include "runtime/atomic.hpp" class JavaThread; class ThreadsList; @@ -34,13 +35,15 @@ class ThreadIdTableConfig; class ThreadIdTable : public AllStatic { friend class ThreadIdTableConfig; - static volatile bool _is_initialized; + static Atomic _is_initialized; static volatile bool _has_work; public: // Initialization static void lazy_initialize(const ThreadsList* threads); - static bool is_initialized() { return _is_initialized; } + static bool is_initialized_acquire() { + return _is_initialized.load_acquire(); + } // Lookup and list management static JavaThread* find_thread_by_tid(jlong tid); diff --git a/test/hotspot/jtreg/serviceability/threads/ThreadInfoTest.java b/test/hotspot/jtreg/serviceability/threads/ThreadInfoTest.java new file mode 100644 index 00000000000..b5db455cc20 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/threads/ThreadInfoTest.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.Utils; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; + +import java.util.ArrayList; +import java.util.List; + +/* + * @test + * @bug 8323792 + * @summary Make sure that jmm_GetThreadInfo() call does not crash JVM + * @library /test/lib + * @modules java.management + * @run main/othervm ThreadInfoTest + * + * @comment Exercise getThreadInfo(ids, 0). Depth parameter of zero means + * no VM operation, which could crash with Threads starting and ending. + */ + +public class ThreadInfoTest { + private static com.sun.management.ThreadMXBean mbean = + (com.sun.management.ThreadMXBean)ManagementFactory.getThreadMXBean(); + + private static final int NUM_THREADS = 2; + static long[] ids = new long[NUM_THREADS]; + static ThreadInfo[] infos = new ThreadInfo[NUM_THREADS]; + static volatile int count = 0; + static int ITERATIONS = 4; + + public static void main(String[] argv) throws Exception { + boolean replacing = false; + + startThreads(ids, NUM_THREADS); + new MyGetThreadInfoThread(ids).start(); + new MyReplacerThread(ids).start(); + for (int i = 0; i < ITERATIONS; i++) { + do { + count = countInfo(infos); + System.out.println("Iteration " + i + ": ThreadInfos found (Threads alive): " + count); + goSleep(100); + } while (count > 0); + } + } + + private static Thread newThread(int i) { + Thread thread = new MyThread(i); + thread.setDaemon(true); + return thread; + } + + private static void startThreads(long[] ids, int count) { + System.out.println("Starting " + count + " Threads..."); + Thread[] threads = new Thread[count]; + for (int i = 0; i < count; i++) { + threads[i] = newThread(i); + threads[i].start(); + ids[i] = threads[i].getId(); + } + System.out.println(ids); + } + + // Count ThreadInfo from array, return how many are non-null. + private static int countInfo(ThreadInfo[] info) { + int count = 0; + if (info != null) { + int i = 0; + for (ThreadInfo ti: info) { + if (ti != null) { + count++; + } + i++; + } + } + return count; + } + + private static int replaceThreads(long[] ids, ThreadInfo[] info) { + int replaced = 0; + if (info != null) { + for (int i = 0; i < info.length; i++) { + ThreadInfo ti = info[i]; + if (ti == null) { + Thread thread = newThread(i); + thread.start(); + ids[i] = thread.getId(); + replaced++; + } + } + } + return replaced; + } + + private static void goSleep(long ms) { + try { + Thread.sleep(ms); + } catch (InterruptedException e) { + System.out.println("Unexpected exception is thrown: " + e); + } + } + + // A Thread which replaces Threads in the shared array of threads. + static class MyReplacerThread extends Thread { + long[] ids; + + public MyReplacerThread(long[] ids) { + this.ids = ids; + this.setDaemon(true); + } + + public void run() { + boolean replacing = false; + while (true) { + if (replacing) { + replaceThreads(ids, infos); + } + if (count < 10) { + replacing = true; + } + if (count > 20) { + replacing = false; + } + goSleep(1); + } + } + } + + // A Thread which lives for a short while. + static class MyThread extends Thread { + long endTimeMs; + + public MyThread(long n) { + super("MyThread-" + n); + endTimeMs = (n * n * 10) + System.currentTimeMillis(); + } + + public void run() { + try { + long sleep = Math.max(1, endTimeMs - System.currentTimeMillis()); + goSleep(sleep); + } catch (Exception e) { + System.out.println(Thread.currentThread().getName() + ": " + e); + } + } + } + + // A Thread to continually call getThreadInfo on a shared array of thread ids. + static class MyGetThreadInfoThread extends Thread { + long[] ids; + + public MyGetThreadInfoThread(long[] ids) { + this.ids = ids; + this.setDaemon(true); + } + + public void run() { + while (true) { + infos = mbean.getThreadInfo(ids, 0); + goSleep(10); + } + } + } +} From 347aae6428358e79a9463b04654f3eaf83450595 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Thu, 19 Mar 2026 12:40:50 +0000 Subject: [PATCH 026/160] 8380011: Path-to-gcroots search should not trigger stack overflows Reviewed-by: egahlin --- .../jfr/leakprofiler/chains/dfsClosure.cpp | 23 +++- .../jfr/leakprofiler/chains/dfsClosure.hpp | 3 + test/jdk/TEST.groups | 1 + .../jdk/jfr/event/oldobject/OldObjects.java | 14 ++- .../oldobject/TestDFSWithSmallStack.java | 101 ++++++++++++++++++ 5 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 test/jdk/jdk/jfr/event/oldobject/TestDFSWithSmallStack.java diff --git a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp index 83eee96091e..8b5819e92c4 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.cpp @@ -34,6 +34,7 @@ #include "memory/resourceArea.hpp" #include "oops/access.inline.hpp" #include "oops/oop.inline.hpp" +#include "runtime/os.hpp" #include "utilities/align.hpp" UnifiedOopRef DFSClosure::_reference_stack[max_dfs_depth]; @@ -67,9 +68,27 @@ void DFSClosure::find_leaks_from_root_set(EdgeStore* edge_store, rs.process(); } +static address calculate_headroom_limit() { + static constexpr size_t required_headroom = K * 64; + const Thread* const t = Thread::current_or_null(); + return t->stack_end() + required_headroom; +} + DFSClosure::DFSClosure(EdgeStore* edge_store, JFRBitSet* mark_bits, const Edge* start_edge) :_edge_store(edge_store), _mark_bits(mark_bits), _start_edge(start_edge), - _max_depth(max_dfs_depth), _depth(0), _ignore_root_set(false) { + _max_depth(max_dfs_depth), _depth(0), _ignore_root_set(false), + _headroom_limit(calculate_headroom_limit()) { +} + +bool DFSClosure::have_headroom() const { + const address sp = (address) os::current_stack_pointer(); +#ifdef ASSERT + const Thread* const t = Thread::current_or_null(); + assert(t->is_VM_thread(), "invariant"); + assert(t->is_in_full_stack(_headroom_limit), "invariant"); + assert(t->is_in_full_stack(sp), "invariant"); +#endif + return sp > _headroom_limit; } void DFSClosure::closure_impl(UnifiedOopRef reference, const oop pointee) { @@ -97,7 +116,7 @@ void DFSClosure::closure_impl(UnifiedOopRef reference, const oop pointee) { } } assert(_max_depth >= 1, "invariant"); - if (_depth < _max_depth - 1) { + if (_depth < _max_depth - 1 && have_headroom()) { _depth++; pointee->oop_iterate(this); assert(_depth > 0, "invariant"); diff --git a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp index 98364690422..9ee15ff07e0 100644 --- a/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp +++ b/src/hotspot/share/jfr/leakprofiler/chains/dfsClosure.hpp @@ -46,12 +46,15 @@ class DFSClosure : public BasicOopIterateClosure { size_t _max_depth; size_t _depth; bool _ignore_root_set; + const address _headroom_limit; DFSClosure(EdgeStore* edge_store, JFRBitSet* mark_bits, const Edge* start_edge); void add_chain(); void closure_impl(UnifiedOopRef reference, const oop pointee); + bool have_headroom() const; + public: virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS_EXCEPT_REFERENT; } diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index ccf745700d1..2bff07b8a55 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -558,6 +558,7 @@ jdk_jfr_sanity = \ jdk/jfr/event/gc/collection/TestGCWithFasttime.java \ jdk/jfr/event/gc/configuration/TestGCConfigurationEvent.java \ jdk/jfr/event/metadata/TestDefaultConfigurations.java \ + jdk/jfr/event/oldobject/TestDFSWithSmallStack.java \ jdk/jfr/startupargs/TestDumpOnExit.java \ jdk/jfr/api/consumer/recordingstream/TestBasics.java diff --git a/test/jdk/jdk/jfr/event/oldobject/OldObjects.java b/test/jdk/jdk/jfr/event/oldobject/OldObjects.java index ba90bb10a9e..bb0ca27836e 100644 --- a/test/jdk/jdk/jfr/event/oldobject/OldObjects.java +++ b/test/jdk/jdk/jfr/event/oldobject/OldObjects.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -276,4 +276,16 @@ final public class OldObjects { throw new RuntimeException("Reference chain max length not respected. Found a chain of length " + length); } } + + public static int countChains(List events) throws IOException { + int found = 0; + for (RecordedEvent e : events) { + RecordedObject ro = e.getValue("object"); + if (ro.getValue("referrer") != null) { + found++; + } + } + System.out.println("Found chains: " + found); + return found; + } } diff --git a/test/jdk/jdk/jfr/event/oldobject/TestDFSWithSmallStack.java b/test/jdk/jdk/jfr/event/oldobject/TestDFSWithSmallStack.java new file mode 100644 index 00000000000..d25a6cd5f67 --- /dev/null +++ b/test/jdk/jdk/jfr/event/oldobject/TestDFSWithSmallStack.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.event.oldobject; + +import java.util.LinkedList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test id=dfsonly + * @summary Tests that DFS works with a small stack + * @library /test/lib /test/jdk + * @requires vm.hasJFR + * @modules jdk.jfr/jdk.jfr.internal.test + * @run main/othervm -Xmx2g -XX:VMThreadStackSize=512 jdk.jfr.event.oldobject.TestDFSWithSmallStack dfsonly + */ + +/** + * @test id=bfsdfs + * @summary Tests that DFS works with a small stack + * @library /test/lib /test/jdk + * @requires vm.hasJFR + * @modules jdk.jfr/jdk.jfr.internal.test + * @run main/othervm -Xmx2g -XX:VMThreadStackSize=512 jdk.jfr.event.oldobject.TestDFSWithSmallStack bfsdfs + */ +public class TestDFSWithSmallStack { + + // Tests depth first search with a small stack. + + // An non-zero exit code, together with a missing hs-err file or possibly a missing jfr file, + // indicates a native stack overflow happened and is a fail condition for this test. + + // We build up an array of linked lists, each containing enough entries for DFS search to + // max out max_dfs_depth (but not greatly surpass it). + + private static final int TOTAL_OBJECTS = 10_000_000; + private static final int OBJECTS_PER_LIST = 5_000; + public static LinkedList[] leak; + + public static void main(String... args) throws Exception { + + switch (args[0]) { + case "dfsonly" -> WhiteBox.setSkipBFS(true); + case "bfsdfs" -> {} /* ignored */ + default -> throw new RuntimeException("Invalid argument"); + } + + WhiteBox.setWriteAllObjectSamples(true); + int count = 10; + + while (count > 0) { + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).with("cutoff", "infinity"); + r.start(); + leak = new LinkedList[TOTAL_OBJECTS / OBJECTS_PER_LIST]; + for (int i = 0; i < leak.length; i++) { + leak[i] = new LinkedList(); + for (int j = 0; j < OBJECTS_PER_LIST; j++) { + leak[i].add(new Object()); + } + } + System.gc(); + r.stop(); + List events = Events.fromRecording(r); + Events.hasEvents(events); + if (OldObjects.countChains(events) >= 30) { + return; + } + System.out.println("Not enough chains found, retrying."); + } + count--; + leak = null; + } + } +} From 3ad532135e47544c202e0f2f8e1df638da6fb20c Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Thu, 19 Mar 2026 14:47:35 +0000 Subject: [PATCH 027/160] 8380150: JavaCallArguments defined too late Reviewed-by: stefank, ayang --- src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp b/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp index 6c8684718fc..8e45490e5b6 100644 --- a/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp +++ b/src/hotspot/os_cpu/bsd_zero/atomicAccess_bsd_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008, 2011, 2015, Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,7 +27,6 @@ #define OS_CPU_BSD_ZERO_ATOMICACCESS_BSD_ZERO_HPP #include "orderAccess_bsd_zero.hpp" -#include "runtime/os.hpp" // Implementation of class AtomicAccess From c715a45431968d328cf01995607110f27515cb41 Mon Sep 17 00:00:00 2001 From: Leo Korinth Date: Thu, 19 Mar 2026 14:56:53 +0000 Subject: [PATCH 028/160] 8380298: Rename _pagesizes and _pre_allocated_pagesizes in hugepages.cpp Reviewed-by: stefank, iwalulya, jsikstro --- src/hotspot/os/linux/hugepages.cpp | 18 +++++++++--------- src/hotspot/os/linux/hugepages.hpp | 8 ++++---- src/hotspot/os/linux/os_linux.cpp | 28 +++++++++++++--------------- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/hotspot/os/linux/hugepages.cpp b/src/hotspot/os/linux/hugepages.cpp index a78f355c179..1340c470dff 100644 --- a/src/hotspot/os/linux/hugepages.cpp +++ b/src/hotspot/os/linux/hugepages.cpp @@ -35,16 +35,16 @@ #include ExplicitHugePageSupport::ExplicitHugePageSupport() : - _initialized(false), _pagesizes(), _pre_allocated_pagesizes(), _default_hugepage_size(SIZE_MAX), _inconsistent(false) {} + _initialized{false}, _os_supported{}, _pre_allocated{}, _default_hugepage_size{SIZE_MAX}, _inconsistent{false} {} -os::PageSizes ExplicitHugePageSupport::pagesizes() const { +os::PageSizes ExplicitHugePageSupport::os_supported() const { assert(_initialized, "Not initialized"); - return _pagesizes; + return _os_supported; } -os::PageSizes ExplicitHugePageSupport::pre_allocated_pagesizes() const { +os::PageSizes ExplicitHugePageSupport::pre_allocated() const { assert(_initialized, "Not initialized"); - return _pre_allocated_pagesizes; + return _pre_allocated; } size_t ExplicitHugePageSupport::default_hugepage_size() const { @@ -151,7 +151,7 @@ static os::PageSizes filter_pre_allocated_hugepages(os::PageSizes pagesizes) { void ExplicitHugePageSupport::print_on(outputStream* os) { if (_initialized) { os->print_cr("Explicit hugepage support:"); - for (size_t s = _pagesizes.smallest(); s != 0; s = _pagesizes.next_larger(s)) { + for (size_t s = _os_supported.smallest(); s != 0; s = _os_supported.next_larger(s)) { os->print_cr(" hugepage size: " EXACTFMT, EXACTFMTARGS(s)); } os->print_cr(" default hugepage size: " EXACTFMT, EXACTFMTARGS(_default_hugepage_size)); @@ -166,13 +166,13 @@ void ExplicitHugePageSupport::print_on(outputStream* os) { void ExplicitHugePageSupport::scan_os() { _default_hugepage_size = scan_default_hugepagesize(); if (_default_hugepage_size > 0) { - _pagesizes = scan_hugepages(); - _pre_allocated_pagesizes = filter_pre_allocated_hugepages(_pagesizes); + _os_supported = scan_hugepages(); + _pre_allocated = filter_pre_allocated_hugepages(_os_supported); // See https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt: /proc/meminfo should match // /sys/kernel/mm/hugepages/hugepages-xxxx. However, we may run on a broken kernel (e.g. on WSL) // that only exposes /proc/meminfo but not /sys/kernel/mm/hugepages. In that case, we are not // sure about the state of hugepage support by the kernel, so we won't use explicit hugepages. - if (!_pagesizes.contains(_default_hugepage_size)) { + if (!_os_supported.contains(_default_hugepage_size)) { log_info(pagesize)("Unexpected configuration: default pagesize (%zu) " "has no associated directory in /sys/kernel/mm/hugepages.", _default_hugepage_size); _inconsistent = true; diff --git a/src/hotspot/os/linux/hugepages.hpp b/src/hotspot/os/linux/hugepages.hpp index 5c190feae97..5a9767b4ff8 100644 --- a/src/hotspot/os/linux/hugepages.hpp +++ b/src/hotspot/os/linux/hugepages.hpp @@ -45,10 +45,10 @@ class ExplicitHugePageSupport { // All supported hugepage sizes (sizes for which entries exist // in /sys/kernel/mm/hugepages/hugepage-xxx) - os::PageSizes _pagesizes; + os::PageSizes _os_supported; // Above pages filtered for where the contents of file nr_hugepages was larger than zero - os::PageSizes _pre_allocated_pagesizes; + os::PageSizes _pre_allocated; // Contains the default hugepage. The "default hugepage size" is the one that // - is marked in /proc/meminfo as "Hugepagesize" @@ -63,8 +63,8 @@ public: void scan_os(); - os::PageSizes pagesizes() const; - os::PageSizes pre_allocated_pagesizes() const; + os::PageSizes os_supported() const; + os::PageSizes pre_allocated() const; size_t default_hugepage_size() const; void print_on(outputStream* os); diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 7bde0ae08c9..c79b0ab9fb5 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -3818,8 +3818,8 @@ static int hugetlbfs_page_size_flag(size_t page_size) { } static bool hugetlbfs_sanity_check(size_t page_size) { - const os::PageSizes page_sizes = HugePages::explicit_hugepage_info().pagesizes(); - assert(page_sizes.contains(page_size), "Invalid page sizes passed (%zu)", page_size); + const os::PageSizes os_supported = HugePages::explicit_hugepage_info().os_supported(); + assert(os_supported.contains(page_size), "Invalid page sizes passed (%zu)", page_size); // Include the page size flag to ensure we sanity check the correct page size. int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB | hugetlbfs_page_size_flag(page_size); @@ -3833,16 +3833,16 @@ static bool hugetlbfs_sanity_check(size_t page_size) { log_info(pagesize)("Large page size (" EXACTFMT ") failed sanity check, " "checking if smaller large page sizes are usable", EXACTFMTARGS(page_size)); - for (size_t page_size_ = page_sizes.next_smaller(page_size); - page_size_ > os::vm_page_size(); - page_size_ = page_sizes.next_smaller(page_size_)) { - flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB | hugetlbfs_page_size_flag(page_size_); - p = mmap(nullptr, page_size_, PROT_READ|PROT_WRITE, flags, -1, 0); + for (size_t size = os_supported.next_smaller(page_size); + size > os::vm_page_size(); + size = os_supported.next_smaller(size)) { + flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB | hugetlbfs_page_size_flag(size); + p = mmap(nullptr, size, PROT_READ|PROT_WRITE, flags, -1, 0); if (p != MAP_FAILED) { // Mapping succeeded, sanity check passed. - munmap(p, page_size_); + munmap(p, size); log_info(pagesize)("Large page size (" EXACTFMT ") passed sanity check", - EXACTFMTARGS(page_size_)); + EXACTFMTARGS(size)); return true; } } @@ -4024,7 +4024,7 @@ void os::Linux::large_page_init() { // - os::large_page_size() is the default explicit hugepage size (/proc/meminfo "Hugepagesize") // - os::pagesizes() contains all hugepage sizes the kernel supports, regardless whether there // are pages configured in the pool or not (from /sys/kernel/hugepages/hugepage-xxxx ...) - os::PageSizes all_large_pages = HugePages::explicit_hugepage_info().pagesizes(); + os::PageSizes all_large_pages = HugePages::explicit_hugepage_info().os_supported(); const size_t default_large_page_size = HugePages::default_explicit_hugepage_size(); // 3) Consistency check and post-processing @@ -4068,7 +4068,7 @@ void os::Linux::large_page_init() { // Populate _page_sizes with _large_page_size (default large page size) even if not pre-allocated. // Then, populate _page_sizes with all smaller large page sizes that have been pre-allocated. - os::PageSizes pre_allocated = HugePages::explicit_hugepage_info().pre_allocated_pagesizes(); + os::PageSizes pre_allocated = HugePages::explicit_hugepage_info().pre_allocated(); for (size_t page_size = _large_page_size; page_size != 0; page_size = pre_allocated.next_smaller(page_size)) { _page_sizes.add(page_size); } @@ -4133,12 +4133,12 @@ static char* reserve_memory_special_huge_tlbfs(size_t bytes, size_t page_size, char* req_addr, bool exec) { - const os::PageSizes page_sizes = HugePages::explicit_hugepage_info().pagesizes(); + const os::PageSizes os_supported = HugePages::explicit_hugepage_info().os_supported(); assert(UseLargePages, "only for Huge TLBFS large pages"); assert(is_aligned(req_addr, alignment), "Must be"); assert(is_aligned(req_addr, page_size), "Must be"); assert(is_aligned(alignment, os::vm_allocation_granularity()), "Must be"); - assert(page_sizes.contains(page_size), "Must be a valid page size"); + assert(os_supported.contains(page_size), "Must be a valid page size"); assert(page_size > os::vm_page_size(), "Must be a large page size"); assert(bytes >= page_size, "Shouldn't allocate large pages for small sizes"); @@ -5461,5 +5461,3 @@ void os::print_open_file_descriptors(outputStream* st) { st->print_cr("Open File Descriptors: %d", fds); } } - - From 28e72430ef3dae96f9b97615ea70a78c22e2424e Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Thu, 19 Mar 2026 15:15:19 +0000 Subject: [PATCH 029/160] 8379707: UserAuthWithAuthenticator::h3Test fails intermittentently Reviewed-by: syan, jpai, djelinski --- .../httpclient/UserAuthWithAuthenticator.java | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/test/jdk/java/net/httpclient/UserAuthWithAuthenticator.java b/test/jdk/java/net/httpclient/UserAuthWithAuthenticator.java index 90df367a619..6062fcd9448 100644 --- a/test/jdk/java/net/httpclient/UserAuthWithAuthenticator.java +++ b/test/jdk/java/net/httpclient/UserAuthWithAuthenticator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,17 +21,33 @@ * questions. */ -import java.io.*; -import java.net.*; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Authenticator; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.PasswordAuthentication; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.URI; +import java.net.URISyntaxException; import java.net.http.HttpClient; +import java.net.http.HttpClient.Version; import java.net.http.HttpRequest; -import java.net.http.HttpOption.Http3DiscoveryMode; import java.net.http.HttpResponse; -import javax.net.ssl.*; +import java.util.LinkedList; +import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.regex.*; -import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.net.ssl.SSLContext; + import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.net.URIBuilder; import jdk.httpclient.test.lib.common.HttpServerAdapters; @@ -42,6 +58,8 @@ import jdk.httpclient.test.lib.http2.Http2TestServer; import com.sun.net.httpserver.BasicAuthenticator; import org.junit.jupiter.api.Test; +import static java.net.http.HttpOption.H3_DISCOVERY; +import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.nio.charset.StandardCharsets.US_ASCII; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -119,7 +137,7 @@ class UserAuthWithAuthenticator { private static void h3Test(final boolean useHeader, boolean rightPassword) throws Exception { SSLContext sslContext = SimpleSSLContext.findSSLContext(); try (ExecutorService executor = Executors.newCachedThreadPool(); - HttpTestServer server = HttpTestServer.create(Http3DiscoveryMode.HTTP_3_URI_ONLY, sslContext, executor); + HttpTestServer server = HttpTestServer.create(HTTP_3_URI_ONLY, sslContext, executor); HttpClient client = HttpServerAdapters.createClientBuilderForH3() .sslContext(sslContext) .executor(executor) @@ -164,11 +182,18 @@ class UserAuthWithAuthenticator { .build(); var authHeaderValue = authHeaderValue("user", rightPassword ? "pwd" : "wrongPwd"); - HttpRequest req = HttpRequest.newBuilder(uri) + HttpRequest.Builder reqBuilder = HttpRequest.newBuilder(uri) .version(version) .header(useHeader ? "Authorization" : "X-Ignore", authHeaderValue) - .GET() - .build(); + .GET(); + if (version == Version.HTTP_3) { + // we should not attempt to default to TCP since our server is HTTP_3_URI_ONLY + // setting the option on the request also has the effect of telling + // the client that it can use the full HTTP/3 timeout, since the server + // is known to support HTTP/3 + reqBuilder.setOption(H3_DISCOVERY, HTTP_3_URI_ONLY); + } + HttpRequest req = reqBuilder.build(); HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString()); var sa = (ServerAuth) client.authenticator().orElseThrow(); From 5a22b6477bdda68df26675419165933cbe787704 Mon Sep 17 00:00:00 2001 From: Ashay Rane <253344819+raneashay@users.noreply.github.com> Date: Thu, 19 Mar 2026 15:37:03 +0000 Subject: [PATCH 030/160] 8278857: C2: optimize (x << 2) & -4 to x (and similar patterns) -> KnownBits for LShift Reviewed-by: qamai, mchevalier --- src/hotspot/share/opto/mulnode.cpp | 27 +-- src/hotspot/share/opto/rangeinference.hpp | 35 +++ src/hotspot/share/utilities/intn_t.hpp | 32 +++ .../gtest/opto/test_rangeinference.cpp | 200 ++++++++++++++---- 4 files changed, 245 insertions(+), 49 deletions(-) diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index cac9f1dcc37..b0aef9b165e 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -1143,21 +1143,24 @@ const Type* LShiftNode::ValueIL(PhaseGVN* phase, BasicType bt) const { return t1; } - // Either input is BOTTOM ==> the result is BOTTOM - if ((t1 == TypeInteger::bottom(bt)) || (t2 == TypeInt::INT) || - (t1 == Type::BOTTOM) || (t2 == Type::BOTTOM)) { + // If nothing is known about the shift amount then the result is BOTTOM + if (t2 == TypeInt::INT) { return TypeInteger::bottom(bt); } const TypeInteger* r1 = t1->is_integer(bt); // Handy access - const TypeInt* r2 = t2->is_int(); // Handy access + // Since the shift semantics in Java take into account only the bottom five + // bits for ints and the bottom six bits for longs, we can further constrain + // the range of values of the shift amount by ANDing with the right mask based + // on whether the type is int or long. + const TypeInt* mask = TypeInt::make(bits_per_java_integer(bt) - 1); + const TypeInt* r2 = RangeInference::infer_and(t2->is_int(), mask); if (!r2->is_con()) { return TypeInteger::bottom(bt); } uint shift = r2->get_con(); - shift &= bits_per_java_integer(bt) - 1; // semantics of Java shifts // Shift by a multiple of 32/64 does nothing: if (shift == 0) { return t1; @@ -1166,22 +1169,20 @@ const Type* LShiftNode::ValueIL(PhaseGVN* phase, BasicType bt) const { // If the shift is a constant, shift the bounds of the type, // unless this could lead to an overflow. if (!r1->is_con()) { - jlong lo = r1->lo_as_long(), hi = r1->hi_as_long(); #ifdef ASSERT if (bt == T_INT) { + jlong lo = r1->lo_as_long(), hi = r1->hi_as_long(); jint lo_int = r1->is_int()->_lo, hi_int = r1->is_int()->_hi; assert((java_shift_right(java_shift_left(lo, shift, bt), shift, bt) == lo) == (((lo_int << shift) >> shift) == lo_int), "inconsistent"); assert((java_shift_right(java_shift_left(hi, shift, bt), shift, bt) == hi) == (((hi_int << shift) >> shift) == hi_int), "inconsistent"); } #endif - if (java_shift_right(java_shift_left(lo, shift, bt), shift, bt) == lo && - java_shift_right(java_shift_left(hi, shift, bt), shift, bt) == hi) { - // No overflow. The range shifts up cleanly. - return TypeInteger::make(java_shift_left(lo, shift, bt), - java_shift_left(hi, shift, bt), - MAX2(r1->_widen, r2->_widen), bt); + + if (bt == T_INT) { + return RangeInference::infer_lshift(r1->is_int(), shift); } - return TypeInteger::bottom(bt); + + return RangeInference::infer_lshift(r1->is_long(), shift); } return TypeInteger::make(java_shift_left(r1->get_con_as_long(bt), shift, bt), bt); diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 66ea741a2da..7c0f12f6ef7 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -28,6 +28,7 @@ #include "cppstdlib/limits.hpp" #include "cppstdlib/type_traits.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/intn_t.hpp" class outputStream; class Type; @@ -407,6 +408,40 @@ public: return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); }); } + + // Compute `known_bits` by shifting known bits of `t1` left and setting the + // low `shift` bits to zeros. Also update the signed and unsigned ranges when + // the shift operation does not cause an overflow. The caller is responsible + // for normalizing the shift amount (i.e. masking with 31 for ints or 63 for + // longs). + template + static CTP infer_lshift(CTP t1, int masked_shift) { + assert(masked_shift >= 0 && + masked_shift < HotSpotNumerics::type_width>(), + "shift is out of range"); + + U pattern = (U(1) << masked_shift) - U(1); + U known_one_bits = t1->_bits._ones << masked_shift; + U known_zero_bits = (t1->_bits._zeros << masked_shift) | pattern; + KnownBits> known_bits{known_zero_bits, known_one_bits}; + + S shifted_slo = S(U(t1->_lo) << masked_shift); + S shifted_shi = S(U(t1->_hi) << masked_shift); + bool s_overflow = (shifted_slo >> masked_shift) != t1->_lo || + (shifted_shi >> masked_shift) != t1->_hi; + S slo = s_overflow ? std::numeric_limits>::min() : shifted_slo; + S shi = s_overflow ? std::numeric_limits>::max() : shifted_shi; + + U shifted_ulo = t1->_ulo << masked_shift; + U shifted_uhi = t1->_uhi << masked_shift; + bool u_overflow = (shifted_ulo >> masked_shift) != t1->_ulo || + (shifted_uhi >> masked_shift) != t1->_uhi; + U ulo = u_overflow ? std::numeric_limits>::min() : shifted_ulo; + U uhi = u_overflow ? std::numeric_limits>::max() : shifted_uhi; + + TypeIntPrototype, U> proto{{slo, shi}, {ulo, uhi}, known_bits}; + return CT::make(proto, t1->_widen); + } }; #endif // SHARE_OPTO_RANGEINFERENCE_HPP diff --git a/src/hotspot/share/utilities/intn_t.hpp b/src/hotspot/share/utilities/intn_t.hpp index 594e62a1694..1b8e7de652e 100644 --- a/src/hotspot/share/utilities/intn_t.hpp +++ b/src/hotspot/share/utilities/intn_t.hpp @@ -84,6 +84,7 @@ public: constexpr bool operator>(intn_t o) const { return int(*this) > int(o); } constexpr bool operator<=(intn_t o) const { return int(*this) <= int(o); } constexpr bool operator>=(intn_t o) const { return int(*this) >= int(o); } + constexpr intn_t operator>>(unsigned int s) const { return intn_t(int(*this) >> s); } }; template @@ -163,4 +164,35 @@ inline unsigned count_leading_zeros(uintn_t v) { return count_leading_zeros(v._v & uintn_t::_mask) - (32 - nbits); } +class HotSpotNumerics { +private: + template + static constexpr int type_width_impl(T value) { + // Count the number of 1s in `value`. We can't use population_count() from + // utilities/population_count.hpp, since it requires `std::is_integral`, which + // fails for `uintn_t`. Since this is a constexpr function, this function + // does not impose a runtime performance overhead. + return value == T(0) ? 0 : 1 + type_width_impl(value >> 1); + } + +public: + // Returns true if T is a signed type. We can't rely on std::is_signed + // because it returns false for intn_t, which is not a standard integral + // type. Instead, we check whether T(-1) is less than T(0). + template + static constexpr bool is_signed() { + return T(-1) < T(0); + } + + // Returns the bit width of the unsigned type T. We can't use sizeof() on T, + // since sizeof(uintn_t) returns the size of the underlying storage rather + // than the logical type width. So we instead compute the number of 1s in the + // maximum value. + template + static constexpr int type_width() { + static_assert(!is_signed(), "type_width requires an unsigned type"); + return type_width_impl(std::numeric_limits::max()); + } +}; + #endif // SHARE_UTILITIES_INTN_T_HPP diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index fd49050d022..641edaba4da 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -370,6 +370,32 @@ static void test_binary_instance_correctness_exhaustive(Operation op, Inference } } +template > +static void populate_sample_values(U* samples, const size_t sample_count, const InputType &input) { + constexpr size_t max_tries = 100; + constexpr size_t start_random_idx = 4; + + assert(sample_count >= 4, "need at least 4 slots for boundary values"); + samples[0] = U(input._lo); + samples[1] = U(input._hi); + samples[2] = input._ulo; + samples[3] = input._uhi; + + // Initialize remaining slots to a known-contained value in case the random + // fill below doesn't find enough values. + for (size_t i = start_random_idx; i < sample_count; i++) { + samples[i] = input._ulo; + } + + for (size_t tries = 0, idx = start_random_idx; tries < max_tries && idx < sample_count; tries++) { + U n = uniform_random(); + if (input.contains(n)) { + samples[idx] = n; + idx++; + } + } +} + // Check the correctness, that is, if v1 is an element of input1, v2 is an element of input2, then // op(v1, v2) must be an element of infer(input1, input2). This version does the check randomly on // a number of elements in input1 and input2. @@ -379,22 +405,11 @@ static void test_binary_instance_correctness_samples(Operation op, Inference inf auto result = infer(input1, input2); constexpr size_t sample_count = 6; - U input1_samples[sample_count] {U(input1._lo), U(input1._hi), input1._ulo, input1._uhi, input1._ulo, input1._ulo}; - U input2_samples[sample_count] {U(input2._lo), U(input2._hi), input2._ulo, input2._uhi, input2._ulo, input2._ulo}; + U input1_samples[sample_count]; + populate_sample_values(input1_samples, sample_count, input1); - auto random_sample = [](U* samples, const InputType& input) { - constexpr size_t max_tries = 100; - constexpr size_t start_random_idx = 4; - for (size_t tries = 0, idx = start_random_idx; tries < max_tries && idx < sample_count; tries++) { - U n = uniform_random(); - if (input.contains(n)) { - samples[idx] = n; - idx++; - } - } - }; - random_sample(input1_samples, input1); - random_sample(input2_samples, input2); + U input2_samples[sample_count]; + populate_sample_values(input2_samples, sample_count, input2); for (size_t i = 0; i < sample_count; i++) { for (size_t j = 0; j < sample_count; j++) { @@ -428,6 +443,22 @@ static void test_binary_instance_monotonicity_exhaustive(Inference infer, const } } +template +static InputType compute_random_superset(const InputType& input) { + using S = std::remove_const_t; + using U = std::remove_const_t; + + S lo = MIN2(input->_lo, S(uniform_random())); + S hi = MAX2(input->_hi, S(uniform_random())); + U ulo = MIN2(input->_ulo, uniform_random()); + U uhi = MAX2(input->_uhi, uniform_random()); + U zeros = input->_bits._zeros & uniform_random(); + U ones = input->_bits._ones & uniform_random(); + InputType super = InputType::make(TypeIntPrototype{{lo, hi}, {ulo, uhi}, {zeros, ones}}, 0); + assert(super.contains(input), "impossible"); + return super; +} + // Check the monotonicity, that is, if input1 is a subset of super1, input2 is a subset of super2, // then infer(input1, input2) must be a subset of infer(super1, super2). This version does the // check randomly on a number of supersets of input1 and input2. @@ -443,20 +474,8 @@ static void test_binary_instance_monotonicity_samples(Inference infer, const Inp ASSERT_TRUE(infer(input1, universe).contains(result)); ASSERT_TRUE(infer(universe, universe).contains(result)); - auto random_superset = [](const InputType& input) { - S lo = MIN2(input->_lo, S(uniform_random())); - S hi = MAX2(input->_hi, S(uniform_random())); - U ulo = MIN2(input->_ulo, uniform_random()); - U uhi = MAX2(input->_uhi, uniform_random()); - U zeros = input->_bits._zeros & uniform_random(); - U ones = input->_bits._ones & uniform_random(); - InputType super = InputType::make(TypeIntPrototype{{lo, hi}, {ulo, uhi}, {zeros, ones}}, 0); - assert(super.contains(input), "impossible"); - return super; - }; - - InputType super1 = random_superset(input1); - InputType super2 = random_superset(input2); + InputType super1 = compute_random_superset(input1); + InputType super2 = compute_random_superset(input2); ASSERT_TRUE(infer(super1, input2).contains(result)); ASSERT_TRUE(infer(input1, super2).contains(result)); ASSERT_TRUE(infer(super1, super2).contains(result)); @@ -480,16 +499,11 @@ static void test_binary_exhaustive(Operation op, Inference infer) { } } -// Verify the correctness and monotonicity of an inference function by randomly sampling instances -// of InputType -template -static void test_binary_random(Operation op, Inference infer) { +template +static void populate_sample_types(InputType* samples, const size_t sample_count) { using S = std::remove_const_t; using U = std::remove_const_t; - constexpr size_t sample_count = 100; - InputType samples[sample_count]; - // Fill with {0} for (size_t i = 0; i < sample_count; i++) { samples[i] = InputType::make(TypeIntPrototype{{S(0), S(0)}, {U(0), U(0)}, {U(0), U(0)}}, 0); @@ -549,6 +563,18 @@ static void test_binary_random(Operation op, Inference infer) { canonicalized_t._data._bits}; idx++; } +} + +// Verify the correctness and monotonicity of an inference function by randomly sampling instances +// of InputType +template +static void test_binary_random(Operation op, Inference infer) { + using S = std::remove_const_t; + using U = std::remove_const_t; + + constexpr size_t sample_count = 100; + InputType samples[sample_count]; + populate_sample_types(samples, sample_count); for (size_t i = 0; i < sample_count; i++) { for (size_t j = 0; j < sample_count; j++) { @@ -570,6 +596,107 @@ static void test_binary() { test_binary_random>(Operation(), Inference>()); } +template +static U lshift_op(U v, int shift) { + return v << shift; +} + +// Test correctness: for all values v in input, (v << shift) must be in the result +template +static void test_lshift_correctness(const InputType& input, int shift) { + using U = std::remove_const_t_ulo)>; + auto result = RangeInference::infer_lshift(input, shift); + for (juint v = 0; v <= juint(std::numeric_limits::max()); v++) { + if (input.contains(U(v))) { + U r = lshift_op(U(v), shift); + ASSERT_TRUE(result.contains(r)); + } + } +} + +// Test correctness for jint/jlong types by sampling values from the input range. +template +static void test_lshift_correctness_samples(const InputType& input, int shift) { + using U = std::remove_const_t_ulo)>; + auto result = RangeInference::infer_lshift(input, shift); + + constexpr size_t sample_count = 6; + U samples[sample_count]; + populate_sample_values(samples, sample_count, input); + + for (size_t i = 0; i < sample_count; i++) { + ASSERT_TRUE(input.contains(samples[i])); + + U r = lshift_op(samples[i], shift); + ASSERT_TRUE(result.contains(r)); + } +} + +// Test monotonicity: if input is a subset of super, then result is a subset of +// infer_lshift(super, shift) +template +static void test_lshift_monotonicity(const InputType& input, int shift) { + auto result = RangeInference::infer_lshift(input, shift); + for (const InputType& super : all_instances()) { + if (super.contains(input)) { + ASSERT_TRUE(RangeInference::infer_lshift(super, shift).contains(result)); + } + } +} + +// Test monotonicity for jint/jlong types by constructing random supersets. +template +static void test_lshift_monotonicity_samples(const InputType& input, int shift) { + using S = std::remove_const_t_lo)>; + using U = std::remove_const_t_ulo)>; + auto result = RangeInference::infer_lshift(input, shift); + + // The universe is a superset of all other sets + InputType universe = InputType{std::numeric_limits::min(), std::numeric_limits::max(), U(0), U(-1), {U(0), U(0)}}; + ASSERT_TRUE(RangeInference::infer_lshift(universe, shift).contains(result)); + + InputType super = compute_random_superset(input); + ASSERT_TRUE(RangeInference::infer_lshift(super, shift).contains(result)); +} + +template +static void test_lshift_for_type() { + using U = std::remove_const_t; + constexpr int type_bits = HotSpotNumerics::type_width(); + for (const InputType& input : all_instances()) { + for (int shift = 0; shift < type_bits; shift++) { + test_lshift_correctness(input, shift); + test_lshift_monotonicity(input, shift); + } + } +} + +// Sample-based test for jint/jlong types +template +static void test_lshift_random() { + using S = std::remove_const_t; + using U = std::remove_const_t; + + constexpr size_t sample_count = 100; + InputType samples[sample_count]; + populate_sample_types(samples, sample_count); + + for (size_t i = 0; i < sample_count; i++) { + for (int shift = 0; shift < HotSpotNumerics::type_width(); shift++) { + test_lshift_correctness_samples(samples[i], shift); + test_lshift_monotonicity_samples(samples[i], shift); + } + } +} + +static void test_lshift() { + test_lshift_for_type, uintn_t<1>>>(); + test_lshift_for_type, uintn_t<2>>>(); + test_lshift_for_type, uintn_t<3>>>(); + test_lshift_random>(); + test_lshift_random>(); +} + template class OpAnd { public: @@ -622,4 +749,5 @@ TEST(opto, range_inference) { test_binary(); test_binary(); test_binary(); + test_lshift(); } From 1b924bfc8979529e50c0298761c921e314a45706 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 19 Mar 2026 16:08:26 +0000 Subject: [PATCH 031/160] 8380204: java/io/File/EmptyPath.java fails due to unexpected listRoots value Reviewed-by: alanb --- test/jdk/java/io/File/EmptyPath.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/test/jdk/java/io/File/EmptyPath.java b/test/jdk/java/io/File/EmptyPath.java index 374a69c7959..67331380531 100644 --- a/test/jdk/java/io/File/EmptyPath.java +++ b/test/jdk/java/io/File/EmptyPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -285,17 +285,6 @@ public class EmptyPath { assertEquals(nioSet, ioSet); } - @Test - public void listRoots() { - Set expected = Arrays.stream(f.getAbsoluteFile().listRoots()) - .map(File::toString) - .collect(Collectors.toSet()); - Set actual = Arrays.stream(f.listRoots()) - .map(File::toString) - .collect(Collectors.toSet()); - assertEquals(expected, actual); - } - @Test public void mkdir() { assertFalse(f.mkdir()); From 76e7c9e68a42b41df1f309815d9f0111fd967d1f Mon Sep 17 00:00:00 2001 From: Damon Nguyen Date: Thu, 19 Mar 2026 16:11:50 +0000 Subject: [PATCH 032/160] 8380078: Update GIFlib to 6.1.2 Reviewed-by: serb, azvegint --- src/java.desktop/share/legal/giflib.md | 2 +- .../share/native/libsplashscreen/giflib/gif_lib.h | 2 +- .../share/native/libsplashscreen/giflib/gifalloc.c | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/java.desktop/share/legal/giflib.md b/src/java.desktop/share/legal/giflib.md index ba2dbb00535..781023dd334 100644 --- a/src/java.desktop/share/legal/giflib.md +++ b/src/java.desktop/share/legal/giflib.md @@ -1,4 +1,4 @@ -## GIFLIB v6.1.1 +## GIFLIB v6.1.2 ### GIFLIB License ``` diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib.h b/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib.h index 5d9c3346090..64b33beefa7 100644 --- a/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib.h +++ b/src/java.desktop/share/native/libsplashscreen/giflib/gif_lib.h @@ -39,7 +39,7 @@ extern "C" { #define GIFLIB_MAJOR 6 #define GIFLIB_MINOR 1 -#define GIFLIB_RELEASE 1 +#define GIFLIB_RELEASE 2 #define GIF_ERROR 0 #define GIF_OK 1 diff --git a/src/java.desktop/share/native/libsplashscreen/giflib/gifalloc.c b/src/java.desktop/share/native/libsplashscreen/giflib/gifalloc.c index aebf2d7662f..25e03914496 100644 --- a/src/java.desktop/share/native/libsplashscreen/giflib/gifalloc.c +++ b/src/java.desktop/share/native/libsplashscreen/giflib/gifalloc.c @@ -373,6 +373,14 @@ SavedImage *GifMakeSavedImage(GifFileType *GifFile, * aliasing problems. */ + /* Null out aliased pointers before any allocations + * so that FreeLastSavedImage won't free CopyFrom's + * data if an allocation fails partway through. */ + sp->ImageDesc.ColorMap = NULL; + sp->RasterBits = NULL; + sp->ExtensionBlocks = NULL; + sp->ExtensionBlockCount = 0; + /* first, the local color map */ if (CopyFrom->ImageDesc.ColorMap != NULL) { sp->ImageDesc.ColorMap = GifMakeMapObject( From 876a68ad41693940b3b05867eb1673f163c83b4a Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Thu, 19 Mar 2026 16:19:51 +0000 Subject: [PATCH 033/160] 8378884: Migrate jdk/jdk/nio/zipfs tests to use JUnit Reviewed-by: lancea --- test/jdk/jdk/nio/zipfs/Basic.java | 185 ++-- test/jdk/jdk/nio/zipfs/CRCWriteTest.java | 23 +- .../zipfs/{testng/test => }/ChannelTests.java | 209 ++-- .../jdk/nio/zipfs/CompressionModeTest.java | 84 +- test/jdk/jdk/nio/zipfs/CopyMoveTests.java | 91 +- .../jdk/nio/zipfs/DirectoryStreamTests.java | 80 +- .../jdk/jdk/nio/zipfs/EndOfCenValidation.java | 5 +- test/jdk/jdk/nio/zipfs/HasDotDotTest.java | 48 +- .../jdk/nio/zipfs/InvalidZipHeaderTests.java | 58 +- .../zipfs/LargeCompressedEntrySizeTest.java | 15 +- test/jdk/jdk/nio/zipfs/LargeEntriesTest.java | 113 +- .../{testng/test => }/ManifestOrderTest.java | 79 +- .../jdk/jdk/nio/zipfs/NewFileSystemTests.java | 99 +- .../jdk/nio/zipfs/NonExistentPathTests.java | 10 +- test/jdk/jdk/nio/zipfs/PathOps.java | 976 +++++++++--------- .../test => }/PosixAttributeViewTest.java | 40 +- .../nio/zipfs/PropertyPermissionTests.java | 20 +- test/jdk/jdk/nio/zipfs/ReleaseDeflater.java | 25 +- test/jdk/jdk/nio/zipfs/UpdateEntryTest.java | 25 +- test/jdk/jdk/nio/zipfs/ZFSTests.java | 103 +- test/jdk/jdk/nio/zipfs/ZeroDate.java | 89 +- .../jdk/nio/zipfs/ZipFSOutputStreamTest.java | 41 +- .../jdk/nio/zipfs/ZipFSPermissionsTest.java | 76 +- test/jdk/jdk/nio/zipfs/ZipFSTester.java | 548 +++++----- test/jdk/jdk/nio/zipfs/jarfs/JFSTester.java | 103 +- .../nio/zipfs/jarfs/MultiReleaseJarTest.java | 140 +-- test/jdk/jdk/nio/zipfs/testng/TEST.properties | 4 - .../{testng => }/util/ZipFsBaseTest.java | 74 +- 28 files changed, 1727 insertions(+), 1636 deletions(-) rename test/jdk/jdk/nio/zipfs/{testng/test => }/ChannelTests.java (93%) rename test/jdk/jdk/nio/zipfs/{testng/test => }/ManifestOrderTest.java (91%) rename test/jdk/jdk/nio/zipfs/{testng/test => }/PosixAttributeViewTest.java (77%) delete mode 100644 test/jdk/jdk/nio/zipfs/testng/TEST.properties rename test/jdk/jdk/nio/zipfs/{testng => }/util/ZipFsBaseTest.java (80%) diff --git a/test/jdk/jdk/nio/zipfs/Basic.java b/test/jdk/jdk/nio/zipfs/Basic.java index faaf634f93c..8416145299a 100644 --- a/test/jdk/jdk/nio/zipfs/Basic.java +++ b/test/jdk/jdk/nio/zipfs/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,15 +21,18 @@ * questions. */ +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.net.URISyntaxException; import java.nio.file.AccessMode; import java.nio.file.ClosedFileSystemException; import java.nio.file.FileStore; -import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.PathMatcher; import java.nio.file.ProviderMismatchException; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; @@ -37,103 +40,135 @@ import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.spi.FileSystemProvider; import java.net.URI; import java.io.IOException; -import java.util.Collections; import java.util.Map; + import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; -/** +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* * @test * @bug 8038500 8040059 8150366 8150496 8147539 8290047 * @summary Basic test for zip provider - * * @modules jdk.zipfs - * @run main Basic + * @run junit Basic */ - public class Basic { - public static void main(String[] args) throws Exception { - // Test: zip should be returned in provider list - boolean found = false; - for (FileSystemProvider provider: FileSystemProvider.installedProviders()) { - if (provider.getScheme().equalsIgnoreCase("jar")) { - found = true; - break; - } - } - if (!found) - throw new RuntimeException("'jar' provider not installed"); - // create JAR file for test - Path jarFile = Utils.createJarFile("basic.jar", + static Path jarFile; + static URI uri; + + @BeforeAll + static void setup() throws IOException, URISyntaxException { + jarFile = Utils.createJarFile("basic.jar", "META-INF/services/java.nio.file.spi.FileSystemProvider"); + uri = new URI("jar", jarFile.toUri().toString(), null); + } + @AfterAll + static void cleanUp() throws IOException { + Files.deleteIfExists(jarFile); + } + + @Test + void providerListTest() { + // Test: zip should be returned in provider list + assertTrue(FileSystemProvider.installedProviders().stream() + .anyMatch(p -> p.getScheme().equalsIgnoreCase("jar")), + "'jar' provider not installed"); + } + + @Test + void newFileSystemTest() throws IOException { + // To test `newFileSystem`, close the shared FileSystem + var fs = FileSystems.newFileSystem(uri, Map.of()); + fs.close(); // Test: FileSystems#newFileSystem(Path) - Map env = Collections.emptyMap(); FileSystems.newFileSystem(jarFile).close(); - // Test: FileSystems#newFileSystem(URI) - URI uri = new URI("jar", jarFile.toUri().toString(), null); - FileSystem fs = FileSystems.newFileSystem(uri, env, null); + FileSystems.newFileSystem(uri, Map.of()).close(); + } - // Test: exercise toUri method - String expected = uri.toString() + "!/foo"; - String actual = fs.getPath("/foo").toUri().toString(); - if (!actual.equals(expected)) { - throw new RuntimeException("toUri returned '" + actual + - "', expected '" + expected + "'"); + @Test + void toUriTest() throws IOException { + try (var fs = FileSystems.newFileSystem(uri, Map.of())) { + // Test: exercise toUri method + String expected = uri.toString() + "!/foo"; + String actual = fs.getPath("/foo").toUri().toString(); + assertEquals(expected, actual, "toUri returned '" + actual + + "', expected '" + expected + "'"); } + } - // Test: exercise directory iterator and retrieval of basic attributes - Files.walkFileTree(fs.getPath("/"), new FileTreePrinter()); + @Test + void directoryIteratorTest() throws IOException { + try (var fs = FileSystems.newFileSystem(uri, Map.of())) { + // Test: exercise directory iterator and retrieval of basic attributes + Files.walkFileTree(fs.getPath("/"), new FileTreePrinter()); + } + } - // Test: copy file from zip file to current (scratch) directory - Path source = fs.getPath("/META-INF/services/java.nio.file.spi.FileSystemProvider"); - if (Files.exists(source)) { - Path target = Path.of(source.getFileName().toString()); - Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); - try { - long s1 = Files.readAttributes(source, BasicFileAttributes.class).size(); - long s2 = Files.readAttributes(target, BasicFileAttributes.class).size(); - if (s2 != s1) - throw new RuntimeException("target size != source size"); - } finally { - Files.delete(target); + @Test + void copyFileTest() throws IOException { + try (var fs = FileSystems.newFileSystem(uri, Map.of())) { + // Test: copy file from zip file to current (scratch) directory + Path source = fs.getPath("/META-INF/services/java.nio.file.spi.FileSystemProvider"); + if (Files.exists(source)) { + Path target = Path.of(source.getFileName().toString()); + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); + try { + long s1 = Files.readAttributes(source, BasicFileAttributes.class).size(); + long s2 = Files.readAttributes(target, BasicFileAttributes.class).size(); + assertEquals(s1, s2, "target size != source size"); + } finally { + Files.delete(target); + } } } + } - // Test: FileStore - FileStore store = Files.getFileStore(fs.getPath("/")); - if (!store.supportsFileAttributeView("basic")) - throw new RuntimeException("BasicFileAttributeView should be supported"); - - // Test: watch register should throw PME - try { - fs.getPath("/") - .register(FileSystems.getDefault().newWatchService(), ENTRY_CREATE); - throw new RuntimeException("watch service is not supported"); - } catch (ProviderMismatchException x) { } - - // Test: IllegalArgumentException - try { - PathMatcher pm = fs.getPathMatcher(":glob"); - throw new RuntimeException("IllegalArgumentException not thrown"); - } catch (IllegalArgumentException iae) { - } - try { - PathMatcher pm = fs.getPathMatcher("glob:"); - } catch (IllegalArgumentException iae) { - iae.printStackTrace(); - throw new RuntimeException("Unexpected IllegalArgumentException"); + @Test + void fileStoreTest() throws IOException { + try (var fs = FileSystems.newFileSystem(uri, Map.of())) { + // Test: FileStore + FileStore store = Files.getFileStore(fs.getPath("/")); + assertTrue(store.supportsFileAttributeView("basic"), + "BasicFileAttributeView should be supported"); } + } + @Test + void watchRegisterNPETest() throws IOException { + try (var fs = FileSystems.newFileSystem(uri, Map.of())) { + // Test: watch register should throw PME + assertThrows(ProviderMismatchException.class, () -> fs.getPath("/") + .register(FileSystems.getDefault().newWatchService(), ENTRY_CREATE), + "watch service is not supported"); + } + } + + @Test + void pathMatcherIAETest() throws IOException { + try (var fs = FileSystems.newFileSystem(uri, Map.of())) { + // Test: IllegalArgumentException + assertThrows(IllegalArgumentException.class, () -> fs.getPathMatcher(":glob"), + "IllegalArgumentException not thrown"); + assertDoesNotThrow(() -> fs.getPathMatcher("glob:"), + "Unexpected IllegalArgumentException"); + } + } + + @Test + void closedFileSystemTest() throws IOException { // Test: ClosedFileSystemException + var fs = FileSystems.newFileSystem(uri, Map.of()); fs.close(); - if (fs.isOpen()) - throw new RuntimeException("FileSystem should be closed"); - try { - fs.provider().checkAccess(fs.getPath("/missing"), AccessMode.READ); - } catch (ClosedFileSystemException x) { } - - Files.deleteIfExists(jarFile); + assertFalse(fs.isOpen(), "FileSystem should be closed"); + assertThrows(ClosedFileSystemException.class, + () -> fs.provider().checkAccess(fs.getPath("/missing"), AccessMode.READ)); } // FileVisitor that pretty prints a file tree diff --git a/test/jdk/jdk/nio/zipfs/CRCWriteTest.java b/test/jdk/jdk/nio/zipfs/CRCWriteTest.java index 3f333ae9a44..268f8f42faf 100644 --- a/test/jdk/jdk/nio/zipfs/CRCWriteTest.java +++ b/test/jdk/jdk/nio/zipfs/CRCWriteTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,7 +21,6 @@ * questions. * */ -import org.testng.annotations.Test; import java.io.FileInputStream; import java.io.IOException; @@ -32,20 +31,20 @@ import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * @test * @bug 8232879 * @summary Test OutputStream::write with Zip FS * @modules jdk.zipfs - * @run testng/othervm CRCWriteTest + * @run junit/othervm CRCWriteTest */ public class CRCWriteTest { @@ -114,16 +113,16 @@ public class CRCWriteTest { // check entries with ZipFile API try (ZipFile zf = new ZipFile(zipfile.toFile())) { // check entry count - assertEquals(entries.length, zf.size()); + assertEquals(zf.size(), entries.length); // Check compression method and content of each entry for (Entry e : entries) { ZipEntry ze = zf.getEntry(e.name); assertNotNull(ze); - assertEquals(e.method, ze.getMethod()); + assertEquals(ze.getMethod(), e.method); try (InputStream in = zf.getInputStream(ze)) { byte[] bytes = in.readAllBytes(); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } @@ -134,9 +133,9 @@ public class CRCWriteTest { new ZipInputStream(new FileInputStream(zipfile.toFile()))) { ZipEntry ze; while ((ze = zis.getNextEntry()) != null) { - assertEquals(e.method, ze.getMethod()); + assertEquals(ze.getMethod(), e.method); byte[] bytes = zis.readAllBytes(); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } @@ -147,13 +146,13 @@ public class CRCWriteTest { Path top = fs.getPath("/"); long count = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile()).count(); - assertEquals(entries.length, count); + assertEquals(count, entries.length); // check content of each entry for (Entry e : entries) { Path file = fs.getPath(e.name); byte[] bytes = Files.readAllBytes(file); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } diff --git a/test/jdk/jdk/nio/zipfs/testng/test/ChannelTests.java b/test/jdk/jdk/nio/zipfs/ChannelTests.java similarity index 93% rename from test/jdk/jdk/nio/zipfs/testng/test/ChannelTests.java rename to test/jdk/jdk/nio/zipfs/ChannelTests.java index 3c7d18988b7..ffeed251649 100644 --- a/test/jdk/jdk/nio/zipfs/testng/test/ChannelTests.java +++ b/test/jdk/jdk/nio/zipfs/ChannelTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,9 +21,9 @@ * questions. * */ -package test; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import util.ZipFsBaseTest; import java.io.ByteArrayOutputStream; @@ -33,7 +33,11 @@ import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.SeekableByteChannel; -import java.nio.file.*; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.Map; import java.util.Random; @@ -41,15 +45,24 @@ import java.util.Set; import java.util.zip.ZipEntry; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.nio.file.StandardOpenOption.*; -import static org.testng.Assert.*; +import static java.nio.file.StandardOpenOption.APPEND; +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.CREATE_NEW; +import static java.nio.file.StandardOpenOption.READ; +import static java.nio.file.StandardOpenOption.WRITE; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -/** +/* * @test * @bug 8242006 * @summary Improve FileChannel and SeekableByteChannel Zip FS test coverage * @modules jdk.zipfs - * @run testng test.ChannelTests + * @run junit ChannelTests */ public class ChannelTests extends ZipFsBaseTest { @@ -81,7 +94,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcFromOSToZipTest(final Map env, final int compression) throws Exception { Entry e00 = Entry.of("Entry-00", compression, FIFTH_MAJOR); @@ -108,7 +122,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcFromZipToOSTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -125,7 +140,7 @@ public class ChannelTests extends ZipFsBaseTest { } // Check to see if the file exists and the bytes match assertTrue(Files.isRegularFile(osFile)); - assertEquals(Files.readAllBytes(osFile), e0.bytes); + assertArrayEquals(e0.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(zipFile); Files.deleteIfExists(osFile); } @@ -138,7 +153,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcFromZipToZipTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -170,7 +186,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param expectedCompression The compression to be used when copying the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap") + @ParameterizedTest + @MethodSource("copyMoveMap") public void sbcChangeCompressionTest(final Map env, final int compression, final int expectedCompression) throws Exception { @@ -206,7 +223,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcReadTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -222,7 +240,7 @@ public class ChannelTests extends ZipFsBaseTest { int bytesRead = sbc.read(buf); // Check to see if the expected bytes were read byte[] result = Arrays.copyOfRange(buf.array(), 0, bytesRead); - assertEquals(THE_SLAMS.getBytes(UTF_8), result); + assertArrayEquals(THE_SLAMS.getBytes(UTF_8), result); } Files.deleteIfExists(zipFile); } @@ -234,7 +252,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcWriteTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -261,7 +280,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcAppendTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -291,7 +311,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcTruncateTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -317,7 +338,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcFAETest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -344,7 +366,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcCloseTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -369,7 +392,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcCCETest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -401,7 +425,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcSizeTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -412,17 +437,17 @@ public class ChannelTests extends ZipFsBaseTest { try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); SeekableByteChannel sbc = Files.newByteChannel( zipfs.getPath(e0.name), Set.of(READ))) { - assertEquals(sbc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), sbc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); SeekableByteChannel sbc = Files.newByteChannel(zipfs.getPath(e0.name))) { - assertEquals(sbc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), sbc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); SeekableByteChannel sbc = Files.newByteChannel(zipfs.getPath("Entry-01") , Set.of(CREATE, WRITE))) { sbc.write(ByteBuffer.wrap(FIFTH_MAJOR.getBytes(UTF_8))); - assertEquals(sbc.size(), FIFTH_MAJOR.length()); + assertEquals(FIFTH_MAJOR.length(), sbc.size()); } Files.deleteIfExists(zipFile); } @@ -435,7 +460,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcOpenClosedTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -462,7 +488,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void sbcPositionTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -480,7 +507,7 @@ public class ChannelTests extends ZipFsBaseTest { for (var i = 0; i < fSize; i++) { long pos = RANDOM.nextInt(seed); sbc.position(pos); - assertEquals(sbc.position(), pos); + assertEquals(pos, sbc.position()); } } Files.deleteIfExists(zipFile); @@ -496,7 +523,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcFromOSToZipTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -522,7 +550,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcFromZipToOSTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -538,7 +567,7 @@ public class ChannelTests extends ZipFsBaseTest { } // Check to see if the file exists and the bytes match assertTrue(Files.isRegularFile(osFile)); - assertEquals(Files.readAllBytes(osFile), e0.bytes); + assertArrayEquals(e0.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(zipFile); Files.deleteIfExists(osFile); } @@ -551,7 +580,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcFromZipToZipTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -583,7 +613,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param expectedCompression The compression to be used when copying the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap") + @ParameterizedTest + @MethodSource("copyMoveMap") public void fcChangeCompressionTest(final Map env, final int compression, final int expectedCompression) throws Exception { @@ -620,7 +651,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcAppendTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -647,7 +679,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTruncateTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -672,7 +705,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcMapTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -701,7 +735,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcOpenClosedTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -728,7 +763,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcFAETest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -752,7 +788,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcCloseTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -777,7 +814,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcCCETest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -839,7 +877,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcReadTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -854,7 +893,7 @@ public class ChannelTests extends ZipFsBaseTest { int bytesRead = fc.read(buf); // Check to see if the expected bytes were read byte[] result = Arrays.copyOfRange(buf.array(), 0, bytesRead); - assertEquals(THE_SLAMS.getBytes(UTF_8), result); + assertArrayEquals(THE_SLAMS.getBytes(UTF_8), result); } Files.deleteIfExists(zipFile); } @@ -867,7 +906,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcReadPosTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -882,7 +922,7 @@ public class ChannelTests extends ZipFsBaseTest { int bytesRead = fc.read(buf, GRAND_SLAMS_HEADER.length()); // Check to see if the expected bytes were read byte[] result = Arrays.copyOfRange(buf.array(), 0, bytesRead); - assertEquals(GRAND_SLAMS.getBytes(UTF_8), result); + assertArrayEquals(GRAND_SLAMS.getBytes(UTF_8), result); } Files.deleteIfExists(zipFile); } @@ -895,7 +935,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcReadArrayTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -921,7 +962,7 @@ public class ChannelTests extends ZipFsBaseTest { bos.write(b.array()); } // Check to see if the returned byte array is what is expected - assertEquals(e0.bytes, bos.toByteArray()); + assertArrayEquals(bos.toByteArray(), e0.bytes); } Files.deleteIfExists(zipFile); } @@ -934,7 +975,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcReadArrayWithOffsetTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -969,7 +1011,7 @@ public class ChannelTests extends ZipFsBaseTest { bos.write(b.array()); } // Check to see if the returned byte array is what is expected - assertEquals(GRAND_SLAMS.getBytes(UTF_8), bos.toByteArray()); + assertArrayEquals(GRAND_SLAMS.getBytes(UTF_8), bos.toByteArray()); Files.deleteIfExists(zipFile); } @@ -981,7 +1023,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTransferToZipTest(final Map env, final int compression) throws Exception { Entry e00 = Entry.of("Entry-00", compression, THE_SLAMS); @@ -995,7 +1038,7 @@ public class ChannelTests extends ZipFsBaseTest { } // Verify the entry was copied verify(zipFile, e00); - assertEquals(Files.readAllBytes(osFile), e00.bytes); + assertArrayEquals(e00.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(osFile); Files.deleteIfExists(zipFile); } @@ -1008,7 +1051,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTransferToOsTest(final Map env, final int compression) throws Exception { Entry e00 = Entry.of("Entry-00", compression, THE_SLAMS); @@ -1021,7 +1065,7 @@ public class ChannelTests extends ZipFsBaseTest { fcTransferTo(zipfs.getPath(e00.name), osFile); } // Verify the entry was copied - assertEquals(Files.readAllBytes(osFile), e00.bytes); + assertArrayEquals(e00.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(osFile); Files.deleteIfExists(zipFile); } @@ -1034,7 +1078,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTransferToZipToZipTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -1065,7 +1110,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTransferFromOsTest(final Map env, final int compression) throws Exception { Entry e00 = Entry.of("Entry-00", compression, THE_SLAMS); @@ -1079,7 +1125,7 @@ public class ChannelTests extends ZipFsBaseTest { } // Verify the entry was copied zip(zipFile, env, e00); - assertEquals(Files.readAllBytes(osFile), e00.bytes); + assertArrayEquals(e00.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(osFile); Files.deleteIfExists(zipFile); } @@ -1092,7 +1138,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTransferFromZipTest(final Map env, final int compression) throws Exception { Entry e00 = Entry.of("Entry-00", compression, THE_SLAMS); @@ -1105,7 +1152,7 @@ public class ChannelTests extends ZipFsBaseTest { fcTransferFrom(zipfs.getPath(e00.name), osFile); } // Verify the bytes match - assertEquals(Files.readAllBytes(osFile), e00.bytes); + assertArrayEquals(e00.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(osFile); Files.deleteIfExists(zipFile); } @@ -1118,7 +1165,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTransferFromZipToZipTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -1148,7 +1196,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcWriteTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -1174,7 +1223,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcWritePosTest(final Map env, final int compression) throws Exception { // Use this value to replace the value specified for AUSTRALIAN_OPEN @@ -1209,7 +1259,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcWriteArrayTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -1227,7 +1278,7 @@ public class ChannelTests extends ZipFsBaseTest { FileChannel fc = FileChannel.open(zipfs.getPath(e0.name), Set.of(CREATE, WRITE))) { fc.write(bb); - assertEquals(fc.size(), GRAND_SLAMS.length()); + assertEquals(GRAND_SLAMS.length(), fc.size()); } // Verify the entry was created verify(zipFile, e0); @@ -1242,7 +1293,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcWriteArrayWithOffsetTest(final Map env, final int compression) throws Exception { @@ -1276,7 +1328,7 @@ public class ChannelTests extends ZipFsBaseTest { fc.position(GRAND_SLAMS_HEADER.length()); // Replace the original values fc.write(bb, 0, 2); - assertEquals(fc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), fc.size()); } // Verify the entry was updated verify(zipFile, e0.content(updatedFile)); @@ -1290,7 +1342,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcForceWriteTest(final Map env, final int compression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, THE_SLAMS); @@ -1317,7 +1370,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcPositionTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -1334,7 +1388,7 @@ public class ChannelTests extends ZipFsBaseTest { for (var i = 0; i < fSize; i++) { long pos = RANDOM.nextInt(seed); fc.position(pos); - assertEquals(fc.position(), pos); + assertEquals(pos, fc.position()); } } Files.deleteIfExists(zipFile); @@ -1347,7 +1401,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcSizeTest(final Map env, final int compression) throws Exception { Path osFile = Path.of("GrandSlams.txt"); @@ -1360,29 +1415,29 @@ public class ChannelTests extends ZipFsBaseTest { // Validate the file sizes match try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); FileChannel fc = FileChannel.open(zipfs.getPath(e0.name))) { - assertEquals(fc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), fc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); FileChannel fc = FileChannel.open(zipfs.getPath(e0.name), Set.of(READ))) { - assertEquals(fc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), fc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); FileChannel fc = FileChannel.open(zipfs.getPath(e0.name), Set.of(READ, WRITE))) { - assertEquals(fc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), fc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); FileChannel fc = FileChannel.open(zipfs.getPath(e0.name), Set.of(WRITE))) { - assertEquals(fc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), fc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); FileChannel fc = FileChannel.open(zipfs.getPath(e0.name), Set.of(APPEND))) { - assertEquals(fc.size(), THE_SLAMS.length()); + assertEquals(THE_SLAMS.length(), fc.size()); } try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env); FileChannel fc = FileChannel.open(zipfs.getPath("Entry-01"), Set.of(CREATE, WRITE))) { fc.write(ByteBuffer.wrap(FIFTH_MAJOR.getBytes(UTF_8))); - assertEquals(fc.size(), FIFTH_MAJOR.length()); + assertEquals(FIFTH_MAJOR.length(), fc.size()); } Files.deleteIfExists(zipFile); Files.deleteIfExists(osFile); @@ -1395,7 +1450,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcLockTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); @@ -1426,7 +1482,8 @@ public class ChannelTests extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void fcTryLockTest(final Map env, final int compression) throws Exception { Path zipFile = generatePath(HERE, "test", ".zip"); diff --git a/test/jdk/jdk/nio/zipfs/CompressionModeTest.java b/test/jdk/jdk/nio/zipfs/CompressionModeTest.java index 1b024d4addf..1307f6f4ed7 100644 --- a/test/jdk/jdk/nio/zipfs/CompressionModeTest.java +++ b/test/jdk/jdk/nio/zipfs/CompressionModeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ * */ -import org.testng.annotations.*; import java.io.IOException; import java.io.InputStream; @@ -32,22 +31,27 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.security.SecureRandom; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import static java.lang.String.format; import static java.util.stream.Collectors.joining; -import static org.testng.Assert.*; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** * @test * @bug 8231093 * @summary Test Zip FS compressionMethod property * @modules jdk.zipfs - * @run testng CompressionModeTest + * @run junit CompressionModeTest */ public class CompressionModeTest { @@ -77,7 +81,8 @@ public class CompressionModeTest { * @throws Exception If an error occurs during the creation, verification or * deletion of the ZIP file */ - @Test(dataProvider = "validCompressionMethods", enabled = true) + @ParameterizedTest + @MethodSource("validCompressionMethods") public void testValidCompressionMehods(Map env, int compression) throws Exception { @@ -99,7 +104,8 @@ public class CompressionModeTest { * @throws Exception if an error occurs other than the expected * IllegalArgumentException */ - @Test(dataProvider = "invalidCompressionMethod") + @ParameterizedTest + @MethodSource("invalidCompressionMethod") public void testInvalidCompressionMethod(Map env) throws Exception { System.out.printf("ZIP FS Map = %s%n ", formatMap(env)); Path zipfile = generatePath(HERE, "test", ".zip"); @@ -131,44 +137,42 @@ public class CompressionModeTest { } /** - * DataProvider used to validate that you can create a ZIP file with and + * MethodSource used to validate that you can create a ZIP file with and * without compression. */ - @DataProvider(name = "validCompressionMethods") - private Object[][] validCompressionMethods() { - return new Object[][]{ - {Map.of("create", "true"), ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "true"), - ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "false"), - ZipEntry.DEFLATED}, - {Map.of("create", "true", "compressionMethod", "STORED"), - ZipEntry.STORED}, - {Map.of("create", "true", "compressionMethod", "DEFLATED"), - ZipEntry.DEFLATED}, - {Map.of("create", "true", "compressionMethod", "stored"), - ZipEntry.STORED}, - {Map.of("create", "true", "compressionMethod", "deflated"), - ZipEntry.DEFLATED} - }; + private static Stream validCompressionMethods() { + return Stream.of( + Arguments.of(Map.of("create", "true"), ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "true"), + ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "false"), + ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "compressionMethod", "STORED"), + ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "compressionMethod", "DEFLATED"), + ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "compressionMethod", "stored"), + ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "compressionMethod", "deflated"), + ZipEntry.DEFLATED) + ); } /** - * DataProvider used to validate that an IllegalArgumentException is thrown + * MethodSource used to validate that an IllegalArgumentException is thrown * for an invalid value for the compressionMethod property. */ - @DataProvider(name = "invalidCompressionMethod") - private Object[][] invalidCompressionMethod() { + private static Stream invalidCompressionMethod() { HashMap map = new HashMap<>(); map.put("create", "true"); map.put("compressionMethod", null); - return new Object[][]{ - {map}, - {Map.of("create", "true", "compressionMethod", "")}, - {Map.of("create", "true", "compressionMethod", - Integer.parseInt("5"))}, - {Map.of("create", "true", "compressionMethod", "invalid")} - }; + return Stream.of( + Arguments.of(map), + Arguments.of(Map.of("create", "true", "compressionMethod", "")), + Arguments.of(Map.of("create", "true", "compressionMethod", + Integer.parseInt("5"))), + Arguments.of(Map.of("create", "true", "compressionMethod", "invalid")) + ); } /** @@ -186,16 +190,16 @@ public class CompressionModeTest { // check entries with ZIP API try (ZipFile zf = new ZipFile(zipfile.toFile())) { // check entry count - assertEquals(entries, zf.size()); + assertEquals(zf.size(), entries); // check compression method and content of each entry for (int i = start; i < entries; i++) { ZipEntry ze = zf.getEntry("Entry-" + i); assertNotNull(ze); - assertEquals(method, ze.getMethod()); + assertEquals(ze.getMethod(), method); try (InputStream is = zf.getInputStream(ze)) { byte[] bytes = is.readAllBytes(); - assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY)); + assertArrayEquals(ZIP_FILE_ENTRY, bytes); } } } @@ -209,13 +213,13 @@ public class CompressionModeTest { path.getFileName() != null && path.getFileName().toString().equals("META-INF"))) .count(); - assertEquals(entries, count); + assertEquals(count, entries); // check content of each entry for (int i = start; i < entries; i++) { Path file = fs.getPath("Entry-" + i); byte[] bytes = Files.readAllBytes(file); - assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY)); + assertArrayEquals(ZIP_FILE_ENTRY, bytes); } } } diff --git a/test/jdk/jdk/nio/zipfs/CopyMoveTests.java b/test/jdk/jdk/nio/zipfs/CopyMoveTests.java index 1991528f521..fc941bfe056 100644 --- a/test/jdk/jdk/nio/zipfs/CopyMoveTests.java +++ b/test/jdk/jdk/nio/zipfs/CopyMoveTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * 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,29 +22,32 @@ * */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.security.SecureRandom; -import java.util.Arrays; import java.util.Map; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import static java.lang.String.format; import static java.util.stream.Collectors.joining; -import static org.testng.Assert.*; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** * @test * @bug 8231766 * @summary Test Files::copy and Files::move with Zip FS * @modules jdk.zipfs - * @run testng/othervm CopyMoveTests + * @run junit/othervm CopyMoveTests */ public class CopyMoveTests { // Enable debugging output @@ -58,33 +61,31 @@ public class CopyMoveTests { private static final SecureRandom random = new SecureRandom(); /* - * DataProvider used to verify that a FileAlreadyExistsException is + * MethodSource used to verify that a FileAlreadyExistsException is * thrown with copying a file without the REPLACE_EXISTING option */ - @DataProvider(name = "zipfsMap") - private Object[][] zipfsMap() { - return new Object[][]{ - {Map.of("create", "true"), ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "true"), - ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "false"), - ZipEntry.DEFLATED} - }; + private static Stream zipfsMap() { + return Stream.of( + Arguments.of(Map.of("create", "true"), ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "true"), + ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "false"), + ZipEntry.DEFLATED) + ); } /* - * DataProvider used to verify that an entry may be copied or moved within + * MethodSource used to verify that an entry may be copied or moved within * a Zip file system with the correct compression method */ - @DataProvider(name = "copyMoveMap") - private Object[][] copyMoveMap() { - return new Object[][]{ - {Map.of("create", "true"), ZipEntry.DEFLATED, ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "true"), - ZipEntry.STORED, ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "false"), - ZipEntry.DEFLATED, ZipEntry.STORED} - }; + private static Stream copyMoveMap() { + return Stream.of( + Arguments.of(Map.of("create", "true"), ZipEntry.DEFLATED, ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "true"), + ZipEntry.STORED, ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "false"), + ZipEntry.DEFLATED, ZipEntry.STORED) + ); } /** @@ -96,7 +97,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when copying the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void copyTest(Map createMap, int compression, int expectedCompression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE); @@ -127,7 +129,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when copying the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void copyZipToZipTest(Map createMap, int compression, int expectedCompression) throws Exception { Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE); @@ -163,7 +166,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when copying the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void copyFromOsTest(Map createMap, int compression, int expectedCompression) throws Exception { @@ -196,7 +200,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when moving the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void CopyFromZipTest(Map createMap, int compression, int expectedCompression) throws Exception { @@ -217,7 +222,7 @@ public class CopyMoveTests { verify(zipFile, e0, e1); // Check to see if the file exists and the bytes match assertTrue(Files.isRegularFile(osFile)); - assertEquals(Files.readAllBytes(osFile), e0.bytes); + assertArrayEquals(e0.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(zipFile); Files.deleteIfExists(osFile); } @@ -231,7 +236,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when moving the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void moveTest(Map createMap, int compression, int expectedCompression) throws Exception { @@ -261,7 +267,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when moving the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void moveZipToZipTest(Map createMap, int compression, int expectedCompression) throws Exception { @@ -299,7 +306,8 @@ public class CopyMoveTests { * @param expectedCompression The compression to be used when moving the entry * @throws Exception If an error occurs */ - @Test(dataProvider = "copyMoveMap", enabled = true) + @ParameterizedTest + @MethodSource("copyMoveMap") public void moveFromZipTest(Map createMap, int compression, int expectedCompression) throws Exception { @@ -320,7 +328,7 @@ public class CopyMoveTests { verify(zipFile, e1); // Check to see if the file exists and the bytes match assertTrue(Files.isRegularFile(osFile)); - assertEquals(Files.readAllBytes(osFile), e0.bytes); + assertArrayEquals(e0.bytes, Files.readAllBytes(osFile)); Files.deleteIfExists(zipFile); Files.deleteIfExists(osFile); } @@ -332,7 +340,8 @@ public class CopyMoveTests { * @param createMap Properties used for creating the ZIP Filesystem * @throws Exception if an error occurs */ - @Test(dataProvider = "zipfsMap", enabled = true) + @ParameterizedTest + @MethodSource("zipfsMap") public void testFAEWithCopy(Map createMap, int compression) throws Exception { if (DEBUG) { @@ -444,17 +453,17 @@ public class CopyMoveTests { // check entries with zip API try (ZipFile zf = new ZipFile(zipfile.toFile())) { // check entry count - assertEquals(entries.length, zf.size()); + assertEquals(zf.size(), entries.length); // check compression method and content of each entry for (Entry e : entries) { ZipEntry ze = zf.getEntry(e.name); //System.out.printf("Name: %s, method: %s, Expected Method: %s%n", e.name, ze.getMethod(), e.method); assertNotNull(ze); - assertEquals(e.method, ze.getMethod()); + assertEquals(ze.getMethod(), e.method); try (InputStream in = zf.getInputStream(ze)) { byte[] bytes = in.readAllBytes(); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } @@ -465,13 +474,13 @@ public class CopyMoveTests { Path top = fs.getPath("/"); long count = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile()).count(); - assertEquals(entries.length, count); + assertEquals(count, entries.length); // check content of each entry for (Entry e : entries) { Path file = fs.getPath(e.name); byte[] bytes = Files.readAllBytes(file); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } diff --git a/test/jdk/jdk/nio/zipfs/DirectoryStreamTests.java b/test/jdk/jdk/nio/zipfs/DirectoryStreamTests.java index 0a255caa812..6aaaa4d5cd7 100644 --- a/test/jdk/jdk/nio/zipfs/DirectoryStreamTests.java +++ b/test/jdk/jdk/nio/zipfs/DirectoryStreamTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,10 +21,6 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.nio.file.*; @@ -33,9 +29,17 @@ import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.regex.PatternSyntaxException; +import java.util.stream.Stream; import java.util.zip.ZipException; -import static org.testng.Assert.*; +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** * @test @@ -43,7 +47,7 @@ import static org.testng.Assert.*; * @summary ZIP File System tests that leverage DirectoryStream * @modules jdk.zipfs * @compile DirectoryStreamTests.java - * @run testng DirectoryStreamTests + * @run junit DirectoryStreamTests */ public class DirectoryStreamTests { @@ -65,8 +69,8 @@ public class DirectoryStreamTests { /** * Create the JAR files used by the tests */ - @BeforeClass - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { emptyJarFile = Paths.get("emptyDir.jar"); try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(emptyJarFile, ZIPFS_MAP)) { @@ -80,8 +84,8 @@ public class DirectoryStreamTests { /** * Remove JAR files used by test as part of clean-up */ - @AfterClass - public void tearDown() throws Exception { + @AfterAll + public static void tearDown() throws Exception { Files.deleteIfExists(jarFile); Files.deleteIfExists(emptyJarFile); } @@ -91,7 +95,8 @@ public class DirectoryStreamTests { * System and that the returned Iterator correctly indicates whether the * filter has been matched */ - @Test(dataProvider = "filterTestValues") + @ParameterizedTest + @MethodSource("filterValues") public void test0000(String glob, boolean expectedResult, String errMsg) throws Exception { @@ -106,7 +111,7 @@ public class DirectoryStreamTests { } })) { - assertEquals(ds.iterator().hasNext(), expectedResult, errMsg); + assertEquals(expectedResult, ds.iterator().hasNext(), errMsg); } } @@ -114,7 +119,8 @@ public class DirectoryStreamTests { * Validate that you can specify a glob using the ZIP File System and that the * returned Iterator correctly indicates whether the glob pattern has been matched */ - @Test(dataProvider = "filterTestValues") + @ParameterizedTest + @MethodSource("filterValues") public void test0001(String glob, boolean expectedResult, String errMsg) throws Exception { @@ -122,7 +128,7 @@ public class DirectoryStreamTests { ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), UNZIPFS_MAP); DirectoryStream ds = Files.newDirectoryStream(zipfs.getPath("/"), glob)) { - assertEquals(ds.iterator().hasNext(), expectedResult, errMsg); + assertEquals(expectedResult, ds.iterator().hasNext(), errMsg); } } @@ -144,7 +150,8 @@ public class DirectoryStreamTests { * Validate that the correct type of paths are returned when creating a * DirectoryStream */ - @Test(dataProvider = "startPaths") + @ParameterizedTest + @MethodSource("Name") public void test0003(String startPath, String expectedPath) throws IOException { try (FileSystem zipfs = @@ -153,9 +160,8 @@ public class DirectoryStreamTests { Files.newDirectoryStream(zipfs.getPath(startPath))) { for (Path entry : stream) { - assertTrue(entry.toString().equals(expectedPath), - String.format("Error: Expected path %s not found when" - + " starting at %s%n", expectedPath, entry)); + assertEquals(entry.toString(), expectedPath, String.format("Error: Expected path %s not found when" + + " starting at %s%n", expectedPath, entry)); } } } @@ -310,35 +316,31 @@ public class DirectoryStreamTests { /** * Glob values to use to validate filtering */ - @DataProvider(name = "filterTestValues") - public static Object[][] filterValues() { + public static Stream filterValues() { String expectedMsg = "Error: Matching entries were expected but not found!!!"; String notExpectedMsg = "Error: No matching entries expected but were found!!!"; - return new Object[][]{ - - {"M*", true, expectedMsg}, - {"I*", false, notExpectedMsg} - }; + return Stream.of( + Arguments.of("M*", true, expectedMsg), + Arguments.of("I*", false, notExpectedMsg) + ); } /** * Starting Path for the DirectoryStream and the expected path to be returned * when traversing the stream */ - @DataProvider(name = "startPaths") - public static Object[][] Name() { - return new Object[][]{ - - {"META-INF", "META-INF/services"}, - {"/META-INF", "/META-INF/services"}, - {"/META-INF/../META-INF","/META-INF/../META-INF/services" }, - {"./META-INF", "./META-INF/services"}, - {"", "META-INF"}, - {"/", "/META-INF"}, - {".", "./META-INF"}, - {"./", "./META-INF"} - }; + public static Stream Name() { + return Stream.of( + Arguments.of("META-INF", "META-INF/services"), + Arguments.of("/META-INF", "/META-INF/services"), + Arguments.of("/META-INF/../META-INF", "/META-INF/../META-INF/services" ), + Arguments.of("./META-INF", "./META-INF/services"), + Arguments.of("", "META-INF"), + Arguments.of("/", "/META-INF"), + Arguments.of(".", "./META-INF"), + Arguments.of("./", "./META-INF") + ); } /** diff --git a/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java b/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java index ed0bdbb52ea..e8012cc3ea3 100644 --- a/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java +++ b/test/jdk/jdk/nio/zipfs/EndOfCenValidation.java @@ -142,9 +142,8 @@ public class EndOfCenValidation { * @param msg exception message to expect */ private static void verifyRejection(Path zip, String msg) { - ZipException ex = assertThrows(ZipException.class, () -> { - FileSystems.newFileSystem(zip); - }); + ZipException ex = assertThrows(ZipException.class, + () -> FileSystems.newFileSystem(zip)); assertEquals(msg, ex.getMessage()); } } diff --git a/test/jdk/jdk/nio/zipfs/HasDotDotTest.java b/test/jdk/jdk/nio/zipfs/HasDotDotTest.java index 6c6111dd0ab..26a4a597d3a 100644 --- a/test/jdk/jdk/nio/zipfs/HasDotDotTest.java +++ b/test/jdk/jdk/nio/zipfs/HasDotDotTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,6 @@ * questions. * */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -37,14 +35,20 @@ import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** * @test * @bug 8251329 * @summary Excercise Zip FS with "." or ".." in a Zip Entry name * @modules jdk.zipfs - * @run testng/othervm HasDotDotTest + * @run junit/othervm HasDotDotTest */ public class HasDotDotTest { // Zip file to be created @@ -56,22 +60,21 @@ public class HasDotDotTest { private static final boolean DEBUG = false; /** - * DataProvider containing Zip entry names which should result in an IOException + * MethodSource containing Zip entry names which should result in an IOException * @return Array of Zip entry names */ - @DataProvider - private Object[][] checkForDotOrDotDotPaths() { - return new Object[][]{ - {"/./foo"}, - {"/../foo"}, - {"/../foo/.."}, - {"/foo/.."}, - {"/foo/."}, - {"/.."}, - {"/."}, - {"/.foo/./"}, - {"/.././"}, - }; + private static Stream checkForDotOrDotDotPaths() { + return Stream.of( + Arguments.of("/./foo"), + Arguments.of("/../foo"), + Arguments.of("/../foo/.."), + Arguments.of("/foo/.."), + Arguments.of("/foo/."), + Arguments.of("/.."), + Arguments.of("/."), + Arguments.of("/.foo/./"), + Arguments.of("/.././") + ); } // Zip entry names to create a Zip file with for validating they are not @@ -111,7 +114,8 @@ public class HasDotDotTest { * @param path * @throws IOException */ - @Test(dataProvider = "checkForDotOrDotDotPaths") + @ParameterizedTest + @MethodSource("checkForDotOrDotDotPaths") public void hasDotOrDotDotTest(String path) throws IOException { if (DEBUG) { System.out.printf("Validating entry: %s%n", path); @@ -147,7 +151,7 @@ public class HasDotDotTest { } } Arrays.sort(EXPECTED_PATHS); - assertTrue(Arrays.equals(entries, EXPECTED_PATHS)); + assertArrayEquals(EXPECTED_PATHS, entries); } } Files.deleteIfExists(ZIPFILE); diff --git a/test/jdk/jdk/nio/zipfs/InvalidZipHeaderTests.java b/test/jdk/jdk/nio/zipfs/InvalidZipHeaderTests.java index 92acc98eb84..7a16f3026f2 100644 --- a/test/jdk/jdk/nio/zipfs/InvalidZipHeaderTests.java +++ b/test/jdk/jdk/nio/zipfs/InvalidZipHeaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,10 +21,6 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.FileOutputStream; import java.io.IOException; @@ -36,9 +32,17 @@ import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.stream.Collectors; +import java.util.stream.Stream; import static java.nio.file.Files.walk; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** * @test @@ -46,7 +50,7 @@ import static org.testng.Assert.*; * @summary Validate that you can iterate a ZIP file with invalid ZIP header entries * @modules jdk.zipfs * @compile InvalidZipHeaderTests.java - * @run testng InvalidZipHeaderTests + * @run junit InvalidZipHeaderTests */ public class InvalidZipHeaderTests { @@ -57,16 +61,16 @@ public class InvalidZipHeaderTests { /** * Create the JAR files used by the tests */ - @BeforeClass - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { createInvalidJarFile(); } /** * Remove JAR files used by test as part of clean-up */ - @AfterClass - public void tearDown() throws Exception { + @AfterAll + public static void tearDown() throws Exception { Files.deleteIfExists(Path.of(INVALID_JAR_FILE)); } @@ -75,17 +79,17 @@ public class InvalidZipHeaderTests { * Validate that you can walk a ZIP archive with header entries * such as "foo//" */ - @Test(dataProvider = "startPaths") + @ParameterizedTest + @MethodSource("Name") public void walkInvalidHeaderTest(String startPath, List expectedPaths) throws IOException { try (FileSystem zipfs = FileSystems.newFileSystem(Path.of(INVALID_JAR_FILE))) { List result = walk(zipfs.getPath(startPath)) .map(f -> f.toString()).collect(Collectors.toList()); - assertTrue(result.equals(expectedPaths), - String.format("Error: Expected paths not found when walking" - + "%s, starting at %s%n", INVALID_JAR_FILE, - startPath)); + assertEquals(result, expectedPaths, String.format("Error: Expected paths not found when walking" + + "%s, starting at %s%n", INVALID_JAR_FILE, + startPath)); } } @@ -94,18 +98,16 @@ public class InvalidZipHeaderTests { * Starting Path for walking the ZIP archive and the expected paths to be returned * when traversing the archive */ - @DataProvider(name = "startPaths") - public static Object[][] Name() { - return new Object[][]{ - - {"luckydog", List.of("luckydog", "luckydog/outfile.txt")}, - {"/luckydog", List.of("/luckydog", "/luckydog/outfile.txt")}, - {"./luckydog", List.of("./luckydog", "./luckydog/outfile.txt")}, - {"", List.of( "", "luckydog", "luckydog/outfile.txt")}, - {"/", List.of("/", "/luckydog", "/luckydog/outfile.txt")}, - {".", List.of(".", "./luckydog", "./luckydog/outfile.txt")}, - {"./", List.of(".", "./luckydog", "./luckydog/outfile.txt")} - }; + public static Stream Name() { + return Stream.of( + Arguments.of("luckydog", List.of("luckydog", "luckydog/outfile.txt")), + Arguments.of("/luckydog", List.of("/luckydog", "/luckydog/outfile.txt")), + Arguments.of("./luckydog", List.of("./luckydog", "./luckydog/outfile.txt")), + Arguments.of("", List.of( "", "luckydog", "luckydog/outfile.txt")), + Arguments.of("/", List.of("/", "/luckydog", "/luckydog/outfile.txt")), + Arguments.of(".", List.of(".", "./luckydog", "./luckydog/outfile.txt")), + Arguments.of("./", List.of(".", "./luckydog", "./luckydog/outfile.txt")) + ); } /** diff --git a/test/jdk/jdk/nio/zipfs/LargeCompressedEntrySizeTest.java b/test/jdk/jdk/nio/zipfs/LargeCompressedEntrySizeTest.java index 1dcfe461dda..44ec884db2d 100644 --- a/test/jdk/jdk/nio/zipfs/LargeCompressedEntrySizeTest.java +++ b/test/jdk/jdk/nio/zipfs/LargeCompressedEntrySizeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * 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,9 +22,6 @@ * */ -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; import java.io.IOException; import java.io.OutputStream; @@ -36,24 +33,28 @@ import java.util.Collections; import java.util.Random; import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + /** * @test * @bug 8190753 8011146 * @summary Verify that using zip filesystem for opening an outputstream for a zip entry whose * compressed size is large, doesn't run into "Negative initial size" exception - * @run testng/manual/othervm LargeCompressedEntrySizeTest + * @run junit/manual/othervm LargeCompressedEntrySizeTest */ public class LargeCompressedEntrySizeTest { private static final String LARGE_FILE_NAME = "LargeZipEntry.txt"; private static final String ZIP_FILE_NAME = "8190753-test-compressed-size.zip"; - @BeforeMethod + @BeforeEach public void setUp() throws IOException { deleteFiles(); } - @AfterMethod + @AfterEach public void tearDown() throws IOException { deleteFiles(); } diff --git a/test/jdk/jdk/nio/zipfs/LargeEntriesTest.java b/test/jdk/jdk/nio/zipfs/LargeEntriesTest.java index 3b05f3339d7..45251c95718 100644 --- a/test/jdk/jdk/nio/zipfs/LargeEntriesTest.java +++ b/test/jdk/jdk/nio/zipfs/LargeEntriesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * 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,32 +22,37 @@ * */ -import org.testng.annotations.*; - import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.*; import java.security.SecureRandom; -import java.util.Arrays; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.function.Consumer; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import static java.lang.Boolean.TRUE; import static java.lang.String.format; import static java.util.stream.Collectors.joining; -import static org.testng.Assert.*; -/** +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/* * @test * @bug 8230870 * @summary Test ZIP Filesystem behavior with ~64k entries * @modules jdk.zipfs - * @run testng LargeEntriesTest + * @run junit/othervm LargeEntriesTest */ public class LargeEntriesTest { @@ -86,34 +91,41 @@ public class LargeEntriesTest { private static final SecureRandom random = new SecureRandom(); /** - * Fields used for timing runs + * Fields used for timing runs. + * Toggle on by running with "-Dtiming.enabled=true". */ private static int testNumberRunning; private static long runningTestTime; private static long startTestRunTime; private static final double NANOS_IN_SECOND = 1_000_000_000.0; + private static final boolean TIMING_ENABLED = + Boolean.getBoolean("timing.enabled"); - @BeforeTest(enabled = false) - public void beforeTest() { + @BeforeAll + public static void beforeTest() { + if (!TIMING_ENABLED) return; startTestRunTime = System.nanoTime(); } - @AfterTest(enabled = false) - public void afterTest() { + @AfterAll + public static void afterTest() { + if (!TIMING_ENABLED) return; long endTestRunTime = System.nanoTime(); long duration = endTestRunTime - startTestRunTime; System.out.printf("#### Completed test run, total running time: %.4f in seconds%n", duration / NANOS_IN_SECOND); } - @BeforeMethod(enabled = false) - public static void beforeMethod() { + @BeforeEach + public void beforeMethod() { + if (!TIMING_ENABLED) return; runningTestTime = System.nanoTime(); System.out.printf("**** Starting test number: %s%n", testNumberRunning); } - @AfterMethod(enabled = false) + @AfterEach public void afterMethod() { + if (!TIMING_ENABLED) return; long endRunningTestTime = System.nanoTime(); long duration = endRunningTestTime - runningTestTime; System.out.printf("**** Completed test number: %s, Time: %.4f%n", @@ -132,7 +144,8 @@ public class LargeEntriesTest { * @throws Exception If an error occurs during the creation, verification or * deletion of the ZIP file */ - @Test(dataProvider = "zipfsMap", enabled = true) + @ParameterizedTest + @MethodSource("zipfsMap") public void testZip(Map env, int compression) throws Exception { System.out.printf("ZIP FS Map = %s, Compression mode= %s%n ", @@ -158,7 +171,8 @@ public class LargeEntriesTest { * @throws Exception If an error occurs during the creation, verification or * deletion of the ZIP file */ - @Test(dataProvider = "zip64Map", enabled = true) + @ParameterizedTest + @MethodSource("zip64Map") public void testForceZIP64End(Map env, int compression) throws Exception { System.out.printf("ZIP FS Map = %s, Compression mode= %s%n ", @@ -183,7 +197,8 @@ public class LargeEntriesTest { * @throws Exception If an error occurs during the creation, verification or * deletion of the JAR file */ - @Test(dataProvider = "zipfsMap", enabled = true) + @ParameterizedTest + @MethodSource("zipfsMap") public void testJar(Map env, int compression) throws Exception { for (int entries = ZIP64_ENTRIES - 1; entries < ZIP64_ENTRIES + 2; entries++) { Path jar = generatePath(HERE, "test", ".jar"); @@ -271,36 +286,34 @@ public class LargeEntriesTest { } /* - * DataProvider used to validate that you can create a ZIP file with and + * MethodSource used to validate that you can create a ZIP file with and * without compression. */ - @DataProvider(name = "zipfsMap") - private Object[][] zipfsMap() { - return new Object[][]{ - {Map.of("create", "true"), ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "true"), - ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "false"), - ZipEntry.DEFLATED} - }; + private static Stream zipfsMap() { + return Stream.of( + Arguments.of(Map.of("create", "true"), ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "true"), + ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "false"), + ZipEntry.DEFLATED) + ); } /* - * DataProvider used to validate that you can create a ZIP file with/without + * MethodSource used to validate that you can create a ZIP file with/without * ZIP64 format extensions */ - @DataProvider(name = "zip64Map") - private Object[][] zip64Map() { - return new Object[][]{ - {Map.of("create", "true", "forceZIP64End", "true"), - ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "true", - "forceZIP64End", "true"), ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "false", - "forceZIP64End", "false"), ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "true", - "forceZIP64End", "false"), ZipEntry.STORED} - }; + private static Stream zip64Map() { + return Stream.of( + Arguments.of(Map.of("create", "true", "forceZIP64End", "true"), + ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "true", + "forceZIP64End", "true"), ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "false", + "forceZIP64End", "false"), ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "true", + "forceZIP64End", "false"), ZipEntry.STORED) + ); } /** @@ -319,16 +332,16 @@ public class LargeEntriesTest { // check entries with ZIP API try (ZipFile zf = new ZipFile(zipfile.toFile())) { // check entry count - assertEquals(entries, zf.size()); + assertEquals(zf.size(), entries); // check compression method and content of each entry for (int i = start; i < entries; i++) { ZipEntry ze = zf.getEntry("Entry-" + i); assertNotNull(ze); - assertEquals(method, ze.getMethod()); + assertEquals(ze.getMethod(), method); try (InputStream is = zf.getInputStream(ze)) { byte[] bytes = is.readAllBytes(); - assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY)); + assertArrayEquals(ZIP_FILE_ENTRY, bytes); } } } @@ -342,13 +355,13 @@ public class LargeEntriesTest { path.getFileName() != null && path.getFileName().toString().equals("META-INF"))) .count(); - assertEquals(entries, count); + assertEquals(count, entries); // check content of each entry for (int i = start; i < entries; i++) { Path file = fs.getPath("Entry-" + i); byte[] bytes = Files.readAllBytes(file); - assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY)); + assertArrayEquals(ZIP_FILE_ENTRY, bytes); } } @@ -359,7 +372,7 @@ public class LargeEntriesTest { boolean requireZip64 = entries >= ZIP64_ENTRIES || isZip64Forced; System.out.printf(" isZip64Forced = %s, foundZip64= %s, requireZip64= %s%n", isZip64Forced, foundZip64, requireZip64); - assertEquals(requireZip64, foundZip64); + assertEquals(foundZip64, requireZip64); } @@ -515,7 +528,7 @@ public class LargeEntriesTest { * @return This Result object */ Result assertSuccess() { - assertEquals(ec, 0, format("Expected ec 0, received: %s, output [%s]", ec, output)); + assertEquals(0, ec, format("Expected ec 0, received: %s, output [%s]", ec, output)); return this; } diff --git a/test/jdk/jdk/nio/zipfs/testng/test/ManifestOrderTest.java b/test/jdk/jdk/nio/zipfs/ManifestOrderTest.java similarity index 91% rename from test/jdk/jdk/nio/zipfs/testng/test/ManifestOrderTest.java rename to test/jdk/jdk/nio/zipfs/ManifestOrderTest.java index e3ee1bac778..7eb8f70d1b7 100644 --- a/test/jdk/jdk/nio/zipfs/testng/test/ManifestOrderTest.java +++ b/test/jdk/jdk/nio/zipfs/ManifestOrderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,33 +21,45 @@ * questions. * */ -package test; -import org.testng.annotations.BeforeSuite; -import org.testng.annotations.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import util.ZipFsBaseTest; import java.io.InputStream; import java.io.OutputStream; -import java.nio.file.*; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.jar.*; +import java.util.jar.Attributes; import java.util.jar.Attributes.Name; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; import java.util.spi.ToolProvider; import java.util.stream.Collectors; import java.util.zip.ZipEntry; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; -/** +/* * @test * @bug 8211917 * @summary Validate that Zip FS will always add META-INF/MANIFEST.MF to the * beginning of a Zip file allowing the Manifest be found and processed * by java.util.jar.JarInputStream. - + * @run junit ManifestOrderTest */ public class ManifestOrderTest extends ZipFsBaseTest { @@ -73,8 +85,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { /** * Create the Manifests and Map of attributes included in the Manifests */ - @BeforeSuite - public void setup() { + @BeforeAll + public static void setup() { String jdkVendor = System.getProperty("java.vendor"); String jdkVersion = System.getProperty("java.version"); String attributeKey = "Player"; @@ -109,7 +121,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void testJarWithManifestAddedFirst(final Map env, final int compression) throws Exception { @@ -137,7 +150,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void testJarWithManifestAddedLast(final Map env, final int compression) throws Exception { final Path jarPath = generatePath(HERE, "test", ".jar"); @@ -164,7 +178,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void testJarWithManifestAddedInBetween(final Map env, final int compression) throws Exception { @@ -192,7 +207,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void testJarWithNoManifest(final Map env, final int compression) throws Exception { final Path jarPath = generatePath(HERE, "test", ".jar"); @@ -218,7 +234,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void testManifestCopiedFromOSFile(final Map env, final int compression) throws Exception { final Path jarPath = generatePath(HERE, "test", ".jar"); @@ -248,7 +265,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void copyJarToJarTest(final Map env, final int compression) throws Exception { final Path jarPath = generatePath(HERE, "test", ".jar"); @@ -290,7 +308,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "compressionMethods") + @ParameterizedTest + @MethodSource("compressionMethods") public void testJarToolGeneratedJarWithManifest(final int compression) throws Exception { @@ -319,7 +338,7 @@ public class ManifestOrderTest extends ZipFsBaseTest { jarPath.getFileName().toString(), manifestFile.toAbsolutePath().toString(), "-C", tmpdir.toAbsolutePath().toString(), "."); - assertEquals(exitCode, 0, "jar tool exited with failure"); + assertEquals(0, exitCode, "jar tool exited with failure"); verify(jarPath, MANIFEST_ATTRS, compression, entries); // Add an additional entry and re-verify @@ -338,7 +357,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void createWithManifestTest(final Map env, final int compression) throws Exception { Path jarPath = generatePath(HERE, "test", ".jar"); @@ -373,7 +393,8 @@ public class ManifestOrderTest extends ZipFsBaseTest { * @param compression The compression used when writing the entries * @throws Exception If an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void updateManifestTest(final Map env, final int compression) throws Exception { final Path jarPath = generatePath(HERE, "test", ".jar"); @@ -442,12 +463,12 @@ public class ManifestOrderTest extends ZipFsBaseTest { } final Entry e = expected.remove(je.getName()); assertNotNull(e, "Unexpected entry in jar "); - assertEquals(je.getMethod(), e.method, "Compression method mismatch"); - assertEquals(jis.readAllBytes(), e.bytes); + assertEquals(e.method, je.getMethod(), "Compression method mismatch"); + assertArrayEquals(e.bytes, jis.readAllBytes()); je = jis.getNextJarEntry(); } - assertEquals(expected.size(), 0, "Missing entries in jar!"); + assertEquals(0, expected.size(), "Missing entries in jar!"); } } @@ -462,14 +483,14 @@ public class ManifestOrderTest extends ZipFsBaseTest { System.out.printf("Entry Name: %s, method: %s, Expected Method: %s%n", e.name, je.getMethod(), e.method); } - assertEquals(e.method, je.getMethod(), "Compression methods mismatch"); + assertEquals(je.getMethod(), e.method, "Compression methods mismatch"); try (InputStream in = jf.getInputStream(je)) { byte[] bytes = in.readAllBytes(); if (DEBUG) { System.out.printf("bytes= %s, actual=%s%n", new String(bytes), new String(e.bytes)); } - assertTrue(Arrays.equals(bytes, e.bytes), "Entries do not match"); + assertArrayEquals(bytes, e.bytes, "Entries do not match"); } } } @@ -480,7 +501,7 @@ public class ManifestOrderTest extends ZipFsBaseTest { Path top = fs.getPath("/"); long count = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile()).count(); - assertEquals(entries.length + (!attributes.isEmpty() ? 1 : 0), count); + assertEquals(count, entries.length + (!attributes.isEmpty() ? 1 : 0)); Path mf = fs.getPath("META-INF", "MANIFEST.MF"); Manifest m = null; @@ -497,7 +518,7 @@ public class ManifestOrderTest extends ZipFsBaseTest { System.out.printf("Entry name = %s, bytes= %s, actual=%s%n", e.name, new String(Files.readAllBytes(file)), new String(e.bytes)); } - assertEquals(Files.readAllBytes(file), e.bytes); + assertArrayEquals(e.bytes, Files.readAllBytes(file)); } } } @@ -519,7 +540,7 @@ public class ManifestOrderTest extends ZipFsBaseTest { System.out.printf("Key: %s, Value: %s%n", k, v); } assertTrue(attrs.containsKey(k)); - assertEquals(v, attrs.get(k)); + assertEquals(attrs.get(k), v); }); } else { assertNull(m, "Manifest was found!"); diff --git a/test/jdk/jdk/nio/zipfs/NewFileSystemTests.java b/test/jdk/jdk/nio/zipfs/NewFileSystemTests.java index d9da79be126..b72fc765b68 100644 --- a/test/jdk/jdk/nio/zipfs/NewFileSystemTests.java +++ b/test/jdk/jdk/nio/zipfs/NewFileSystemTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,11 +21,6 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.testng.SkipException; import java.io.IOException; import java.net.URI; @@ -36,14 +31,26 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Iterator; import java.util.Map; +import java.util.stream.Stream; + import jdk.test.lib.Platform; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullSource; /** * @test @@ -52,7 +59,7 @@ import static org.testng.Assert.assertTrue; * @modules jdk.zipfs * @library /test/lib * @compile NewFileSystemTests.java - * @run testng NewFileSystemTests + * @run junit NewFileSystemTests */ public class NewFileSystemTests { @@ -68,8 +75,8 @@ public class NewFileSystemTests { /** * Create the JAR file used by the tests */ - @BeforeClass - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { jarFile = Utils.createJarFile("basic.jar", "README"); jarURI = new URI(ZIPFS_SCHEME, jarFile.toUri().toString(), null); @@ -79,8 +86,8 @@ public class NewFileSystemTests { /** * Remove JAR file used by test as part of clean-up */ - @AfterClass - public void tearDown() throws Exception { + @AfterAll + public static void tearDown() throws Exception { Files.deleteIfExists(jarFile); } @@ -117,7 +124,9 @@ public class NewFileSystemTests { * * @throws IOException */ - @Test(dataProvider = "classLoaders") + @ParameterizedTest + @MethodSource("classLoaders") + @NullSource public void testNewFileSystemPathClassLoader(ClassLoader cl) throws Exception { try (FileSystem zipfs = FileSystems.newFileSystem(Path.of("basic.jar"), cl)) { @@ -131,7 +140,9 @@ public class NewFileSystemTests { * * @throws IOException */ - @Test(dataProvider = "classLoaders") + @ParameterizedTest + @MethodSource("classLoaders") + @NullSource public void testNewFileSystemPathMapClassLoader(ClassLoader cl) throws Exception { try (FileSystem zipfs = FileSystems.newFileSystem(Path.of("basic.jar"), ZIPFS_OPTIONS, cl)) { @@ -158,7 +169,9 @@ public class NewFileSystemTests { * * @throws IOException */ - @Test(dataProvider = "classLoaders") + @ParameterizedTest + @MethodSource("classLoaders") + @NullSource public void testNewFileSystemURIMapClassLoader(ClassLoader cl) throws Exception { try (FileSystem zipfs = FileSystems.newFileSystem(jarURI, ZIPFS_OPTIONS, cl)) { @@ -184,17 +197,17 @@ public class NewFileSystemTests { * opened if the underlying file is missing, but even with this set, a ZIP * file system cannot be opened for conflicting or invalid access modes. */ - @DataProvider(name = "badEnvMap") - protected Object[][] badEnvMap() { - return new Object[][]{ - {Map.of(), NoSuchFileException.class}, - {Map.of("accessMode", "readOnly"), NoSuchFileException.class}, - {Map.of("accessMode", "readWrite"), NoSuchFileException.class}, - {Map.of("create", true, "accessMode", "readOnly"), IllegalArgumentException.class}, - {Map.of("create", true, "accessMode", "badValue"), IllegalArgumentException.class}, - }; + protected static Stream badEnvMap() { + return Stream.of( + Arguments.of(Map.of(), NoSuchFileException.class), + Arguments.of(Map.of("accessMode", "readOnly"), NoSuchFileException.class), + Arguments.of(Map.of("accessMode", "readWrite"), NoSuchFileException.class), + Arguments.of(Map.of("create", true, "accessMode", "readOnly"), IllegalArgumentException.class), + Arguments.of(Map.of("create", true, "accessMode", "badValue"), IllegalArgumentException.class) + ); } - @Test(dataProvider = "badEnvMap") + @ParameterizedTest + @MethodSource("badEnvMap") public void badArgumentsFailure(Map env, Class exception) throws IOException { assertThrows(exception, () -> FileSystems.newFileSystem(Path.of("no_such.zip"), env)); } @@ -209,9 +222,7 @@ public class NewFileSystemTests { Path multiReleaseJar = createMultiReleaseJar(); try (FileSystem fs = FileSystems.newFileSystem(multiReleaseJar, Map.of("accessMode", "readWrite"))) { assertFalse(fs.isReadOnly()); - assertEquals( - Files.readString(fs.getPath("file.txt"), UTF_8), - "Default version", + assertEquals("Default version", Files.readString(fs.getPath("file.txt"), UTF_8), "unexpected file content"); } } @@ -222,9 +233,7 @@ public class NewFileSystemTests { */ @Test public void readOnlyZipFileFailure() throws IOException { - if (Platform.isRoot()) { - throw new SkipException("Test skipped when executed by root user."); - } + Assumptions.assumeFalse(Platform.isRoot(), "Test skipped when executed by root user."); // Underlying file is read-only. Path readOnlyZip = Utils.createJarFile("read_only.zip", Map.of("file.txt", "Hello World")); // In theory this can fail, and we should avoid unwanted false-negatives. @@ -243,9 +252,7 @@ public class NewFileSystemTests { Path multiReleaseJar = createMultiReleaseJar(); try (FileSystem fs = FileSystems.newFileSystem(multiReleaseJar, Map.of("releaseVersion", "1"))) { assertTrue(fs.isReadOnly()); - assertEquals( - Files.readString(fs.getPath("file.txt"), UTF_8), - "First version", + assertEquals("First version", Files.readString(fs.getPath("file.txt"), UTF_8), "unexpected file content"); } } @@ -273,15 +280,13 @@ public class NewFileSystemTests { } /* - * DataProvider used to verify that a Zip file system may be returned + * MethodSource used to verify that a Zip file system may be returned * when specifying a class loader */ - @DataProvider(name = "classLoaders") - private Object[][] classLoaders() { - return new Object[][]{ - {null}, - {ClassLoader.getSystemClassLoader()} - }; + private static Stream classLoaders() { + return Stream.of( + Arguments.of(ClassLoader.getSystemClassLoader()) + ); } /** @@ -294,11 +299,11 @@ public class NewFileSystemTests { assertNotNull(fs, "Error: FileSystem was not returned"); assertTrue(fs.provider().getScheme().equalsIgnoreCase(ZIPFS_SCHEME)); assertTrue(fs.isOpen()); - assertEquals(fs.getSeparator(), "/"); + assertEquals("/", fs.getSeparator()); // one root Iterator roots = fs.getRootDirectories().iterator(); - assertTrue(roots.next().toString().equals("/")); + assertEquals("/", roots.next().toString()); assertFalse(roots.hasNext()); } } diff --git a/test/jdk/jdk/nio/zipfs/NonExistentPathTests.java b/test/jdk/jdk/nio/zipfs/NonExistentPathTests.java index 20ed552212c..70c864c44ea 100644 --- a/test/jdk/jdk/nio/zipfs/NonExistentPathTests.java +++ b/test/jdk/jdk/nio/zipfs/NonExistentPathTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,17 +21,15 @@ * questions. * */ -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import java.io.IOException; import java.net.URI; -import java.nio.file.FileSystemNotFoundException; import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.Map; -import static org.testng.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Test; /** * @test @@ -39,7 +37,7 @@ import static org.testng.Assert.assertThrows; * @summary Validate the correct Exception is thrown if the Zip/JAR is not found * * @modules jdk.zipfs - * @run testng/othervm NonExistentPathTests + * @run junit/othervm NonExistentPathTests */ public class NonExistentPathTests { private static final String ZIPFS_SCHEME = "jar"; diff --git a/test/jdk/jdk/nio/zipfs/PathOps.java b/test/jdk/jdk/nio/zipfs/PathOps.java index b976894a301..a035d425a3d 100644 --- a/test/jdk/jdk/nio/zipfs/PathOps.java +++ b/test/jdk/jdk/nio/zipfs/PathOps.java @@ -21,6 +21,14 @@ * questions. */ +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -29,607 +37,573 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.ProviderMismatchException; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; /* - * * @test * @bug 8038500 8040059 8139956 8146754 8172921 8186142 8326487 * @summary Tests path operations for zip provider. - * * @modules jdk.zipfs - * @run main PathOps - * @run main/othervm PathOps + * @run junit PathOps + * @run junit/othervm PathOps */ - public class PathOps { - static final java.io.PrintStream out = System.out; static FileSystem fs; - private Path path; - private Exception exc; - - private PathOps(String first, String... more) { - out.println(); - try { - path = fs.getPath(first, more); - out.format("%s -> %s", first, path); - } catch (Exception x) { - exc = x; - out.format("%s -> %s", first, x); - } - out.println(); + // This test uses a static file system since some ops tested on + // Path depend on the same underlying `fs` instance + @BeforeAll + static void setup() throws IOException { + // create empty JAR file, test doesn't require any contents + Path emptyJar = Utils.createJarFile("empty.jar"); + fs = FileSystems.newFileSystem(emptyJar); } - Path path() { - return path; + @AfterAll + static void cleanup() throws IOException { + fs.close(); } - void fail() { - throw new RuntimeException("PathOps failed"); + @Test + void nullPointerTest() { + Path path = fs.getPath("foo"); + assertThrows(NullPointerException.class, () -> path.resolve((String) null)); + assertThrows(NullPointerException.class, () -> path.relativize(null)); + assertThrows(NullPointerException.class, () -> path.compareTo(null)); + assertThrows(NullPointerException.class, () -> path.startsWith((Path) null)); + assertThrows(NullPointerException.class, () -> path.endsWith((Path) null)); } - void checkPath() { - if (path == null) { - throw new InternalError("path is null"); - } + @Test + void mismatchedProvidersTest() { + Path path = fs.getPath("foo"); + Path other = Paths.get("foo"); + assertThrows(ProviderMismatchException.class, () -> path.compareTo(other)); + assertThrows(ProviderMismatchException.class, () -> path.resolve(other)); + assertThrows(ProviderMismatchException.class, () -> path.relativize(other)); + assertFalse(path.startsWith(other), "providerMismatched startsWith() returns true "); + assertFalse(path.endsWith(other), "providerMismatched endsWith() returns true "); } - void check(Object result, String expected) { - out.format("\tExpected: %s\n", expected); - out.format("\tActual: %s\n", result); + @ParameterizedTest + @MethodSource + void constructionTest(String first, String[] more, String expected) { + string(getPath(first, more), expected); + } + + static Stream constructionTest() { + return Stream.of( + Arguments.of("/", new String[]{}, "/"), + Arguments.of("/", new String[]{""}, "/"), + Arguments.of("/", new String[]{"foo"}, "/foo"), + Arguments.of("/", new String[]{"/foo"}, "/foo"), + Arguments.of("/", new String[]{"foo/"}, "/foo"), + Arguments.of("foo", new String[]{"bar", "gus"}, "foo/bar/gus"), + Arguments.of("", new String[]{}, ""), + Arguments.of("", new String[]{"/"}, "/"), + Arguments.of("", new String[]{"foo", "", "bar", "", "/gus"}, "foo/bar/gus") + ); + } + + @Test + void allComponentsTest() { + var path = getPath("/a/b/c"); + root(path, "/"); + parent(path, "/a/b"); + name(path, "c"); + } + + @ParameterizedTest + @MethodSource + void nameCountTest(String first, String root, String parent, String name, int nameCount) { + var path = getPath(first); + root(path, root); + parent(path, parent); + name(path, name); + nameCount(path, nameCount); + } + + static Stream nameCountTest() { + return Stream.of( + // root component only + Arguments.of("/", "/", null, null, 0), + // empty name + Arguments.of("", null, null, "", 1) + ); + } + + @ParameterizedTest + @MethodSource + void parentNameTest(String first, String root, String parent, String name) { + var path = getPath(first); + root(path, root); + parent(path, parent); + name(path, name); + } + + static Stream parentNameTest() { + return Stream.of( + // no root component + Arguments.of("a/b", null, "a", "b"), + // name component only + Arguments.of("foo", null, null, "foo") + ); + } + + @ParameterizedTest + @MethodSource + void startsWithTest(String first, String prefix) { + starts(getPath(first), prefix); + } + + static Stream startsWithTest() { + return Stream.of( + Arguments.of("", ""), + Arguments.of("/", "/"), + Arguments.of("foo", "foo"), + Arguments.of("/foo", "/"), + Arguments.of("/foo", "/foo"), + Arguments.of("/foo/bar", "/"), + Arguments.of("/foo/bar", "/foo"), + Arguments.of("/foo/bar", "/foo/"), + Arguments.of("/foo/bar", "/foo/bar"), + Arguments.of("foo/bar", "foo"), + Arguments.of("foo/bar", "foo/"), + Arguments.of("foo/bar", "foo/bar") + ); + } + + @ParameterizedTest + @MethodSource + void notStartsWithTest(String first, String prefix) { + notStarts(getPath(first), prefix); + } + + static Stream notStartsWithTest() { + return Stream.of( + Arguments.of("", "/"), + Arguments.of("/", "/foo"), + Arguments.of("foo", "f"), + Arguments.of("/foo", "/f"), + Arguments.of("/foo", ""), + Arguments.of("/foo/bar", "/f"), + Arguments.of("/foo/bar", "foo"), + Arguments.of("/foo/bar", "foo/bar"), + Arguments.of("/foo/bar", ""), + Arguments.of("foo/bar", "f"), + Arguments.of("foo/bar", "/foo"), + Arguments.of("foo/bar", "/foo/bar") + ); + } + + @ParameterizedTest + @MethodSource + void endsWithTest(String first, String suffix) { + ends(getPath(first), suffix); + } + + static Stream endsWithTest() { + return Stream.of( + Arguments.of("", ""), + Arguments.of("/", "/"), + Arguments.of("/foo", "foo"), + Arguments.of("/foo","/foo"), + Arguments.of("/foo/bar", "bar"), + Arguments.of("/foo/bar", "foo/bar"), + Arguments.of("/foo/bar", "foo/bar/"), + Arguments.of("/foo/bar", "/foo/bar"), + Arguments.of("/foo/bar/", "bar"), + Arguments.of("/foo/bar/", "foo/bar"), + Arguments.of("/foo/bar/", "foo/bar/"), + Arguments.of("/foo/bar/", "/foo/bar"), + Arguments.of("foo", "foo"), + Arguments.of("foo/bar", "bar"), + Arguments.of("foo/bar", "bar/"), + Arguments.of("foo/bar", "foo/bar/"), + Arguments.of("foo/bar", "foo/bar") + ); + } + + @ParameterizedTest + @MethodSource + void notEndsWithTest(String first, String suffix) { + notEnds(getPath(first), suffix); + } + + static Stream notEndsWithTest() { + return Stream.of( + Arguments.of("", "/"), + Arguments.of("/", "foo"), + Arguments.of("/", "/foo"), + Arguments.of("/foo", "/"), + Arguments.of("/foo/bar", "/bar"), + Arguments.of("/foo/bar/", "/bar") + ); + } + + @ParameterizedTest + @MethodSource + void elementTest(int index, String expected) { + element(getPath("a/b/c"), index, expected); + } + + static Stream elementTest() { + return Stream.of( + Arguments.of(0, "a"), + Arguments.of(1, "b"), + Arguments.of(2, "c") + ); + } + + @ParameterizedTest + @ValueSource(strings = {"/", "/tmp"} ) + void isAbsoluteTest(String first) { + absolute(getPath(first)); + } + + @ParameterizedTest + @ValueSource(strings = {"tmp", ""} ) + void notAbsoluteTest(String first) { + notAbsolute(getPath(first)); + } + + @ParameterizedTest + @MethodSource + void resolveTest(String first, String other, String expected) { + resolve(getPath(first), other, expected); + } + + static Stream resolveTest() { + return Stream.of( + Arguments.of("/tmp", "foo", "/tmp/foo"), + Arguments.of("/tmp", "/foo", "/foo"), + Arguments.of("/tmp", "", "/tmp"), + Arguments.of("tmp", "foo", "tmp/foo"), + Arguments.of("tmp", "/foo", "/foo"), + Arguments.of("tmp", "", "tmp"), + Arguments.of("", "", ""), + Arguments.of("", "foo", "foo"), + Arguments.of("", "/foo", "/foo"), + Arguments.of("/", "", "/"), + Arguments.of("/", "foo", "/foo"), + Arguments.of("/", "/foo", "/foo"), + Arguments.of("/", "/foo/", "/foo") + ); + } + + @ParameterizedTest + @MethodSource + void resolvePathTest(String first, String other, String expected) { + resolvePath(getPath(first), other, expected); + } + + static Stream resolvePathTest() { + return Stream.of( + Arguments.of("/tmp", "foo", "/tmp/foo"), + Arguments.of("/tmp", "/foo", "/foo"), + Arguments.of("/tmp", "", "/tmp"), + Arguments.of("tmp", "foo", "tmp/foo"), + Arguments.of("tmp", "/foo", "/foo"), + Arguments.of("tmp", "", "tmp"), + Arguments.of("", "", ""), + Arguments.of("", "foo", "foo"), + Arguments.of("", "/foo", "/foo"), + Arguments.of("/", "", "/"), + Arguments.of("/", "foo", "/foo"), + Arguments.of("/", "/foo", "/foo"), + Arguments.of("/", "/foo/", "/foo") + ); + } + + @ParameterizedTest + @MethodSource + void resolveSiblingTest(String first, String other, String expected) { + resolveSibling(getPath(first), other, expected); + } + + static Stream resolveSiblingTest() { + return Stream.of( + Arguments.of("foo", "bar", "bar"), + Arguments.of("foo", "/bar", "/bar"), + Arguments.of("foo", "", ""), + Arguments.of("foo/bar", "gus", "foo/gus"), + Arguments.of("foo/bar", "/gus", "/gus"), + Arguments.of("foo/bar", "", "foo"), + Arguments.of("/foo", "gus", "/gus"), + Arguments.of("/foo", "/gus", "/gus"), + Arguments.of("/foo", "", "/"), + Arguments.of("/foo/bar", "gus", "/foo/gus"), + Arguments.of("/foo/bar", "/gus", "/gus"), + Arguments.of("/foo/bar", "", "/foo") + ); + } + + @Test + void resolveSiblingAndResolveTest() { + var path = getPath(""); + resolveSibling(path, "foo", "foo"); + resolveSibling(path, "/foo", "/foo"); + resolve(path, "", ""); + } + + @ParameterizedTest + @MethodSource + void relativizeTest(String first, String other, String expected) { + relativize(getPath(first), other, expected); + } + + static Stream relativizeTest() { + return Stream.of( + Arguments.of("/a/b/c", "/a/b/c", ""), + Arguments.of("/a/b/c", "/a/b/c/d/e", "d/e"), + Arguments.of("/a/b/c", "/a/x", "../../x"), + Arguments.of("/a/b/c", "/x", "../../../x"), + Arguments.of("a/b/c", "a/b/c/d", "d"), + Arguments.of("a/b/c", "a/x", "../../x"), + Arguments.of("a/b/c", "x", "../../../x"), + Arguments.of("a/b/c", "", "../../.."), + Arguments.of("", "a", "a"), + Arguments.of("", "a/b/c", "a/b/c"), + Arguments.of("", "", ""), + Arguments.of("/", "/a", "a"), + Arguments.of("/", "/a/c", "a/c"), + // 8146754 + Arguments.of("/tmp/path", "/tmp/path/a.txt", "a.txt"), + Arguments.of("/tmp/path/", "/tmp/path/a.txt", "a.txt") + ); + } + + @ParameterizedTest + @MethodSource + void normalizeTest(String first, String expected) { + normalize(getPath(first), expected); + } + + static Stream normalizeTest() { + return Stream.of( + Arguments.of("/", "/"), + Arguments.of("foo", "foo"), + Arguments.of("/foo", "/foo"), + Arguments.of(".", ""), + Arguments.of("..", ".."), + Arguments.of("/..", "/"), + Arguments.of("/../..", "/"), + Arguments.of("foo/.", "foo"), + Arguments.of("./foo", "foo"), + Arguments.of("foo/..", ""), + Arguments.of("../foo", "../foo"), + Arguments.of("../../foo", "../../foo"), + Arguments.of("foo/bar/..", "foo"), + Arguments.of("foo/bar/gus/../..", "foo"), + Arguments.of("/foo/bar/gus/../..", "/foo"), + Arguments.of("/./.", "/"), + Arguments.of("/.", "/"), + Arguments.of("/./abc", "/abc") + ); + } + + @ParameterizedTest + @MethodSource + void invalidTest(String first) { + assertThrows(InvalidPathException.class, () -> getPath(first)); + } + + static Stream invalidTest() { + return Stream.of( + "foo\u0000bar", + "\u0000foo", + "bar\u0000", + "//foo\u0000bar", + "//\u0000foo", + "//bar\u0000" + ); + } + + @Test + void normalizationTest() { + var path = getPath("//foo//bar"); + string(path, "/foo/bar"); + root(path, "/"); + parent(path, "/foo"); + name(path, "bar"); + } + + @Test + void isSameFileTest() { + isSameFile(getPath("/fileDoesNotExist"), "/fileDoesNotExist"); + } + + @Test + void getNameCountTest() { + // 8139956 + System.out.println("check getNameCount"); + int nc = fs.getPath("/").relativize(fs.getPath("/")).getNameCount(); + assertEquals(1, nc, "getNameCount of empty path failed"); + } + + // Utilities for testing + + static void checkPath(Path path) { + assertNotNull(path, "path is null"); + } + + static void check(Object result, String expected) { if (result == null) { if (expected == null) return; } else { // compare string representations - if (expected != null && result.toString().equals(expected.toString())) + if (expected != null && result.toString().equals(expected)) return; } fail(); } - void check(Object result, boolean expected) { + static void check(Object result, boolean expected) { check(result, Boolean.toString(expected)); } - void check(Object result, int expected) { + static void check(Object result, int expected) { check(result, Integer.toString(expected)); } - PathOps root(String expected) { - out.println("check root"); - checkPath(); + static void root(Path path, String expected) { + System.out.println("check root"); + checkPath(path); check(path.getRoot(), expected); - return this; } - PathOps parent(String expected) { - out.println("check parent"); - checkPath(); + static void parent(Path path, String expected) { + System.out.println("check parent"); + checkPath(path); check(path.getParent(), expected); - return this; } - PathOps name(String expected) { - out.println("check name"); - checkPath(); + static void name(Path path, String expected) { + System.out.println("check name"); + checkPath(path); check(path.getFileName(), expected); - return this; } - PathOps nameCount(int expected) { - out.println("check nameCount"); - checkPath(); + static void nameCount(Path path, int expected) { + System.out.println("check nameCount"); + checkPath(path); check(path.getNameCount(), expected); - return this; } - PathOps element(int index, String expected) { - out.format("check element %d\n", index); - checkPath(); + static void element(Path path, int index, String expected) { + System.out.format("check element %d\n", index); + checkPath(path); check(path.getName(index), expected); - return this; } - PathOps subpath(int startIndex, int endIndex, String expected) { - out.format("test subpath(%d,%d)\n", startIndex, endIndex); - checkPath(); + static void subpath(Path path, int startIndex, int endIndex, String expected) { + System.out.format("test subpath(%d,%d)\n", startIndex, endIndex); + checkPath(path); check(path.subpath(startIndex, endIndex), expected); - return this; } - PathOps starts(String prefix) { - out.format("test startsWith with %s\n", prefix); - checkPath(); + static void starts(Path path, String prefix) { + System.out.format("test startsWith with %s\n", prefix); + checkPath(path); Path s = fs.getPath(prefix); check(path.startsWith(s), true); - return this; } - PathOps notStarts(String prefix) { - out.format("test not startsWith with %s\n", prefix); - checkPath(); + static void notStarts(Path path, String prefix) { + System.out.format("test not startsWith with %s\n", prefix); + checkPath(path); Path s = fs.getPath(prefix); check(path.startsWith(s), false); - return this; } - PathOps ends(String suffix) { - out.format("test endsWith %s\n", suffix); - checkPath(); + static void ends(Path path, String suffix) { + System.out.format("test endsWith %s\n", suffix); + checkPath(path); Path s = fs.getPath(suffix); check(path.endsWith(s), true); - return this; } - PathOps notEnds(String suffix) { - out.format("test not endsWith %s\n", suffix); - checkPath(); + static void notEnds(Path path, String suffix) { + System.out.format("test not endsWith %s\n", suffix); + checkPath(path); Path s = fs.getPath(suffix); check(path.endsWith(s), false); - return this; } - PathOps absolute() { - out.println("check path is absolute"); - checkPath(); + static void absolute(Path path) { + System.out.println("check path is absolute"); + checkPath(path); check(path.isAbsolute(), true); - return this; } - PathOps notAbsolute() { - out.println("check path is not absolute"); - checkPath(); + static void notAbsolute(Path path) { + System.out.println("check path is not absolute"); + checkPath(path); check(path.isAbsolute(), false); - return this; } - PathOps resolve(String other, String expected) { - out.format("test resolve %s\n", other); - checkPath(); + static void resolve(Path path, String other, String expected) { + System.out.format("test resolve %s\n", other); + checkPath(path); check(path.resolve(other), expected); - return this; } - PathOps resolvePath(String other, String expected) { - out.format("test resolve %s\n", other); - checkPath(); + static void resolvePath(Path path, String other, String expected) { + System.out.format("test resolve %s\n", other); + checkPath(path); check(path.resolve(fs.getPath(other)), expected); - return this; } - PathOps resolveSibling(String other, String expected) { - out.format("test resolveSibling %s\n", other); - checkPath(); + static void resolveSibling(Path path, String other, String expected) { + System.out.format("test resolveSibling %s\n", other); + checkPath(path); check(path.resolveSibling(other), expected); - return this; + } - PathOps relativize(String other, String expected) { - out.format("test relativize %s\n", other); - checkPath(); + static void relativize(Path path, String other, String expected) { + System.out.format("test relativize %s\n", other); + checkPath(path); Path that = fs.getPath(other); check(path.relativize(that), expected); - return this; + } - PathOps normalize(String expected) { - out.println("check normalized path"); - checkPath(); + static void normalize(Path path, String expected) { + System.out.println("check normalized path"); + checkPath(path); check(path.normalize(), expected); - return this; + } - PathOps string(String expected) { - out.println("check string representation"); - checkPath(); + static void string(Path path, String expected) { + System.out.println("check string representation"); + checkPath(path); check(path, expected); - return this; } - PathOps isSameFile(String target) { + static void isSameFile(Path path, String target) { try { - out.println("check two paths are same"); - checkPath(); - check(Files.isSameFile(path, test(target).path()), true); + System.out.println("check two paths are same"); + checkPath(path); + check(Files.isSameFile(path, fs.getPath(target)), true); } catch (IOException ioe) { fail(); } - return this; } - PathOps invalid() { - if (!(exc instanceof InvalidPathException)) { - out.println("InvalidPathException not thrown as expected"); - fail(); - } - return this; + static Path getPath(String s) { + return fs.getPath(s); } - static PathOps test(String s) { - return new PathOps(s); - } - - static PathOps test(String first, String... more) { - return new PathOps(first, more); - } - - // -- PathOpss -- - - static void header(String s) { - out.println(); - out.println(); - out.println("-- " + s + " --"); - } - - static void doPathOpTests() { - header("Path operations"); - - // construction - test("/") - .string("/"); - test("/", "") - .string("/"); - test("/", "foo") - .string("/foo"); - test("/", "/foo") - .string("/foo"); - test("/", "foo/") - .string("/foo"); - test("foo", "bar", "gus") - .string("foo/bar/gus"); - test("") - .string(""); - test("", "/") - .string("/"); - test("", "foo", "", "bar", "", "/gus") - .string("foo/bar/gus"); - - // all components - test("/a/b/c") - .root("/") - .parent("/a/b") - .name("c"); - - // root component only - test("/") - .root("/") - .parent(null) - .name(null) - .nameCount(0); - - // empty name - test("") - .root(null) - .parent(null) - .name("") - .nameCount(1); - - // no root component - test("a/b") - .root(null) - .parent("a") - .name("b"); - - // name component only - test("foo") - .root(null) - .parent(null) - .name("foo"); - - // startsWith - test("") - .starts("") - .notStarts("/"); - test("/") - .starts("/") - .notStarts("/foo"); - test("/foo") - .starts("/") - .starts("/foo") - .notStarts("/f") - .notStarts(""); - test("/foo/bar") - .starts("/") - .starts("/foo") - .starts("/foo/") - .starts("/foo/bar") - .notStarts("/f") - .notStarts("foo") - .notStarts("foo/bar") - .notStarts(""); - test("foo") - .starts("foo") - .notStarts("f"); - test("foo/bar") - .starts("foo") - .starts("foo/") - .starts("foo/bar") - .notStarts("f") - .notStarts("/foo") - .notStarts("/foo/bar"); - - // endsWith - test("") - .ends("") - .notEnds("/"); - test("/") - .ends("/") - .notEnds("foo") - .notEnds("/foo"); - test("/foo") - .ends("foo") - .ends("/foo") - .notEnds("/"); - test("/foo/bar") - .ends("bar") - .ends("foo/bar") - .ends("foo/bar/") - .ends("/foo/bar") - .notEnds("/bar"); - test("/foo/bar/") - .ends("bar") - .ends("foo/bar") - .ends("foo/bar/") - .ends("/foo/bar") - .notEnds("/bar"); - test("foo") - .ends("foo"); - test("foo/bar") - .ends("bar") - .ends("bar/") - .ends("foo/bar/") - .ends("foo/bar"); - - // elements - test("a/b/c") - .element(0,"a") - .element(1,"b") - .element(2,"c"); - - // isAbsolute - test("/") - .absolute(); - test("/tmp") - .absolute(); - test("tmp") - .notAbsolute(); - test("") - .notAbsolute(); - - // resolve - test("/tmp") - .resolve("foo", "/tmp/foo") - .resolve("/foo", "/foo") - .resolve("", "/tmp"); - test("tmp") - .resolve("foo", "tmp/foo") - .resolve("/foo", "/foo") - .resolve("", "tmp"); - test("") - .resolve("", "") - .resolve("foo", "foo") - .resolve("/foo", "/foo"); - test("/") - .resolve("", "/") - .resolve("foo", "/foo") - .resolve("/foo", "/foo") - .resolve("/foo/", "/foo"); - - // resolve(Path) - test("/tmp") - .resolvePath("foo", "/tmp/foo") - .resolvePath("/foo", "/foo") - .resolvePath("", "/tmp"); - test("tmp") - .resolvePath("foo", "tmp/foo") - .resolvePath("/foo", "/foo") - .resolvePath("", "tmp"); - test("") - .resolvePath("", "") - .resolvePath("foo", "foo") - .resolvePath("/foo", "/foo"); - test("/") - .resolvePath("", "/") - .resolvePath("foo", "/foo") - .resolvePath("/foo", "/foo") - .resolvePath("/foo/", "/foo"); - - // resolveSibling - test("foo") - .resolveSibling("bar", "bar") - .resolveSibling("/bar", "/bar") - .resolveSibling("", ""); - test("foo/bar") - .resolveSibling("gus", "foo/gus") - .resolveSibling("/gus", "/gus") - .resolveSibling("", "foo"); - test("/foo") - .resolveSibling("gus", "/gus") - .resolveSibling("/gus", "/gus") - .resolveSibling("", "/"); - test("/foo/bar") - .resolveSibling("gus", "/foo/gus") - .resolveSibling("/gus", "/gus") - .resolveSibling("", "/foo"); - test("") - .resolveSibling("foo", "foo") - .resolveSibling("/foo", "/foo") - .resolve("", ""); - - // relativize - test("/a/b/c") - .relativize("/a/b/c", "") - .relativize("/a/b/c/d/e", "d/e") - .relativize("/a/x", "../../x") - .relativize("/x", "../../../x"); - test("a/b/c") - .relativize("a/b/c/d", "d") - .relativize("a/x", "../../x") - .relativize("x", "../../../x") - .relativize("", "../../.."); - test("") - .relativize("a", "a") - .relativize("a/b/c", "a/b/c") - .relativize("", ""); - test("/") - .relativize("/a", "a") - .relativize("/a/c", "a/c"); - // 8146754 - test("/tmp/path") - .relativize("/tmp/path/a.txt", "a.txt"); - test("/tmp/path/") - .relativize("/tmp/path/a.txt", "a.txt"); - - // normalize - test("/") - .normalize("/"); - test("foo") - .normalize("foo"); - test("/foo") - .normalize("/foo"); - test(".") - .normalize(""); - test("..") - .normalize(".."); - test("/..") - .normalize("/"); - test("/../..") - .normalize("/"); - test("foo/.") - .normalize("foo"); - test("./foo") - .normalize("foo"); - test("foo/..") - .normalize(""); - test("../foo") - .normalize("../foo"); - test("../../foo") - .normalize("../../foo"); - test("foo/bar/..") - .normalize("foo"); - test("foo/bar/gus/../..") - .normalize("foo"); - test("/foo/bar/gus/../..") - .normalize("/foo"); - test("/./.") - .normalize("/"); - test("/.") - .normalize("/"); - test("/./abc") - .normalize("/abc"); - // invalid - test("foo\u0000bar") - .invalid(); - test("\u0000foo") - .invalid(); - test("bar\u0000") - .invalid(); - test("//foo\u0000bar") - .invalid(); - test("//\u0000foo") - .invalid(); - test("//bar\u0000") - .invalid(); - - // normalization - test("//foo//bar") - .string("/foo/bar") - .root("/") - .parent("/foo") - .name("bar"); - - // isSameFile - test("/fileDoesNotExist") - .isSameFile("/fileDoesNotExist"); - - // 8139956 - out.println("check getNameCount"); - int nc = fs.getPath("/").relativize(fs.getPath("/")).getNameCount(); - if (nc != 1) { - out.format("\tExpected: 1\n"); - out.format("\tActual: %d\n", nc); - throw new RuntimeException("getNameCount of empty path failed"); - } - } - - static void npes() { - header("NullPointerException"); - - Path path = fs.getPath("foo"); - - try { - path.resolve((String)null); - throw new RuntimeException("NullPointerException not thrown"); - } catch (NullPointerException npe) { - } - - try { - path.relativize(null); - throw new RuntimeException("NullPointerException not thrown"); - } catch (NullPointerException npe) { - } - - try { - path.compareTo(null); - throw new RuntimeException("NullPointerException not thrown"); - } catch (NullPointerException npe) { - } - - try { - path.startsWith((Path)null); - throw new RuntimeException("NullPointerException not thrown"); - } catch (NullPointerException npe) { - } - - try { - path.endsWith((Path)null); - throw new RuntimeException("NullPointerException not thrown"); - } catch (NullPointerException npe) { - } - - } - - static void mismatchedProviders() { - header("ProviderMismatchException"); - Path path = fs.getPath("foo"); - Path other = Paths.get("foo"); - try { - path.compareTo(other); - throw new RuntimeException("ProviderMismatchException not thrown"); - } catch (ProviderMismatchException pme) {} - - try { - path.resolve(other); - throw new RuntimeException("ProviderMismatchException not thrown"); - } catch (ProviderMismatchException pme) {} - - try { - path.relativize(other); - throw new RuntimeException("ProviderMismatchException not thrown"); - } catch (ProviderMismatchException pme) {} - - try { - if (path.startsWith(other)) - throw new RuntimeException("providerMismatched startsWith() returns true "); - if (path.endsWith(other)) - throw new RuntimeException("providerMismatched endsWith() returns true "); - } catch (ProviderMismatchException pme) { - throw new RuntimeException("ProviderMismatchException is thrown for starts/endsWith()"); - } - } - - public static void main(String[] args) throws IOException { - // create empty JAR file, test doesn't require any contents - Path emptyJar = Utils.createJarFile("empty.jar"); - - fs = FileSystems.newFileSystem(emptyJar); - try { - npes(); - mismatchedProviders(); - doPathOpTests(); - } finally { - fs.close(); - } + static Path getPath(String first, String... more) { + return fs.getPath(first, more); } } diff --git a/test/jdk/jdk/nio/zipfs/testng/test/PosixAttributeViewTest.java b/test/jdk/jdk/nio/zipfs/PosixAttributeViewTest.java similarity index 77% rename from test/jdk/jdk/nio/zipfs/testng/test/PosixAttributeViewTest.java rename to test/jdk/jdk/nio/zipfs/PosixAttributeViewTest.java index 7734ea733c9..ff20741135d 100644 --- a/test/jdk/jdk/nio/zipfs/testng/test/PosixAttributeViewTest.java +++ b/test/jdk/jdk/nio/zipfs/PosixAttributeViewTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,12 +21,12 @@ * questions. * */ -package test; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import util.ZipFsBaseTest; import java.io.IOException; @@ -36,13 +36,15 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.PosixFileAttributeView; import java.util.Map; +import java.util.stream.Stream; import java.util.zip.ZipEntry; -/** +/* * @test * @bug 8273935 * @summary Validate that Files.getFileAttributeView will not throw an * Exception when the attribute view PosixFileAttributeView is not available + * @run junit PosixAttributeViewTest */ public class PosixAttributeViewTest extends ZipFsBaseTest { public static final String ZIP_ENTRY = "Entry-0"; @@ -52,8 +54,8 @@ public class PosixAttributeViewTest extends ZipFsBaseTest { * Create initial Zip File * @throws IOException if an error occurs */ - @BeforeTest - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { Files.deleteIfExists(ZIP_FILE); Entry entry = Entry.of(ZIP_ENTRY, ZipEntry.DEFLATED, "Tennis Anyone"); @@ -64,22 +66,21 @@ public class PosixAttributeViewTest extends ZipFsBaseTest { * Remove Zip File used by Test * @throws IOException if an error occurs */ - @AfterTest - public void cleanup() throws IOException { + @AfterAll + public static void cleanup() throws IOException { Files.deleteIfExists(ZIP_FILE); } /** - * DataProvider used to specify the Map indicating whether Posix + * MethodSource used to specify the Map indicating whether Posix * file attributes have been enabled * @return map of the Zip FS properties to configure */ - @DataProvider - protected Object[][] zipfsMap() { - return new Object[][]{ - {Map.of()}, - {Map.of("enablePosixFileAttributes", "true")} - }; + protected static Stream zipfsMap() { + return Stream.of( + Arguments.of(Map.of()), + Arguments.of(Map.of("enablePosixFileAttributes", "true")) + ); } /** @@ -89,7 +90,8 @@ public class PosixAttributeViewTest extends ZipFsBaseTest { * @param env map of the Zip FS properties to configure * @throws Exception if an error occurs */ - @Test(dataProvider = "zipfsMap") + @ParameterizedTest + @MethodSource("zipfsMap") public void testPosixAttributeView(Map env) throws Exception { try (FileSystem fs = FileSystems.newFileSystem(ZIP_FILE, env)) { Path entry = fs.getPath(ZIP_ENTRY); diff --git a/test/jdk/jdk/nio/zipfs/PropertyPermissionTests.java b/test/jdk/jdk/nio/zipfs/PropertyPermissionTests.java index de8992bce74..5f31d87f81c 100644 --- a/test/jdk/jdk/nio/zipfs/PropertyPermissionTests.java +++ b/test/jdk/jdk/nio/zipfs/PropertyPermissionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,9 +21,6 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; import java.io.IOException; import java.nio.file.FileSystem; @@ -33,13 +30,18 @@ import java.nio.file.Paths; import java.nio.file.spi.FileSystemProvider; import java.util.Map; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + + /** * @test * @bug 8210469 * @summary Verify ZIP FileSystem works with a Security Manager * @modules jdk.zipfs * @compile PropertyPermissionTests.java - * @run testng/othervm PropertyPermissionTests + * @run junit/othervm PropertyPermissionTests */ public class PropertyPermissionTests { @@ -55,8 +57,8 @@ public class PropertyPermissionTests { /** * Create the JAR files used by the tests */ - @BeforeClass - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { jarFile = Utils.createJarFile("basic.jar", "META-INF/services/java.nio.file.spi.FileSystemProvider"); } @@ -64,8 +66,8 @@ public class PropertyPermissionTests { /** * Remove JAR files used by test as part of clean-up */ - @AfterClass - public void tearDown() throws Exception { + @AfterAll + public static void tearDown() throws Exception { Files.deleteIfExists(jarFile); } diff --git a/test/jdk/jdk/nio/zipfs/ReleaseDeflater.java b/test/jdk/jdk/nio/zipfs/ReleaseDeflater.java index 82369b4fe39..95446da9267 100644 --- a/test/jdk/jdk/nio/zipfs/ReleaseDeflater.java +++ b/test/jdk/jdk/nio/zipfs/ReleaseDeflater.java @@ -1,5 +1,6 @@ /* * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,12 +26,15 @@ /* * @test * @bug 8234011 - * @summary Check that jdk.nio.zipfs.ZipFileSystem doesn't cache more than ZipFileSystem.MAX_FLATER Inflater/Deflater objects - * @run main ReleaseDeflater + * @summary Check that jdk.nio.zipfs.ZipFileSystem doesn't cache more than + * ZipFileSystem.MAX_FLATER Inflater/Deflater objects. * @modules jdk.zipfs/jdk.nio.zipfs:+open - * @author Volker Simonis + * @run junit ReleaseDeflater */ +import org.junit.jupiter.api.Test; + +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; @@ -44,8 +48,12 @@ import java.util.List; import java.util.Map; import java.util.ArrayList; +import static org.junit.jupiter.api.Assertions.assertFalse; + public class ReleaseDeflater { - public static void main(String[] args) throws Throwable { + + @Test + void cacheTest() throws IOException, IllegalAccessException { Path zipFile = Paths.get("ReleaseDeflaterTest.zip"); try (FileSystem fs = FileSystems.newFileSystem(zipFile, Map.of("create", true))) { FileSystemProvider zprov = fs.provider(); @@ -75,15 +83,11 @@ public class ReleaseDeflater { Field inflaters = fs.getClass().getDeclaredField("inflaters"); inflaters.setAccessible(true); int inflater_count = ((List) inflaters.get(fs)).size(); - if (inflater_count > MAX_FLATERS) { - throw new Exception("Too many inflaters " + inflater_count); - } + assertFalse(inflater_count > MAX_FLATERS, "Too many inflaters " + inflater_count); Field deflaters = fs.getClass().getDeclaredField("deflaters"); deflaters.setAccessible(true); int deflater_count = ((List) deflaters.get(fs)).size(); - if (deflater_count > MAX_FLATERS) { - throw new Exception("Too many deflaters " + deflater_count); - } + assertFalse(deflater_count > MAX_FLATERS, "Too many deflaters " + deflater_count); } catch (NoSuchFieldException nsfe) { // Probably the implementation has changed, so there's not much we can do... throw new RuntimeException("Implementation of jdk.nio.zipfs.ZipFileSystem changed - disable or fix the test"); @@ -91,6 +95,5 @@ public class ReleaseDeflater { } finally { Files.deleteIfExists(zipFile); } - } } diff --git a/test/jdk/jdk/nio/zipfs/UpdateEntryTest.java b/test/jdk/jdk/nio/zipfs/UpdateEntryTest.java index 6611efced82..8a65f08afc2 100644 --- a/test/jdk/jdk/nio/zipfs/UpdateEntryTest.java +++ b/test/jdk/jdk/nio/zipfs/UpdateEntryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,6 @@ * */ -import org.testng.annotations.Test; import java.io.IOException; import java.io.InputStream; @@ -32,7 +31,6 @@ import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.Map; import java.util.spi.ToolProvider; import java.util.zip.CRC32; @@ -40,16 +38,16 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * @test * @bug 8229887 * @summary Validate ZIP FileSystem can replace existing STORED and DEFLATED entries * @modules jdk.zipfs - * @run testng UpdateEntryTest + * @run junit UpdateEntryTest */ -@Test public class UpdateEntryTest { private static final Path HERE = Path.of("."); @@ -109,6 +107,7 @@ public class UpdateEntryTest { * Validate that you can replace an existing entry in a JAR file that * was added with the STORED(no-compression) option */ + @Test public void testReplaceStoredEntry() throws IOException { String jarFileName = "updateStoredEntry.jar"; String storedFileName = "storedFile.txt"; @@ -132,6 +131,7 @@ public class UpdateEntryTest { /** * Test updating an entry that is STORED (not compressed) */ + @Test public void test1() throws IOException { Entry e1 = Entry.of("foo", ZipEntry.STORED, "hello"); Entry e2 = Entry.of("bar", ZipEntry.STORED, "world"); @@ -141,6 +141,7 @@ public class UpdateEntryTest { /** * Test updating an entry that is DEFLATED (compressed) */ + @Test public void test2() throws IOException { Entry e1 = Entry.of("foo", ZipEntry.DEFLATED, "hello"); Entry e2 = Entry.of("bar", ZipEntry.STORED, "world"); @@ -183,16 +184,16 @@ public class UpdateEntryTest { // check entries with zip API try (ZipFile zf = new ZipFile(zipfile.toFile())) { // check entry count - assertTrue(zf.size() == entries.length); + assertEquals(zf.size(), entries.length); // check compression method and content of each entry for (Entry e : entries) { ZipEntry ze = zf.getEntry(e.name); - assertTrue(ze != null); - assertTrue(ze.getMethod() == e.method); + assertNotNull(ze); + assertEquals(ze.getMethod(), e.method); try (InputStream in = zf.getInputStream(ze)) { byte[] bytes = in.readAllBytes(); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } @@ -203,13 +204,13 @@ public class UpdateEntryTest { Path top = fs.getPath("/"); long count = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile()).count(); - assertTrue(count == entries.length); + assertEquals(count, entries.length); // check content of each entry for (Entry e : entries) { Path file = fs.getPath(e.name); byte[] bytes = Files.readAllBytes(file); - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } diff --git a/test/jdk/jdk/nio/zipfs/ZFSTests.java b/test/jdk/jdk/nio/zipfs/ZFSTests.java index f00ff58f877..b4e625dfe38 100644 --- a/test/jdk/jdk/nio/zipfs/ZFSTests.java +++ b/test/jdk/jdk/nio/zipfs/ZFSTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,18 +24,20 @@ /* @test * @bug 7156873 8040059 8028480 8034773 8153248 8061777 8197398 8210394 * @summary ZipFileSystem regression tests - * * @modules jdk.zipfs - * @run main ZFSTests - * @run main/othervm ZFSTests + * @run junit ZFSTests + * @run junit/othervm ZFSTests */ +import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.nio.ByteBuffer; import java.nio.channels.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.spi.*; @@ -45,16 +47,16 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + public class ZFSTests { - public static void main(String[] args) throws Throwable { - test8197398(); - test7156873(); - test8061777(); - tests(); - } + static final Charset ASCII = StandardCharsets.US_ASCII; - static void test8197398() throws Throwable { + @Test + void test8197398() throws Throwable { // root entry "/" Path path = Paths.get("rootdir.zip"); @@ -72,33 +74,29 @@ public class ZFSTests { } AtomicInteger cnt = new AtomicInteger(); int max = 3; - try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + try (FileSystem fs = FileSystems.newFileSystem(uri, Map.of())) { Files.walkFileTree(fs.getRootDirectories().iterator().next(), new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (cnt.incrementAndGet() > max) - throw new RuntimeException("visited too many files/dirs"); + assertFalse(cnt.incrementAndGet() > max, "visited too many files/dirs"); files.remove(file.toString()); - if (!Arrays.equals(Files.readAllBytes(file), file.toString().getBytes())) - throw new RuntimeException("visited files has wrong content: " + file); + assertArrayEquals(file.toString().getBytes(), Files.readAllBytes(file), + "visited files has wrong content: " + file); return FileVisitResult.CONTINUE; } @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) - throws IOException { - if (cnt.incrementAndGet() > max) - throw new RuntimeException("visited too many files/dirs"); + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + assertFalse(cnt.incrementAndGet() > max, "visited too many files/dirs"); dirs.remove(dir.toString()); return FileVisitResult.CONTINUE; } }); - if (cnt.get() != max || dirs.size() != 0 || files.size() != 0) - throw new RuntimeException("walk files/dirs failed"); - + assertFalse(cnt.get() != max || dirs.size() != 0 || files.size() != 0, + "walk files/dirs failed"); } finally { Files.deleteIfExists(path); } @@ -119,15 +117,15 @@ public class ZFSTests { zos.write("/fooo/bar".getBytes()); } - try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + try (FileSystem fs = FileSystems.newFileSystem(uri, Map.of())) { Files.walkFileTree(fs.getRootDirectories().iterator().next(), new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { files.remove(file.toString()); - if (!Arrays.equals(Files.readAllBytes(file), file.toString().getBytes())) - throw new RuntimeException("visited files has wrong content: " + file); + assertArrayEquals(file.toString().getBytes(), Files.readAllBytes(file), + "visited files has wrong content: " + file); return FileVisitResult.CONTINUE; } @Override @@ -137,8 +135,8 @@ public class ZFSTests { return FileVisitResult.CONTINUE; } }); - if (dirs.size() != 0 || files.size() != 0) - throw new RuntimeException("walk files/dirs failed"); + assertFalse(dirs.size() != 0 || files.size() != 0, + "walk files/dirs failed"); // for next test: updated any entry, the result zipfs file should have no // absolute path entry @@ -151,22 +149,22 @@ public class ZFSTests { files.add("/foo"); files.add("/bar"); files.add("/fooo/bar"); - try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + try (FileSystem fs = FileSystems.newFileSystem(uri, Map.of())) { Files.walk(fs.getPath("/")).forEach( p -> { if (Files.isDirectory(p)) { dirs.remove(p.toString()); } else { files.remove(p.toString()); try { - if (!Arrays.equals(Files.readAllBytes(p), p.toString().getBytes())) - throw new RuntimeException("visited files has wrong content: " + p); + assertArrayEquals(p.toString().getBytes(), Files.readAllBytes(p), + "visited files has wrong content: " + p); } catch (IOException x) { throw new RuntimeException(x); } } }); - if (dirs.size() != 0 || files.size() != 0) - throw new RuntimeException("walk files/dirs failed"); + assertFalse(dirs.size() != 0 || files.size() != 0, + "walk files/dirs failed"); } // updated zip file should not have "/" and entry with absolute path @@ -175,17 +173,16 @@ public class ZFSTests { .map(ZipEntry::toString) .sorted() .toArray(String[]::new); - if (!Arrays.equals(entries, new String[] {"bar", "foo", "fooo/", "fooo/bar" })) { - System.out.println("unexpeded: " + Arrays.toString(entries)); - throw new RuntimeException("unexpected entreis in updated zipfs file"); - } + assertArrayEquals(new String[] {"bar", "foo", "fooo/", "fooo/bar" }, entries, + "unexpected entreis in updated zipfs file"); } } finally { Files.deleteIfExists(path); } } - static void test7156873() throws Throwable { + @Test + void test7156873() throws Throwable { String DIRWITHSPACE = "testdir with spaces"; Path dir = Paths.get(DIRWITHSPACE); Path path = Paths.get(DIRWITHSPACE, "file.zip"); @@ -201,7 +198,8 @@ public class ZFSTests { } } - static void test8061777() throws Throwable { + @Test + void test8061777() throws Throwable { Path path = Paths.get("file.zip"); try { URI uri = URI.create("jar:" + path.toUri()); @@ -212,24 +210,25 @@ public class ZFSTests { FileSystemProvider fsp = fs.provider(); Path p = fs.getPath("/\u8868\u7533.txt"); // 0x95 0x5c 0x90 0x5c try (OutputStream os = fsp.newOutputStream(p)) { - os.write("Hello!".getBytes("ASCII")); + os.write("Hello!".getBytes(ASCII)); } Path dir = fs.getPath("/"); Files.list(dir) .forEach( child -> { System.out.println("child:" + child); - if (!child.toString().equals(p.toString())) - throw new RuntimeException("wrong path name created"); + assertEquals(p.toString(), child.toString(), + "wrong path name created"); }); - if (!"Hello!".equals(new String(Files.readAllBytes(p), "ASCII"))) - throw new RuntimeException("wrong content in newly created file"); + assertEquals("Hello!", new String(Files.readAllBytes(p), ASCII), + "wrong content in newly created file"); } } finally { Files.deleteIfExists(path); } } - static void tests() throws Throwable { + @Test + void tests() throws Throwable { Path path = Paths.get("file.zip"); try { URI uri = URI.create("jar:" + path.toUri()); @@ -245,16 +244,14 @@ public class ZFSTests { StandardOpenOption.WRITE, StandardOpenOption.APPEND); try (FileChannel ch = fsp.newFileChannel(p, options)) { - ch.write(ByteBuffer.wrap("Hello!".getBytes("ASCII"))); + ch.write(ByteBuffer.wrap("Hello!".getBytes(ASCII))); } // 8034773 try (OutputStream os = fsp.newOutputStream(p, new OpenOption[0])) { - os.write("Hello2!".getBytes("ASCII")); - } - if (!"Hello2!".equals(new String( - Files.readAllBytes(fs.getPath("test.txt"))))) { - throw new RuntimeException("failed to open as truncate_existing"); + os.write("Hello2!".getBytes(ASCII)); } + assertEquals("Hello2!", new String( + Files.readAllBytes(fs.getPath("test.txt"))), "failed to open as truncate_existing"); options = EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.APPEND, @@ -273,8 +270,8 @@ public class ZFSTests { Files.list(dir) .forEach( child -> { System.out.println("child:" + child); - if (child.toString().endsWith("/")) - throw new RuntimeException("subdir names ends with /"); + assertFalse(child.toString().endsWith("/"), + "subdir names ends with /"); }); } } finally { diff --git a/test/jdk/jdk/nio/zipfs/ZeroDate.java b/test/jdk/jdk/nio/zipfs/ZeroDate.java index ff702f63488..9c7f3abef0d 100644 --- a/test/jdk/jdk/nio/zipfs/ZeroDate.java +++ b/test/jdk/jdk/nio/zipfs/ZeroDate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ import static java.util.zip.ZipFile.CENTIM; import static java.util.zip.ZipFile.ENDHDR; import static java.util.zip.ZipFile.ENDOFF; import static java.util.zip.ZipFile.LOCTIM; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.io.InputStream; @@ -40,11 +41,17 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; -import java.util.Collections; +import java.util.Map; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import jdk.test.lib.Utils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /* @test * @bug 8184940 8186227 8188869 @@ -53,42 +60,54 @@ import jdk.test.lib.Utils; * @author Liam Miller-Cushon * @modules jdk.zipfs * @library /test/lib + * @run junit ZeroDate */ public class ZeroDate { - public static void main(String[] args) throws Exception { - // create a zip file, and read it in as a byte array - Path path = Utils.createTempFile("bad", ".zip"); - try { - try (OutputStream os = Files.newOutputStream(path); - ZipOutputStream zos = new ZipOutputStream(os)) { - ZipEntry e = new ZipEntry("x"); - zos.putNextEntry(e); - zos.write((int) 'x'); - } - int len = (int) Files.size(path); - byte[] data = new byte[len]; - try (InputStream is = Files.newInputStream(path)) { - is.read(data); - } + static Path path; + static byte[] data; - // year, month, day are zero - testDate(data.clone(), 0, LocalDate.of(1979, 11, 30).atStartOfDay()); - // only year is zero - testDate(data.clone(), 0 << 25 | 4 << 21 | 5 << 16, LocalDate.of(1980, 4, 5).atStartOfDay()); - // month is greater than 12 - testDate(data.clone(), 0 << 25 | 13 << 21 | 1 << 16, LocalDate.of(1981, 1, 1).atStartOfDay()); - // 30th of February - testDate(data.clone(), 0 << 25 | 2 << 21 | 30 << 16, LocalDate.of(1980, 3, 1).atStartOfDay()); - // 30th of February, 24:60:60 - testDate(data.clone(), 0 << 25 | 2 << 21 | 30 << 16 | 24 << 11 | 60 << 5 | 60 >> 1, - LocalDateTime.of(1980, 3, 2, 1, 1, 0)); - } finally { - Files.delete(path); + @BeforeAll + static void setup() throws IOException { + // create a zip file, and read it in as a byte array + path = Utils.createTempFile("bad", ".zip"); + try (OutputStream os = Files.newOutputStream(path); + ZipOutputStream zos = new ZipOutputStream(os)) { + ZipEntry e = new ZipEntry("x"); + zos.putNextEntry(e); + zos.write((int) 'x'); + } + int len = (int) Files.size(path); + data = new byte[len]; + try (InputStream is = Files.newInputStream(path)) { + is.read(data); } } - private static void testDate(byte[] data, int date, LocalDateTime expected) throws IOException { + @AfterAll + static void cleanup () throws IOException { + Files.delete(path); + } + + static Stream dateData() { + return Stream.of( + // year, month, day are zero + Arguments.of(data.clone(), 0, LocalDate.of(1979, 11, 30).atStartOfDay()), + // only year is zero + Arguments.of(data.clone(), 0 << 25 | 4 << 21 | 5 << 16, LocalDate.of(1980, 4, 5).atStartOfDay()), + // month is greater than 12 + Arguments.of(data.clone(), 0 << 25 | 13 << 21 | 1 << 16, LocalDate.of(1981, 1, 1).atStartOfDay()), + // 30th of February + Arguments.of(data.clone(), 0 << 25 | 2 << 21 | 30 << 16, LocalDate.of(1980, 3, 1).atStartOfDay()), + // 30th of February, 24:60:60 + Arguments.of(data.clone(), 0 << 25 | 2 << 21 | 30 << 16 | 24 << 11 | 60 << 5 | 60 >> 1, + LocalDateTime.of(1980, 3, 2, 1, 1, 0)) + ); + } + + @ParameterizedTest + @MethodSource("dateData") + void testDate(byte[] data, int date, LocalDateTime expected) throws IOException { // set the datetime int endpos = data.length - ENDHDR; int cenpos = u16(data, endpos + ENDOFF); @@ -102,7 +121,7 @@ public class ZeroDate { os.write(data); } URI uri = URI.create("jar:" + path.toUri()); - try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + try (FileSystem fs = FileSystems.newFileSystem(uri, Map.of())) { Path entry = fs.getPath("x"); Instant actualInstant = Files.readAttributes(entry, BasicFileAttributes.class) @@ -110,10 +129,8 @@ public class ZeroDate { .toInstant(); Instant expectedInstant = expected.atZone(ZoneId.systemDefault()).toInstant(); - if (!actualInstant.equals(expectedInstant)) { - throw new AssertionError( - String.format("actual: %s, expected: %s", actualInstant, expectedInstant)); - } + assertEquals(expectedInstant, actualInstant, + String.format("actual: %s, expected: %s", actualInstant, expectedInstant)); } finally { Files.delete(path); } diff --git a/test/jdk/jdk/nio/zipfs/ZipFSOutputStreamTest.java b/test/jdk/jdk/nio/zipfs/ZipFSOutputStreamTest.java index 8175eec070d..4e797b57380 100644 --- a/test/jdk/jdk/nio/zipfs/ZipFSOutputStreamTest.java +++ b/test/jdk/jdk/nio/zipfs/ZipFSOutputStreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * 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,11 +22,6 @@ * */ -import org.testng.Assert; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.io.InputStream; @@ -37,6 +32,16 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Map; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -44,7 +49,7 @@ import java.util.Map; * @summary Verify that the outputstream created for zip file entries, through the ZipFileSystem * works fine for varying sizes of the zip file entries * @bug 8190753 8011146 8279536 - * @run testng/timeout=300 ZipFSOutputStreamTest + * @run junit/timeout=300 ZipFSOutputStreamTest */ public class ZipFSOutputStreamTest { // List of files to be added to the ZIP file along with their sizes in bytes @@ -56,12 +61,12 @@ public class ZipFSOutputStreamTest { private static final Path ZIP_FILE = Path.of("zipfs-outputstream-test.zip"); - @BeforeMethod + @BeforeEach public void setUp() throws IOException { deleteFiles(); } - @AfterMethod + @AfterEach public void tearDown() throws IOException { deleteFiles(); } @@ -70,13 +75,12 @@ public class ZipFSOutputStreamTest { Files.deleteIfExists(ZIP_FILE); } - @DataProvider(name = "zipFSCreationEnv") - private Object[][] zipFSCreationEnv() { - return new Object[][]{ - {Map.of("create", "true", "noCompression", "true")}, // STORED - {Map.of("create", "true", "noCompression", "false")} // DEFLATED + private static Stream zipFSCreationEnv() { + return Stream.of( + Arguments.of(Map.of("create", "true", "noCompression", "true")), // STORED + Arguments.of(Map.of("create", "true", "noCompression", "false")) // DEFLATED - }; + ); } /** @@ -84,7 +88,8 @@ public class ZipFSOutputStreamTest { * by the ZipFileSystem. Then verify that the generated zip file entries are as expected, * both in size and content */ - @Test(dataProvider = "zipFSCreationEnv") + @ParameterizedTest + @MethodSource("zipFSCreationEnv") public void testOutputStream(final Map env) throws Exception { final byte[] chunk = new byte[1024]; // fill it with some fixed content (the fixed content will later on help ease @@ -117,12 +122,12 @@ public class ZipFSOutputStreamTest { while ((numRead = is.read(buf)) != -1) { totalRead += numRead; // verify the content - Assert.assertEquals(Arrays.mismatch(buf, 0, numRead, chunk, 0, numRead), -1, + assertEquals(-1, Arrays.mismatch(buf, 0, numRead, chunk, 0, numRead), "Unexpected content in " + entryPath); } System.out.println("Read entry " + entryPath + " of bytes " + totalRead + " in " + (System.currentTimeMillis() - start) + " milli seconds"); - Assert.assertEquals(totalRead, (long) entry.getValue(), + assertEquals((long) entry.getValue(), totalRead, "Unexpected number of bytes read from zip entry " + entryPath); } } diff --git a/test/jdk/jdk/nio/zipfs/ZipFSPermissionsTest.java b/test/jdk/jdk/nio/zipfs/ZipFSPermissionsTest.java index d5e0a03a9fa..24cba1f301b 100644 --- a/test/jdk/jdk/nio/zipfs/ZipFSPermissionsTest.java +++ b/test/jdk/jdk/nio/zipfs/ZipFSPermissionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,6 @@ * */ -import org.testng.SkipException; -import org.testng.annotations.*; import java.io.IOException; import java.nio.file.FileSystem; @@ -36,9 +34,21 @@ import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.util.Map; import java.util.Set; +import java.util.stream.Stream; import static java.nio.file.attribute.PosixFilePermission.*; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullSource; /** * @test @@ -46,7 +56,7 @@ import static org.testng.Assert.assertEquals; * @summary Updating an existing zip file does not preserve original permissions * @library /test/lib * @modules jdk.zipfs - * @run testng/othervm ZipFSPermissionsTest + * @run junit/othervm ZipFSPermissionsTest */ public class ZipFSPermissionsTest { @@ -62,18 +72,14 @@ public class ZipFSPermissionsTest { /** * Create the files used by the test */ - @BeforeSuite - public void setUp() throws Exception { + @BeforeAll + public static void setUp() throws Exception { boolean supportsPosix = FileSystems.getDefault() .supportedFileAttributeViews().contains("posix"); - // Check to see if File System supports POSIX permissions - if (supportsPosix) { - System.out.println("File Store Supports Posix"); - } else { - // As there is no POSIX permission support, skip running the test - throw new SkipException("Cannot set permissions on this File Store"); - } + + Assumptions.assumeTrue(supportsPosix, "Cannot set permissions on this File Store"); + System.out.println("File Store Supports Posix"); Files.writeString(entry0, "Tennis Pro"); Files.writeString(entry1, "Tennis is a lifetime sport!"); } @@ -81,7 +87,7 @@ public class ZipFSPermissionsTest { /** * Re-create the initial Zip file prior to each run. */ - @BeforeMethod + @BeforeEach public void before() throws Exception { Files.deleteIfExists(zipFile); zip(zipFile, Map.of("create", "true"), entry0); @@ -90,7 +96,7 @@ public class ZipFSPermissionsTest { /** * Remove Zip file used by test after each run. */ - @AfterMethod + @AfterEach public void tearDown() throws Exception { Files.deleteIfExists(zipFile); } @@ -98,8 +104,8 @@ public class ZipFSPermissionsTest { /** * Remove files used by test as part of final test run clean-up */ - @AfterSuite - public void suiteCleanUp() throws Exception { + @AfterAll + public static void suiteCleanUp() throws Exception { Files.deleteIfExists(zipFile); Files.deleteIfExists(entry0); Files.deleteIfExists(entry1); @@ -112,7 +118,9 @@ public class ZipFSPermissionsTest { * file * @throws Exception If an error occurs */ - @Test(dataProvider = "posixPermissions") + @ParameterizedTest + @MethodSource("posixPermissions") + @NullSource public void testZipPerms(Set newPerms) throws Exception { if (DEBUG) { System.out.printf("Test Run with perms= %s%n", newPerms); @@ -138,7 +146,7 @@ public class ZipFSPermissionsTest { // Zip file PosixFileAttributes afterAttrs = getPosixAttributes(zipFile); displayPermissions("Permissions after updating the Zip File", zipFile); - assertEquals(afterAttrs.permissions(), newPerms, + assertEquals(newPerms, afterAttrs.permissions(), "Permissions were not updated as expected!"); } @@ -206,23 +214,21 @@ public class ZipFSPermissionsTest { } /* - * DataProvider used to verify the permissions on a Zip file + * MethodSource used to verify the permissions on a Zip file * are as expected after updating the Zip file */ - @DataProvider(name = "posixPermissions") - private Object[][] posixPermissions() { - return new Object[][]{ - {null}, - {Set.of(OWNER_READ, OWNER_WRITE, OTHERS_READ)}, - {Set.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE)}, - {Set.of(OWNER_READ, OWNER_WRITE, OTHERS_READ, OTHERS_WRITE)}, - {Set.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, OTHERS_READ, - OTHERS_WRITE, OTHERS_EXECUTE)}, - {Set.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, + private static Stream posixPermissions() { + return Stream.of( + Arguments.of(Set.of(OWNER_READ, OWNER_WRITE, OTHERS_READ)), + Arguments.of(Set.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE)), + Arguments.of(Set.of(OWNER_READ, OWNER_WRITE, OTHERS_READ, OTHERS_WRITE)), + Arguments.of(Set.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, OTHERS_READ, + OTHERS_WRITE, OTHERS_EXECUTE)), + Arguments.of(Set.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE,GROUP_EXECUTE, OTHERS_READ, - OTHERS_WRITE, OTHERS_EXECUTE)}, - {Set.of(OWNER_READ, OWNER_WRITE, GROUP_READ, GROUP_WRITE, - OTHERS_READ, OTHERS_WRITE)}, - }; + OTHERS_WRITE, OTHERS_EXECUTE)), + Arguments.of(Set.of(OWNER_READ, OWNER_WRITE, GROUP_READ, GROUP_WRITE, + OTHERS_READ, OTHERS_WRITE)) + ); } } diff --git a/test/jdk/jdk/nio/zipfs/ZipFSTester.java b/test/jdk/jdk/nio/zipfs/ZipFSTester.java index e6300a90004..84135fd0d7d 100644 --- a/test/jdk/jdk/nio/zipfs/ZipFSTester.java +++ b/test/jdk/jdk/nio/zipfs/ZipFSTester.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,6 +21,9 @@ * questions. */ +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -68,6 +71,13 @@ import java.util.zip.ZipOutputStream; import static java.nio.file.StandardOpenOption.*; import static java.nio.file.StandardCopyOption.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /* * Tests various zipfs operations. @@ -78,57 +88,46 @@ import static java.nio.file.StandardCopyOption.*; * 8131067 8034802 8210899 8273961 8271079 8299864 * @summary Test Zip filesystem provider * @modules jdk.zipfs - * @run main ZipFSTester - * @run main/othervm ZipFSTester + * @run junit ZipFSTester + * @run junit/othervm ZipFSTester */ - public class ZipFSTester { - public static void main(String[] args) throws Exception { - // create JAR file for test, actual contents don't matter - Path jarFile = Utils.createJarFile("tester.jar", + + private static final Random RDM = new Random(); + + // create JAR file for test, actual contents don't matter + static Path jarFile; + + @BeforeAll + static void setup() throws Exception { + jarFile = Utils.createJarFile("tester.jar", "META-INF/MANIFEST.MF", "dir1/foo", "dir2/bar", "dir1/dir3/fooo"); - - try (FileSystem fs = newZipFileSystem(jarFile, Collections.emptyMap())) { - test0(fs); - test1(fs); - test2(fs); - testFileStoreNullArgs(fs); // more tests - } - testStreamChannel(); - testTime(jarFile); - test8069211(); - test8131067(); } - private static final Random RDM = new Random(); - - static void test0(FileSystem fs) - throws Exception - { + @Test + void test0() throws Exception { List list = new LinkedList<>(); - try (ZipFile zf = new ZipFile(fs.toString())) { + try (var fs = newZipFileSystem(jarFile, Map.of()); + ZipFile zf = new ZipFile(fs.toString())) { Enumeration zes = zf.entries(); while (zes.hasMoreElements()) { list.add(zes.nextElement().getName()); } for (String pname : list) { Path path = fs.getPath(pname); - if (!Files.exists(path)) - throw new RuntimeException("path existence check failed!"); + assertTrue(Files.exists(path), "path existence check failed!"); while ((path = path.getParent()) != null) { - if (!Files.exists(path)) - throw new RuntimeException("parent existence check failed!"); + assertTrue(Files.exists(path), "parent existence check failed!"); } } } } - static void test1(FileSystem fs0) - throws Exception - { + @Test + void test1() throws Exception { // prepare a src for testing Path src = getTempPath(); String tmpName = src.toString(); @@ -142,11 +141,12 @@ public class ZipFSTester { Path tmpfsPath = getTempPath(); Map env = new HashMap(); env.put("create", "true"); - try (FileSystem copy = newZipFileSystem(tmpfsPath, env)) { - z2zcopy(fs0, copy, "/", 0); + try (var fs = newZipFileSystem(jarFile, Map.of()); + FileSystem copy = newZipFileSystem(tmpfsPath, env)) { + z2zcopy(fs, copy, "/", 0); // copy the test jar itself in - Files.copy(Paths.get(fs0.toString()), copy.getPath("/foo.jar")); + Files.copy(Paths.get(fs.toString()), copy.getPath("/foo.jar")); Path zpath = copy.getPath("/foo.jar"); try (FileSystem zzfs = FileSystems.newFileSystem(zpath)) { Files.copy(src, zzfs.getPath("/srcInjarjar")); @@ -158,23 +158,15 @@ public class ZipFSTester { FileSystemProvider provider = fs.provider(); // newFileSystem(path...) should not throw exception try (FileSystem fsPath = provider.newFileSystem(tmpfsPath, new HashMap())){} - try (FileSystem fsUri = provider.newFileSystem( - new URI("jar", tmpfsPath.toUri().toString(), null), - new HashMap())) - { - throw new RuntimeException("newFileSystem(URI...) does not throw exception"); - } catch (FileSystemAlreadyExistsException fsaee) {} - - try { - provider.newFileSystem(new File(System.getProperty("test.src", ".")).toPath(), - new HashMap()); - throw new RuntimeException("newFileSystem() opens a directory as zipfs"); - } catch (UnsupportedOperationException uoe) {} - - try { - provider.newFileSystem(src, new HashMap()); - throw new RuntimeException("newFileSystem() opens a non-zip file as zipfs"); - } catch (UnsupportedOperationException uoe) {} + assertThrows(FileSystemAlreadyExistsException.class, + () -> provider.newFileSystem(new URI("jar", tmpfsPath.toUri().toString(), null), + new HashMap<>()), "newFileSystem(URI...) does not throw exception"); + assertThrows(UnsupportedOperationException.class, + () -> provider.newFileSystem(new File(System.getProperty("test.src", ".")).toPath(), + new HashMap<>()), "newFileSystem() opens a directory as zipfs"); + assertThrows(UnsupportedOperationException.class, + () -> provider.newFileSystem(src, new HashMap()), + "newFileSystem() opens a non-zip file as zipfs"); // walk walk(fs.getPath("/")); @@ -193,15 +185,13 @@ public class ZipFSTester { // delete Files.delete(dst); - if (Files.exists(dst)) - throw new RuntimeException("Failed!"); + assertFalse(Files.exists(dst)); // moveout Path dst3 = Paths.get(tmpName + "_Tmp"); Files.move(dst2, dst3); checkEqual(src, dst3); - if (Files.exists(dst2)) - throw new RuntimeException("Failed!"); + assertFalse(Files.exists(dst2)); // copyback + move Files.copy(dst3, dst); @@ -211,11 +201,9 @@ public class ZipFSTester { // delete Files.delete(dst4); - if (Files.exists(dst4)) - throw new RuntimeException("Failed!"); + assertFalse(Files.exists(dst4)); Files.delete(dst3); - if (Files.exists(dst3)) - throw new RuntimeException("Failed!"); + assertFalse(Files.exists(dst3)); // move (existing entry) Path dst5 = fs.getPath("META-INF/MANIFEST.MF"); @@ -227,12 +215,7 @@ public class ZipFSTester { // newInputStream on dir Path parent = dst2.getParent(); - try { - Files.newInputStream(parent); - throw new RuntimeException("Failed"); - } catch (FileSystemException e) { - // expected fse - } + assertThrows(FileSystemException.class, () -> Files.newInputStream(parent)); // rmdirs try { @@ -278,8 +261,8 @@ public class ZipFSTester { } } - static void test2(FileSystem fs) throws Exception { - + @Test + void test2() throws Exception { Path fs1Path = getTempPath(); Path fs2Path = getTempPath(); Path fs3Path = getTempPath(); @@ -293,129 +276,137 @@ public class ZipFSTester { final FileSystem fs3 = newZipFileSystem(fs3Path, env); System.out.println("copy src: fs -> fs0..."); - z2zcopy(fs, fs0, "/", 0); // copy fs -> fs1 - fs0.close(); // dump to file + try (var fs = newZipFileSystem(jarFile, Map.of())) { + z2zcopy(fs, fs0, "/", 0); // copy fs -> fs1 + fs0.close(); // dump to file - System.out.println("open fs0 as fs1"); - env = new HashMap(); - final FileSystem fs1 = newZipFileSystem(fs1Path, env); + System.out.println("open fs0 as fs1"); + env = new HashMap(); + final FileSystem fs1 = newZipFileSystem(fs1Path, env); - System.out.println("listing..."); - final ArrayList files = new ArrayList<>(); - final ArrayList dirs = new ArrayList<>(); - list(fs1.getPath("/"), files, dirs); + System.out.println("listing..."); + final ArrayList files = new ArrayList<>(); + final ArrayList dirs = new ArrayList<>(); + list(fs1.getPath("/"), files, dirs); - Thread t0 = new Thread(new Runnable() { - public void run() { - List list = new ArrayList<>(dirs); - Collections.shuffle(list); - for (String path : list) { - try { - z2zcopy(fs1, fs2, path, 0); - } catch (Exception x) { - x.printStackTrace(); - } - } - } - - }); - - Thread t1 = new Thread(new Runnable() { - public void run() { - List list = new ArrayList<>(dirs); - Collections.shuffle(list); - for (String path : list) { - try { - z2zcopy(fs1, fs2, path, 1); - } catch (Exception x) { - x.printStackTrace(); - } - } - } - - }); - - Thread t2 = new Thread(new Runnable() { - public void run() { - List list = new ArrayList<>(dirs); - Collections.shuffle(list); - for (String path : list) { - try { - z2zcopy(fs1, fs2, path, 2); - } catch (Exception x) { - x.printStackTrace(); - } - } - } - - }); - - Thread t3 = new Thread(new Runnable() { - public void run() { - List list = new ArrayList<>(files); - Collections.shuffle(list); - while (!list.isEmpty()) { - Iterator itr = list.iterator(); - while (itr.hasNext()) { - String path = itr.next(); + Thread t0 = new Thread(new Runnable() { + public void run() { + List list = new ArrayList<>(dirs); + Collections.shuffle(list); + for (String path : list) { try { - if (Files.exists(fs2.getPath(path))) { - z2zmove(fs2, fs3, path); - itr.remove(); - } - } catch (FileAlreadyExistsException x) { - itr.remove(); + z2zcopy(fs1, fs2, path, 0); } catch (Exception x) { x.printStackTrace(); } } } + + }); + + Thread t1 = new Thread(new Runnable() { + public void run() { + List list = new ArrayList<>(dirs); + Collections.shuffle(list); + for (String path : list) { + try { + z2zcopy(fs1, fs2, path, 1); + } catch (Exception x) { + x.printStackTrace(); + } + } + } + + }); + + Thread t2 = new Thread(new Runnable() { + public void run() { + List list = new ArrayList<>(dirs); + Collections.shuffle(list); + for (String path : list) { + try { + z2zcopy(fs1, fs2, path, 2); + } catch (Exception x) { + x.printStackTrace(); + } + } + } + + }); + + Thread t3 = new Thread(new Runnable() { + public void run() { + List list = new ArrayList<>(files); + Collections.shuffle(list); + while (!list.isEmpty()) { + Iterator itr = list.iterator(); + while (itr.hasNext()) { + String path = itr.next(); + try { + if (Files.exists(fs2.getPath(path))) { + z2zmove(fs2, fs3, path); + itr.remove(); + } + } catch (FileAlreadyExistsException x) { + itr.remove(); + } catch (Exception x) { + x.printStackTrace(); + } + } + } + } + + }); + + System.out.println("copying/removing..."); + t0.start(); + t1.start(); + t2.start(); + t3.start(); + t0.join(); + t1.join(); + t2.join(); + t3.join(); + + System.out.println("closing: fs1, fs2"); + fs1.close(); + fs2.close(); + + int failed = 0; + System.out.println("checkEqual: fs vs fs3"); + for (String path : files) { + try { + checkEqual(fs.getPath(path), fs3.getPath(path)); + } catch (IOException x) { + //x.printStackTrace(); + failed++; + } } + System.out.println("closing: fs3"); + fs3.close(); - }); + System.out.println("opening: fs3 as fs4"); + FileSystem fs4 = newZipFileSystem(fs3Path, env); - System.out.println("copying/removing..."); - t0.start(); t1.start(); t2.start(); t3.start(); - t0.join(); t1.join(); t2.join(); t3.join(); - System.out.println("closing: fs1, fs2"); - fs1.close(); - fs2.close(); + ArrayList files2 = new ArrayList<>(); + ArrayList dirs2 = new ArrayList<>(); + list(fs4.getPath("/"), files2, dirs2); - int failed = 0; - System.out.println("checkEqual: fs vs fs3"); - for (String path : files) { - try { - checkEqual(fs.getPath(path), fs3.getPath(path)); - } catch (IOException x) { - //x.printStackTrace(); - failed++; + System.out.println("checkEqual: fs vs fs4"); + for (String path : files2) { + checkEqual(fs.getPath(path), fs4.getPath(path)); } + System.out.println("walking: fs4"); + walk(fs4.getPath("/")); + System.out.println("closing: fs4"); + fs4.close(); + System.out.printf("failed=%d%n", failed); + + Files.delete(fs1Path); + Files.delete(fs2Path); + Files.delete(fs3Path); } - System.out.println("closing: fs3"); - fs3.close(); - - System.out.println("opening: fs3 as fs4"); - FileSystem fs4 = newZipFileSystem(fs3Path, env); - - - ArrayList files2 = new ArrayList<>(); - ArrayList dirs2 = new ArrayList<>(); - list(fs4.getPath("/"), files2, dirs2); - - System.out.println("checkEqual: fs vs fs4"); - for (String path : files2) { - checkEqual(fs.getPath(path), fs4.getPath(path)); - } - System.out.println("walking: fs4"); - walk(fs4.getPath("/")); - System.out.println("closing: fs4"); - fs4.close(); - System.out.printf("failed=%d%n", failed); - - Files.delete(fs1Path); - Files.delete(fs2Path); - Files.delete(fs3Path); } static final int METHOD_STORED = 0; @@ -448,29 +439,21 @@ public class ZipFSTester { CRC32 crc32 = new CRC32(); crc32.update(expected); - if (((Long)Files.getAttribute(path, "zip:crc")).intValue() != - (int)crc32.getValue()) { - System.out.printf(" getAttribute.crc <%s> failed %x vs %x ...%n", + assertEquals((int)crc32.getValue(), ((Long)Files.getAttribute(path, "zip:crc")).intValue(), + " getAttribute.crc <%s> failed %x vs %x ...%n".formatted( path.toString(), ((Long)Files.getAttribute(path, "zip:crc")).intValue(), - (int)crc32.getValue()); - throw new RuntimeException("CHECK FAILED!"); - } + (int)crc32.getValue())); - if (((Long)Files.getAttribute(path, "zip:size")).intValue() != expected.length) { - System.out.printf(" getAttribute.size <%s> failed %x vs %x ...%n", + assertEquals(expected.length, ((Long)Files.getAttribute(path, "zip:size")).intValue(), + " getAttribute.size <%s> failed %x vs %x ...%n".formatted( path.toString(), ((Long)Files.getAttribute(path, "zip:size")).intValue(), - expected.length); - throw new RuntimeException("CHECK FAILED!"); - } + expected.length)); //streams try (InputStream is = Files.newInputStream(path)) { - if (!Arrays.equals(is.readAllBytes(), expected)) { - System.out.printf(" newInputStream <%s> failed...%n", path.toString()); - throw new RuntimeException("CHECK FAILED!"); - } + assertArrayEquals(expected, is.readAllBytes(), " newInputStream <%s> failed...%n".formatted(path.toString())); } // channels -- via sun.nio.ch.ChannelInputStream @@ -478,17 +461,12 @@ public class ZipFSTester { InputStream is = Channels.newInputStream(sbc)) { // check all bytes match - if (!Arrays.equals(is.readAllBytes(), expected)) { - System.out.printf(" newByteChannel <%s> failed...%n", path.toString()); - throw new RuntimeException("CHECK FAILED!"); - } + assertArrayEquals(expected, is.readAllBytes(), + " newByteChannel <%s> failed...%n".formatted(path.toString())); // Check if read position is at the end - if (sbc.position() != expected.length) { - System.out.printf("pos [%s]: size=%d, position=%d%n", - path.toString(), expected.length, sbc.position()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(expected.length, sbc.position(), "pos [%s]: size=%d, position=%d%n".formatted( + path.toString(), expected.length, sbc.position())); // Check position(x) + read() at the random/specific pos/len byte[] buf = new byte[1024]; @@ -503,21 +481,20 @@ public class ZipFSTester { // System.out.printf(" --> %d, %d%n", pos, len); bb.position(0).limit(len); // bb.flip().limit(len); int expectedReadResult = sbc.size() == 0 ? -1 : len; - if (sbc.position(pos).position() != pos || + assertFalse(sbc.position(pos).position() != pos || sbc.read(bb) != expectedReadResult || - !Arrays.equals(buf, 0, bb.position(), expected, pos, pos + len)) { - System.out.printf("read()/position() failed%n"); - throw new RuntimeException("CHECK FAILED!"); - } + !Arrays.equals(buf, 0, bb.position(), expected, pos, pos + len), + "read()/position() failed%n"); } } catch (IOException x) { x.printStackTrace(); - throw new RuntimeException("CHECK FAILED!"); + fail("CHECK FAILED!"); } } // test entry stream/channel reading - static void testStreamChannel() throws Exception { + @Test + void testStreamChannel() throws Exception { Path zpath = getTempPath(); try { var crc = new CRC32(); @@ -542,7 +519,7 @@ public class ZipFSTester { zos.closeEntry(); } } - try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) { + try (var zfs = newZipFileSystem(zpath, Map.of())) { for (Object[] e : entries) { Path path = zfs.getPath((String)e[0]); byte[] bytes = (byte[])e[2]; @@ -561,7 +538,7 @@ public class ZipFSTester { } } } - try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) { + try (var zfs = newZipFileSystem(zpath, Map.of())) { for (Object[] e : entries) { checkRead(zfs.getPath((String)e[0]), (byte[])e[2]); } @@ -574,7 +551,7 @@ public class ZipFSTester { Files.write(zfs.getPath((String)e[0]), (byte[])e[2]); } } - try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) { + try (var zfs = newZipFileSystem(zpath, Map.of())) { for (Object[] e : entries) { checkRead(zfs.getPath((String)e[0]), (byte[])e[2]); } @@ -591,7 +568,7 @@ public class ZipFSTester { } } } - try (var zfs = newZipFileSystem(zpath, Collections.emptyMap())) { + try (var zfs = newZipFileSystem(zpath, Map.of())) { for (Object[] e : entries) { checkRead(zfs.getPath((String)e[0]), (byte[])e[2]); } @@ -604,9 +581,10 @@ public class ZipFSTester { } // test file stamp - static void testTime(Path src) throws Exception { + @Test + void testTime() throws Exception { BasicFileAttributes attrs = Files - .getFileAttributeView(src, BasicFileAttributeView.class) + .getFileAttributeView(jarFile, BasicFileAttributeView.class) .readAttributes(); // create a new filesystem, copy this file into it Map env = new HashMap(); @@ -616,8 +594,8 @@ public class ZipFSTester { System.out.println("test copy with timestamps..."); // copyin Path dst = getPathWithParents(fs, "me"); - Files.copy(src, dst, COPY_ATTRIBUTES); - checkEqual(src, dst); + Files.copy(jarFile, dst, COPY_ATTRIBUTES); + checkEqual(jarFile, dst); System.out.println("mtime: " + attrs.lastModifiedTime()); System.out.println("ctime: " + attrs.creationTime()); System.out.println("atime: " + attrs.lastAccessTime()); @@ -630,20 +608,19 @@ public class ZipFSTester { System.out.println("atime: " + dstAttrs.lastAccessTime()); // 1-second granularity - if (attrs.lastModifiedTime().to(TimeUnit.SECONDS) != + assertFalse(attrs.lastModifiedTime().to(TimeUnit.SECONDS) != dstAttrs.lastModifiedTime().to(TimeUnit.SECONDS) || attrs.lastAccessTime().to(TimeUnit.SECONDS) != dstAttrs.lastAccessTime().to(TimeUnit.SECONDS) || attrs.creationTime().to(TimeUnit.SECONDS) != - dstAttrs.creationTime().to(TimeUnit.SECONDS)) { - throw new RuntimeException("Timestamp Copy Failed!"); - } + dstAttrs.creationTime().to(TimeUnit.SECONDS), "Timestamp Copy Failed!"); } finally { Files.delete(fsPath); } } - static void test8069211() throws Exception { + @Test + void test8069211() throws Exception { // create a new filesystem, copy this file into it Map env = new HashMap(); env.put("create", "true"); @@ -655,18 +632,17 @@ public class ZipFSTester { out.close(); } try (FileSystem fs = newZipFileSystem(fsPath, new HashMap())) { - if (!Arrays.equals(Files.readAllBytes(fs.getPath("/foo")), - "hello".getBytes())) { - throw new RuntimeException("entry close() failed"); - } + assertArrayEquals("hello".getBytes(), Files.readAllBytes(fs.getPath("/foo")), + "entry close() failed"); } catch (Exception x) { - throw new RuntimeException("entry close() failed", x); + fail("entry close() failed", x); } finally { Files.delete(fsPath); } } - static void test8131067() throws Exception { + @Test + void test8131067() throws Exception { Map env = new HashMap(); env.put("create", "true"); @@ -677,10 +653,8 @@ public class ZipFSTester { try (FileSystem fs = newZipFileSystem(fsPath, env);) { Files.write(fs.getPath("/foo"), "hello".getBytes()); URI fooUri = fs.getPath("/foo").toUri(); - if (!Arrays.equals(Files.readAllBytes(Paths.get(fooUri)), - "hello".getBytes())) { - throw new RuntimeException("entry close() failed"); - } + assertArrayEquals("hello".getBytes(), Files.readAllBytes(Paths.get(fooUri)), + "entry close() failed"); } finally { Files.delete(fsPath); } @@ -868,19 +842,13 @@ public class ZipFSTester { int nDst = 0; while (nDst < nSrc) { int n = isDst.read(bufDst, nDst, nSrc - nDst); - if (n == -1) { - System.out.printf("checking <%s> vs <%s>...%n", - src.toString(), dst.toString()); - throw new RuntimeException("CHECK FAILED!"); - } + assertNotEquals(-1, n, "checking <%s> vs <%s>...%n".formatted( + src.toString(), dst.toString())); nDst += n; } while (--nSrc >= 0) { - if (bufSrc[nSrc] != bufDst[nSrc]) { - System.out.printf("checking <%s> vs <%s>...%n", - src.toString(), dst.toString()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(bufSrc[nSrc], bufDst[nSrc], "checking <%s> vs <%s>...%n".formatted( + src.toString(), dst.toString())); nSrc--; } } @@ -890,29 +858,20 @@ public class ZipFSTester { try (SeekableByteChannel chSrc = Files.newByteChannel(src); SeekableByteChannel chDst = Files.newByteChannel(dst)) { - if (chSrc.size() != chDst.size()) { - System.out.printf("src[%s].size=%d, dst[%s].size=%d%n", + assertEquals(chSrc.size(), chDst.size(), "src[%s].size=%d, dst[%s].size=%d%n".formatted( chSrc.toString(), chSrc.size(), - chDst.toString(), chDst.size()); - throw new RuntimeException("CHECK FAILED!"); - } + chDst.toString(), chDst.size())); ByteBuffer bbSrc = ByteBuffer.allocate(8192); ByteBuffer bbDst = ByteBuffer.allocate(8192); int nSrc = 0; while ((nSrc = chSrc.read(bbSrc)) != -1) { int nDst = chDst.read(bbDst); - if (nSrc != nDst) { - System.out.printf("checking <%s> vs <%s>...%n", - src.toString(), dst.toString()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(nSrc, nDst, + "checking <%s> vs <%s>...%n".formatted(src.toString(), dst.toString())); while (--nSrc >= 0) { - if (bbSrc.get(nSrc) != bbDst.get(nSrc)) { - System.out.printf("checking <%s> vs <%s>...%n", - src.toString(), dst.toString()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(bbSrc.get(nSrc), bbDst.get(nSrc), + "checking <%s> vs <%s>...%n".formatted(src.toString(), dst.toString())); nSrc--; } bbSrc.flip(); @@ -920,18 +879,12 @@ public class ZipFSTester { } // Check if source read position is at the end - if (chSrc.position() != chSrc.size()) { - System.out.printf("src[%s]: size=%d, position=%d%n", - chSrc.toString(), chSrc.size(), chSrc.position()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(chSrc.position(), chSrc.size(), "src[%s]: size=%d, position=%d%n".formatted( + chSrc.toString(), chSrc.size(), chSrc.position())); // Check if destination read position is at the end - if (chDst.position() != chDst.size()) { - System.out.printf("dst[%s]: size=%d, position=%d%n", - chDst.toString(), chDst.size(), chDst.position()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(chDst.position(), chDst.size(), "dst[%s]: size=%d, position=%d%n".formatted( + chDst.toString(), chDst.size(), chDst.position())); // Check position(x) + read() at the specific pos/len for (int i = 0; i < 10; i++) { @@ -996,18 +949,12 @@ public class ZipFSTester { } // Check if source read position is at the end - if (srcCh.position() != srcCh.size()) { - System.out.printf("src[%s]: size=%d, position=%d%n", - srcCh.toString(), srcCh.size(), srcCh.position()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(srcCh.position(), srcCh.size(), "src[%s]: size=%d, position=%d%n".formatted( + srcCh.toString(), srcCh.size(), srcCh.position())); // Check if destination write position is at the end - if (dstCh.position() != dstCh.size()) { - System.out.printf("dst[%s]: size=%d, position=%d%n", - dstCh.toString(), dstCh.size(), dstCh.position()); - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(dstCh.position(), dstCh.size(), "dst[%s]: size=%d, position=%d%n".formatted( + dstCh.toString(), dstCh.size(), dstCh.position())); } } @@ -1037,17 +984,13 @@ public class ZipFSTester { try (SeekableByteChannel sbc = Files.newByteChannel(path)) { System.out.printf(" sbc[0]: pos=%d, size=%d%n", sbc.position(), sbc.size()); - if (sbc.position() != 0) { - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(0, sbc.position()); bb = ByteBuffer.allocate((int)sbc.size()); n = sbc.read(bb); System.out.printf(" sbc[1]: read=%d, pos=%d, size=%d%n", n, sbc.position(), sbc.size()); - if (sbc.position() != sbc.size()) { - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(sbc.position(), sbc.size()); bb2 = ByteBuffer.allocate((int)sbc.size()); } @@ -1057,16 +1000,13 @@ public class ZipFSTester { sbc.position(N); System.out.printf(" sbc[2]: pos=%d, size=%d%n", sbc.position(), sbc.size()); - if (sbc.position() != N) { - throw new RuntimeException("CHECK FAILED!"); - } + assertEquals(N, sbc.position()); bb2.limit(100); n = sbc.read(bb2); System.out.printf(" sbc[3]: read=%d, pos=%d, size=%d%n", n, sbc.position(), sbc.size()); - if (n < 0 || sbc.position() != (N + n)) { - throw new RuntimeException("CHECK FAILED!"); - } + assertFalse(n < 0); + assertFalse(sbc.position() != (N + n)); System.out.printf(" sbc[4]: bb[%d]=%d, bb1[0]=%d%n", N, bb.get(N) & 0xff, bb2.get(0) & 0xff); } @@ -1086,38 +1026,22 @@ public class ZipFSTester { /** * Tests if certain methods throw a NullPointerException if invoked with null * as specified in java.nio.file.package-info.java - * @param fs file system containing at least one ZipFileStore * * @see 8299864 */ - static void testFileStoreNullArgs(FileSystem fs) { - FileStore store = fs.getFileStores().iterator().next(); + @Test + void testFileStoreNullArgs() throws Exception { + try (var fs = newZipFileSystem(jarFile, Map.of())) { + // file system containing at least one ZipFileStore + FileStore store = fs.getFileStores().iterator().next(); - // Make sure we are testing the right thing - if (!"jdk.nio.zipfs.ZipFileStore".equals(store.getClass().getName())) - throw new AssertionError(store.getClass().getName()); + // Make sure we are testing the right thing + assertEquals("jdk.nio.zipfs.ZipFileStore", store.getClass().getName()); - assertThrowsNPE(() -> store.supportsFileAttributeView((String) null)); - assertThrowsNPE(() -> store.supportsFileAttributeView((Class) null)); - assertThrowsNPE(() -> store.getAttribute(null)); - assertThrowsNPE(() -> store.getFileStoreAttributeView(null)); - } - - @FunctionalInterface - private interface ThrowingRunnable { - void run() throws Exception; - } - - static void assertThrowsNPE(ThrowingRunnable r) { - try { - r.run(); - // Didn't throw an exception - throw new AssertionError(); - } catch (NullPointerException expected) { - // happy path - } catch (Exception e) { - throw new AssertionError(e); + assertThrows(NullPointerException.class, () -> store.supportsFileAttributeView((String) null)); + assertThrows(NullPointerException.class, () -> store.supportsFileAttributeView((Class) null)); + assertThrows(NullPointerException.class, () -> store.getAttribute(null)); + assertThrows(NullPointerException.class, () -> store.getFileStoreAttributeView(null)); } } - } diff --git a/test/jdk/jdk/nio/zipfs/jarfs/JFSTester.java b/test/jdk/jdk/nio/zipfs/jarfs/JFSTester.java index 82505e24e6c..46bb27dcf10 100644 --- a/test/jdk/jdk/nio/zipfs/jarfs/JFSTester.java +++ b/test/jdk/jdk/nio/zipfs/jarfs/JFSTester.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,12 +30,9 @@ * @library /test/lib/ * @build jdk.test.lib.util.JarBuilder * jdk.test.lib.compiler.Compiler - * @run testng JFSTester + * @run junit JFSTester */ -import org.testng.Assert; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; import java.io.IOException; import java.io.UncheckedIOException; @@ -52,35 +49,41 @@ import java.util.stream.Collectors; import jdk.test.lib.util.JarBuilder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + + public class JFSTester { - private URI jarURI; + private static URI jarURI; - final private String root_dir1_leaf1_txt = "This is leaf 1." + System.lineSeparator(); - final private String root_dir1_leaf2_txt = "This is leaf 2." + System.lineSeparator(); - final private String root_dir2_leaf3_txt = "This is leaf 3." + System.lineSeparator(); - final private String root_dir2_leaf4_txt = "This is leaf 4." + System.lineSeparator(); - final private String v9_root_dir2_leaf3_txt = "This is version 9 leaf 3." + System.lineSeparator(); - final private String v9_root_dir2_leaf4_txt = "This is version 9 leaf 4." + System.lineSeparator(); - final private String v9_root_dir3_leaf5_txt = "This is version 9 leaf 5." + System.lineSeparator(); - final private String v9_root_dir3_leaf6_txt = "This is version 9 leaf 6." + System.lineSeparator(); - final private String v10_root_dir3_leaf5_txt = "This is version 10 leaf 5." + System.lineSeparator(); - final private String v10_root_dir3_leaf6_txt = "This is version 10 leaf 6." + System.lineSeparator(); + private static final String ROOT_DIR_1_LEAF_1_TXT = "This is leaf 1." + System.lineSeparator(); + private static final String ROOT_DIR_1_LEAF_2_TXT = "This is leaf 2." + System.lineSeparator(); + private static final String ROOT_DIR_2_LEAF_3_TXT = "This is leaf 3." + System.lineSeparator(); + private static final String ROOT_DIR_2_LEAF_4_TXT = "This is leaf 4." + System.lineSeparator(); + private static final String V_9_ROOT_DIR_2_LEAF_3_TXT = "This is version 9 leaf 3." + System.lineSeparator(); + private static final String V_9_ROOT_DIR_2_LEAF_4_TXT = "This is version 9 leaf 4." + System.lineSeparator(); + private static final String V_9_ROOT_DIR_3_LEAF_5_TXT = "This is version 9 leaf 5." + System.lineSeparator(); + private static final String V_9_ROOT_DIR_3_LEAF_6_TXT = "This is version 9 leaf 6." + System.lineSeparator(); + private static final String V_10_ROOT_DIR_3_LEAF_5_TXT = "This is version 10 leaf 5." + System.lineSeparator(); + private static final String V_10_ROOT_DIR_3_LEAF_6_TXT = "This is version 10 leaf 6." + System.lineSeparator(); - @BeforeClass - public void initialize() throws Exception { + @BeforeAll + public static void initialize() throws Exception { Path jarfile = Paths.get("test.jar"); JarBuilder jb = new JarBuilder(jarfile.toString()); jb.addAttribute("Multi-Release", "true"); - jb.addEntry("root/dir1/leaf1.txt", root_dir1_leaf1_txt.getBytes()); - jb.addEntry("root/dir1/leaf2.txt", root_dir1_leaf2_txt.getBytes()); - jb.addEntry("root/dir2/leaf3.txt", root_dir2_leaf3_txt.getBytes()); - jb.addEntry("root/dir2/leaf4.txt", root_dir2_leaf4_txt.getBytes()); - jb.addEntry("META-INF/versions/9/root/dir2/leaf3.txt", v9_root_dir2_leaf3_txt.getBytes()); - jb.addEntry("META-INF/versions/9/root/dir2/leaf4.txt", v9_root_dir2_leaf4_txt.getBytes()); - jb.addEntry("META-INF/versions/9/root/dir3/leaf5.txt", v9_root_dir3_leaf5_txt.getBytes()); - jb.addEntry("META-INF/versions/9/root/dir3/leaf6.txt", v9_root_dir3_leaf6_txt.getBytes()); - jb.addEntry("META-INF/versions/10/root/dir3/leaf5.txt", v10_root_dir3_leaf5_txt.getBytes()); - jb.addEntry("META-INF/versions/10/root/dir3/leaf6.txt", v10_root_dir3_leaf6_txt.getBytes()); + jb.addEntry("root/dir1/leaf1.txt", ROOT_DIR_1_LEAF_1_TXT.getBytes()); + jb.addEntry("root/dir1/leaf2.txt", ROOT_DIR_1_LEAF_2_TXT.getBytes()); + jb.addEntry("root/dir2/leaf3.txt", ROOT_DIR_2_LEAF_3_TXT.getBytes()); + jb.addEntry("root/dir2/leaf4.txt", ROOT_DIR_2_LEAF_4_TXT.getBytes()); + jb.addEntry("META-INF/versions/9/root/dir2/leaf3.txt", V_9_ROOT_DIR_2_LEAF_3_TXT.getBytes()); + jb.addEntry("META-INF/versions/9/root/dir2/leaf4.txt", V_9_ROOT_DIR_2_LEAF_4_TXT.getBytes()); + jb.addEntry("META-INF/versions/9/root/dir3/leaf5.txt", V_9_ROOT_DIR_3_LEAF_5_TXT.getBytes()); + jb.addEntry("META-INF/versions/9/root/dir3/leaf6.txt", V_9_ROOT_DIR_3_LEAF_6_TXT.getBytes()); + jb.addEntry("META-INF/versions/10/root/dir3/leaf5.txt", V_10_ROOT_DIR_3_LEAF_5_TXT.getBytes()); + jb.addEntry("META-INF/versions/10/root/dir3/leaf6.txt", V_10_ROOT_DIR_3_LEAF_6_TXT.getBytes()); jb.build(); System.out.println("Created " + jarfile + ": " + Files.exists(jarfile)); jarURI = new URI("jar", jarfile.toUri().toString(), null); @@ -92,38 +95,38 @@ public class JFSTester { Map env = new HashMap<>(); Set contents = doTest(env); Set expectedContents = Set.of( - root_dir1_leaf1_txt, - root_dir1_leaf2_txt, - root_dir2_leaf3_txt, - root_dir2_leaf4_txt + ROOT_DIR_1_LEAF_1_TXT, + ROOT_DIR_1_LEAF_2_TXT, + ROOT_DIR_2_LEAF_3_TXT, + ROOT_DIR_2_LEAF_4_TXT ); - Assert.assertEquals(contents, expectedContents); + assertEquals(expectedContents, contents); // open file as multi-release for version 9 env.put("multi-release", "9"); contents = doTest(env); expectedContents = Set.of( - root_dir1_leaf1_txt, - root_dir1_leaf2_txt, - v9_root_dir2_leaf3_txt, - v9_root_dir2_leaf4_txt, - v9_root_dir3_leaf5_txt, - v9_root_dir3_leaf6_txt + ROOT_DIR_1_LEAF_1_TXT, + ROOT_DIR_1_LEAF_2_TXT, + V_9_ROOT_DIR_2_LEAF_3_TXT, + V_9_ROOT_DIR_2_LEAF_4_TXT, + V_9_ROOT_DIR_3_LEAF_5_TXT, + V_9_ROOT_DIR_3_LEAF_6_TXT ); - Assert.assertEquals(contents, expectedContents); + assertEquals(expectedContents, contents); // open file as multi-release for version 10 env.put("multi-release", "10"); contents = doTest(env); expectedContents = Set.of( - root_dir1_leaf1_txt, - root_dir1_leaf2_txt, - v9_root_dir2_leaf3_txt, - v9_root_dir2_leaf4_txt, - v10_root_dir3_leaf5_txt, - v10_root_dir3_leaf6_txt + ROOT_DIR_1_LEAF_1_TXT, + ROOT_DIR_1_LEAF_2_TXT, + V_9_ROOT_DIR_2_LEAF_3_TXT, + V_9_ROOT_DIR_2_LEAF_4_TXT, + V_10_ROOT_DIR_3_LEAF_5_TXT, + V_10_ROOT_DIR_3_LEAF_6_TXT ); - Assert.assertEquals(contents, expectedContents); + assertEquals(expectedContents, contents); } private Set doTest(Map env) throws IOException { @@ -158,7 +161,7 @@ public class JFSTester { "!/root/dir2/leaf3.txt", "!/root/dir2/leaf4.txt" ); - Assert.assertEquals(contents, expectedContents); + assertEquals(expectedContents, contents); // open file as multi-release for version 9 env.put("multi-release", "9"); @@ -171,7 +174,7 @@ public class JFSTester { "!/META-INF/versions/9/root/dir3/leaf5.txt", "!/META-INF/versions/9/root/dir3/leaf6.txt" ); - Assert.assertEquals(contents, expectedContents); + assertEquals(expectedContents, contents); // open file as multi-release for version 10 env.put("multi-release", "10"); @@ -184,7 +187,7 @@ public class JFSTester { "!/META-INF/versions/10/root/dir3/leaf5.txt", "!/META-INF/versions/10/root/dir3/leaf6.txt" ); - Assert.assertEquals(contents, expectedContents); + assertEquals(expectedContents, contents); } private Set doTestUri(Map env) throws IOException { diff --git a/test/jdk/jdk/nio/zipfs/jarfs/MultiReleaseJarTest.java b/test/jdk/jdk/nio/zipfs/jarfs/MultiReleaseJarTest.java index 1a336278089..ad0b47a75ee 100644 --- a/test/jdk/jdk/nio/zipfs/jarfs/MultiReleaseJarTest.java +++ b/test/jdk/jdk/nio/zipfs/jarfs/MultiReleaseJarTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ * @build CreateMultiReleaseTestJars * jdk.test.lib.util.JarBuilder * jdk.test.lib.compiler.Compiler - * @run testng MultiReleaseJarTest + * @run junit MultiReleaseJarTest */ import java.io.IOException; @@ -46,11 +46,22 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; -import org.testng.Assert; -import org.testng.annotations.*; import jdk.test.lib.util.JarBuilder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class MultiReleaseJarTest { final private int MAJOR_VERSION = Runtime.version().feature(); private static final String PROPERTY_RELEASE_VERSION = "releaseVersion"; @@ -69,7 +80,7 @@ public class MultiReleaseJarTest { private URI mruri; private URI smruri; - @BeforeClass + @BeforeAll public void initialize() throws Exception { creator.compileEntries(); creator.buildUnversionedJar(); @@ -90,55 +101,51 @@ public class MultiReleaseJarTest { Files.delete(Paths.get(userdir, "short-multi-release.jar")); } - @DataProvider(name="strings") - public Object[][] createStrings() { - return new Object[][]{ - {"runtime", MAJOR_VERSION, "8"}, - {null, 8, Integer.toString(MAJOR_VERSION)}, - {"8", 8, "9"}, - {"9", 9, null}, - {Integer.toString(MAJOR_VERSION), MAJOR_VERSION, "8"}, - {Integer.toString(MAJOR_VERSION+1), MAJOR_VERSION, "8"}, - {"50", MAJOR_VERSION, "9"} - }; + public Stream createStrings() { + return Stream.of( + Arguments.of("runtime", MAJOR_VERSION, "8"), + Arguments.of(null, 8, Integer.toString(MAJOR_VERSION)), + Arguments.of("8", 8, "9"), + Arguments.of("9", 9, null), + Arguments.of(Integer.toString(MAJOR_VERSION), MAJOR_VERSION, "8"), + Arguments.of(Integer.toString(MAJOR_VERSION+1), MAJOR_VERSION, "8"), + Arguments.of("50", MAJOR_VERSION, "9") + ); } - @DataProvider(name="integers") - public Object[][] createIntegers() { - return new Object[][] { - {null, 8, Integer.valueOf(9)}, - {Integer.valueOf(8), 8, Integer.valueOf(9)}, - {Integer.valueOf(9), 9, Integer.valueOf(MAJOR_VERSION)}, - {Integer.valueOf(MAJOR_VERSION), MAJOR_VERSION, Integer.valueOf(8)}, - {Integer.valueOf(MAJOR_VERSION + 1), MAJOR_VERSION, null}, - {Integer.valueOf(100), MAJOR_VERSION, Integer.valueOf(8)} - }; + public Stream createIntegers() { + return Stream.of( + Arguments.of(null, 8, Integer.valueOf(9)), + Arguments.of(Integer.valueOf(8), 8, Integer.valueOf(9)), + Arguments.of(Integer.valueOf(9), 9, Integer.valueOf(MAJOR_VERSION)), + Arguments.of(Integer.valueOf(MAJOR_VERSION), MAJOR_VERSION, Integer.valueOf(8)), + Arguments.of(Integer.valueOf(MAJOR_VERSION + 1), MAJOR_VERSION, null), + Arguments.of(Integer.valueOf(100), MAJOR_VERSION, Integer.valueOf(8)) + ); } - @DataProvider(name="versions") - public Object[][] createVersions() { - return new Object[][] { - {null, 8, Version.parse("14")}, - {Version.parse("8"), 8, Version.parse("7")}, - {Version.parse("9"), 9, null}, - {Version.parse(Integer.toString(MAJOR_VERSION)), MAJOR_VERSION, Version.parse("8")}, - {Version.parse(Integer.toString(MAJOR_VERSION) + 1), MAJOR_VERSION, Version.parse("9")}, - {Version.parse("100"), MAJOR_VERSION, Version.parse("14")} - }; + public Stream createVersions() { + return Stream.of( + Arguments.of(null, 8, Version.parse("14")), + Arguments.of(Version.parse("8"), 8, Version.parse("7")), + Arguments.of(Version.parse("9"), 9, null), + Arguments.of(Version.parse(Integer.toString(MAJOR_VERSION)), MAJOR_VERSION, Version.parse("8")), + Arguments.of(Version.parse(Integer.toString(MAJOR_VERSION) + 1), MAJOR_VERSION, Version.parse("9")), + Arguments.of(Version.parse("100"), MAJOR_VERSION, Version.parse("14")) + ); } - @DataProvider(name="invalidVersions") - public Object[][] invalidVersions() { - return new Object[][] { - {Map.of(PROPERTY_RELEASE_VERSION, "")}, - {Map.of(PROPERTY_RELEASE_VERSION, "invalid")}, - {Map.of(PROPERTY_RELEASE_VERSION, "0")}, - {Map.of(PROPERTY_RELEASE_VERSION, "-1")}, - {Map.of(PROPERTY_RELEASE_VERSION, "11.0.1")}, - {Map.of(PROPERTY_RELEASE_VERSION, new ArrayList())}, - {Map.of(PROPERTY_RELEASE_VERSION, Integer.valueOf(0))}, - {Map.of(PROPERTY_RELEASE_VERSION, Integer.valueOf(-1))} - }; + public Stream invalidVersions() { + return Stream.of( + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, "")), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, "invalid")), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, "0")), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, "-1")), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, "11.0.1")), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, new ArrayList())), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, Integer.valueOf(0))), + Arguments.of(Map.of(PROPERTY_RELEASE_VERSION, Integer.valueOf(-1))) + ); } // Not the best test but all I can do since ZipFileSystem @@ -148,16 +155,16 @@ public class MultiReleaseJarTest { Map env = new HashMap<>(); // no configuration, treat multi-release jar as unversioned try (FileSystem fs = FileSystems.newFileSystem(mruri, env)) { - Assert.assertTrue(readAndCompare(fs, 8)); + assertTrue(readAndCompare(fs, 8)); } env.put(PROPERTY_RELEASE_VERSION, "runtime"); // a configuration and jar file is multi-release try (FileSystem fs = FileSystems.newFileSystem(mruri, env)) { - Assert.assertTrue(readAndCompare(fs, MAJOR_VERSION)); + assertTrue(readAndCompare(fs, MAJOR_VERSION)); } // a configuration but jar file is unversioned try (FileSystem fs = FileSystems.newFileSystem(uvuri, env)) { - Assert.assertTrue(readAndCompare(fs, 8)); + assertTrue(readAndCompare(fs, 8)); } } @@ -167,7 +174,8 @@ public class MultiReleaseJarTest { return src.contains("return " + expected); } - @Test(dataProvider="strings") + @ParameterizedTest + @MethodSource("createStrings") public void testStrings(String value, int expected, String ignorable) throws Throwable { stringEnv.clear(); stringEnv.put(PROPERTY_RELEASE_VERSION, value); @@ -176,7 +184,8 @@ public class MultiReleaseJarTest { runTest(stringEnv, expected); } - @Test(dataProvider="integers") + @ParameterizedTest + @MethodSource("createIntegers") public void testIntegers(Integer value, int expected, Integer ignorable) throws Throwable { integerEnv.clear(); integerEnv.put(PROPERTY_RELEASE_VERSION, value); @@ -185,7 +194,8 @@ public class MultiReleaseJarTest { runTest(integerEnv, expected); } - @Test(dataProvider="versions") + @ParameterizedTest + @MethodSource("createVersions") public void testVersions(Version value, int expected, Version ignorable) throws Throwable { versionEnv.clear(); versionEnv.put(PROPERTY_RELEASE_VERSION, value); @@ -210,30 +220,34 @@ public class MultiReleaseJarTest { * @throws Throwable Exception thrown for anything other than the expected * IllegalArgumentException */ - @Test(dataProvider="invalidVersions") + @ParameterizedTest + @MethodSource("invalidVersions") public void testInvalidVersions(Map env) throws Throwable { - Assert.assertThrows(IllegalArgumentException.class, () -> + assertThrows(IllegalArgumentException.class, () -> FileSystems.newFileSystem(Path.of(userdir, "multi-release.jar"), env)); } // The following tests are for backwards compatibility to validate that // the original property still works - @Test(dataProvider="strings") + @ParameterizedTest + @MethodSource("createStrings") public void testMRStrings(String value, int expected, String ignorable) throws Throwable { stringEnv.clear(); stringEnv.put(PROPERTY_MULTI_RELEASE, value); runTest(stringEnv, expected); } - @Test(dataProvider="integers") + @ParameterizedTest + @MethodSource("createIntegers") public void testMRIntegers(Integer value, int expected, Integer ignorable) throws Throwable { integerEnv.clear(); integerEnv.put(PROPERTY_MULTI_RELEASE, value); runTest(integerEnv, expected); } - @Test(dataProvider="versions") + @ParameterizedTest + @MethodSource("createVersions") public void testMRVersions(Version value, int expected, Version ignorable) throws Throwable { versionEnv.clear(); versionEnv.put(PROPERTY_MULTI_RELEASE, value); @@ -250,7 +264,7 @@ public class MultiReleaseJarTest { byte [] bytes = Files.readAllBytes(version); Class vcls = (new ByteArrayClassLoader(fs)).defineClass(className, bytes); MethodHandle mh = MethodHandles.lookup().findVirtual(vcls, "getVersion", mt); - Assert.assertEquals((int)mh.invoke(vcls.getDeclaredConstructor().newInstance()), expected); + assertEquals(expected, (int)mh.invoke(vcls.getDeclaredConstructor().newInstance())); } } @@ -281,7 +295,7 @@ public class MultiReleaseJarTest { jb.build(); Map env = Map.of(PROPERTY_RELEASE_VERSION, "runtime"); try (FileSystem fs = FileSystems.newFileSystem(uri, env)) { - Assert.assertTrue(true); + assertTrue(true); } Files.delete(jfpath); } @@ -300,9 +314,9 @@ public class MultiReleaseJarTest { URI customJar = new URI("jar", ssp , null); try (FileSystem fs = FileSystems.newFileSystem(customJar, env)) { if (expected) { - Assert.assertTrue(readAndCompare(fs, MAJOR_VERSION)); + assertTrue(readAndCompare(fs, MAJOR_VERSION)); } else { - Assert.assertTrue(readAndCompare(fs, 8)); + assertTrue(readAndCompare(fs, 8)); } } Files.delete(filePath); diff --git a/test/jdk/jdk/nio/zipfs/testng/TEST.properties b/test/jdk/jdk/nio/zipfs/testng/TEST.properties deleted file mode 100644 index f069323c2f9..00000000000 --- a/test/jdk/jdk/nio/zipfs/testng/TEST.properties +++ /dev/null @@ -1,4 +0,0 @@ -# Zip FS unit tests uses TestNG -modules = jdk.zipfs -TestNG.dirs = . - diff --git a/test/jdk/jdk/nio/zipfs/testng/util/ZipFsBaseTest.java b/test/jdk/jdk/nio/zipfs/util/ZipFsBaseTest.java similarity index 80% rename from test/jdk/jdk/nio/zipfs/testng/util/ZipFsBaseTest.java rename to test/jdk/jdk/nio/zipfs/util/ZipFsBaseTest.java index 6686ff0e920..1e6e1a385b1 100644 --- a/test/jdk/jdk/nio/zipfs/testng/util/ZipFsBaseTest.java +++ b/test/jdk/jdk/nio/zipfs/util/ZipFsBaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ */ package util; -import org.testng.annotations.DataProvider; +import org.junit.jupiter.params.provider.Arguments; import java.io.File; import java.io.IOException; @@ -34,7 +34,6 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.security.SecureRandom; -import java.util.Arrays; import java.util.Comparator; import java.util.Map; import java.util.stream.Stream; @@ -43,7 +42,9 @@ import java.util.zip.ZipFile; import static java.lang.String.format; import static java.util.stream.Collectors.joining; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class ZipFsBaseTest { @@ -53,49 +54,46 @@ public class ZipFsBaseTest { private static final SecureRandom random = new SecureRandom(); /** - * DataProvider used to specify the Zip FS properties to use when creating + * MethodSource used to specify the Zip FS properties to use when creating * the Zip File along with the compression method used * * @return Zip FS properties and compression method used by the tests */ - @DataProvider(name = "zipfsMap") - protected Object[][] zipfsMap() { - return new Object[][]{ - {Map.of("create", "true"), ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "true"), - ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "false"), - ZipEntry.DEFLATED} - }; + protected static Stream zipfsMap() { + return Stream.of( + Arguments.of(Map.of("create", "true"), ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "true"), + ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "false"), + ZipEntry.DEFLATED) + ); } /* - * DataProvider used to verify that an entry can be copied or moved within + * MethodSource used to verify that an entry can be copied or moved within * a Zip file system using a different compression from when the entry * was first created */ - @DataProvider(name = "copyMoveMap") - protected Object[][] copyMoveMap() { - return new Object[][]{ - {Map.of("create", "true"), ZipEntry.DEFLATED, ZipEntry.STORED}, - {Map.of("create", "true", "noCompression", "true"), - ZipEntry.STORED, ZipEntry.DEFLATED}, - {Map.of("create", "true", "noCompression", "false"), - ZipEntry.DEFLATED, ZipEntry.STORED} - }; + protected static Stream copyMoveMap() { + return Stream.of( + Arguments.of(Map.of("create", "true"), ZipEntry.DEFLATED, ZipEntry.STORED), + Arguments.of(Map.of("create", "true", "noCompression", "true"), + ZipEntry.STORED, ZipEntry.DEFLATED), + Arguments.of(Map.of("create", "true", "noCompression", "false"), + ZipEntry.DEFLATED, ZipEntry.STORED) + ); } /** - * DataProvider with the compression methods to be used for a given test run + * MethodSource with the compression methods to be used for a given test run * * @return Compression methods to test with */ - @DataProvider(name = "compressionMethods") - protected Object[][] compressionMethods() { - return new Object[][]{ - {ZipEntry.DEFLATED}, - {ZipEntry.STORED} - }; + protected static Stream compressionMethods() { + return Stream.of( + Arguments.of(ZipEntry.DEFLATED), + Arguments.of(ZipEntry.STORED) + ); } /** @@ -137,7 +135,7 @@ public class ZipFsBaseTest { // check entries with Zip API try (ZipFile zf = new ZipFile(zipfile.toFile())) { // check entry count - assertEquals(entries.length, zf.size()); + assertEquals(zf.size(), entries.length); // Check compression method and content of each entry for (Entry e : entries) { @@ -147,7 +145,7 @@ public class ZipFsBaseTest { System.out.printf("Entry Name: %s, method: %s, Expected Method: %s%n", e.name, ze.getMethod(), e.method); } - assertEquals(e.method, ze.getMethod()); + assertEquals(ze.getMethod(), e.method); try (InputStream in = zf.getInputStream(ze)) { byte[] bytes = in.readAllBytes(); if (DEBUG) { @@ -155,7 +153,7 @@ public class ZipFsBaseTest { new String(bytes), new String(e.bytes)); } - assertTrue(Arrays.equals(bytes, e.bytes)); + assertArrayEquals(bytes, e.bytes); } } } @@ -166,7 +164,7 @@ public class ZipFsBaseTest { Path top = fs.getPath("/"); long count = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile()).count(); - assertEquals(entries.length, count); + assertEquals(count, entries.length); // Check content of each entry for (Entry e : entries) { @@ -175,7 +173,7 @@ public class ZipFsBaseTest { System.out.printf("Entry name = %s, bytes= %s, actual=%s%n", e.name, new String(Files.readAllBytes(file)), new String(e.bytes)); } - assertEquals(Files.readAllBytes(file), e.bytes); + assertArrayEquals(e.bytes, Files.readAllBytes(file)); } } } @@ -189,7 +187,7 @@ public class ZipFsBaseTest { * @param source The path of the file to add to the Zip File * @throws IOException If an error occurs while creating/updating the Zip file */ - protected void zip(Path zipFile, Map env, Path source) throws IOException { + protected static void zip(Path zipFile, Map env, Path source) throws IOException { if (DEBUG) { System.out.printf("File:%s, adding:%s%n", zipFile.toAbsolutePath(), source); } @@ -208,7 +206,7 @@ public class ZipFsBaseTest { * @param entries The entries to add to the Zip File * @throws IOException If an error occurs while creating the Zip file */ - protected void zip(Path zipFile, Map env, + protected static void zip(Path zipFile, Map env, Entry... entries) throws IOException { if (DEBUG) { System.out.printf("Creating file: %s, env: %s%n", zipFile, formatMap(env)); From 9ea893d87749fd87a3d1947e6d71c5899c6380bb Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Thu, 19 Mar 2026 16:56:26 +0000 Subject: [PATCH 034/160] 8380223: Refactor test/jdk/sun/util/resources TestNG tests to JUnit Reviewed-by: bpb, jlu --- .../util/resources/TimeZone/Bug8139107.java | 7 ++-- .../TimeZone/ChineseTimeZoneNameTest.java | 25 +++++++-------- .../sun/util/resources/cldr/Bug8202764.java | 21 ++++++------ .../resources/cldr/TimeZoneNamesTest.java | 32 +++++++++---------- 4 files changed, 42 insertions(+), 43 deletions(-) diff --git a/test/jdk/sun/util/resources/TimeZone/Bug8139107.java b/test/jdk/sun/util/resources/TimeZone/Bug8139107.java index e00d4c88889..575dd7c291e 100644 --- a/test/jdk/sun/util/resources/TimeZone/Bug8139107.java +++ b/test/jdk/sun/util/resources/TimeZone/Bug8139107.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,12 +27,13 @@ * @summary Test that date parsing with DateTimeFormatter pattern * that contains timezone field doesn't trigger NPE. All supported * locales are tested. - * @run testng/timeout=480 Bug8139107 + * @run junit/timeout=480 Bug8139107 */ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.Locale; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.Test; public class Bug8139107 { diff --git a/test/jdk/sun/util/resources/TimeZone/ChineseTimeZoneNameTest.java b/test/jdk/sun/util/resources/TimeZone/ChineseTimeZoneNameTest.java index 335d147f9ab..258edc38a59 100644 --- a/test/jdk/sun/util/resources/TimeZone/ChineseTimeZoneNameTest.java +++ b/test/jdk/sun/util/resources/TimeZone/ChineseTimeZoneNameTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ * @bug 8275721 8174269 * @modules jdk.localedata * @summary Checks Chinese time zone names for `UTC` using CLDR are consistent - * @run testng ChineseTimeZoneNameTest + * @run junit ChineseTimeZoneNameTest */ import java.time.Instant; @@ -35,11 +35,10 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Locale; -import static org.testng.Assert.assertEquals; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Test public class ChineseTimeZoneNameTest { private static final Locale SIMPLIFIED_CHINESE = Locale.forLanguageTag("zh-Hans"); @@ -47,8 +46,7 @@ public class ChineseTimeZoneNameTest { private static final ZonedDateTime EPOCH_UTC = ZonedDateTime.ofInstant(Instant.ofEpochSecond (0), ZoneId.of ("UTC")); - @DataProvider(name="locales") - Object[][] data() { + private static Object[][] data() { return new Object[][] { {Locale.CHINESE, SIMPLIFIED_CHINESE}, {Locale.SIMPLIFIED_CHINESE, SIMPLIFIED_CHINESE}, @@ -61,11 +59,12 @@ public class ChineseTimeZoneNameTest { }; } - @Test(dataProvider="locales") + @ParameterizedTest + @MethodSource("data") public void test_ChineseTimeZoneNames(Locale testLoc, Locale resourceLoc) { - assertEquals(DateTimeFormatter.ofPattern("z", testLoc).format(EPOCH_UTC), - DateTimeFormatter.ofPattern("z", resourceLoc).format(EPOCH_UTC)); - assertEquals(DateTimeFormatter.ofPattern("zzzz", testLoc).format(EPOCH_UTC), - DateTimeFormatter.ofPattern("zzzz", resourceLoc).format(EPOCH_UTC)); + assertEquals(DateTimeFormatter.ofPattern("z", resourceLoc).format(EPOCH_UTC), + DateTimeFormatter.ofPattern("z", testLoc).format(EPOCH_UTC)); + assertEquals(DateTimeFormatter.ofPattern("zzzz", resourceLoc).format(EPOCH_UTC), + DateTimeFormatter.ofPattern("zzzz", testLoc).format(EPOCH_UTC)); } } diff --git a/test/jdk/sun/util/resources/cldr/Bug8202764.java b/test/jdk/sun/util/resources/cldr/Bug8202764.java index 6f3e40e6201..e7fa110671a 100644 --- a/test/jdk/sun/util/resources/cldr/Bug8202764.java +++ b/test/jdk/sun/util/resources/cldr/Bug8202764.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,11 +28,9 @@ * @summary Checks time zone names are consistent with aliased ids, * between DateFormatSymbols.getZoneStrings() and getDisplayName() * of TimeZone/ZoneId classes - * @run testng/othervm Bug8202764 + * @run junit/othervm Bug8202764 */ -import static org.testng.Assert.assertEquals; - import java.time.ZoneId; import java.time.format.TextStyle; import java.text.DateFormatSymbols; @@ -41,7 +39,8 @@ import java.util.Locale; import java.util.Set; import java.util.TimeZone; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Bug8202764 { @@ -53,15 +52,15 @@ public class Bug8202764 { .forEach(zone -> { System.out.println(zone[0]); TimeZone tz = TimeZone.getTimeZone(zone[0]); - assertEquals(zone[1], tz.getDisplayName(false, TimeZone.LONG, Locale.US)); - assertEquals(zone[2], tz.getDisplayName(false, TimeZone.SHORT, Locale.US)); - assertEquals(zone[3], tz.getDisplayName(true, TimeZone.LONG, Locale.US)); - assertEquals(zone[4], tz.getDisplayName(true, TimeZone.SHORT, Locale.US)); + assertEquals(tz.getDisplayName(false, TimeZone.LONG, Locale.US), zone[1]); + assertEquals(tz.getDisplayName(false, TimeZone.SHORT, Locale.US), zone[2]); + assertEquals(tz.getDisplayName(true, TimeZone.LONG, Locale.US), zone[3]); + assertEquals(tz.getDisplayName(true, TimeZone.SHORT, Locale.US), zone[4]); if (zoneIds.contains(zone[0])) { // Some of the ids, e.g. three-letter ids are not supported in ZoneId ZoneId zi = tz.toZoneId(); - assertEquals(zone[5], zi.getDisplayName(TextStyle.FULL, Locale.US)); - assertEquals(zone[6], zi.getDisplayName(TextStyle.SHORT, Locale.US)); + assertEquals(zi.getDisplayName(TextStyle.FULL, Locale.US), zone[5]); + assertEquals(zi.getDisplayName(TextStyle.SHORT, Locale.US), zone[6]); } }); } diff --git a/test/jdk/sun/util/resources/cldr/TimeZoneNamesTest.java b/test/jdk/sun/util/resources/cldr/TimeZoneNamesTest.java index 05b0114f1b4..6d21cd5613a 100644 --- a/test/jdk/sun/util/resources/cldr/TimeZoneNamesTest.java +++ b/test/jdk/sun/util/resources/cldr/TimeZoneNamesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ * @modules jdk.localedata * @summary Checks CLDR time zone names are generated correctly at * either build or runtime - * @run testng TimeZoneNamesTest + * @run junit TimeZoneNamesTest */ import java.text.DateFormatSymbols; @@ -39,16 +39,15 @@ import java.util.Locale; import java.util.Objects; import java.util.TimeZone; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; -@Test public class TimeZoneNamesTest { - @DataProvider - Object[][] sampleTZs() { + private static Object[][] sampleTZs() { return new Object[][] { // tzid, locale, style, expected @@ -219,19 +218,20 @@ public class TimeZoneNamesTest { } - @Test(dataProvider="sampleTZs") + @ParameterizedTest + @MethodSource("sampleTZs") public void test_tzNames(String tzid, Locale locale, String lstd, String sstd, String ldst, String sdst, String lgen, String sgen) { // Standard time - assertEquals(TimeZone.getTimeZone(tzid).getDisplayName(false, TimeZone.LONG, locale), lstd); - assertEquals(TimeZone.getTimeZone(tzid).getDisplayName(false, TimeZone.SHORT, locale), sstd); + assertEquals(lstd, TimeZone.getTimeZone(tzid).getDisplayName(false, TimeZone.LONG, locale)); + assertEquals(sstd, TimeZone.getTimeZone(tzid).getDisplayName(false, TimeZone.SHORT, locale)); // daylight saving time - assertEquals(TimeZone.getTimeZone(tzid).getDisplayName(true, TimeZone.LONG, locale), ldst); - assertEquals(TimeZone.getTimeZone(tzid).getDisplayName(true, TimeZone.SHORT, locale), sdst); + assertEquals(ldst, TimeZone.getTimeZone(tzid).getDisplayName(true, TimeZone.LONG, locale)); + assertEquals(sdst, TimeZone.getTimeZone(tzid).getDisplayName(true, TimeZone.SHORT, locale)); // generic name - assertEquals(ZoneId.of(tzid).getDisplayName(TextStyle.FULL, locale), lgen); - assertEquals(ZoneId.of(tzid).getDisplayName(TextStyle.SHORT, locale), sgen); + assertEquals(lgen, ZoneId.of(tzid).getDisplayName(TextStyle.FULL, locale)); + assertEquals(sgen, ZoneId.of(tzid).getDisplayName(TextStyle.SHORT, locale)); } // Make sure getZoneStrings() returns non-empty string array From 13b954b996262c6d686b5e7818781324d54c1e4c Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Thu, 19 Mar 2026 17:58:43 +0000 Subject: [PATCH 035/160] 8380407: GenShen: Problem with com/sun/jdi/ThreadMemoryLeakTest Reviewed-by: shade --- .../shenandoah/heuristics/shenandoahYoungHeuristics.cpp | 4 +--- src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp | 4 ++-- .../share/gc/shenandoah/shenandoahGenerationalHeap.cpp | 8 ++++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp index 3779f969723..68935aba5a8 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp @@ -174,10 +174,8 @@ size_t ShenandoahYoungHeuristics::bytes_of_allocation_runway_before_gc_trigger(s size_t usage = _space_info->used(); size_t available = (capacity > usage)? capacity - usage: 0; size_t allocated = _free_set->get_bytes_allocated_since_gc_start(); + size_t anticipated_available = available + young_regions_to_be_reclaimed * ShenandoahHeapRegion::region_size_bytes(); - size_t available_young_collected = ShenandoahHeap::heap()->collection_set()->get_young_available_bytes_collected(); - size_t anticipated_available = - available + young_regions_to_be_reclaimed * ShenandoahHeapRegion::region_size_bytes() - available_young_collected; size_t spike_headroom = capacity * ShenandoahAllocSpikeFactor / 100; size_t penalties = capacity * _gc_time_penalties / 100; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index b2d5e5423dd..750389ae6c3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -315,8 +315,8 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { _free_set->prepare_to_rebuild(young_trashed_regions, old_trashed_regions, first_old, last_old, num_old); if (heap->mode()->is_generational()) { ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); - size_t allocation_runway = - gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_trashed_regions); + size_t allocation_runway = + gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_trashed_regions); gen_heap->compute_old_generation_balance(allocation_runway, old_trashed_regions, young_trashed_regions); } _free_set->finish_rebuild(young_trashed_regions, old_trashed_regions, num_old); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index b302bde8510..c731221adeb 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -638,9 +638,13 @@ void ShenandoahGenerationalHeap::compute_old_generation_balance(size_t mutator_x bound_on_old_reserve: MIN2((young_reserve * ShenandoahOldEvacPercent) / (100 - ShenandoahOldEvacPercent), bound_on_old_reserve)); - if (young_reserve > young_available) { - young_reserve = young_available; + assert(mutator_xfer_limit <= young_available, + "Cannot transfer (%zu) memory that is not available (%zu)", mutator_xfer_limit, young_available); + // Young reserves are to be taken out of the mutator_xfer_limit. + if (young_reserve > mutator_xfer_limit) { + young_reserve = mutator_xfer_limit; } + mutator_xfer_limit -= young_reserve; // Decide how much old space we should reserve for a mixed collection size_t proposed_reserve_for_mixed = 0; From cdd64dbb5da3c6f2e316397cee71838984b323bf Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Thu, 19 Mar 2026 19:12:27 +0000 Subject: [PATCH 036/160] 8379433: Throwing proper exception for invalid encapsulation length in Hybrid Reviewed-by: weijun --- .../classes/sun/security/ssl/Hybrid.java | 4 +- .../ssl/HybridKeyExchange/TestHybrid.java | 67 +++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 test/jdk/sun/security/ssl/HybridKeyExchange/TestHybrid.java diff --git a/src/java.base/share/classes/sun/security/ssl/Hybrid.java b/src/java.base/share/classes/sun/security/ssl/Hybrid.java index e3e2cfa0b23..43634ce2f34 100644 --- a/src/java.base/share/classes/sun/security/ssl/Hybrid.java +++ b/src/java.base/share/classes/sun/security/ssl/Hybrid.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -355,7 +355,7 @@ public class Hybrid { int to, String algorithm) throws DecapsulateException { int expectedEncSize = engineEncapsulationSize(); if (encapsulation.length != expectedEncSize) { - throw new IllegalArgumentException( + throw new DecapsulateException( "Invalid key encapsulation message length: " + encapsulation.length + ", expected = " + expectedEncSize); diff --git a/test/jdk/sun/security/ssl/HybridKeyExchange/TestHybrid.java b/test/jdk/sun/security/ssl/HybridKeyExchange/TestHybrid.java new file mode 100644 index 00000000000..82314c31dcf --- /dev/null +++ b/test/jdk/sun/security/ssl/HybridKeyExchange/TestHybrid.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 8379433 + * @summary Test expected DecapsulateException thrown by Hybrid KEM implementation + * @modules java.base/sun.security.ssl + * @run main/othervm TestHybrid +*/ +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.util.Arrays; +import javax.crypto.DecapsulateException; +import javax.crypto.KEM; + +public class TestHybrid { + + public static void main(String[] args) throws Exception { + + Class clazz = Class.forName("sun.security.ssl.HybridProvider"); + Provider p = (Provider) clazz.getField("PROVIDER").get(null); + + String alg = "X25519MLKEM768"; + KeyPairGenerator kpg = KeyPairGenerator.getInstance(alg, p); + KeyPair kp = kpg.generateKeyPair(); + + KEM kem = KEM.getInstance(alg, p); + KEM.Encapsulator e = kem.newEncapsulator(kp.getPublic()); + KEM.Decapsulator d = kem.newDecapsulator(kp.getPrivate()); + + int secretSize = e.secretSize(); + KEM.Encapsulated enc = e.encapsulate(); + byte[] ciphertext = enc.encapsulation(); + + byte[] badCiphertext = Arrays.copyOf(ciphertext, + ciphertext.length - 1); + try { + d.decapsulate(badCiphertext, 0, secretSize, "Generic"); + throw new RuntimeException( + "Expected DecapsulateException not thrown"); + } catch (DecapsulateException expected) { + System.out.println("Expected DecapsulateException thrown"); + } + } +} From 80fb1f72456dd2ea272cfe24475a561b6fb9a1a1 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 19 Mar 2026 19:44:38 +0000 Subject: [PATCH 037/160] 8379824: Refactor java/io tests to use JUnit Reviewed-by: jlu, alanb --- .../io/BufferedInputStream/TransferTo.java | 18 +- test/jdk/java/io/BufferedReader/Lines.java | 150 ++++++------ .../ByteArrayOutputStream/EncodingTest.java | 33 +-- .../java/io/ByteArrayOutputStream/Write.java | 76 +++---- .../io/CharArrayReader/ReadCharBuffer.java | 41 ++-- .../java/io/DataOutputStream/WriteUTF.java | 18 +- test/jdk/java/io/File/LastModifiedTest.java | 12 +- .../java/io/FileReader/ConstructorTest.java | 33 +-- .../java/io/FileWriter/ConstructorTest.java | 43 ++-- .../java/io/InputStream/NullInputStream.java | 214 ++++++------------ .../io/InputStreamReader/ReadCharBuffer.java | 28 +-- .../StatefulDecoderNearEOF.java | 27 +-- .../io/LineNumberReader/MarkSplitCRLF.java | 14 +- .../ObjectStreamClassCaching.java | 19 +- .../TestOSCClassLoaderLeak.java | 12 +- .../io/OutputStream/NullOutputStream.java | 69 ++---- .../jdk/java/io/PrintStream/EncodingTest.java | 35 +-- .../io/PrintStream/InheritEncodingTest.java | 50 ++-- .../jdk/java/io/PrintWriter/EncodingTest.java | 33 +-- .../io/PushbackInputStream/TransferTo.java | 16 +- test/jdk/java/io/Reader/NullReader.java | 79 ++++--- test/jdk/java/io/Reader/Of.java | 107 +++++---- test/jdk/java/io/Reader/ReadCharBuffer.java | 59 +++-- .../io/Reader/ReadIntoZeroLengthArray.java | 58 ++--- test/jdk/java/io/Reader/Skip.java | 68 +++--- .../io/SequenceInputStream/TransferTo.java | 22 +- test/jdk/java/io/Writer/NullWriter.java | 104 ++++----- 27 files changed, 693 insertions(+), 745 deletions(-) diff --git a/test/jdk/java/io/BufferedInputStream/TransferTo.java b/test/jdk/java/io/BufferedInputStream/TransferTo.java index a0d7b0ee9bd..fe42c0a8b2e 100644 --- a/test/jdk/java/io/BufferedInputStream/TransferTo.java +++ b/test/jdk/java/io/BufferedInputStream/TransferTo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,22 +46,22 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Supplier; -import org.testng.annotations.Test; - import jdk.test.lib.RandomFactory; import static java.lang.String.format; import static java.nio.file.StandardOpenOption.*; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng/othervm/timeout=180 -Xmx1280m TransferTo + * @run junit/othervm/timeout=180 -Xmx1280m TransferTo * @bug 8279283 8294541 * @summary Tests whether java.io.BufferedInputStream.transferTo conforms to the * InputStream.transferTo specification @@ -183,7 +183,7 @@ public class TransferTo { int count = inBytes.length - posIn; int expected = count - bufferBytes; - assertEquals(reported, expected, + assertEquals(expected, reported, format("transferred %d bytes but should report %d", reported, expected)); byte[] outBytes = recorder.get().get(); @@ -198,7 +198,7 @@ public class TransferTo { reported = in.transferTo(out); expected = count - bufferBytes; - assertEquals(reported, expected, + assertEquals(expected, reported, format("replayed %d bytes but should report %d", reported, expected)); outBytes = recorder.get().get(); diff --git a/test/jdk/java/io/BufferedReader/Lines.java b/test/jdk/java/io/BufferedReader/Lines.java index 2e35f75887d..3b4edb80e70 100644 --- a/test/jdk/java/io/BufferedReader/Lines.java +++ b/test/jdk/java/io/BufferedReader/Lines.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /* * @test * @bug 8003258 8029434 - * @run testng Lines + * @run junit Lines */ import java.io.BufferedReader; @@ -41,10 +41,15 @@ import java.util.NoSuchElementException; import java.util.Spliterator; import java.util.stream.Stream; import java.util.concurrent.atomic.AtomicInteger; -import org.testng.annotations.Test; -import static org.testng.Assert.*; -@Test(groups = "unit") +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class Lines { private static final Map cases = new HashMap<>(); @@ -155,75 +160,81 @@ public class Lines { private static void verify(Map.Entry e) { final String data = e.getKey(); final int total_lines = e.getValue(); - try (BufferedReader br = new BufferedReader( - new StringReader(data))) { - assertEquals(br.lines() - .mapToInt(l -> 1).reduce(0, (x, y) -> x + y), - total_lines, - data + " should produce " + total_lines + " lines."); - } catch (IOException ioe) { - fail("Should not have any exception."); - } + assertDoesNotThrow + (() -> { + try (BufferedReader br = + new BufferedReader(new StringReader(data))) { + assertEquals(total_lines, + br.lines().mapToInt(l -> 1).reduce(0, (x, y) -> x + y), + data + " should produce " + total_lines + " lines."); + } + }); } + @Test public void testLinesBasic() { // Basic test cases cases.entrySet().stream().forEach(Lines::verify); // Similar test, also verify MockLineReader is correct - for (int i = 0; i < 10; i++) { - try (BufferedReader br = new BufferedReader(new MockLineReader(i))) { - assertEquals(br.lines() - .peek(l -> assertTrue(l.matches("^Line \\d+$"))) - .mapToInt(l -> 1).reduce(0, (x, y) -> x + y), - i, - "MockLineReader(" + i + ") should produce " + i + " lines."); - } catch (IOException ioe) { - fail("Unexpected IOException."); - } - } + assertDoesNotThrow + (() -> { + for (int i = 0; i < 10; i++) { + try (BufferedReader br = + new BufferedReader(new MockLineReader(i))) { + assertEquals(i, + br.lines() + .peek(l -> assertTrue(l.matches("^Line \\d+$"))) + .mapToInt(l -> 1).reduce(0, (x, y) -> x + y), + "MockLineReader(" + i + ") should produce " + i + " lines."); + } + } + }); } + @Test public void testUncheckedIOException() throws IOException { MockLineReader r = new MockLineReader(10, 3); ArrayList ar = new ArrayList<>(); - try (BufferedReader br = new BufferedReader(r)) { - br.lines().limit(3L).forEach(ar::add); - assertEquals(ar.size(), 3, "Should be able to read 3 lines."); - } catch (UncheckedIOException uioe) { - fail("Unexpected UncheckedIOException"); - } + assertDoesNotThrow + (() -> { + try (BufferedReader br = new BufferedReader(r)) { + br.lines().limit(3L).forEach(ar::add); + assertEquals(3, ar.size(), "Should be able to read 3 lines."); + } + }); r.reset(); - try (BufferedReader br = new BufferedReader(r)) { - br.lines().forEach(ar::add); - fail("Should had thrown UncheckedIOException."); - } catch (UncheckedIOException uioe) { - assertEquals(r.getLineNumber(), 4, "should fail to read 4th line"); - assertEquals(ar.size(), 6, "3 + 3 lines read"); - } + assertThrows(UncheckedIOException.class, + () -> { + try (BufferedReader br = new BufferedReader(r)) { + br.lines().forEach(ar::add); + } + }); + assertEquals(4, r.getLineNumber(), "should fail to read 4th line"); + assertEquals(6, ar.size(), "3 + 3 lines read"); for (int i = 0; i < ar.size(); i++) { - assertEquals(ar.get(i), "Line " + (i % 3 + 1)); + assertEquals("Line " + (i % 3 + 1), ar.get(i)); } } + @Test public void testIterator() throws IOException { MockLineReader r = new MockLineReader(6); BufferedReader br = new BufferedReader(r); String line = br.readLine(); - assertEquals(r.getLineNumber(), 1, "Read one line"); + assertEquals(1, r.getLineNumber(), "Read one line"); Stream s = br.lines(); Iterator it = s.iterator(); // Ensure iterate with only next works for (int i = 0; i < 5; i++) { String str = it.next(); - assertEquals(str, "Line " + (i + 2), "Addtional five lines"); + assertEquals("Line " + (i + 2), str, "Addtional five lines"); } // NoSuchElementException - try { - it.next(); - fail("Should have run out of lines."); - } catch (NoSuchElementException nsse) {} + assertThrows(NoSuchElementException.class, () -> it.next(), + "Should have run out of lines."); } + @Test public void testPartialReadAndLineNo() throws IOException { MockLineReader r = new MockLineReader(5); LineNumberReader lr = new LineNumberReader(r); @@ -231,23 +242,24 @@ public class Lines { lr.read(buf, 0, 5); assertEquals(0, lr.getLineNumber(), "LineNumberReader start with line 0"); assertEquals(1, r.getLineNumber(), "MockLineReader start with line 1"); - assertEquals(new String(buf), "Line "); + assertEquals("Line ", new String(buf)); String l1 = lr.readLine(); - assertEquals(l1, "1", "Remaining of the first line"); + assertEquals("1", l1, "Remaining of the first line"); assertEquals(1, lr.getLineNumber(), "Line 1 is read"); assertEquals(1, r.getLineNumber(), "MockLineReader not yet go next line"); lr.read(buf, 0, 4); assertEquals(1, lr.getLineNumber(), "In the middle of line 2"); - assertEquals(new String(buf, 0, 4), "Line"); + assertEquals("Line", new String(buf, 0, 4)); ArrayList ar = lr.lines() .peek(l -> assertEquals(lr.getLineNumber(), r.getLineNumber())) .collect(ArrayList::new, ArrayList::add, ArrayList::addAll); - assertEquals(ar.get(0), " 2", "Remaining in the second line"); + assertEquals(" 2", ar.get(0), "Remaining in the second line"); for (int i = 1; i < ar.size(); i++) { - assertEquals(ar.get(i), "Line " + (i + 2), "Rest are full lines"); + assertEquals("Line " + (i + 2), ar.get(i), "Rest are full lines"); } } + @Test public void testInterlacedRead() throws IOException { MockLineReader r = new MockLineReader(10); BufferedReader br = new BufferedReader(r); @@ -256,26 +268,24 @@ public class Lines { Iterator it = s.iterator(); br.read(buf); - assertEquals(new String(buf), "Line "); - assertEquals(it.next(), "1"); - try { - s.iterator().next(); - fail("Should failed on second attempt to get iterator from s"); - } catch (IllegalStateException ise) {} + assertEquals("Line ", new String(buf)); + assertEquals("1", it.next()); + assertThrows(IllegalStateException.class, () -> s.iterator().next(), + "Should fail on second call to Iterator next method"); br.read(buf, 0, 2); - assertEquals(new String(buf, 0, 2), "Li"); + assertEquals("Li", new String(buf, 0, 2)); // Get stream again should continue from where left // Only read remaining of the line br.lines().limit(1L).forEach(line -> assertEquals(line, "ne 2")); br.read(buf, 0, 2); - assertEquals(new String(buf, 0, 2), "Li"); + assertEquals("Li", new String(buf, 0, 2)); br.read(buf, 0, 2); - assertEquals(new String(buf, 0, 2), "ne"); - assertEquals(it.next(), " 3"); + assertEquals("ne", new String(buf, 0, 2)); + assertEquals(" 3", it.next()); // Line 4 br.readLine(); // interator pick - assertEquals(it.next(), "Line 5"); + assertEquals("Line 5", it.next()); // Another stream instantiated by lines() AtomicInteger line_no = new AtomicInteger(6); br.lines().forEach(l -> assertEquals(l, "Line " + line_no.getAndIncrement())); @@ -283,14 +293,16 @@ public class Lines { assertFalse(it.hasNext()); } + @Test public void testCharacteristics() { - try (BufferedReader br = new BufferedReader( - new StringReader(""))) { - Spliterator instance = br.lines().spliterator(); - assertTrue(instance.hasCharacteristics(Spliterator.NONNULL)); - assertTrue(instance.hasCharacteristics(Spliterator.ORDERED)); - } catch (IOException ioe) { - fail("Should not have any exception."); - } + assertDoesNotThrow + (() -> { + try (BufferedReader br = + new BufferedReader(new StringReader(""))) { + Spliterator instance = br.lines().spliterator(); + assertTrue(instance.hasCharacteristics(Spliterator.NONNULL)); + assertTrue(instance.hasCharacteristics(Spliterator.ORDERED)); + } + }); } } diff --git a/test/jdk/java/io/ByteArrayOutputStream/EncodingTest.java b/test/jdk/java/io/ByteArrayOutputStream/EncodingTest.java index 1a9d8d4c20b..40bef97294a 100644 --- a/test/jdk/java/io/ByteArrayOutputStream/EncodingTest.java +++ b/test/jdk/java/io/ByteArrayOutputStream/EncodingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,29 +25,31 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @bug 8183743 * @summary Test to verify the new overload method with Charset functions the same * as the existing method that takes a charset name. - * @run testng EncodingTest + * @run junit EncodingTest */ public class EncodingTest { /* - * DataProvider for the toString method test. Provides the following fields: + * MethodSource for the toString method test. Provides the following fields: * byte array, charset name string, charset object */ - @DataProvider(name = "parameters") - public Object[][] getParameters() throws IOException { + public static Stream parameters() throws IOException { byte[] data = getData(); - return new Object[][]{ - {data, StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8}, - {data, StandardCharsets.ISO_8859_1.name(), StandardCharsets.ISO_8859_1}, - }; + return Stream.of + (Arguments.of(data, StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8), + Arguments.of(data, StandardCharsets.ISO_8859_1.name(), StandardCharsets.ISO_8859_1)); } /** @@ -58,20 +60,21 @@ public class EncodingTest { * @param charset the charset * @throws Exception if the test fails */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("parameters") public void test(byte[] data, String csn, Charset charset) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(data); String str1 = baos.toString(csn); String str2 = baos.toString(charset); - Assert.assertEquals(str1, str2); + assertEquals(str2, str1); } /* * Returns an array containing a character that's invalid for UTF-8 * but valid for ISO-8859-1 */ - byte[] getData() throws IOException { + static byte[] getData() throws IOException { String str1 = "A string that contains "; String str2 = " , an invalid character for UTF-8."; diff --git a/test/jdk/java/io/ByteArrayOutputStream/Write.java b/test/jdk/java/io/ByteArrayOutputStream/Write.java index 72c399916e0..99d8a206635 100644 --- a/test/jdk/java/io/ByteArrayOutputStream/Write.java +++ b/test/jdk/java/io/ByteArrayOutputStream/Write.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ * @bug 4017158 8180410 * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng Write + * @run junit Write * @summary Check for correct implementation of ByteArrayInputStream.write * @key randomness */ @@ -35,49 +35,40 @@ import java.io.ByteArrayOutputStream; import java.util.Arrays; import java.util.Random; import jdk.test.lib.RandomFactory; -import org.testng.annotations.Test; -import static org.testng.Assert.*; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; public class Write { private static void doBoundsTest(byte[] b, int off, int len, ByteArrayOutputStream baos) throws Exception { if (b != null) { - System.out.println("ByteArrayOutStream.write: b.length = " + + System.err.println("ByteArrayOutStream.write: b.length = " + b.length + " off = " + off + " len = " + len); } else{ - System.out.println("ByteArrayOutStream.write: b is null off = " + + System.err.println("ByteArrayOutStream.write: b is null off = " + off + " len = " + len); } - try { - baos.write(b, off, len); - } catch (IndexOutOfBoundsException e) { - System.out.println("IndexOutOfBoundsException is thrown: OKAY"); - } catch (NullPointerException e) { - System.out.println("NullPointerException is thrown: OKAY"); - } catch (Throwable e){ - throw new RuntimeException("Unexpected Exception is thrown", e); - } + Class expectedException = (b == null) + ? NullPointerException.class : IndexOutOfBoundsException.class; + assertThrows(expectedException, () -> baos.write(b, off, len)); if (b != null) { - System.out.println("ByteArrayOutStream.writeBytes: b.length = " + + System.err.println("ByteArrayOutStream.writeBytes: b.length = " + b.length); } else{ - System.out.println("ByteArrayOutStream.writeBytes: b is null"); - } - - try { - baos.writeBytes(b); - } catch (NullPointerException e) { - System.out.println("NullPointerException is thrown: OKAY"); - } catch (Throwable e){ - throw new RuntimeException("Unexpected Exception is thrown", e); + System.err.println("ByteArrayOutStream.writeBytes: b is null"); + assertThrows(NullPointerException.class, () -> baos.writeBytes(b)); } } @Test - public static void boundsTest() throws Exception { + public void boundsTest() throws Exception { byte array1[] = {1 , 2 , 3 , 4 , 5}; // Simple array //Create new ByteArrayOutputStream object @@ -91,7 +82,7 @@ public class Write { } @Test - public static void writeTest() throws Exception { + public void writeTest() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); Random rnd = RandomFactory.getRandom(); final int size = 17 + rnd.nextInt(128); @@ -104,39 +95,36 @@ public class Write { int off2 = rnd.nextInt(size / 2) + 1; int len2 = Math.min(rnd.nextInt(size / 2) + 1, size - off2); - System.out.format("size: %d, off1: %d, len1: %d, off2: %d, len2: %d%n", + System.err.format("size: %d, off1: %d, len1: %d, off2: %d, len2: %d%n", size, off1, len1, off2, len2); baos.write(b, off1, len1); byte[] b1 = baos.toByteArray(); - assertEquals(b1.length, len1, "Array length test 1 failed."); - assertEquals(b1, Arrays.copyOfRange(b, off1, off1 + len1), + assertEquals(len1, b1.length, "Array length test 1 failed."); + assertArrayEquals(Arrays.copyOfRange(b, off1, off1 + len1), b1, "Array equality test 1 failed."); baos.write(b, off2, len2); byte[] b2 = baos.toByteArray(); - assertEquals(b2.length, len1 + len2, "Array length test 2 failed."); - assertEquals(Arrays.copyOfRange(b2, 0, len1), - Arrays.copyOfRange(b, off1, off1 + len1), + assertEquals(len1 + len2, b2.length, "Array length test 2 failed."); + assertArrayEquals(Arrays.copyOfRange(b, off1, off1 + len1), + Arrays.copyOfRange(b2, 0, len1), "Array equality test 2A failed."); - assertEquals(Arrays.copyOfRange(b2, len1, len1 + len2), - Arrays.copyOfRange(b, off2, off2 + len2), + assertArrayEquals(Arrays.copyOfRange(b, off2, off2 + len2), + Arrays.copyOfRange(b2, len1, len1 + len2), "Array equality test 2B failed."); baos.writeBytes(b); byte[] b3 = baos.toByteArray(); int len3 = len1 + len2 + b.length; - if (b3.length != len1 + len2 + b.length) { - throw new RuntimeException("Array length test 3 failed."); - } - assertEquals(b3.length, len3, "Array length test 3 failed."); - assertEquals(Arrays.copyOfRange(b3, 0, len1), - Arrays.copyOfRange(b, off1, off1 + len1), + assertEquals(len3, b3.length, "Array length test 3 failed."); + assertArrayEquals(Arrays.copyOfRange(b, off1, off1 + len1), + Arrays.copyOfRange(b3, 0, len1), "Array equality test 3A failed."); - assertEquals(Arrays.copyOfRange(b3, len1, len1 + len2), - Arrays.copyOfRange(b, off2, off2 + len2), + assertArrayEquals(Arrays.copyOfRange(b, off2, off2 + len2), + Arrays.copyOfRange(b3, len1, len1 + len2), "Array equality test 3B failed."); - assertEquals(Arrays.copyOfRange(b3, len1 + len2, len3), b, + assertArrayEquals(b, Arrays.copyOfRange(b3, len1 + len2, len3), "Array equality test 3C failed."); } } diff --git a/test/jdk/java/io/CharArrayReader/ReadCharBuffer.java b/test/jdk/java/io/CharArrayReader/ReadCharBuffer.java index 545ba8ba029..87571a399ac 100644 --- a/test/jdk/java/io/CharArrayReader/ReadCharBuffer.java +++ b/test/jdk/java/io/CharArrayReader/ReadCharBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,13 +25,9 @@ * @test * @bug 4926314 * @summary Test for CharArrayReader#read(CharBuffer). - * @run testng ReadCharBuffer + * @run junit ReadCharBuffer */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - - import java.io.CharArrayReader; import java.io.IOException; import java.io.Reader; @@ -39,43 +35,46 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.Arrays; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class ReadCharBuffer { private static final int BUFFER_SIZE = 7; - @DataProvider(name = "buffers") - public Object[][] createBuffers() { + public static CharBuffer[] buffers() { // test both on-heap and off-heap buffers as they may use different code paths - return new Object[][]{ - new Object[]{CharBuffer.allocate(BUFFER_SIZE)}, - new Object[]{ByteBuffer.allocateDirect(BUFFER_SIZE * 2).asCharBuffer()} + return new CharBuffer[] { + CharBuffer.allocate(BUFFER_SIZE), + ByteBuffer.allocateDirect(BUFFER_SIZE * 2).asCharBuffer() }; } - @Test(dataProvider = "buffers") + @ParameterizedTest + @MethodSource("buffers") public void read(CharBuffer buffer) throws IOException { fillBuffer(buffer); try (Reader reader = new CharArrayReader("ABCD".toCharArray())) { buffer.limit(3); buffer.position(1); - assertEquals(reader.read(buffer), 2); - assertEquals(buffer.position(), 3); - assertEquals(buffer.limit(), 3); + assertEquals(2, reader.read(buffer)); + assertEquals(3, buffer.position()); + assertEquals(3, buffer.limit()); buffer.limit(7); buffer.position(4); - assertEquals(reader.read(buffer), 2); - assertEquals(buffer.position(), 6); - assertEquals(buffer.limit(), 7); + assertEquals(2, reader.read(buffer)); + assertEquals(6, buffer.position()); + assertEquals(7, buffer.limit()); - assertEquals(reader.read(buffer), -1); + assertEquals(-1, reader.read(buffer)); } buffer.clear(); - assertEquals(buffer.toString(), "xABxCDx"); + assertEquals("xABxCDx", buffer.toString()); } private void fillBuffer(CharBuffer buffer) { diff --git a/test/jdk/java/io/DataOutputStream/WriteUTF.java b/test/jdk/java/io/DataOutputStream/WriteUTF.java index d5557ec85ed..ed8b7397971 100644 --- a/test/jdk/java/io/DataOutputStream/WriteUTF.java +++ b/test/jdk/java/io/DataOutputStream/WriteUTF.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ * @bug 4260284 8219196 8223254 * @summary Test if DataOutputStream will overcount written field. * @requires (sun.arch.data.model == "64" & os.maxMemory >= 4g) - * @run testng/othervm -Xmx4g WriteUTF + * @run junit/othervm -Xmx4g WriteUTF */ import java.io.ByteArrayOutputStream; @@ -33,11 +33,13 @@ import java.io.DataOutputStream; import java.io.IOException; import java.io.UTFDataFormatException; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; public class WriteUTF { @Test - public static void overcountWrittenField() throws IOException { + public void overcountWrittenField() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeUTF("Hello, World!"); // 15 @@ -54,16 +56,16 @@ public class WriteUTF { dos.writeUTF(s); } - @Test(expectedExceptions = UTFDataFormatException.class) + @Test public void utfDataFormatException() throws IOException { - writeUTF(1 << 16); + assertThrows(UTFDataFormatException.class, () -> writeUTF(1 << 16)); } // Without 8219196 fix, throws ArrayIndexOutOfBoundsException instead of // expected UTFDataFormatException. Requires 4GB of heap (-Xmx4g) to run // without throwing an OutOfMemoryError. - @Test(expectedExceptions = UTFDataFormatException.class) + @Test public void arrayIndexOutOfBoundsException() throws IOException { - writeUTF(Integer.MAX_VALUE / 3 + 1); + assertThrows(UTFDataFormatException.class, () -> writeUTF(Integer.MAX_VALUE / 3 + 1)); } } diff --git a/test/jdk/java/io/File/LastModifiedTest.java b/test/jdk/java/io/File/LastModifiedTest.java index 52c7d390401..4eaf5fa34c2 100644 --- a/test/jdk/java/io/File/LastModifiedTest.java +++ b/test/jdk/java/io/File/LastModifiedTest.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -21,20 +22,19 @@ * questions. */ -import org.testng.annotations.Test; - import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.time.Instant; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotEquals; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @library .. - * @run testng LastModifiedTest + * @run junit LastModifiedTest * @summary Test to validate that java.nio.Files returns the same value * as java.io.File */ @@ -51,7 +51,7 @@ public class LastModifiedTest { long ioTimestamp = tempFile.lastModified(); long nioTimestamp = Files.getLastModifiedTime(tempFile.toPath()).toMillis(); - assertEquals(ioTimestamp, nioTimestamp); + assertEquals(nioTimestamp, ioTimestamp); } finally { tempFile.delete(); } diff --git a/test/jdk/java/io/FileReader/ConstructorTest.java b/test/jdk/java/io/FileReader/ConstructorTest.java index b082a710079..71cd525917a 100644 --- a/test/jdk/java/io/FileReader/ConstructorTest.java +++ b/test/jdk/java/io/FileReader/ConstructorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,15 +30,19 @@ import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @bug 8183554 * @summary Test to verify the new Constructors that take a Charset. - * @run testng ConstructorTest + * @run junit ConstructorTest */ public class ConstructorTest { static String USER_DIR = System.getProperty("user.dir", "."); @@ -51,17 +55,15 @@ public class ConstructorTest { static final String TEST_STRING = "abc \u0100 \u0101 \u0555 \u07FD \u07FF"; static final int BUFFER_SIZE = 8192; - @DataProvider(name = "parameters") - public Object[][] getParameters() throws IOException { + public static Stream parameters() throws IOException { File file1 = new File(USER_DIR, "FileReaderTest1.txt"); File file2 = new File(USER_DIR, "FileReaderTest2.txt"); - return new Object[][]{ - {ConstructorType.STRING, file1, file2, StandardCharsets.UTF_8}, - {ConstructorType.FILE, file1, file2, StandardCharsets.UTF_8}, - {ConstructorType.STRING, file1, file2, StandardCharsets.ISO_8859_1}, - {ConstructorType.FILE, file1, file2, StandardCharsets.ISO_8859_1}, - }; + return Stream.of + (Arguments.of(ConstructorType.STRING, file1, file2, StandardCharsets.UTF_8), + Arguments.of(ConstructorType.FILE, file1, file2, StandardCharsets.UTF_8), + Arguments.of(ConstructorType.STRING, file1, file2, StandardCharsets.ISO_8859_1), + Arguments.of(ConstructorType.FILE, file1, file2, StandardCharsets.ISO_8859_1)); } /** @@ -75,7 +77,8 @@ public class ConstructorTest { * @param charset the charset * @throws IOException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("parameters") void test(ConstructorType type, File file1, File file2, Charset charset) throws Exception { prepareFile(file1, TEST_STRING, charset); @@ -86,7 +89,7 @@ public class ConstructorTest { InputStreamReader isr = new InputStreamReader(is, charset);) { String result1 = readAll(fr, BUFFER_SIZE); String result2 = readAll(isr, BUFFER_SIZE); - Assert.assertEquals(result1, result2); + assertEquals(result2, result1); } } diff --git a/test/jdk/java/io/FileWriter/ConstructorTest.java b/test/jdk/java/io/FileWriter/ConstructorTest.java index 691b52504f9..64a43395f51 100644 --- a/test/jdk/java/io/FileWriter/ConstructorTest.java +++ b/test/jdk/java/io/FileWriter/ConstructorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,15 +31,21 @@ import java.io.OutputStreamWriter; import java.io.Reader; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @bug 8183554 * @summary Test to verify the new Constructors that take a Charset. - * @run testng ConstructorTest + * @run junit ConstructorTest */ public class ConstructorTest { static String USER_DIR = System.getProperty("user.dir", "."); @@ -54,21 +60,19 @@ public class ConstructorTest { static final String TEST_STRING = "abc \u0100 \u0101 \u0555 \u07FD \u07FF"; static final int BUFFER_SIZE = 8192; - @DataProvider(name = "parameters") - public Object[][] getParameters() throws IOException { + public static Stream parameters() throws IOException { File file1 = new File(USER_DIR, "FileWriterTest1.txt"); File file2 = new File(USER_DIR, "FileWriterTest2.txt"); - return new Object[][]{ - {ConstructorType.STRING, file1, file2, StandardCharsets.UTF_8}, - {ConstructorType.FILE, file1, file2, StandardCharsets.UTF_8}, - {ConstructorType.STRING_APPEND, file1, file2, StandardCharsets.UTF_8}, - {ConstructorType.FILE_APPEND, file1, file2, StandardCharsets.UTF_8}, - {ConstructorType.STRING, file1, file2, StandardCharsets.ISO_8859_1}, - {ConstructorType.FILE, file1, file2, StandardCharsets.ISO_8859_1}, - {ConstructorType.STRING_APPEND, file1, file2, StandardCharsets.ISO_8859_1}, - {ConstructorType.FILE_APPEND, file1, file2, StandardCharsets.ISO_8859_1}, - }; + return Stream.of + (Arguments.of(ConstructorType.STRING, file1, file2, StandardCharsets.UTF_8), + Arguments.of(ConstructorType.FILE, file1, file2, StandardCharsets.UTF_8), + Arguments.of(ConstructorType.STRING_APPEND, file1, file2, StandardCharsets.UTF_8), + Arguments.of(ConstructorType.FILE_APPEND, file1, file2, StandardCharsets.UTF_8), + Arguments.of(ConstructorType.STRING, file1, file2, StandardCharsets.ISO_8859_1), + Arguments.of(ConstructorType.FILE, file1, file2, StandardCharsets.ISO_8859_1), + Arguments.of(ConstructorType.STRING_APPEND, file1, file2, StandardCharsets.ISO_8859_1), + Arguments.of(ConstructorType.FILE_APPEND, file1, file2, StandardCharsets.ISO_8859_1)); } /** @@ -82,7 +86,8 @@ public class ConstructorTest { * @param charset the charset * @throws IOException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("parameters") void test(ConstructorType type, File file1, File file2, Charset charset) throws Exception { writeWithFileWriter(type, file1, TEST_STRING, charset); @@ -94,7 +99,7 @@ public class ConstructorTest { ) { String result1 = readAll(r1, BUFFER_SIZE); String result2 = readAll(r2, BUFFER_SIZE); - Assert.assertEquals(result1, result2); + assertEquals(result1, result2); } } diff --git a/test/jdk/java/io/InputStream/NullInputStream.java b/test/jdk/java/io/InputStream/NullInputStream.java index 74317822404..d911edab380 100644 --- a/test/jdk/java/io/InputStream/NullInputStream.java +++ b/test/jdk/java/io/InputStream/NullInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,232 +21,148 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; -import static org.testng.Assert.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 4358774 6516099 8139206 - * @run testng NullInputStream + * @run junit NullInputStream * @summary Check for expected behavior of InputStream.nullInputStream(). */ public class NullInputStream { private static InputStream openStream; private static InputStream closedStream; - @BeforeClass + @BeforeAll public static void setup() { openStream = InputStream.nullInputStream(); closedStream = InputStream.nullInputStream(); - try { - closedStream.close(); - } catch (IOException e) { - fail("Unexpected IOException"); - } + assertDoesNotThrow(() -> closedStream.close()); } - @AfterClass + @AfterAll public static void closeStream() { - try { - openStream.close(); - } catch (IOException e) { - fail("Unexpected IOException"); - } + assertDoesNotThrow(() -> openStream.close()); } @Test - public static void testOpen() { + public void testOpen() { assertNotNull(openStream, "InputStream.nullInputStream() returned null"); } @Test - public static void testAvailable() { - try { - assertEquals(0, openStream.available(), "available() != 0"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testAvailable() throws IOException { + assertEquals(0, openStream.available()); } @Test - public static void testRead() { - try { - assertEquals(-1, openStream.read(), "read() != -1"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testRead() throws IOException { + assertEquals(-1, openStream.read()); } @Test - public static void testReadBII() { - try { - assertEquals(-1, openStream.read(new byte[1], 0, 1), - "read(byte[],int,int) != -1"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testReadBII() throws IOException { + assertEquals(-1, openStream.read(new byte[1], 0, 1)); } @Test - public static void testReadAllBytes() { - try { - assertEquals(0, openStream.readAllBytes().length, - "readAllBytes().length != 0"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testReadAllBytes() throws IOException { + assertEquals(0, openStream.readAllBytes().length); } @Test - public static void testReadNBytes() { - try { - assertEquals(0, openStream.readNBytes(new byte[1], 0, 1), - "readNBytes(byte[],int,int) != 0"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testReadNBytes() throws IOException { + assertEquals(0, openStream.readNBytes(new byte[1], 0, 1)); } @Test - public static void testReadNBytesWithLength() { - try { - assertEquals(0, openStream.readNBytes(-1).length, - "readNBytes(-1) != 0"); - fail("Expected IllegalArgumentException not thrown"); - } catch (IllegalArgumentException iae) { - } catch (IOException ioe) { - fail("Unexpected IOException"); - } - try { - assertEquals(0, openStream.readNBytes(0).length, - "readNBytes(0, false) != 0"); - assertEquals(0, openStream.readNBytes(1).length, - "readNBytes(1, false) != 0"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testReadNBytesWithLength() throws IOException { + assertThrows(IllegalArgumentException.class, + () -> openStream.readNBytes(-1)); + assertEquals(0, openStream.readNBytes(0).length); + assertEquals(0, openStream.readNBytes(1).length); } @Test - public static void testSkip() { - try { - assertEquals(0, openStream.skip(1), "skip() != 0"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testSkip() throws IOException { + assertEquals(0L, openStream.skip(1)); } @Test - public static void testSkipNBytes() { - try { - openStream.skipNBytes(-1); - openStream.skipNBytes(0); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } - } - - @Test(expectedExceptions = EOFException.class) - public static void testSkipNBytesEOF() throws IOException { - openStream.skipNBytes(1); + public void testSkipNBytes() { + assertDoesNotThrow(() -> { + openStream.skipNBytes(-1); + openStream.skipNBytes(0); + }); } @Test - public static void testTransferTo() { - try { - assertEquals(0, openStream.transferTo(new ByteArrayOutputStream(7)), - "transferTo() != 0"); - } catch (IOException ioe) { - fail("Unexpected IOException"); - } + public void testSkipNBytesEOF() throws IOException { + assertThrows(EOFException.class, () -> openStream.skipNBytes(1)); } @Test - public static void testAvailableClosed() { - try { - closedStream.available(); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testTransferTo() throws IOException { + assertEquals(0L, openStream.transferTo(new ByteArrayOutputStream(7))); } @Test - public static void testReadClosed() { - try { - closedStream.read(); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testAvailableClosed() { + assertThrows(IOException.class, () -> closedStream.available()); } @Test - public static void testReadBIIClosed() { - try { - closedStream.read(new byte[1], 0, 1); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testReadClosed() { + assertThrows(IOException.class, () -> closedStream.read()); } @Test - public static void testReadAllBytesClosed() { - try { - closedStream.readAllBytes(); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testReadBIIClosed() { + assertThrows(IOException.class, + () -> closedStream.read(new byte[1], 0, 1)); } @Test - public static void testReadNBytesClosed() { - try { - closedStream.readNBytes(new byte[1], 0, 1); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testReadAllBytesClosed() { + assertThrows(IOException.class, () -> closedStream.readAllBytes()); } @Test - public static void testReadNBytesWithLengthClosed() { - try { - closedStream.readNBytes(1); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testReadNBytesClosed() { + assertThrows(IOException.class, () -> + closedStream.readNBytes(new byte[1], 0, 1)); } @Test - public static void testSkipClosed() { - try { - closedStream.skip(1); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testReadNBytesWithLengthClosed() { + assertThrows(IOException.class, () -> closedStream.readNBytes(1)); } @Test - public static void testSkipNBytesClosed() { - try { - closedStream.skipNBytes(1); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testSkipClosed() { + assertThrows(IOException.class, () -> closedStream.skip(1)); } @Test - public static void testTransferToClosed() { - try { - closedStream.transferTo(new ByteArrayOutputStream(7)); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testSkipNBytesClosed() { + assertThrows(IOException.class, () -> closedStream.skipNBytes(1)); + } + + @Test + public void testTransferToClosed() { + assertThrows(IOException.class, + () -> closedStream.transferTo(new ByteArrayOutputStream(7))); } } diff --git a/test/jdk/java/io/InputStreamReader/ReadCharBuffer.java b/test/jdk/java/io/InputStreamReader/ReadCharBuffer.java index 8a4094c512a..6df3feb756f 100644 --- a/test/jdk/java/io/InputStreamReader/ReadCharBuffer.java +++ b/test/jdk/java/io/InputStreamReader/ReadCharBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,13 +25,9 @@ * @test * @bug 4926314 8287003 * @summary Test for InputStreamReader#read(CharBuffer). - * @run testng ReadCharBuffer + * @run junit ReadCharBuffer */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; @@ -40,22 +36,25 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CodingErrorAction; import java.util.Arrays; +import java.util.stream.Stream; import static java.nio.charset.StandardCharsets.US_ASCII; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertEquals; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class ReadCharBuffer { private static final int BUFFER_SIZE = 24; - @DataProvider(name = "buffers") - public Object[][] createBuffers() { + public static Stream buffers() { // test both on-heap and off-heap buffers as they make use different code paths - return new Object[][]{ - new Object[]{CharBuffer.allocate(BUFFER_SIZE)}, - new Object[]{ByteBuffer.allocateDirect(BUFFER_SIZE * 2).asCharBuffer()} - }; + return Stream.of(CharBuffer.allocate(BUFFER_SIZE), + ByteBuffer.allocateDirect(BUFFER_SIZE * 2).asCharBuffer()); } private void fillBuffer(CharBuffer buffer) { @@ -65,7 +64,8 @@ public class ReadCharBuffer { buffer.clear(); } - @Test(dataProvider = "buffers") + @ParameterizedTest + @MethodSource("buffers") public void read(CharBuffer buffer) throws IOException { fillBuffer(buffer); diff --git a/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java b/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java index 95759df5522..f456d176e98 100644 --- a/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java +++ b/test/jdk/java/io/InputStreamReader/StatefulDecoderNearEOF.java @@ -23,7 +23,7 @@ /* @test * @bug 8292043 - * @run testng StatefulDecoderNearEOF + * @run junit StatefulDecoderNearEOF * @summary Check MalformedInputException is thrown with stateful decoders * with malformed input before EOF */ @@ -36,29 +36,30 @@ import java.nio.charset.CodingErrorAction; import java.nio.charset.MalformedInputException; import java.nio.charset.StandardCharsets; import java.util.stream.IntStream; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -@Test public class StatefulDecoderNearEOF { - @DataProvider - public Object[][] inputs() { - return new Object[][] { + public static Stream inputs() { + return Stream.of( // BOM, followed by High surrogate (in UTF-16LE). // First read() should throw an exception. - {new byte[] {(byte)0xff, (byte)0xfe, 0, (byte)0xd8}, 0}, + Arguments.of(new byte[] {(byte)0xff, (byte)0xfe, 0, (byte)0xd8}, 0), // BOM, followed by 'A', 'B', 'C', then by High surrogate (in UTF-16LE). // Fourth read() should throw an exception. - {new byte[] {(byte)0xff, (byte)0xfe, (byte)0x41, 0, (byte)0x42, 0, (byte)0x43, 0, 0, (byte)0xd8}, 3}, - }; + Arguments.of(new byte[] {(byte)0xff, (byte)0xfe, (byte)0x41, 0, (byte)0x42, 0, (byte)0x43, 0, 0, (byte)0xd8}, 3)); } - @Test (dataProvider = "inputs") + @ParameterizedTest + @MethodSource("inputs") public void testStatefulDecoderNearEOF(byte[] ba, int numSucessReads) throws IOException { try (var r = new InputStreamReader( new ByteArrayInputStream(ba), diff --git a/test/jdk/java/io/LineNumberReader/MarkSplitCRLF.java b/test/jdk/java/io/LineNumberReader/MarkSplitCRLF.java index d735e78f684..187bef037c5 100644 --- a/test/jdk/java/io/LineNumberReader/MarkSplitCRLF.java +++ b/test/jdk/java/io/LineNumberReader/MarkSplitCRLF.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /* @test * @bug 8218280 * @summary Make sure marking a line feed within a CRLF sequence works correctly - * @run testng MarkSplitCRLF + * @run junit MarkSplitCRLF */ import java.io.IOException; @@ -32,13 +32,13 @@ import java.io.LineNumberReader; import java.io.Reader; import java.io.StringReader; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; public class MarkSplitCRLF { @Test - public static void testSpecifiedBufferSize() throws IOException { + public void testSpecifiedBufferSize() throws IOException { final String string = "foo\r\nbar"; try (Reader reader = new LineNumberReader(new StringReader(string), 5)) { @@ -56,7 +56,7 @@ public class MarkSplitCRLF { } @Test - public static void testCRNotFollowedByLF() throws IOException { + public void testCRNotFollowedByLF() throws IOException { final String string = "foo\rbar"; try (Reader reader = new LineNumberReader(new StringReader(string), 5)) { @@ -74,7 +74,7 @@ public class MarkSplitCRLF { } @Test - public static void testDefaultBufferSize() throws IOException { + public void testDefaultBufferSize() throws IOException { StringBuilder sb = new StringBuilder(8195); for (int i = 0; i < 8190; i++) { char c = (char)i; diff --git a/test/jdk/java/io/ObjectStreamClass/ObjectStreamClassCaching.java b/test/jdk/java/io/ObjectStreamClass/ObjectStreamClassCaching.java index 4004cbcf859..b1765b04060 100644 --- a/test/jdk/java/io/ObjectStreamClass/ObjectStreamClassCaching.java +++ b/test/jdk/java/io/ObjectStreamClass/ObjectStreamClassCaching.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,9 +26,10 @@ import java.lang.ref.WeakReference; import java.io.ObjectStreamClass; import java.io.Serializable; import java.util.ArrayList; -import org.testng.annotations.Test; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test id=G1 @@ -36,7 +37,7 @@ import static org.testng.Assert.assertTrue; * @bug 8277072 * @library /test/lib/ * @summary ObjectStreamClass caches keep ClassLoaders alive (G1 GC) - * @run testng/othervm -Xmx64m -XX:+UseG1GC ObjectStreamClassCaching + * @run junit/othervm -Xmx64m -XX:+UseG1GC ObjectStreamClassCaching */ /* @@ -45,7 +46,7 @@ import static org.testng.Assert.assertTrue; * @bug 8277072 * @library /test/lib/ * @summary ObjectStreamClass caches keep ClassLoaders alive (Parallel GC) - * @run testng/othervm -Xmx64m -XX:+UseParallelGC ObjectStreamClassCaching + * @run junit/othervm -Xmx64m -XX:+UseParallelGC ObjectStreamClassCaching */ /* @@ -54,7 +55,7 @@ import static org.testng.Assert.assertTrue; * @bug 8277072 8327180 * @library /test/lib/ * @summary ObjectStreamClass caches keep ClassLoaders alive (ZGC) - * @run testng/othervm -Xmx64m -XX:+UseZGC ObjectStreamClassCaching + * @run junit/othervm -Xmx64m -XX:+UseZGC ObjectStreamClassCaching */ /* @@ -63,7 +64,7 @@ import static org.testng.Assert.assertTrue; * @bug 8277072 * @library /test/lib/ * @summary ObjectStreamClass caches keep ClassLoaders alive (Shenandoah GC) - * @run testng/othervm -Xmx64m -XX:+UseShenandoahGC ObjectStreamClassCaching + * @run junit/othervm -Xmx64m -XX:+UseShenandoahGC ObjectStreamClassCaching */ /* @@ -72,7 +73,7 @@ import static org.testng.Assert.assertTrue; * @bug 8277072 8327180 * @library /test/lib/ * @summary ObjectStreamClass caches keep ClassLoaders alive (Serial GC) - * @run testng/othervm -Xmx64m -XX:+UseSerialGC ObjectStreamClassCaching + * @run junit/othervm -Xmx64m -XX:+UseSerialGC ObjectStreamClassCaching */ public class ObjectStreamClassCaching { diff --git a/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java b/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java index 1816aa45fe7..e7b944f6574 100644 --- a/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java +++ b/test/jdk/java/io/ObjectStreamClass/TestOSCClassLoaderLeak.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,18 +29,20 @@ import java.io.ObjectStreamClass; import java.io.ObjectStreamField; import java.io.Serializable; import java.util.Arrays; -import org.testng.annotations.Test; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertFalse; import jdk.test.lib.util.ForceGC; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + /* @test * @bug 8277072 * @library /test/lib/ * @build jdk.test.lib.util.ForceGC * @summary ObjectStreamClass caches keep ClassLoaders alive - * @run testng TestOSCClassLoaderLeak + * @run junit TestOSCClassLoaderLeak */ public class TestOSCClassLoaderLeak { diff --git a/test/jdk/java/io/OutputStream/NullOutputStream.java b/test/jdk/java/io/OutputStream/NullOutputStream.java index e0041e424d8..d0f76596db3 100644 --- a/test/jdk/java/io/OutputStream/NullOutputStream.java +++ b/test/jdk/java/io/OutputStream/NullOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,85 +21,64 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.io.IOException; import java.io.OutputStream; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.fail; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 4358774 - * @run testng NullOutputStream + * @run junit NullOutputStream * @summary Check for expected behavior of OutputStream.nullOutputStream(). */ public class NullOutputStream { private static OutputStream openStream; private static OutputStream closedStream; - @BeforeClass + @BeforeAll public static void setup() { openStream = OutputStream.nullOutputStream(); closedStream = OutputStream.nullOutputStream(); - try { - closedStream.close(); - } catch (IOException e) { - fail("Unexpected IOException"); - } + assertDoesNotThrow(() -> closedStream.close()); } - @AfterClass + @AfterAll public static void closeStream() { - try { - openStream.close(); - } catch (IOException e) { - fail("Unexpected IOException"); - } + assertDoesNotThrow(() -> openStream.close()); } @Test - public static void testOpen() { + public void testOpen() { assertNotNull(openStream, "OutputStream.nullOutputStream() returned null"); } @Test - public static void testWrite() { - try { - openStream.write(62832); - } catch (IOException e) { - fail("Unexpected IOException"); - } + public void testWrite() throws IOException { + openStream.write(62832); } @Test - public static void testWriteBII() { - try { - openStream.write(new byte[] {(byte)6}, 0, 1); - } catch (Exception e) { - fail("Unexpected IOException"); - } + public void testWriteBII() { + assertDoesNotThrow(() -> openStream.write(new byte[] {(byte)6}, 0, 1)); } @Test - public static void testWriteClosed() { - try { - closedStream.write(62832); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testWriteClosed() { + assertThrows(IOException.class, () -> closedStream.write(62832)); } @Test - public static void testWriteBIIClosed() { - try { - closedStream.write(new byte[] {(byte)6}, 0, 1); - fail("Expected IOException not thrown"); - } catch (IOException e) { - } + public void testWriteBIIClosed() { + assertThrows(IOException.class, + () -> closedStream.write(new byte[] {(byte)6}, 0, 1)); } } diff --git a/test/jdk/java/io/PrintStream/EncodingTest.java b/test/jdk/java/io/PrintStream/EncodingTest.java index d767b5801d3..a0fe27b81cb 100644 --- a/test/jdk/java/io/PrintStream/EncodingTest.java +++ b/test/jdk/java/io/PrintStream/EncodingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,16 +29,20 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @bug 8183743 * @summary Test to verify the new overload method with Charset functions the same * as the existing method that takes a charset name. - * @run testng EncodingTest + * @run junit EncodingTest */ public class EncodingTest { static String USER_DIR = System.getProperty("user.dir", "."); @@ -50,22 +54,20 @@ public class EncodingTest { } /* - * DataProvider fields: + * MethodSource fields: * Type of the constructor, a file to be written with a charset name, * a file to be written with a charset, charset name, charset. */ - @DataProvider(name = "parameters") - public Object[][] getParameters() throws IOException { + public static Stream parameters() throws IOException { String csn = StandardCharsets.UTF_8.name(); Charset charset = StandardCharsets.UTF_8; File file1 = new File(USER_DIR, "PSCharsetTest1.txt"); File file2 = new File(USER_DIR, "PSCharsetTest2.txt"); - return new Object[][]{ - {ConstructorType.STRING, file1, file2, csn, charset}, - {ConstructorType.FILE, file1, file2, csn, charset}, - {ConstructorType.OUTPUTSTREAM, file1, file2, csn, charset} - }; + return Stream.of + (Arguments.of(ConstructorType.STRING, file1, file2, csn, charset), + Arguments.of(ConstructorType.FILE, file1, file2, csn, charset), + Arguments.of(ConstructorType.OUTPUTSTREAM, file1, file2, csn, charset)); } /** @@ -78,14 +80,15 @@ public class EncodingTest { * @param charset the charset * @throws IOException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("parameters") public void test(ConstructorType type, File file1, File file2, String csn, Charset charset) throws Exception { createFile(getPrintStream(type, file1.getPath(), csn, null)); createFile(getPrintStream(type, file2.getPath(), null, charset)); - Assert.assertEquals(Files.readAllLines(Paths.get(file1.getPath()), charset), - Files.readAllLines(Paths.get(file2.getPath()), charset)); + assertEquals(Files.readAllLines(Paths.get(file2.getPath()), charset), + Files.readAllLines(Paths.get(file1.getPath()), charset)); } public void createFile(PrintStream out) throws IOException { diff --git a/test/jdk/java/io/PrintStream/InheritEncodingTest.java b/test/jdk/java/io/PrintStream/InheritEncodingTest.java index e31404114a0..094a1d4e0be 100644 --- a/test/jdk/java/io/PrintStream/InheritEncodingTest.java +++ b/test/jdk/java/io/PrintStream/InheritEncodingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,10 +21,6 @@ * questions. */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; @@ -33,31 +29,35 @@ import java.io.PrintWriter; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; + /** * @test * @bug 8276970 * @summary Test to verify the charset in PrintStream is inherited * in the OutputStreamWriter/PrintWriter - * @run testng InheritEncodingTest + * @run junit InheritEncodingTest */ -@Test public class InheritEncodingTest { private static final String testString = "\u00e9\u3042"; // "รฉใ‚" - @DataProvider - public Object[][] encodings() { - return new Object[][]{ - {StandardCharsets.ISO_8859_1}, - {StandardCharsets.US_ASCII}, - {StandardCharsets.UTF_8}, - {StandardCharsets.UTF_16}, - {StandardCharsets.UTF_16BE}, - {StandardCharsets.UTF_16LE}, + public static Charset[] encodings() { + return new Charset[]{ + StandardCharsets.ISO_8859_1, + StandardCharsets.US_ASCII, + StandardCharsets.UTF_8, + StandardCharsets.UTF_16, + StandardCharsets.UTF_16BE, + StandardCharsets.UTF_16LE }; } - @Test (dataProvider = "encodings") + @ParameterizedTest + @MethodSource("encodings") public void testOutputStreamWriter(Charset stdCharset) throws IOException { var ba = new ByteArrayOutputStream(); var ps = new PrintStream(ba, true, stdCharset); @@ -65,16 +65,17 @@ public class InheritEncodingTest { // tests OutputStreamWriter's encoding explicitly var osw = new OutputStreamWriter(ps); - assertEquals(Charset.forName(osw.getEncoding()), stdCharset); + assertEquals(stdCharset, Charset.forName(osw.getEncoding())); // tests roundtrip result osw.write(testString); osw.flush(); var result = ba.toString(stdCharset); - assertEquals(result, expected); + assertEquals(expected, result); } - @Test (dataProvider = "encodings") + @ParameterizedTest + @MethodSource("encodings") public void testPrintWriter(Charset stdCharset) throws IOException { var ba = new ByteArrayOutputStream(); var ps = new PrintStream(ba, true, stdCharset); @@ -85,10 +86,11 @@ public class InheritEncodingTest { pw.write(testString); pw.flush(); var result = ba.toString(stdCharset); - assertEquals(result, expected); + assertEquals(expected, result); } - @Test (dataProvider = "encodings") + @ParameterizedTest + @MethodSource("encodings") public void testPrintStream(Charset stdCharset) throws IOException { var ba = new ByteArrayOutputStream(); var ps = new PrintStream(ba, true, stdCharset); @@ -96,12 +98,12 @@ public class InheritEncodingTest { // tests PrintStream's charset explicitly var psWrapper = new PrintStream(ps); - assertEquals(psWrapper.charset(), stdCharset); + assertEquals(stdCharset, psWrapper.charset()); // tests roundtrip result psWrapper.print(testString); psWrapper.flush(); var result = ba.toString(stdCharset); - assertEquals(result, expected); + assertEquals(expected, result); } } diff --git a/test/jdk/java/io/PrintWriter/EncodingTest.java b/test/jdk/java/io/PrintWriter/EncodingTest.java index f02944d9900..ef6e8b0094b 100644 --- a/test/jdk/java/io/PrintWriter/EncodingTest.java +++ b/test/jdk/java/io/PrintWriter/EncodingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,16 +29,20 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @test * @bug 8183743 * @summary Test to verify the new overload method with Charset functions the same * as the existing method that takes a charset name. - * @run testng EncodingTest + * @run junit EncodingTest */ public class EncodingTest { static String USER_DIR = System.getProperty("user.dir", "."); @@ -54,18 +58,16 @@ public class EncodingTest { * Type of the constructor, a file to be written with a charset name, * a file to be written with a charset, charset name, charset. */ - @DataProvider(name = "parameters") - public Object[][] getParameters() throws IOException { + public static Stream parameters() throws IOException { String csn = StandardCharsets.UTF_8.name(); Charset charset = StandardCharsets.UTF_8; File file1 = new File(USER_DIR, "PWCharsetTest1.txt"); File file2 = new File(USER_DIR, "PWCharsetTest2.txt"); - return new Object[][]{ - {ConstructorType.STRING, file1, file2, csn, charset}, - {ConstructorType.FILE, file1, file2, csn, charset}, - {ConstructorType.OUTPUTSTREAM, file1, file2, csn, charset} - }; + return Stream.of + (Arguments.of(ConstructorType.STRING, file1, file2, csn, charset), + Arguments.of(ConstructorType.FILE, file1, file2, csn, charset), + Arguments.of(ConstructorType.OUTPUTSTREAM, file1, file2, csn, charset)); } /** @@ -79,14 +81,15 @@ public class EncodingTest { * @param charset the charset * @throws IOException */ - @Test(dataProvider = "parameters") + @ParameterizedTest + @MethodSource("parameters") public void test(ConstructorType type, File file1, File file2, String csn, Charset charset) throws Exception { createFile(getWriter(type, file1.getPath(), csn, null)); createFile(getWriter(type, file2.getPath(), null, charset)); - Assert.assertEquals(Files.readAllLines(Paths.get(file1.getPath()), charset), - Files.readAllLines(Paths.get(file2.getPath()), charset)); + assertEquals(Files.readAllLines(Paths.get(file2.getPath()), charset), + Files.readAllLines(Paths.get(file1.getPath()), charset)); } void createFile(PrintWriter out) throws IOException { diff --git a/test/jdk/java/io/PushbackInputStream/TransferTo.java b/test/jdk/java/io/PushbackInputStream/TransferTo.java index 0a27d88bc70..5d601748003 100644 --- a/test/jdk/java/io/PushbackInputStream/TransferTo.java +++ b/test/jdk/java/io/PushbackInputStream/TransferTo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,21 +32,21 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Supplier; -import org.testng.annotations.Test; +import static java.lang.String.format; import jdk.test.lib.RandomFactory; -import static java.lang.String.format; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng/othervm/timeout=180 TransferTo + * @run junit/othervm/timeout=180 TransferTo * @bug 8296431 * @summary Tests whether java.io.PushbackInputStream.transferTo conforms to the * InputStream.transferTo specification @@ -162,7 +162,7 @@ public class TransferTo { long reported = in.transferTo(out); int count = inBytes.length - posIn; - assertEquals(reported, count, + assertEquals(count, reported, format("reported %d bytes but should report %d", reported, count)); byte[] outBytes = recorder.get().get(); diff --git a/test/jdk/java/io/Reader/NullReader.java b/test/jdk/java/io/Reader/NullReader.java index 9c16f33df1c..b80c1a9f67a 100644 --- a/test/jdk/java/io/Reader/NullReader.java +++ b/test/jdk/java/io/Reader/NullReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,118 +27,125 @@ import java.io.StringWriter; import java.nio.CharBuffer; import java.nio.ReadOnlyBufferException; -import org.testng.annotations.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 8196298 8204930 - * @run testng NullReader + * @run junit NullReader * @summary Check for expected behavior of Reader.nullReader(). */ public class NullReader { private static Reader openReader; private static Reader closedReader; - @BeforeClass + @BeforeAll public static void setup() throws IOException { openReader = Reader.nullReader(); closedReader = Reader.nullReader(); closedReader.close(); } - @AfterClass + @AfterAll public static void closeStream() throws IOException { openReader.close(); } @Test - public static void testOpen() { + public void testOpen() { assertNotNull(openReader, "Reader.nullReader() returned null"); } @Test - public static void testRead() throws IOException { + public void testRead() throws IOException { assertEquals(-1, openReader.read(), "read() != -1"); } @Test - public static void testReadBII() throws IOException { + public void testReadBII() throws IOException { assertEquals(-1, openReader.read(new char[1], 0, 1), "read(char[],int,int) != -1"); } @Test - public static void testReadBIILenZero() throws IOException { + public void testReadBIILenZero() throws IOException { assertEquals(0, openReader.read(new char[1], 0, 0), "read(char[],int,int) != 0"); } @Test - public static void testReadCharBuffer() throws IOException { + public void testReadCharBuffer() throws IOException { CharBuffer charBuffer = CharBuffer.allocate(1); assertEquals(-1, openReader.read(charBuffer), "read(CharBuffer) != -1"); } @Test - public static void testReadCharBufferZeroRemaining() throws IOException { + public void testReadCharBufferZeroRemaining() throws IOException { CharBuffer charBuffer = CharBuffer.allocate(0); assertEquals(0, openReader.read(charBuffer), "read(CharBuffer) != 0"); } @Test - public static void testReady() throws IOException { + public void testReady() throws IOException { assertFalse(openReader.ready()); } @Test - public static void testSkip() throws IOException { + public void testSkip() throws IOException { assertEquals(0, openReader.skip(1), "skip() != 0"); } @Test - public static void testTransferTo() throws IOException { + public void testTransferTo() throws IOException { assertEquals(0, openReader.transferTo(new StringWriter(7)), "transferTo() != 0"); } - @Test(expectedExceptions = IOException.class) - public static void testReadClosed() throws IOException { - closedReader.read(); + @Test + public void testReadClosed() throws IOException { + assertThrows(IOException.class, () -> closedReader.read()); } - @Test(expectedExceptions = IOException.class) - public static void testReadBIIClosed() throws IOException { - closedReader.read(new char[1], 0, 1); + @Test + public void testReadBIIClosed() throws IOException { + assertThrows(IOException.class, + () -> closedReader.read(new char[1], 0, 1)); } - @Test(expectedExceptions = IOException.class) - public static void testReadCharBufferClosed() throws IOException { + @Test + public void testReadCharBufferClosed() throws IOException { CharBuffer charBuffer = CharBuffer.allocate(0); - closedReader.read(charBuffer); + assertThrows(IOException.class, () -> closedReader.read(charBuffer)); } - @Test(expectedExceptions = IOException.class) - public static void testReadCharBufferZeroRemainingClosed() throws IOException { + @Test + public void testReadCharBufferZeroRemainingClosed() throws IOException { CharBuffer charBuffer = CharBuffer.allocate(0); - closedReader.read(charBuffer); + assertThrows(IOException.class, () -> closedReader.read(charBuffer)); } - @Test(expectedExceptions = IOException.class) - public static void testReadyClosed() throws IOException { - closedReader.ready(); + @Test + public void testReadyClosed() throws IOException { + assertThrows(IOException.class, () -> closedReader.ready()); } - @Test(expectedExceptions = IOException.class) - public static void testSkipClosed() throws IOException { - closedReader.skip(1); + @Test + public void testSkipClosed() throws IOException { + assertThrows(IOException.class, () -> closedReader.skip(1)); } - @Test(expectedExceptions = IOException.class) - public static void testTransferToClosed() throws IOException { - closedReader.transferTo(new StringWriter(7)); + @Test + public void testTransferToClosed() throws IOException { + assertThrows(IOException.class, + () -> closedReader.transferTo(new StringWriter(7))); } } diff --git a/test/jdk/java/io/Reader/Of.java b/test/jdk/java/io/Reader/Of.java index 491c0499e6b..7d3e039b920 100644 --- a/test/jdk/java/io/Reader/Of.java +++ b/test/jdk/java/io/Reader/Of.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,15 +29,18 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.ReadOnlyBufferException; -import org.testng.annotations.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @bug 8341566 * @summary Check for expected behavior of Reader.of(). - * @run testng Of + * @run junit Of */ public class Of { final static String CONTENT = "Some Reader Test"; @@ -45,7 +48,6 @@ public class Of { /* * Readers to be tested. */ - @DataProvider public static Reader[] readers() { return new Reader[] { new StringReader(CONTENT), @@ -81,135 +83,152 @@ public class Of { }; } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testRead(Reader reader) throws IOException { String s = ""; for (int c; (c = reader.read()) != -1; s += (char) c); - assertEquals(s, CONTENT, "read() returned wrong value"); + assertEquals(CONTENT, s, "read() returned wrong value"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadBII(Reader reader) throws IOException { char[] c = new char[16]; - assertEquals(reader.read(c, 8, 8), 8, + assertEquals(8, reader.read(c, 8, 8), "read(char[],int,int) does not respect given start or end"); - assertEquals(reader.read(c, 0, 16), 8, + assertEquals(8, reader.read(c, 0, 16), "read(char[],int,int) does not respect end of stream"); - assertEquals(new String(c), - CONTENT.substring(8, 16) + CONTENT.substring(0, 8), + assertEquals(CONTENT.substring(8, 16) + CONTENT.substring(0, 8), + new String(c), "read(char[],int,int) provides wrong content"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadBIILenZero(Reader reader) throws IOException { - assertEquals(reader.read(new char[1], 0, 0), 0, + assertEquals(0, reader.read(new char[1], 0, 0), "read(char[],int,int) != 0"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadDirectCharBuffer(Reader reader) throws IOException { CharBuffer charBuffer = ByteBuffer.allocateDirect(32).asCharBuffer(); charBuffer.position(8); - assertEquals(reader.read(charBuffer), 8, + assertEquals(8, reader.read(charBuffer), "read(CharBuffer) does not respect position or limit"); charBuffer.rewind(); - assertEquals(reader.read(charBuffer), 8, + assertEquals(8, reader.read(charBuffer), "read(CharBuffer) does not respect end of stream"); charBuffer.rewind(); - assertEquals(charBuffer.toString(), - // last part first proofs that copy loops correctly stopped - CONTENT.substring(8, 16) + CONTENT.substring(0, 8), + // last part first proves that copy loops correctly stopped + assertEquals(CONTENT.substring(8, 16) + CONTENT.substring(0, 8), + charBuffer.toString(), "read(CharBuffer) provides wrong content"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadNonDirectCharBuffer(Reader reader) throws IOException { CharBuffer charBuffer = CharBuffer.allocate(16); charBuffer.position(8); - assertEquals(reader.read(charBuffer), 8, + assertEquals(8, reader.read(charBuffer), "read(CharBuffer) does not respect position or limit"); charBuffer.rewind(); - assertEquals(reader.read(charBuffer), 8, + assertEquals(8, reader.read(charBuffer), "read(CharBuffer) does not respect end of stream"); charBuffer.rewind(); - assertEquals(charBuffer.toString(), - CONTENT.substring(8, 16) + CONTENT.substring(0, 8), + assertEquals(CONTENT.substring(8, 16) + CONTENT.substring(0, 8), + charBuffer.toString(), "read(CharBuffer) provides wrong content"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadCharBufferZeroRemaining(Reader reader) throws IOException { CharBuffer charBuffer = CharBuffer.allocate(0); - assertEquals(reader.read(charBuffer), 0, "read(CharBuffer) != 0"); + assertEquals(0, reader.read(charBuffer), "read(CharBuffer) != 0"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReady(Reader reader) throws IOException { assertTrue(reader.ready()); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testSkip(Reader reader) throws IOException { - assertEquals(reader.skip(8), 8, "skip() does not respect limit"); - assertEquals(reader.skip(9), 8, "skip() does not respect end of stream"); - assertEquals(reader.skip(1), 0, "skip() does not respect empty stream"); + assertEquals(8, reader.skip(8), "skip() does not respect limit"); + assertEquals(8, reader.skip(9), "skip() does not respect end of stream"); + assertEquals(0, reader.skip(1), "skip() does not respect empty stream"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testTransferTo(Reader reader) throws IOException { StringWriter sw = new StringWriter(16); - assertEquals(reader.transferTo(sw), 16, "transferTo() != 16"); - assertEquals(reader.transferTo(sw), 0, + assertEquals(16, reader.transferTo(sw), "transferTo() != 16"); + assertEquals(0, reader.transferTo(sw), "transferTo() does not respect empty stream"); - assertEquals(sw.toString(), CONTENT, + assertEquals(CONTENT, sw.toString(), "transferTo() provides wrong content"); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadClosed(Reader reader) throws IOException { reader.close(); assertThrows(IOException.class, () -> {reader.read();}); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadBIIClosed(Reader reader) throws IOException { reader.close(); assertThrows(IOException.class, () -> reader.read(new char[1], 0, 1)); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadCharBufferClosed(Reader reader) throws IOException { CharBuffer charBuffer = CharBuffer.allocate(1); reader.close(); assertThrows(IOException.class, () -> reader.read(charBuffer)); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadCharBufferZeroRemainingClosed(Reader reader) throws IOException { CharBuffer charBuffer = CharBuffer.allocate(0); reader.close(); assertThrows(IOException.class, () -> reader.read(charBuffer)); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testReadyClosed(Reader reader) throws IOException { reader.close(); assertThrows(IOException.class, () -> reader.ready()); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testSkipClosed(Reader reader) throws IOException { reader.close(); assertThrows(IOException.class, () -> reader.skip(1)); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testTransferToClosed(Reader reader) throws IOException { reader.close(); assertThrows(IOException.class, () -> reader.transferTo(new StringWriter(1))); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void testCloseClosed(Reader reader) throws IOException { reader.close(); reader.close(); diff --git a/test/jdk/java/io/Reader/ReadCharBuffer.java b/test/jdk/java/io/Reader/ReadCharBuffer.java index c57860601fb..dc858999287 100644 --- a/test/jdk/java/io/Reader/ReadCharBuffer.java +++ b/test/jdk/java/io/Reader/ReadCharBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,40 +25,38 @@ * @test * @bug 4926314 8266014 * @summary Test for Reader#read(CharBuffer). - * @run testng ReadCharBuffer + * @run junit ReadCharBuffer */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - - -import java.io.IOException; import java.io.BufferedReader; import java.io.CharArrayReader; +import java.io.IOException; import java.io.Reader; import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.Arrays; import java.util.Objects; +import java.util.stream.Stream; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; -@Test(groups = "unit") public class ReadCharBuffer { private static final int BUFFER_SIZE = 8 + 8192 + 2; - @DataProvider(name = "buffers") - public Object[][] createBuffers() { + public static Stream buffers() { // test both on-heap and off-heap buffers as they make use different code paths - return new Object[][]{ - new Object[]{CharBuffer.allocate(BUFFER_SIZE)}, - new Object[]{ByteBuffer.allocateDirect(BUFFER_SIZE * 2).asCharBuffer()} - }; + return Stream.of(CharBuffer.allocate(BUFFER_SIZE), + ByteBuffer.allocateDirect(BUFFER_SIZE * 2).asCharBuffer()); } - @Test(dataProvider = "buffers") + @ParameterizedTest + @MethodSource("buffers") public void read(CharBuffer buffer) throws IOException { fillBuffer(buffer); @@ -74,21 +72,21 @@ public class ReadCharBuffer { int limit = 1 + 6; buffer.limit(limit); buffer.position(1); - assertEquals(reader.read(buffer), 6); - assertEquals(buffer.position(), limit); - assertEquals(buffer.limit(), limit); + assertEquals(6, reader.read(buffer)); + assertEquals(limit, buffer.position()); + assertEquals(limit, buffer.limit()); // read the full temporary buffer // and then accurately reduce the next #read call limit = 8 + 8192 + 1; buffer.limit(8 + 8192 + 1); buffer.position(8); - assertEquals(reader.read(buffer), 8192 + 1); - assertEquals(buffer.position(), limit); - assertEquals(buffer.limit(), limit); + assertEquals(8192 + 1, reader.read(buffer)); + assertEquals(limit, buffer.position()); + assertEquals(limit, buffer.limit()); - assertEquals(reader.read(), 'H'); - assertEquals(reader.read(), -1); + assertEquals('H', reader.read()); + assertEquals(-1, reader.read()); } buffer.clear(); @@ -98,20 +96,15 @@ public class ReadCharBuffer { expected.append('y'); } expected.append("Gx"); - assertEquals(buffer.toString(), expected.toString()); + assertEquals(expected.toString(), buffer.toString()); } @Test - public void readZeroLength() { + public void readZeroLength() throws IOException { char[] buf = new char[] {1, 2, 3}; BufferedReader r = new BufferedReader(new CharArrayReader(buf)); - int n = -1; - try { - n = r.read(CharBuffer.allocate(0)); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - assertEquals(n, 0); + int n = r.read(CharBuffer.allocate(0)); + assertEquals(0, n); } private void fillBuffer(CharBuffer buffer) { diff --git a/test/jdk/java/io/Reader/ReadIntoZeroLengthArray.java b/test/jdk/java/io/Reader/ReadIntoZeroLengthArray.java index 480cd32c981..e69a24d38f4 100644 --- a/test/jdk/java/io/Reader/ReadIntoZeroLengthArray.java +++ b/test/jdk/java/io/Reader/ReadIntoZeroLengthArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,55 +29,57 @@ import java.io.LineNumberReader; import java.io.PushbackReader; import java.io.Reader; import java.io.StringReader; -import org.testng.Assert; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; /* * @test * @bug 8248383 * @summary Ensure that zero is returned for read into zero length array - * @run testng ReadIntoZeroLengthArray + * @run junit ReadIntoZeroLengthArray */ public class ReadIntoZeroLengthArray { - private File file; + private static File file; - private char[] cbuf0; - private char[] cbuf1; + private static char[] cbuf0; + private static char[] cbuf1; - @BeforeTest - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { file = File.createTempFile("foo", "bar", new File(".")); + cbuf0 = new char[0]; cbuf1 = new char[1]; } - @AfterTest - public void teardown() throws IOException { + @AfterAll + public static void teardown() throws IOException { file.delete(); } - @DataProvider(name = "readers") - public Object[][] getReaders() throws IOException { - Reader fileReader = new FileReader(file); - return new Object[][] { - {new LineNumberReader(fileReader)}, - {new CharArrayReader(new char[] {27})}, - {new PushbackReader(fileReader)}, - {fileReader}, - {new StringReader(new String(new byte[] {(byte)42}))} - }; + public static Stream readers() throws IOException { + return Stream.of(new LineNumberReader(new FileReader(file)), + new CharArrayReader(new char[] {27}), + new PushbackReader(new FileReader(file)), + new FileReader(file), + new StringReader(new String(new byte[] {(byte)42}))); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") void test0(Reader r) throws IOException { - Assert.assertEquals(r.read(cbuf0), 0); + assertEquals(0, r.read(cbuf0)); } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") void test1(Reader r) throws IOException { - Assert.assertEquals(r.read(cbuf1, 0, 0), 0); + assertEquals(0, r.read(cbuf1, 0, 0)); } } diff --git a/test/jdk/java/io/Reader/Skip.java b/test/jdk/java/io/Reader/Skip.java index 9e311686507..80d260d4276 100644 --- a/test/jdk/java/io/Reader/Skip.java +++ b/test/jdk/java/io/Reader/Skip.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /* @test * @bug 4134311 8247918 * @summary Test if skip works correctly - * @run testng Skip + * @run junit Skip */ import java.io.CharArrayReader; @@ -36,10 +36,16 @@ import java.io.PushbackReader; import java.io.RandomAccessFile; import java.io.Reader; import java.io.StringReader; +import java.util.stream.Stream; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; public class Skip { private static String FILENAME = @@ -52,52 +58,52 @@ public class Skip { long nchars = 8200; long actual = fr.skip(nchars); - Assert.assertFalse(actual > nchars, + assertFalse(actual > nchars, "Should skip " + nchars + ", but skipped " +actual+" chars"); } } - @DataProvider(name = "readers") - public Object[][] getReaders() throws IOException { - return new Object[][] { - {new LineNumberReader(new FileReader(file))}, - {new CharArrayReader(new char[] {27})}, - {new PushbackReader(new FileReader(file))}, - {new FileReader(file)}, - {new StringReader(new String(new byte[] {(byte)42}))} + public static Reader[] readers() throws IOException { + return new Reader[] { + new LineNumberReader(new FileReader(file)), + new CharArrayReader(new char[] {27}), + new PushbackReader(new FileReader(file)), + new FileReader(file), + new StringReader(new String(new byte[] {(byte)42})) }; } - @Test(dataProvider = "readers") + @ParameterizedTest + @MethodSource("readers") public void eof(Reader r) throws IOException { r.skip(Long.MAX_VALUE); - Assert.assertEquals(r.skip(1), 0); - Assert.assertEquals(r.read(), -1); + assertEquals(0, r.skip(1)); + assertEquals(-1, r.read()); } - @DataProvider(name = "skipIAE") - public Object[][] getSkipIAEs() throws IOException { - return new Object[][] { - {new LineNumberReader(new FileReader(file))}, - {new PushbackReader(new FileReader(file))}, - {new FileReader(file)} + public static Reader[] skipIAE() throws IOException { + return new Reader[] { + new LineNumberReader(new FileReader(file)), + new PushbackReader(new FileReader(file)), + new FileReader(file) }; } - @Test(dataProvider = "skipIAE", expectedExceptions = IllegalArgumentException.class) + @ParameterizedTest + @MethodSource("skipIAE") public void testThrowsIAE(Reader r) throws IOException { - r.skip(-1); + assertThrows(IllegalArgumentException.class, () -> r.skip(-1)); } - @DataProvider(name = "skipNoIAE") - public Object[][] getSkipNoIAEs() throws IOException { - return new Object[][] { - {new CharArrayReader(new char[] {27})}, - {new StringReader(new String(new byte[] {(byte)42}))} + public static Reader[] skipNoIAE() throws IOException { + return new Reader[] { + new CharArrayReader(new char[] {27}), + new StringReader(new String(new byte[] {(byte)42})) }; } - @Test(dataProvider = "skipNoIAE") + @ParameterizedTest + @MethodSource("skipNoIAE") public void testNoIAE(Reader r) throws IOException { r.skip(-1); } diff --git a/test/jdk/java/io/SequenceInputStream/TransferTo.java b/test/jdk/java/io/SequenceInputStream/TransferTo.java index 4c8ff71a4f3..8ea9a77672e 100644 --- a/test/jdk/java/io/SequenceInputStream/TransferTo.java +++ b/test/jdk/java/io/SequenceInputStream/TransferTo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,22 +33,22 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Supplier; -import org.testng.annotations.Test; +import static java.lang.String.format; import jdk.test.lib.RandomFactory; -import static java.lang.String.format; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @library /test/lib * @build jdk.test.lib.RandomFactory - * @run testng/othervm/timeout=180 TransferTo + * @run junit/othervm/timeout=180 TransferTo * @bug 8297298 * @summary Tests whether java.io.SequenceInputStream.transferTo conforms to the * InputStream.transferTo specification @@ -141,8 +141,8 @@ public class TransferTo { SequenceInputStream sis = new SequenceInputStream(is1, is2); OutputStream nos = OutputStream.nullOutputStream(); sis.transferTo(nos); - assertEquals(is1.available(), 0); - assertEquals(is2.available(), 0); + assertEquals(0, is1.available()); + assertEquals(0, is2.available()); } /* @@ -200,7 +200,7 @@ public class TransferTo { long reported = in.transferTo(out); int count = inBytes.length - posIn; - assertEquals(reported, count, + assertEquals(count, reported, format("reported %d bytes but should report %d", reported, count)); byte[] outBytes = recorder.get().get(); diff --git a/test/jdk/java/io/Writer/NullWriter.java b/test/jdk/java/io/Writer/NullWriter.java index d632947f129..e37c8bd54dd 100644 --- a/test/jdk/java/io/Writer/NullWriter.java +++ b/test/jdk/java/io/Writer/NullWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,144 +21,146 @@ * questions. */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - import java.io.IOException; import java.io.Writer; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertSame; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test * @bug 8196298 - * @run testng NullWriter + * @run junit NullWriter * @summary Check for expected behavior of Writer.nullWriter(). */ public class NullWriter { private static Writer openWriter; private static Writer closedWriter; - @BeforeClass + @BeforeAll public static void setup() throws IOException { openWriter = Writer.nullWriter(); closedWriter = Writer.nullWriter(); closedWriter.close(); } - @AfterClass + @AfterAll public static void closeStream() throws IOException { openWriter.close(); } @Test - public static void testOpen() { + public void testOpen() { assertNotNull(openWriter, "Writer.nullWriter() returned null"); } @Test - public static void testAppendChar() throws IOException { + public void testAppendChar() throws IOException { assertSame(openWriter, openWriter.append('x')); } @Test - public static void testAppendCharSequence() throws IOException { + public void testAppendCharSequence() throws IOException { CharSequence cs = "abc"; assertSame(openWriter, openWriter.append(cs)); } @Test - public static void testAppendCharSequenceNull() throws IOException { + public void testAppendCharSequenceNull() throws IOException { assertSame(openWriter, openWriter.append(null)); } @Test - public static void testAppendCharSequenceII() throws IOException { + public void testAppendCharSequenceII() throws IOException { CharSequence cs = "abc"; assertSame(openWriter, openWriter.append(cs, 0, 1)); } @Test - public static void testAppendCharSequenceIINull() throws IOException { + public void testAppendCharSequenceIINull() throws IOException { assertSame(openWriter, openWriter.append(null, 2, 1)); } @Test - public static void testFlush() throws IOException { + public void testFlush() throws IOException { openWriter.flush(); } @Test - public static void testWrite() throws IOException { + public void testWrite() throws IOException { openWriter.write(62832); } @Test - public static void testWriteString() throws IOException { + public void testWriteString() throws IOException { openWriter.write(""); } @Test - public static void testWriteStringII() throws IOException { + public void testWriteStringII() throws IOException { openWriter.write("", 0, 0); } @Test - public static void testWriteBII() throws IOException, Exception { + public void testWriteBII() throws IOException, Exception { openWriter.write(new char[]{(char) 6}, 0, 1); } - @Test(expectedExceptions = IOException.class) - public static void testAppendCharClosed() throws IOException { - closedWriter.append('x'); + @Test + public void testAppendCharClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.append('x')); } - @Test(expectedExceptions = IOException.class) - public static void testAppendCharSequenceClosed() throws IOException { + @Test + public void testAppendCharSequenceClosed() throws IOException { CharSequence cs = "abc"; - closedWriter.append(cs); + assertThrows(IOException.class, () -> closedWriter.append(cs)); } - @Test(expectedExceptions = IOException.class) - public static void testAppendCharSequenceNullClosed() throws IOException { - closedWriter.append(null); + @Test + public void testAppendCharSequenceNullClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.append(null)); } - @Test(expectedExceptions = IOException.class) - public static void testAppendCharSequenceIIClosed() throws IOException { + @Test + public void testAppendCharSequenceIIClosed() throws IOException { CharSequence cs = "abc"; - closedWriter.append(cs, 0, 1); + assertThrows(IOException.class, () -> closedWriter.append(cs, 0, 1)); } - @Test(expectedExceptions = IOException.class) - public static void testAppendCharSequenceIINullClosed() throws IOException { - closedWriter.append(null, 2, 1); + @Test + public void testAppendCharSequenceIINullClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.append(null, 2, 1)); } - @Test(expectedExceptions = IOException.class) - public static void testFlushClosed() throws IOException { - closedWriter.flush(); + @Test + public void testFlushClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.flush()); } - @Test(expectedExceptions = IOException.class) - public static void testWriteClosed() throws IOException { - closedWriter.write(62832); + @Test + public void testWriteClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.write(62832)); } - @Test(expectedExceptions = IOException.class) - public static void testWriteStringClosed() throws IOException { - closedWriter.write(""); + @Test + public void testWriteStringClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.write("")); } - @Test(expectedExceptions = IOException.class) - public static void testWriteStringIIClosed() throws IOException { - closedWriter.write("", 0, 0); + @Test + public void testWriteStringIIClosed() throws IOException { + assertThrows(IOException.class, () -> closedWriter.write("", 0, 0)); } - @Test(expectedExceptions = IOException.class) - public static void testWriteBIIClosed() throws IOException { - closedWriter.write(new char[]{(char) 6}, 0, 1); + @Test + public void testWriteBIIClosed() throws IOException { + assertThrows(IOException.class, + () -> closedWriter.write(new char[]{(char) 6}, 0, 1)); } } From 615aba8257460edd08dc1d825d9394d98cef8e35 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Thu, 19 Mar 2026 20:30:18 +0000 Subject: [PATCH 038/160] 8374304: MultiResolutionSplashTest.java fails in CI: "Image with wrong resolution is used for splash screen!" Reviewed-by: dmarkov, prr --- .../MultiResolutionSplashTest.java | 185 +++++++++--------- 1 file changed, 89 insertions(+), 96 deletions(-) diff --git a/test/jdk/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java b/test/jdk/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java index 1f2de081cce..c0a9c90feba 100644 --- a/test/jdk/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java +++ b/test/jdk/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * 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,30 +22,25 @@ */ import java.awt.Color; -import java.awt.Dialog; import java.awt.Frame; import java.awt.Graphics; -import java.awt.Graphics2D; import java.awt.GraphicsEnvironment; -import java.awt.Panel; import java.awt.Rectangle; import java.awt.Robot; import java.awt.SplashScreen; import java.awt.TextField; -import java.awt.Window; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.File; +import java.io.IOException; + import javax.imageio.ImageIO; -import sun.java2d.SunGraphics2D; - -/** +/* * @test * @key headful * @bug 8043869 8075244 8078082 8145173 8151787 8212213 * @summary Tests the HiDPI splash screen support for windows and MAC - * @modules java.desktop/sun.java2d * @run main MultiResolutionSplashTest GENERATE_IMAGES * @run main/othervm -splash:splash1.png MultiResolutionSplashTest TEST_SPLASH 0 * @run main/othervm -splash:splash2 MultiResolutionSplashTest TEST_SPLASH 1 @@ -56,47 +51,54 @@ public class MultiResolutionSplashTest { private static final int IMAGE_WIDTH = 300; private static final int IMAGE_HEIGHT = 200; - private static boolean isMac; - static { - isMac = System.getProperty("os.name").contains("OS X"); - } + private static final boolean isMac = System.getProperty("os.name") + .contains("OS X"); + private static final ImageInfo[] tests = { - new ImageInfo("splash1.png", "splash1@2x.png", Color.BLUE, Color.GREEN), - new ImageInfo("splash2", "splash2@2x", Color.WHITE, Color.BLACK), - new ImageInfo("splash3.", "splash3@2x.", Color.YELLOW, Color.RED) + new ImageInfo("splash1", ".png", Color.BLUE, Color.GREEN), + new ImageInfo("splash2", "", Color.WHITE, Color.BLACK), + new ImageInfo("splash3", ".", Color.YELLOW, Color.RED) }; public static void main(String[] args) throws Exception { - - String test = args[0]; - switch (test) { + switch (args[0]) { case "GENERATE_IMAGES": generateImages(); break; case "TEST_SPLASH": - int index = Integer.parseInt(args[1]); - testSplash(tests[index]); + testSplash(tests[Integer.parseInt(args[1])]); break; case "TEST_FOCUS": testFocus(); break; default: - throw new RuntimeException("Unknown test: " + test); + throw new RuntimeException("Unknown test: " + args[0]); } } static void testSplash(ImageInfo test) throws Exception { SplashScreen splashScreen = SplashScreen.getSplashScreen(); - if (splashScreen == null) { throw new RuntimeException("Splash screen is not shown!"); } - Graphics2D g = splashScreen.createGraphics(); - Rectangle splashBounds = splashScreen.getBounds(); - int screenX = (int) splashBounds.getCenterX(); - int screenY = (int) splashBounds.getCenterY(); + final Rectangle splashBounds = splashScreen.getBounds(); + final double scaleFactor = getScreenScaleFactor(); + + final Robot robot = new Robot(); + // Allow time for the splash screen to show + robot.delay(100); + + BufferedImage splashCapture = robot.createScreenCapture(splashBounds); + String captureFileName = "splashscreen-%1.2f-%s.png" + .formatted(scaleFactor, test.name1x); + saveImageNoError(splashCapture, new File(captureFileName)); + + // Close the splash screen; this gives time for it to be fully removed + splashScreen.close(); + robot.waitForIdle(); + if (splashBounds.width != IMAGE_WIDTH) { throw new RuntimeException( "SplashScreen#getBounds has wrong width"); @@ -106,19 +108,19 @@ public class MultiResolutionSplashTest { "SplashScreen#getBounds has wrong height"); } - Robot robot = new Robot(); - Color splashScreenColor = robot.getPixelColor(screenX, screenY); - float scaleFactor = getScaleFactor(); + Color splashScreenColor = + new Color(splashCapture.getRGB(splashBounds.width / 2, + splashBounds.height / 2)); Color testColor = (1 < scaleFactor) ? test.color2x : test.color1x; if (!compare(testColor, splashScreenColor)) { throw new RuntimeException( - "Image with wrong resolution is used for splash screen!"); + "Image with wrong resolution is used for splash screen! " + + "Refer to " + captureFileName); } } static void testFocus() throws Exception { - Robot robot = new Robot(); robot.setAutoWaitForIdle(true); robot.setAutoDelay(50); @@ -157,94 +159,85 @@ public class MultiResolutionSplashTest { return Math.abs(n - m) <= 50; } - static float getScaleFactor() { - - final Dialog dialog = new Dialog((Window) null); - dialog.setSize(100, 100); - dialog.setModal(true); - final float[] scaleFactors = new float[1]; - Panel panel = new Panel() { - - @Override - public void paint(Graphics g) { - float scaleFactor = 1; - if (g instanceof SunGraphics2D) { - scaleFactor = getScreenScaleFactor(); - } - scaleFactors[0] = scaleFactor; - dialog.setVisible(false); - } - }; - - dialog.add(panel); - dialog.setVisible(true); - dialog.dispose(); - - return scaleFactors[0]; - } - static void generateImages() throws Exception { for (ImageInfo test : tests) { - generateImage(test.name1x, test.color1x, 1); - generateImage(test.name2x, test.color2x, getScreenScaleFactor()); + generateImage(test.name1x, test.color1x, 1.0); + + // Ensure the second image uses scale greater than 1.0 + double scale = getAdjustedScaleFactor(); + generateImage(test.name2x, test.color2x, scale); } } - static void generateImage(String name, Color color, float scale) throws Exception { + static void generateImage(final String name, + final Color color, + final double scale) throws Exception { File file = new File(name); if (file.exists()) { return; } - BufferedImage image = new BufferedImage((int) (scale * IMAGE_WIDTH), - (int) (scale * IMAGE_HEIGHT), BufferedImage.TYPE_INT_RGB); + + final int width = (int) (scale * IMAGE_WIDTH); + final int height = (int) (scale * IMAGE_HEIGHT); + BufferedImage image = new BufferedImage(width, + height, + BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); g.setColor(color); - g.fillRect(0, 0, (int) (scale * IMAGE_WIDTH), (int) (scale * IMAGE_HEIGHT)); + g.fillRect(0, 0, width, height); + + saveImage(image, file); + } + + private static void saveImage(BufferedImage image, + File file) throws IOException { ImageIO.write(image, "png", file); } - static float getScreenScaleFactor() { - return (float) GraphicsEnvironment. - getLocalGraphicsEnvironment(). - getDefaultScreenDevice().getDefaultConfiguration(). - getDefaultTransform().getScaleX(); + private static void saveImageNoError(BufferedImage image, + File file) { + try { + saveImage(image, file); + } catch (IOException ignored) { + } + } + + static double getScreenScaleFactor() { + return GraphicsEnvironment + .getLocalGraphicsEnvironment() + .getDefaultScreenDevice() + .getDefaultConfiguration() + .getDefaultTransform() + .getScaleX(); + } + + // Ensure the second image uses scale greater than 1.0 + static double getAdjustedScaleFactor() { + double scale = getScreenScaleFactor(); + return scale < 1.25 ? 2.0 : scale; } static class ImageInfo { - final String name1x; final String name2x; final Color color1x; final Color color2x; - public ImageInfo(String name1x, String name2x, Color color1x, Color color2x) { - this.name1x = name1x; - if (!isMac) { - float scale = getScreenScaleFactor(); - StringBuffer buff = new StringBuffer(); - if (scale - (int) scale > 0) { - buff.append("@").append((int) (scale * 100)).append("pct"); - } else { - buff.append("@").append((int) scale).append("x"); - } - StringBuffer buffer = new StringBuffer(); - String[] splitStr = name1x.split("\\."); - if (splitStr.length == 2) { - this.name2x = buffer.append(splitStr[0]).append(buff) - .append(".").append(splitStr[1]).toString(); - } else { - if (name1x.indexOf(".") > 0) { - this.name2x = buffer.append(splitStr[0]).append(buff).append(".").toString(); - } else { - this.name2x = buffer.append(splitStr[0]).append(buff).toString(); - } - } - } else { - this.name2x = name2x; - } + public ImageInfo(String baseName, String ext, + Color color1x, Color color2x) { + this.name1x = baseName + ext; + this.name2x = createName2x(baseName, ext); this.color1x = color1x; this.color2x = color2x; } + + private static String createName2x(String baseName, String ext) { + double scale = getAdjustedScaleFactor(); + if (!isMac && (((int) (scale * 100)) % 100 != 0)) { + return baseName + "@" + ((int) (scale * 100)) + "pct" + ext; + } else { + return baseName + "@" + ((int) scale) + "x" + ext; + } + } } } - From 96f6ffbff49e39d6efd5bddd8f8b0a55ea696aa7 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Thu, 19 Mar 2026 21:59:55 +0000 Subject: [PATCH 039/160] 8278591: Jpackage post installation information message Reviewed-by: almatvee, erikj --- make/modules/jdk.jpackage/Java.gmk | 4 +- .../jpackage/internal/cli/StandardOption.java | 2 + .../resources/HelpResources.properties | 4 +- src/jdk.jpackage/share/man/jpackage.md | 6 +- .../jdk/jpackage/internal/MsiMutator.java | 56 ++ .../jdk/jpackage/internal/WinFromOptions.java | 2 + .../internal/WinMsiPackageBuilder.java | 9 +- .../jdk/jpackage/internal/WinMsiPackager.java | 37 +- .../jpackage/internal/WixFragmentBuilder.java | 14 +- .../jdk/jpackage/internal/WixPipeline.java | 179 +++--- .../internal/WixUiFragmentBuilder.java | 520 ++++++------------ .../jdk/jpackage/internal/WixVariables.java | 97 +++- .../internal/model/WinMsiPackageMixin.java | 18 +- .../internal/resources/msi-disable-actions.js | 79 +++ .../jdk/jpackage/internal/wixui/Control.java | 36 ++ .../jpackage/internal/wixui/CustomDialog.java | 38 ++ .../jdk/jpackage/internal/wixui/Dialog.java | 36 ++ .../jpackage/internal/wixui/DialogPair.java | 49 ++ .../jdk/jpackage/internal/wixui/Publish.java | 92 ++++ .../internal/wixui/ShowActionSuppresser.java | 72 +++ .../internal/wixui/StandardControl.java | 47 ++ .../jdk/jpackage/internal/wixui/UIConfig.java | 79 +++ .../jdk/jpackage/internal/wixui/UISpec.java | 293 ++++++++++ .../jpackage/internal/wixui/WixDialog.java | 43 ++ .../jdk/jpackage/internal/wixui/WixUI.java | 46 ++ .../jdk/jpackage/test/MsiDatabase.java | 135 ++++- .../jdk/jpackage/test/WindowsHelper.java | 9 + .../jpackage/internal/cli/help-windows.txt | 2 + .../jpackage/internal/cli/jpackage-options.md | 1 + .../jpackage/internal/WixVariablesTest.java | 269 +++++++++ .../jpackage/internal/wixui/UISpecTest.java | 85 +++ .../tools/jpackage/junit/windows/junit.java | 19 +- .../ControlEvents.md | 8 + .../InstallUISequence.md | 3 + .../dir_chooser+license/ControlEvents.md | 4 + .../dir_chooser+license/InstallUISequence.md | 3 + .../ControlEvents.md | 10 + .../InstallUISequence.md | 3 + .../dir_chooser/ControlEvents.md | 6 + .../dir_chooser/InstallUISequence.md | 3 + .../license+shortcut_prompt/ControlEvents.md | 7 + .../InstallUISequence.md | 3 + .../license/ControlEvents.md | 2 + .../license/InstallUISequence.md | 4 + .../shortcut_prompt/ControlEvents.md | 7 + .../shortcut_prompt/InstallUISequence.md | 3 + .../WinInstallerUiTest/ui/ControlEvents.md | 2 + .../ui/InstallUISequence.md | 4 + .../tools/jpackage/resources/msi-export.js | 4 +- .../jpackage/windows/WinInstallerUiTest.java | 332 ++++++++--- .../tools/jpackage/windows/WinL10nTest.java | 31 +- 51 files changed, 2219 insertions(+), 598 deletions(-) create mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/MsiMutator.java create mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/msi-disable-actions.js create mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Control.java create mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/CustomDialog.java create mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Dialog.java create mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/DialogPair.java create mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Publish.java create mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/ShowActionSuppresser.java create mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/StandardControl.java create mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UIConfig.java create mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UISpec.java create mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixDialog.java create mode 100644 src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixUI.java create mode 100644 test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/WixVariablesTest.java create mode 100644 test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/wixui/UISpecTest.java create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/ControlEvents.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/InstallUISequence.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/ControlEvents.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/InstallUISequence.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/ControlEvents.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/InstallUISequence.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/ControlEvents.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/InstallUISequence.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/ControlEvents.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/InstallUISequence.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/ControlEvents.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/InstallUISequence.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/ControlEvents.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/InstallUISequence.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/ControlEvents.md create mode 100644 test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/InstallUISequence.md diff --git a/make/modules/jdk.jpackage/Java.gmk b/make/modules/jdk.jpackage/Java.gmk index da66fc14009..1fd4d527217 100644 --- a/make/modules/jdk.jpackage/Java.gmk +++ b/make/modules/jdk.jpackage/Java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,7 @@ DISABLED_WARNINGS_java += dangling-doc-comments COPY += .gif .png .txt .spec .script .prerm .preinst \ .postrm .postinst .list .sh .desktop .copyright .control .plist .template \ - .icns .scpt .wxs .wxl .wxi .wxf .ico .bmp .tiff .service .xsl + .icns .scpt .wxs .wxl .wxi .wxf .ico .bmp .tiff .service .xsl .js CLEAN += .properties diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java index f554fc12ec6..c2338b87fad 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java @@ -400,6 +400,8 @@ public final class StandardOption { public static final OptionValue WIN_INSTALLDIR_CHOOSER = booleanOption("win-dir-chooser").scope(nativeBundling()).create(); + public static final OptionValue WIN_WITH_UI = booleanOption("win-with-ui").scope(nativeBundling()).create(); + public static final OptionValue WIN_UPGRADE_UUID = uuidOption("win-upgrade-uuid").scope(nativeBundling()).create(); public static final OptionValue WIN_CONSOLE_HINT = booleanOption("win-console") diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties index 466f58ee68e..0b2ca83a7d7 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -426,3 +426,5 @@ help.option.win-update-url=\ help.option.win-upgrade-uuid=\ \ UUID associated with upgrades for this package +help.option.win-with-ui=\ +\ Enforces the installer to have UI diff --git a/src/jdk.jpackage/share/man/jpackage.md b/src/jdk.jpackage/share/man/jpackage.md index f78bec9808c..9ba5949866f 100644 --- a/src/jdk.jpackage/share/man/jpackage.md +++ b/src/jdk.jpackage/share/man/jpackage.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -436,6 +436,10 @@ The `jpackage` tool will take as input a Java application and a Java run-time im : UUID associated with upgrades for this package +`--win-with-ui` + +: Enforces the installer to have UI + #### Linux platform options (available only when running on Linux): `--linux-package-name` *name* diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/MsiMutator.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/MsiMutator.java new file mode 100644 index 00000000000..592cc55c0df --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/MsiMutator.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import jdk.jpackage.internal.resources.ResourceLocator; + +/** + * WSH script altering cooked .msi file. + */ +record MsiMutator(String scriptResourceName) { + + MsiMutator { + Objects.requireNonNull(scriptResourceName); + if (Path.of(scriptResourceName).getNameCount() != 1) { + throw new IllegalArgumentException(); + } + } + + void addToConfigRoot(Path configRoot) throws IOException { + var scriptFile = configRoot.resolve(pathInConfigRoot()); + try (var in = ResourceLocator.class.getResourceAsStream(scriptResourceName)) { + Files.createDirectories(scriptFile.getParent()); + Files.copy(in, scriptFile); + } + } + + Path pathInConfigRoot() { + return Path.of(scriptResourceName); + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java index f6080523e89..59701777396 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java @@ -39,6 +39,7 @@ import static jdk.jpackage.internal.cli.StandardOption.WIN_SHORTCUT_HINT; import static jdk.jpackage.internal.cli.StandardOption.WIN_SHORTCUT_PROMPT; import static jdk.jpackage.internal.cli.StandardOption.WIN_UPDATE_URL; import static jdk.jpackage.internal.cli.StandardOption.WIN_UPGRADE_UUID; +import static jdk.jpackage.internal.cli.StandardOption.WIN_WITH_UI; import static jdk.jpackage.internal.model.StandardPackageType.WIN_MSI; import jdk.jpackage.internal.cli.Options; @@ -93,6 +94,7 @@ final class WinFromOptions { WIN_UPDATE_URL.ifPresentIn(options, pkgBuilder::updateURL); WIN_INSTALLDIR_CHOOSER.ifPresentIn(options, pkgBuilder::withInstallDirChooser); WIN_SHORTCUT_PROMPT.ifPresentIn(options, pkgBuilder::withShortcutPrompt); + WIN_WITH_UI.ifPresentIn(options, pkgBuilder::withUi); if (app.isService()) { RESOURCE_DIR.ifPresentIn(options, resourceDir -> { diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackageBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackageBuilder.java index 7695cf04ac1..c2564028ecb 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackageBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackageBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,6 +65,7 @@ final class WinMsiPackageBuilder { MsiVersion.of(pkg.version()), withInstallDirChooser, withShortcutPrompt, + withUi, Optional.ofNullable(helpURL), Optional.ofNullable(updateURL), Optional.ofNullable(startMenuGroupName).orElseGet(DEFAULTS::startMenuGroupName), @@ -92,6 +93,11 @@ final class WinMsiPackageBuilder { return this; } + WinMsiPackageBuilder withUi(boolean v) { + withUi = v; + return this; + } + WinMsiPackageBuilder helpURL(String v) { helpURL = v; return this; @@ -131,6 +137,7 @@ final class WinMsiPackageBuilder { private boolean withInstallDirChooser; private boolean withShortcutPrompt; + private boolean withUi; private String helpURL; private String updateURL; private String startMenuGroupName; diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java index a53d083847a..3eb7b10b846 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java @@ -36,7 +36,6 @@ import java.nio.file.Path; import java.nio.file.PathMatcher; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -315,50 +314,50 @@ final class WinMsiPackager implements Consumer { wixPipeline.buildMsi(msiOut.toAbsolutePath()); } - private Map createWixVars() throws IOException { - Map data = new HashMap<>(); + private WixVariables createWixVars() throws IOException { + var wixVars = new WixVariables(); - data.put("JpProductCode", pkg.productCode().toString()); - data.put("JpProductUpgradeCode", pkg.upgradeCode().toString()); + wixVars.put("JpProductCode", pkg.productCode().toString()); + wixVars.put("JpProductUpgradeCode", pkg.upgradeCode().toString()); Log.verbose(I18N.format("message.product-code", pkg.productCode())); Log.verbose(I18N.format("message.upgrade-code", pkg.upgradeCode())); - data.put("JpAllowUpgrades", "yes"); + wixVars.define("JpAllowUpgrades"); if (!pkg.isRuntimeInstaller()) { - data.put("JpAllowDowngrades", "yes"); + wixVars.define("JpAllowDowngrades"); } - data.put("JpAppName", pkg.packageName()); - data.put("JpAppDescription", pkg.description()); - data.put("JpAppVendor", pkg.app().vendor()); - data.put("JpAppVersion", pkg.version()); + wixVars.put("JpAppName", pkg.packageName()); + wixVars.put("JpAppDescription", pkg.description()); + wixVars.put("JpAppVendor", pkg.app().vendor()); + wixVars.put("JpAppVersion", pkg.version()); if (Files.exists(installerIcon)) { - data.put("JpIcon", installerIcon.toString()); + wixVars.put("JpIcon", installerIcon.toString()); } pkg.helpURL().ifPresent(value -> { - data.put("JpHelpURL", value); + wixVars.put("JpHelpURL", value); }); pkg.updateURL().ifPresent(value -> { - data.put("JpUpdateURL", value); + wixVars.put("JpUpdateURL", value); }); pkg.aboutURL().ifPresent(value -> { - data.put("JpAboutURL", value); + wixVars.put("JpAboutURL", value); }); - data.put("JpAppSizeKb", Long.toString(AppImageLayout.toPathGroup( + wixVars.put("JpAppSizeKb", Long.toString(AppImageLayout.toPathGroup( env.appImageLayout()).sizeInBytes() >> 10)); - data.put("JpConfigDir", env.configDir().toAbsolutePath().toString()); + wixVars.put("JpConfigDir", env.configDir().toAbsolutePath().toString()); if (pkg.isSystemWideInstall()) { - data.put("JpIsSystemWide", "yes"); + wixVars.define("JpIsSystemWide"); } - return data; + return wixVars; } private static List getWxlFilesFromDir(Path dir) { diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixFragmentBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixFragmentBuilder.java index 46894699d98..c842c366f63 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixFragmentBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixFragmentBuilder.java @@ -32,7 +32,6 @@ import java.nio.file.Path; import java.util.Collection; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.stream.XMLStreamWriter; @@ -65,16 +64,14 @@ abstract class WixFragmentBuilder { } void initFromParams(BuildEnv env, WinMsiPackage pkg) { - wixVariables = null; + wixVariables = new WixVariables(); additionalResources = null; configRoot = env.configDir(); fragmentResource = env.createResource(defaultResourceName).setPublicName(outputFileName); } void configureWixPipeline(WixPipeline.Builder wixPipeline) { - wixPipeline.addSource(configRoot.resolve(outputFileName), - Optional.ofNullable(wixVariables).map(WixVariables::getValues).orElse( - null)); + wixPipeline.addSource(configRoot.resolve(outputFileName), wixVariables); } void addFilesToConfigRoot() throws IOException { @@ -147,14 +144,11 @@ abstract class WixFragmentBuilder { protected abstract Collection getFragmentWriters(); protected final void defineWixVariable(String variableName) { - setWixVariable(variableName, "yes"); + wixVariables.define(variableName); } protected final void setWixVariable(String variableName, String variableValue) { - if (wixVariables == null) { - wixVariables = new WixVariables(); - } - wixVariables.setWixVariable(variableName, variableValue); + wixVariables.put(variableName, variableValue); } protected final void addResource(OverridableResource resource, String saveAsName) { diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixPipeline.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixPipeline.java index 40160192862..a09db389ca5 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixPipeline.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixPipeline.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,22 +24,18 @@ */ package jdk.jpackage.internal; +import static jdk.jpackage.internal.ShortPathUtils.adjustPath; + import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; -import java.util.function.Function; +import java.util.function.Consumer; import java.util.function.UnaryOperator; -import java.util.stream.Collectors; import java.util.stream.Stream; -import static jdk.jpackage.internal.ShortPathUtils.adjustPath; import jdk.jpackage.internal.util.PathUtils; /** @@ -61,18 +57,20 @@ final class WixPipeline { final var absWorkDir = workDir.normalize().toAbsolutePath(); - final UnaryOperator normalizePath = path -> { - return path.normalize().toAbsolutePath(); - }; + final var absObjWorkDir = PathUtils.normalizedAbsolutePath(wixObjDir); - final var absObjWorkDir = normalizePath.apply(wixObjDir); - - var relSources = sources.stream().map(source -> { - return source.overridePath(normalizePath.apply(source.path)); + final var absSources = sources.stream().map(source -> { + return source.copyWithPath(PathUtils.normalizedAbsolutePath(source.path)); }).toList(); - return new WixPipeline(toolset, adjustPath(absWorkDir), absObjWorkDir, - wixVariables, mapLightOptions(normalizePath), relSources); + return new WixPipeline( + toolset, + adjustPath(absWorkDir), + absObjWorkDir, + wixVariables.createdImmutableCopy(), + mapLightOptions(PathUtils::normalizedAbsolutePath), + absSources, + msiMutators); } Builder setWixObjDir(Path v) { @@ -85,17 +83,30 @@ final class WixPipeline { return this; } - Builder setWixVariables(Map v) { - wixVariables.clear(); + Builder putWixVariables(WixVariables v) { wixVariables.putAll(v); return this; } - Builder addSource(Path source, Map wixVariables) { - sources.add(new WixSource(source, wixVariables)); + Builder putWixVariables(Map v) { + wixVariables.putAll(v); return this; } + Builder addSource(Path source, WixVariables wixVariables) { + sources.add(new WixSource(source, wixVariables.createdImmutableCopy())); + return this; + } + + Builder addMsiMutator(MsiMutator msiMutator, List args) { + msiMutators.add(new MsiMutatorWithArgs(msiMutator, args)); + return this; + } + + Builder addSource(Path source) { + return addSource(source, WixVariables.EMPTY); + } + Builder addLightOptions(String ... v) { lightOptions.addAll(List.of(v)); return this; @@ -119,87 +130,59 @@ final class WixPipeline { private Path workDir; private Path wixObjDir; - private final Map wixVariables = new HashMap<>(); + private final WixVariables wixVariables = new WixVariables(); private final List lightOptions = new ArrayList<>(); private final List sources = new ArrayList<>(); + private final List msiMutators = new ArrayList<>(); } static Builder build() { return new Builder(); } - private WixPipeline(WixToolset toolset, Path workDir, Path wixObjDir, - Map wixVariables, List lightOptions, - List sources) { - this.toolset = toolset; - this.workDir = workDir; - this.wixObjDir = wixObjDir; - this.wixVariables = wixVariables; - this.lightOptions = lightOptions; - this.sources = sources; + private WixPipeline( + WixToolset toolset, + Path workDir, + Path wixObjDir, + WixVariables wixVariables, + List lightOptions, + List sources, + List msiMutators) { + + this.toolset = Objects.requireNonNull(toolset); + this.workDir = Objects.requireNonNull(workDir); + this.wixObjDir = Objects.requireNonNull(wixObjDir); + this.wixVariables = Objects.requireNonNull(wixVariables); + this.lightOptions = Objects.requireNonNull(lightOptions); + this.sources = Objects.requireNonNull(sources); + this.msiMutators = Objects.requireNonNull(msiMutators); } void buildMsi(Path msi) throws IOException { - Objects.requireNonNull(workDir); // Use short path to the output msi to workaround // WiX limitations of handling long paths. var transientMsi = wixObjDir.resolve("a.msi"); + var configRoot = workDir.resolve(transientMsi).getParent(); + + for (var msiMutator : msiMutators) { + msiMutator.addToConfigRoot(configRoot); + } + switch (toolset.getType()) { case Wix3 -> buildMsiWix3(transientMsi); case Wix4 -> buildMsiWix4(transientMsi); - default -> throw new IllegalArgumentException(); + } + + for (var msiMutator : msiMutators) { + msiMutator.execute(configRoot, workDir.resolve(transientMsi)); } IOUtils.copyFile(workDir.resolve(transientMsi), msi); } - private void addWixVariblesToCommandLine( - Map otherWixVariables, List cmdline) { - Stream.of(wixVariables, Optional.ofNullable(otherWixVariables). - orElseGet(Collections::emptyMap)).filter(Objects::nonNull). - reduce((a, b) -> { - a.putAll(b); - return a; - }).ifPresent(wixVars -> { - var entryStream = wixVars.entrySet().stream(); - - Stream stream; - switch (toolset.getType()) { - case Wix3 -> { - stream = entryStream.map(wixVar -> { - return String.format("-d%s=%s", wixVar.getKey(), wixVar. - getValue()); - }); - } - case Wix4 -> { - stream = entryStream.map(wixVar -> { - return Stream.of("-d", String.format("%s=%s", wixVar. - getKey(), wixVar.getValue())); - }).flatMap(Function.identity()); - } - default -> { - throw new IllegalArgumentException(); - } - } - - stream.reduce(cmdline, (ctnr, wixVar) -> { - ctnr.add(wixVar); - return ctnr; - }, (x, y) -> { - x.addAll(y); - return x; - }); - }); - } - private void buildMsiWix4(Path msi) throws IOException { - var mergedSrcWixVars = sources.stream().map(wixSource -> { - return Optional.ofNullable(wixSource.variables).orElseGet( - Collections::emptyMap).entrySet().stream(); - }).flatMap(Function.identity()).collect(Collectors.toMap( - Map.Entry::getKey, Map.Entry::getValue)); List cmdline = new ArrayList<>(List.of( toolset.getToolPath(WixTool.Wix4).toString(), @@ -213,7 +196,7 @@ final class WixPipeline { cmdline.addAll(lightOptions); - addWixVariblesToCommandLine(mergedSrcWixVars, cmdline); + addWixVariablesToCommandLine(sources.stream(), cmdline::addAll); cmdline.addAll(sources.stream().map(wixSource -> { return wixSource.path.toString(); @@ -241,7 +224,6 @@ final class WixPipeline { lightCmdline.addAll(lightOptions); wixObjs.stream().map(Path::toString).forEach(lightCmdline::add); - Files.createDirectories(msi.getParent()); execute(lightCmdline); } @@ -262,7 +244,7 @@ final class WixPipeline { cmdline.add("-fips"); } - addWixVariblesToCommandLine(wixSource.variables, cmdline); + addWixVariablesToCommandLine(Stream.of(wixSource), cmdline::addAll); execute(cmdline); @@ -273,16 +255,47 @@ final class WixPipeline { Executor.of(new ProcessBuilder(cmdline).directory(workDir.toFile())).executeExpectSuccess(); } - private record WixSource(Path path, Map variables) { - WixSource overridePath(Path path) { + private void addWixVariablesToCommandLine(Stream wixSources, Consumer> sink) { + sink.accept(wixSources.map(WixSource::variables).reduce(wixVariables, (a, b) -> { + return new WixVariables().putAll(a).putAll(b); + }).toWixCommandLine(toolset.getType())); + } + + private record WixSource(Path path, WixVariables variables) { + WixSource { + Objects.requireNonNull(path); + Objects.requireNonNull(variables); + } + + WixSource copyWithPath(Path path) { return new WixSource(path, variables); } } + private record MsiMutatorWithArgs(MsiMutator mutator, List args) { + MsiMutatorWithArgs { + Objects.requireNonNull(mutator); + Objects.requireNonNull(args); + } + + void addToConfigRoot(Path configRoot) throws IOException { + mutator.addToConfigRoot(configRoot); + } + + void execute(Path configRoot, Path transientMsi) throws IOException { + Executor.of("cscript", "//Nologo") + .args(PathUtils.normalizedAbsolutePathString(configRoot.resolve(mutator.pathInConfigRoot()))) + .args(PathUtils.normalizedAbsolutePathString(transientMsi)) + .args(args) + .executeExpectSuccess(); + } + } + private final WixToolset toolset; - private final Map wixVariables; + private final WixVariables wixVariables; private final List lightOptions; private final Path wixObjDir; private final Path workDir; private final List sources; + private final List msiMutators; } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixUiFragmentBuilder.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixUiFragmentBuilder.java index 4a2a0756dbd..4748dcfb827 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixUiFragmentBuilder.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixUiFragmentBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,25 +24,32 @@ */ package jdk.jpackage.internal; -import jdk.jpackage.internal.model.WinMsiPackage; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; -import java.util.function.Supplier; +import java.util.stream.Stream; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import jdk.jpackage.internal.WixAppImageFragmentBuilder.ShortcutsFolder; import jdk.jpackage.internal.WixToolset.WixToolsetType; +import jdk.jpackage.internal.model.WinMsiPackage; import jdk.jpackage.internal.resources.ResourceLocator; import jdk.jpackage.internal.util.XmlConsumer; +import jdk.jpackage.internal.wixui.Dialog; +import jdk.jpackage.internal.wixui.DialogPair; +import jdk.jpackage.internal.wixui.Publish; +import jdk.jpackage.internal.wixui.ShowActionSuppresser; +import jdk.jpackage.internal.wixui.UIConfig; +import jdk.jpackage.internal.wixui.UISpec; /** * Creates UI WiX fragment. @@ -53,63 +60,83 @@ final class WixUiFragmentBuilder extends WixFragmentBuilder { void initFromParams(BuildEnv env, WinMsiPackage pkg) { super.initFromParams(env, pkg); - withLicenseDlg = pkg.licenseFile().isPresent(); - if (withLicenseDlg) { + final var shortcutFolders = ShortcutsFolder.getForPackage(pkg); + + uiConfig = UIConfig.build() + .withLicenseDlg(pkg.licenseFile().isPresent()) + .withInstallDirChooserDlg(pkg.withInstallDirChooser()) + .withShortcutPromptDlg(!shortcutFolders.isEmpty() && pkg.withShortcutPrompt()) + .create(); + + if (!uiConfig.equals(UIConfig.build().create()) || pkg.withUI()) { + uiSpec = Optional.of(UISpec.create(uiConfig)); + } else { + uiSpec = Optional.empty(); + } + + if (uiConfig.isWithLicenseDlg()) { Path licenseFileName = pkg.licenseFile().orElseThrow().getFileName(); Path destFile = getConfigRoot().resolve(licenseFileName); setWixVariable("JpLicenseRtf", destFile.toAbsolutePath().toString()); } - withInstallDirChooserDlg = pkg.withInstallDirChooser(); - - final var shortcutFolders = ShortcutsFolder.getForPackage(pkg); - - withShortcutPromptDlg = !shortcutFolders.isEmpty() && pkg.withShortcutPrompt(); - customDialogs = new ArrayList<>(); - if (withShortcutPromptDlg) { - CustomDialog dialog = new CustomDialog(env::createResource, I18N.getString( - "resource.shortcutpromptdlg-wix-file"), + if (uiConfig.isWithShortcutPromptDlg()) { + CustomDialog dialog = new CustomDialog( + env::createResource, + I18N.getString("resource.shortcutpromptdlg-wix-file"), "ShortcutPromptDlg.wxs"); for (var shortcutFolder : shortcutFolders) { - dialog.wixVariables.defineWixVariable( + dialog.wixVariables.define( shortcutFolder.getWixVariableName()); } customDialogs.add(dialog); } - if (withInstallDirChooserDlg) { - CustomDialog dialog = new CustomDialog(env::createResource, I18N.getString( - "resource.installdirnotemptydlg-wix-file"), + if (uiConfig.isWithInstallDirChooserDlg()) { + CustomDialog dialog = new CustomDialog( + env::createResource, + I18N.getString("resource.installdirnotemptydlg-wix-file"), "InstallDirNotEmptyDlg.wxs"); - List dialogIds = getUI().dialogIdsSupplier.apply(this); - dialog.wixVariables.setWixVariable("JpAfterInstallDirDlg", - dialogIds.get(dialogIds.indexOf(Dialog.InstallDirDlg) + 1).id); customDialogs.add(dialog); } + } @Override void configureWixPipeline(WixPipeline.Builder wixPipeline) { super.configureWixPipeline(wixPipeline); - if (withShortcutPromptDlg || withInstallDirChooserDlg || withLicenseDlg) { - final String extName; - switch (getWixType()) { - case Wix3 -> extName = "WixUIExtension"; - case Wix4 -> extName = "WixToolset.UI.wixext"; - default -> throw new IllegalArgumentException(); - } - wixPipeline.addLightOptions("-ext", extName); - } - // Only needed if we using CA dll, so Wix can find it if (withCustomActionsDll) { wixPipeline.addLightOptions("-b", getConfigRoot().toAbsolutePath().toString()); } + if (uiSpec.isEmpty()) { + return; + } + + var extName = switch (getWixType()) { + case Wix3 -> "WixUIExtension"; + case Wix4 -> "WixToolset.UI.wixext"; + }; + wixPipeline.addLightOptions("-ext", extName); + wixPipeline.putWixVariables(uiSpec.get().wixVariables()); + + if (!uiSpec.get().hideDialogs().isEmpty() && getWixType() == WixToolsetType.Wix3) { + // Older WiX doesn't support multiple overrides of a "ShowAction" element. + // Have to run a script to alter the msi. + var removeActions = uiSpec.get().hideDialogs().stream() + .map(ShowActionSuppresser::dialog) + .sorted(Dialog.DEFAULT_COMPARATOR) + .map(Dialog::id); + wixPipeline.addMsiMutator( + new MsiMutator("msi-disable-actions.js"), + Stream.concat(Stream.of("InstallUISequence"), removeActions).toList()); + } + for (var customDialog : customDialogs) { customDialog.addToWixPipeline(wixPipeline); } @@ -132,26 +159,24 @@ final class WixUiFragmentBuilder extends WixFragmentBuilder { return List.of(this::addUI); } - private void addUI(XMLStreamWriter xml) throws XMLStreamException, - IOException { + private void addUI(XMLStreamWriter xml) throws XMLStreamException, IOException { - if (withInstallDirChooserDlg) { + if (uiConfig.isWithInstallDirChooserDlg()) { xml.writeStartElement("Property"); xml.writeAttribute("Id", "WIXUI_INSTALLDIR"); xml.writeAttribute("Value", "INSTALLDIR"); xml.writeEndElement(); // Property } - if (withLicenseDlg) { + if (uiConfig.isWithLicenseDlg()) { xml.writeStartElement("WixVariable"); xml.writeAttribute("Id", "WixUILicenseRtf"); xml.writeAttribute("Value", "$(var.JpLicenseRtf)"); xml.writeEndElement(); // WixVariable } - var ui = getUI(); - if (ui != null) { - ui.write(getWixType(), this, xml); + if (uiSpec.isPresent()) { + writeNonEmptyUIElement(xml); } else { xml.writeStartElement("UI"); xml.writeAttribute("Id", "JpUI"); @@ -159,371 +184,140 @@ final class WixUiFragmentBuilder extends WixFragmentBuilder { } } - private UI getUI() { - if (withInstallDirChooserDlg || withShortcutPromptDlg) { - // WixUI_InstallDir for shortcut prompt dialog too because in - // WixUI_Minimal UI sequence WelcomeEulaDlg dialog doesn't have "Next" - // button, but has "Install" button. So inserting shortcut prompt dialog - // after welcome dialog in WixUI_Minimal UI sequence would be confusing - return UI.InstallDir; - } else if (withLicenseDlg) { - return UI.Minimal; - } else { - return null; - } - } + void writeNonEmptyUIElement(XMLStreamWriter xml) throws XMLStreamException, IOException { - private enum UI { - InstallDir("WixUI_InstallDir", - WixUiFragmentBuilder::dialogSequenceForWixUI_InstallDir, - Dialog::createPairsForWixUI_InstallDir), - Minimal("WixUI_Minimal", null, null); + switch (getWixType()) { + case Wix3 -> {} + case Wix4 -> { + // https://wixtoolset.org/docs/fourthree/faqs/#converting-custom-wixui-dialog-sets + xml.writeProcessingInstruction("foreach WIXUIARCH in X86;X64;A64"); + writeWix4UIRef(xml, uiSpec.get().wixUI().id(), "JpUIInternal_$(WIXUIARCH)"); + xml.writeProcessingInstruction("endforeach"); - UI(String wixUIRef, - Function> dialogIdsSupplier, - Supplier>> dialogPairsSupplier) { - this.wixUIRef = wixUIRef; - this.dialogIdsSupplier = dialogIdsSupplier; - this.dialogPairsSupplier = dialogPairsSupplier; - } - - void write(WixToolsetType wixType, WixUiFragmentBuilder outer, XMLStreamWriter xml) throws XMLStreamException, IOException { - switch (wixType) { - case Wix3 -> {} - case Wix4 -> { - // https://wixtoolset.org/docs/fourthree/faqs/#converting-custom-wixui-dialog-sets - xml.writeProcessingInstruction("foreach WIXUIARCH in X86;X64;A64"); - writeWix4UIRef(xml, wixUIRef, "JpUIInternal_$(WIXUIARCH)"); - xml.writeProcessingInstruction("endforeach"); - - writeWix4UIRef(xml, "JpUIInternal", "JpUI"); - } - default -> { - throw new IllegalArgumentException(); - } - } - - xml.writeStartElement("UI"); - switch (wixType) { - case Wix3 -> { - xml.writeAttribute("Id", "JpUI"); - xml.writeStartElement("UIRef"); - xml.writeAttribute("Id", wixUIRef); - xml.writeEndElement(); // UIRef - } - case Wix4 -> { - xml.writeAttribute("Id", "JpUIInternal"); - } - default -> { - throw new IllegalArgumentException(); - } - } - writeContents(wixType, outer, xml); - xml.writeEndElement(); // UI - } - - private void writeContents(WixToolsetType wixType, WixUiFragmentBuilder outer, - XMLStreamWriter xml) throws XMLStreamException, IOException { - if (dialogIdsSupplier != null) { - List dialogIds = dialogIdsSupplier.apply(outer); - Map> dialogPairs = dialogPairsSupplier.get(); - - if (dialogIds.contains(Dialog.InstallDirDlg)) { - xml.writeStartElement("DialogRef"); - xml.writeAttribute("Id", "InstallDirNotEmptyDlg"); - xml.writeEndElement(); // DialogRef - } - - var it = dialogIds.iterator(); - Dialog firstId = it.next(); - while (it.hasNext()) { - Dialog secondId = it.next(); - DialogPair pair = new DialogPair(firstId, secondId); - for (var curPair : List.of(pair, pair.flip())) { - for (var publish : dialogPairs.get(curPair)) { - writePublishDialogPair(wixType, xml, publish, curPair); - } - } - firstId = secondId; - } + writeWix4UIRef(xml, "JpUIInternal", "JpUI"); } } - private static void writeWix4UIRef(XMLStreamWriter xml, String uiRef, String id) throws XMLStreamException, IOException { - // https://wixtoolset.org/docs/fourthree/faqs/#referencing-the-standard-wixui-dialog-sets - xml.writeStartElement("UI"); - xml.writeAttribute("Id", id); - xml.writeStartElement("ui:WixUI"); - xml.writeAttribute("Id", uiRef); - xml.writeNamespace("ui", "http://wixtoolset.org/schemas/v4/wxs/ui"); - xml.writeEndElement(); // UIRef - xml.writeEndElement(); // UI - } - - private final String wixUIRef; - private final Function> dialogIdsSupplier; - private final Supplier>> dialogPairsSupplier; - } - - private List dialogSequenceForWixUI_InstallDir() { - List dialogIds = new ArrayList<>( - List.of(Dialog.WixUI_WelcomeDlg)); - if (withLicenseDlg) { - dialogIds.add(Dialog.WixUI_LicenseAgreementDlg); - } - - if (withInstallDirChooserDlg) { - dialogIds.add(Dialog.InstallDirDlg); - } - - if (withShortcutPromptDlg) { - dialogIds.add(Dialog.ShortcutPromptDlg); - } - - dialogIds.add(Dialog.WixUI_VerifyReadyDlg); - - return dialogIds; - } - - private enum Dialog { - WixUI_WelcomeDlg, - WixUI_LicenseAgreementDlg, - InstallDirDlg, - ShortcutPromptDlg, - WixUI_VerifyReadyDlg; - - Dialog() { - if (name().startsWith("WixUI_")) { - id = name().substring("WixUI_".length()); - } else { - id = name(); + xml.writeStartElement("UI"); + switch (getWixType()) { + case Wix3 -> { + xml.writeAttribute("Id", "JpUI"); + xml.writeStartElement("UIRef"); + xml.writeAttribute("Id", uiSpec.get().wixUI().id()); + xml.writeEndElement(); // UIRef + } + case Wix4 -> { + xml.writeAttribute("Id", "JpUIInternal"); } } - - static Map> createPair(Dialog firstId, - Dialog secondId, List nextBuilders, - List prevBuilders) { - var pair = new DialogPair(firstId, secondId); - return Map.of(pair, nextBuilders.stream().map(b -> { - return buildPublish(b.create()).next().create(); - }).toList(), pair.flip(), - prevBuilders.stream().map(b -> { - return buildPublish(b.create()).back().create(); - }).toList()); - } - - static Map> createPair(Dialog firstId, - Dialog secondId, List builders) { - return createPair(firstId, secondId, builders, builders); - } - - static Map> createPairsForWixUI_InstallDir() { - Map> map = new HashMap<>(); - - // Order is a "weight" of action. If there are multiple - // "NewDialog" action for the same dialog Id, MSI would pick the one - // with higher order value. In WixUI_InstallDir dialog sequence the - // highest order value is 4. InstallDirNotEmptyDlg adds NewDialog - // action with order 5. Setting order to 6 for all - // actions configured in this function would make them executed - // instead of corresponding default actions defined in - // WixUI_InstallDir dialog sequence. - var order = 6; - - // Based on WixUI_InstallDir.wxs - var backFromVerifyReadyDlg = List.of(buildPublish().condition( - "NOT Installed").order(order)); - var uncondinal = List.of(buildPublish().condition("1")); - var ifNotIstalled = List.of( - buildPublish().condition("NOT Installed").order(order)); - var ifLicenseAccepted = List.of(buildPublish().condition( - "LicenseAccepted = \"1\"").order(order)); - - // Empty condition list for the default dialog sequence - map.putAll(createPair(WixUI_WelcomeDlg, WixUI_LicenseAgreementDlg, - List.of())); - map.putAll( - createPair(WixUI_WelcomeDlg, InstallDirDlg, ifNotIstalled)); - map.putAll(createPair(WixUI_WelcomeDlg, ShortcutPromptDlg, - ifNotIstalled)); - - map.putAll(createPair(WixUI_LicenseAgreementDlg, InstallDirDlg, - List.of())); - map.putAll(createPair(WixUI_LicenseAgreementDlg, ShortcutPromptDlg, - ifLicenseAccepted, uncondinal)); - map.putAll(createPair(WixUI_LicenseAgreementDlg, - WixUI_VerifyReadyDlg, ifLicenseAccepted, - backFromVerifyReadyDlg)); - - map.putAll(createPair(InstallDirDlg, ShortcutPromptDlg, List.of(), - uncondinal)); - map.putAll(createPair(InstallDirDlg, WixUI_VerifyReadyDlg, List.of())); - - map.putAll(createPair(ShortcutPromptDlg, WixUI_VerifyReadyDlg, - uncondinal, backFromVerifyReadyDlg)); - - return map; - } - - private final String id; + writeUIElementContents(xml); + xml.writeEndElement(); // UI } - private static final class DialogPair { + private void writeUIElementContents(XMLStreamWriter xml) throws XMLStreamException, IOException { - DialogPair(Dialog first, Dialog second) { - this(first.id, second.id); + if (uiConfig.isWithInstallDirChooserDlg()) { + xml.writeStartElement("DialogRef"); + xml.writeAttribute("Id", "InstallDirNotEmptyDlg"); + xml.writeEndElement(); // DialogRef } - DialogPair(String firstId, String secondId) { - this.firstId = firstId; - this.secondId = secondId; + for (var e : uiSpec.get().customDialogSequence().entrySet().stream() + .sorted(Comparator.comparing(Map.Entry::getKey, DialogPair.DEFAULT_COMPARATOR)) + .toList()) { + writePublishDialogPair(getWixType(), xml, e.getValue(), e.getKey()); } - DialogPair flip() { - return new DialogPair(secondId, firstId); - } - - @Override - public int hashCode() { - int hash = 3; - hash = 97 * hash + Objects.hashCode(this.firstId); - hash = 97 * hash + Objects.hashCode(this.secondId); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final DialogPair other = (DialogPair) obj; - if (!Objects.equals(this.firstId, other.firstId)) { - return false; - } - if (!Objects.equals(this.secondId, other.secondId)) { - return false; - } - return true; - } - - private final String firstId; - private final String secondId; + hideDialogs(getWixType(), xml, uiSpec.get().hideDialogs()); } - private static final class Publish { - - Publish(String control, String condition, int order) { - this.control = control; - this.condition = condition; - this.order = order; - } - - private final String control; - private final String condition; - private final int order; + private static void writeWix4UIRef(XMLStreamWriter xml, String uiRef, String id) throws XMLStreamException, IOException { + // https://wixtoolset.org/docs/fourthree/faqs/#referencing-the-standard-wixui-dialog-sets + xml.writeStartElement("UI"); + xml.writeAttribute("Id", Objects.requireNonNull(id)); + xml.writeStartElement("ui:WixUI"); + xml.writeAttribute("Id", Objects.requireNonNull(uiRef)); + xml.writeNamespace("ui", "http://wixtoolset.org/schemas/v4/wxs/ui"); + xml.writeEndElement(); // UIRef + xml.writeEndElement(); // UI } - private static final class PublishBuilder { + private static void writePublishDialogPair( + WixToolsetType wixType, + XMLStreamWriter xml, + Publish publish, + DialogPair dialogPair) throws IOException, XMLStreamException { - PublishBuilder() { - order(0); - next(); - condition("1"); - } - - PublishBuilder(Publish publish) { - order(publish.order); - control(publish.control); - condition(publish.condition); - } - - public PublishBuilder control(String v) { - control = v; - return this; - } - - public PublishBuilder next() { - return control("Next"); - } - - public PublishBuilder back() { - return control("Back"); - } - - public PublishBuilder condition(String v) { - condition = v; - return this; - } - - public PublishBuilder order(int v) { - order = v; - return this; - } - - Publish create() { - return new Publish(control, condition, order); - } - - private String control; - private String condition; - private int order; - } - - private static PublishBuilder buildPublish() { - return new PublishBuilder(); - } - - private static PublishBuilder buildPublish(Publish publish) { - return new PublishBuilder(publish); - } - - private static void writePublishDialogPair(WixToolsetType wixType, XMLStreamWriter xml, - Publish publish, DialogPair dialogPair) throws IOException, XMLStreamException { xml.writeStartElement("Publish"); - xml.writeAttribute("Dialog", dialogPair.firstId); - xml.writeAttribute("Control", publish.control); + xml.writeAttribute("Dialog", dialogPair.first().id()); + xml.writeAttribute("Control", publish.control().id()); xml.writeAttribute("Event", "NewDialog"); - xml.writeAttribute("Value", dialogPair.secondId); - if (publish.order != 0) { - xml.writeAttribute("Order", String.valueOf(publish.order)); + xml.writeAttribute("Value", dialogPair.second().id()); + if (publish.order() != 0) { + xml.writeAttribute("Order", String.valueOf(publish.order())); } + switch (wixType) { - case Wix3 -> xml.writeCharacters(publish.condition); - case Wix4 -> xml.writeAttribute("Condition", publish.condition); - default -> throw new IllegalArgumentException(); + case Wix3 -> { + xml.writeCharacters(publish.condition()); + } + case Wix4 -> { + xml.writeAttribute("Condition", publish.condition()); + } } + + xml.writeEndElement(); + } + + private static void hideDialogs( + WixToolsetType wixType, + XMLStreamWriter xml, + Collection hideDialogs) throws IOException, XMLStreamException { + + if (!hideDialogs.isEmpty()) { + if (wixType == WixToolsetType.Wix4) { + xml.writeStartElement("InstallUISequence"); + for (var showAction : hideDialogs.stream().sorted(ShowActionSuppresser.DEFAULT_COMPARATOR).toList()) { + writeWix4ShowAction(wixType, xml, showAction); + } + xml.writeEndElement(); + } + } + } + + private static void writeWix4ShowAction( + WixToolsetType wixType, + XMLStreamWriter xml, + ShowActionSuppresser hideDialog) throws IOException, XMLStreamException { + + xml.writeStartElement("Show"); + xml.writeAttribute("Dialog", String.format("override %s", hideDialog.dialog().id())); + xml.writeAttribute(switch (hideDialog.order()) { + case AFTER -> "After"; + }, hideDialog.anchor().id()); + xml.writeAttribute("Condition", "0"); xml.writeEndElement(); } private final class CustomDialog { - CustomDialog(Function createResource, String category, - String wxsFileName) { + CustomDialog(Function createResource, String category, String wxsFileName) { this.wxsFileName = wxsFileName; this.wixVariables = new WixVariables(); - addResource(createResource.apply(wxsFileName).setCategory(category).setPublicName( - wxsFileName), wxsFileName); + addResource(createResource.apply(wxsFileName).setCategory(category).setPublicName(wxsFileName), wxsFileName); } void addToWixPipeline(WixPipeline.Builder wixPipeline) { - wixPipeline.addSource(getConfigRoot().toAbsolutePath().resolve( - wxsFileName), wixVariables.getValues()); + wixPipeline.addSource(getConfigRoot().toAbsolutePath().resolve(wxsFileName), wixVariables); } private final WixVariables wixVariables; private final String wxsFileName; } - private boolean withInstallDirChooserDlg; - private boolean withShortcutPromptDlg; - private boolean withLicenseDlg; + private UIConfig uiConfig; + private Optional uiSpec; private boolean withCustomActionsDll = true; private List customDialogs; } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixVariables.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixVariables.java index 36ed1d99738..88a25810182 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixVariables.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixVariables.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,22 +24,103 @@ */ package jdk.jpackage.internal; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; +import jdk.jpackage.internal.WixToolset.WixToolsetType; final class WixVariables { - void defineWixVariable(String variableName) { - setWixVariable(variableName, "yes"); + WixVariables() { + this.values = new HashMap<>(); } - void setWixVariable(String variableName, String variableValue) { - values.put(variableName, variableValue); + private WixVariables(Map values) { + this.values = values; + this.isImmutable = true; } - Map getValues() { - return values; + WixVariables define(String variableName) { + return put(variableName, "yes"); } - private final Map values = new HashMap<>(); + WixVariables put(String variableName, String variableValue) { + Objects.requireNonNull(variableName); + Objects.requireNonNull(variableValue); + validateMutable(); + values.compute(variableName, (k, v) -> { + if (!allowOverrides && v != null) { + throw overridingDisabled(); + } + return variableValue; + }); + return this; + } + + WixVariables putAll(Map values) { + Objects.requireNonNull(values); + validateMutable(); + if (!allowOverrides && !Collections.disjoint(this.values.keySet(), values.keySet())) { + throw overridingDisabled(); + } else { + values.entrySet().forEach(e -> { + put(e.getKey(), e.getValue()); + }); + } + return this; + } + + WixVariables putAll(WixVariables other) { + return putAll(other.values); + } + + WixVariables allowOverrides(boolean v) { + validateMutable(); + allowOverrides = v; + return this; + } + + WixVariables createdImmutableCopy() { + if (isImmutable) { + return this; + } else { + return new WixVariables(Map.copyOf(values)); + } + } + + List toWixCommandLine(WixToolsetType wixType) { + var orderedWixVars = values.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)); + return (switch (Objects.requireNonNull(wixType)) { + case Wix3 -> { + yield orderedWixVars.map(wixVar -> { + return String.format("-d%s=%s", wixVar.getKey(), wixVar.getValue()); + }); + } + case Wix4 -> { + yield orderedWixVars.flatMap(wixVar -> { + return Stream.of("-d", String.format("%s=%s", wixVar.getKey(), wixVar.getValue())); + }); + } + }).toList(); + } + + private void validateMutable() { + if (isImmutable) { + throw new IllegalStateException("WiX variables container is immutable"); + } + } + + private static IllegalStateException overridingDisabled() { + return new IllegalStateException("Overriding variables is unsupported"); + } + + private final Map values; + private boolean allowOverrides; + private boolean isImmutable; + + static final WixVariables EMPTY = new WixVariables().createdImmutableCopy(); } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinMsiPackageMixin.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinMsiPackageMixin.java index 94d7c30fe57..ec004da2cee 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinMsiPackageMixin.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/model/WinMsiPackageMixin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * 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,6 +36,8 @@ public interface WinMsiPackageMixin { boolean withShortcutPrompt(); + boolean withUI(); + Optional helpURL(); Optional updateURL(); @@ -50,8 +52,16 @@ public interface WinMsiPackageMixin { Optional serviceInstaller(); - record Stub(DottedVersion msiVersion, boolean withInstallDirChooser, boolean withShortcutPrompt, - Optional helpURL, Optional updateURL, String startMenuGroupName, - boolean isSystemWideInstall, UUID upgradeCode, UUID productCode, + record Stub( + DottedVersion msiVersion, + boolean withInstallDirChooser, + boolean withShortcutPrompt, + boolean withUI, + Optional helpURL, + Optional updateURL, + String startMenuGroupName, + boolean isSystemWideInstall, + UUID upgradeCode, + UUID productCode, Optional serviceInstaller) implements WinMsiPackageMixin {} } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/msi-disable-actions.js b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/msi-disable-actions.js new file mode 100644 index 00000000000..2f0f3a5d019 --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/msi-disable-actions.js @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +function modifyMsi(msiPath, callback) { + var installer = new ActiveXObject('WindowsInstaller.Installer') + var database = installer.OpenDatabase(msiPath, 1 /* msiOpenDatabaseModeTransact */) + + callback(installer, database) + + database.Commit() +} + + +function disableActions(installer, db, sequence, actionIDs) { + var tables = {} + + var view = db.OpenView("SELECT `Action`, `Condition`, `Sequence` FROM " + sequence) + view.Execute() + + try { + while (true) { + var record = view.Fetch() + if (!record) { + break + } + + var action = record.StringData(1) + + if (actionIDs.hasOwnProperty(action)) { + WScript.Echo("Set condition of [" + action + "] action in [" + sequence + "] sequence to [0]") + var newRecord = installer.CreateRecord(3) + for (var i = 1; i !== newRecord.FieldCount + 1; i++) { + newRecord.StringData(i) = record.StringData(i) + } + newRecord.StringData(2) = "0" // Set condition value to `0` + view.Modify(3 /* msiViewModifyAssign */, newRecord) // Replace existing record + } + } + } finally { + view.Close() + } +} + + +(function () { + var msi = WScript.arguments(0) + var sequence = WScript.arguments(1) + var actionIDs = {} + for (var i = 0; i !== WScript.arguments.Count(); i++) { + actionIDs[WScript.arguments(i)] = true + } + + modifyMsi(msi, function (installer, db) { + disableActions(installer, db, sequence, actionIDs) + }) +})() diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Control.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Control.java new file mode 100644 index 00000000000..d41fc435126 --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Control.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.wixui; + +import java.util.Comparator; + +/** + * WiX Dialog's control. + */ +public sealed interface Control permits StandardControl { + String id(); + + public static final Comparator DEFAULT_COMPARATOR = Comparator.comparing(Control::id); +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/CustomDialog.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/CustomDialog.java new file mode 100644 index 00000000000..7325685469c --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/CustomDialog.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.wixui; + +/** + * Custom jpackage dialogs. + */ +public enum CustomDialog implements Dialog { + ShortcutPromptDlg, + ; + + @Override + public String id() { + return name(); + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Dialog.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Dialog.java new file mode 100644 index 00000000000..a43648042a9 --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Dialog.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.wixui; + +import java.util.Comparator; + +/** + * WiX Dialog. + */ +public sealed interface Dialog permits WixDialog, CustomDialog { + String id(); + + public static final Comparator DEFAULT_COMPARATOR = Comparator.comparing(Dialog::id); +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/DialogPair.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/DialogPair.java new file mode 100644 index 00000000000..7f0f3bb6090 --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/DialogPair.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.wixui; + +import static java.util.Comparator.comparing; + +import java.util.Comparator; +import java.util.Objects; + +public record DialogPair(Dialog first, Dialog second) { + + public DialogPair { + Objects.requireNonNull(first); + Objects.requireNonNull(second); + if (first.equals(second) || first.id().equals(second.id())) { + throw new IllegalArgumentException("Dialogs must be different"); + } + } + + DialogPair flip() { + return new DialogPair(second, first); + } + + public static final Comparator DEFAULT_COMPARATOR = + comparing(DialogPair::first, Dialog.DEFAULT_COMPARATOR) + .thenComparing(comparing(DialogPair::second, Dialog.DEFAULT_COMPARATOR)); +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Publish.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Publish.java new file mode 100644 index 00000000000..7741ad823bb --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/Publish.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.wixui; + +import java.util.Objects; + +public record Publish(Control control, String condition, int order) { + + public Publish { + Objects.requireNonNull(control); + Objects.requireNonNull(condition); + if (order < 0) { + throw new IllegalArgumentException("Negative order"); + } + } + + Builder toBuilder() { + return new Builder(this); + } + + static Builder build() { + return new Builder(); + } + + static final class Builder { + + private Builder() { + order(0); + next(); + condition("1"); + } + + private Builder(Publish publish) { + order(publish.order); + control(publish.control); + condition(publish.condition); + } + + Publish create() { + return new Publish(control, condition, order); + } + + Builder control(Control v) { + control = v; + return this; + } + + Builder next() { + return control(StandardControl.NEXT); + } + + Builder back() { + return control(StandardControl.BACK); + } + + Builder condition(String v) { + condition = v; + return this; + } + + Builder order(int v) { + order = v; + return this; + } + + private int order; + private Control control; + private String condition; + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/ShowActionSuppresser.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/ShowActionSuppresser.java new file mode 100644 index 00000000000..f89b15987ee --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/ShowActionSuppresser.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.wixui; + +import static java.util.Comparator.comparing; + +import java.util.Comparator; +import java.util.Objects; + +public record ShowActionSuppresser(Dialog dialog, Dialog anchor, Order order) { + + public enum Order { + AFTER, + ; + } + + public ShowActionSuppresser { + Objects.requireNonNull(order); + validate(dialog); + validate(anchor); + } + + static Builder suppressShowAction(WixDialog dialog) { + return new Builder(dialog); + } + + static final class Builder { + + private Builder(WixDialog dialog) { + this.dialog = Objects.requireNonNull(dialog); + } + + ShowActionSuppresser after(WixDialog anchor) { + return new ShowActionSuppresser(dialog, anchor, Order.AFTER); + } + + private final WixDialog dialog; + } + + private static void validate(Dialog v) { + if (!(Objects.requireNonNull(v) instanceof WixDialog)) { + throw new IllegalArgumentException(); + } + } + + public static final Comparator DEFAULT_COMPARATOR = + comparing(ShowActionSuppresser::dialog, Dialog.DEFAULT_COMPARATOR) + .thenComparing(comparing(ShowActionSuppresser::anchor, Dialog.DEFAULT_COMPARATOR)) + .thenComparing(comparing(ShowActionSuppresser::order)); +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/StandardControl.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/StandardControl.java new file mode 100644 index 00000000000..43c5c5a7e9e --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/StandardControl.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.wixui; + +import java.util.Objects; + +/** + * Dialog controls referenced in adjustments of the standard WiX UI. + */ +enum StandardControl implements Control { + NEXT("Next"), + BACK("Back"), + ; + + StandardControl(String id) { + this.id = Objects.requireNonNull(id); + } + + @Override + public String id() { + return id; + } + + private final String id; +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UIConfig.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UIConfig.java new file mode 100644 index 00000000000..c695694788f --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UIConfig.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.wixui; + +/** + * UI config. + */ +public record UIConfig( + boolean isWithInstallDirChooserDlg, + boolean isWithShortcutPromptDlg, + boolean isWithLicenseDlg) { + + public static Builder build() { + return new Builder(); + } + + public static final class Builder { + + private Builder() { + } + + public UIConfig create() { + return new UIConfig(isWithInstallDirChooserDlg, isWithShortcutPromptDlg, isWithLicenseDlg); + } + + public Builder withInstallDirChooserDlg(boolean v) { + isWithInstallDirChooserDlg = v; + return this; + } + + public Builder withInstallDirChooserDlg() { + return withInstallDirChooserDlg(true); + } + + public Builder withShortcutPromptDlg(boolean v) { + isWithShortcutPromptDlg = v; + return this; + } + + public Builder withShortcutPromptDlg() { + return withShortcutPromptDlg(true); + } + + public Builder withLicenseDlg(boolean v) { + isWithLicenseDlg = v; + return this; + } + + public Builder withLicenseDlg() { + return withLicenseDlg(true); + } + + private boolean isWithInstallDirChooserDlg; + private boolean isWithShortcutPromptDlg; + private boolean isWithLicenseDlg; + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UISpec.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UISpec.java new file mode 100644 index 00000000000..38cf3775c2b --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/UISpec.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.wixui; + +import static jdk.jpackage.internal.wixui.CustomDialog.ShortcutPromptDlg; +import static jdk.jpackage.internal.wixui.ShowActionSuppresser.suppressShowAction; +import static jdk.jpackage.internal.wixui.WixDialog.InstallDirDlg; +import static jdk.jpackage.internal.wixui.WixDialog.LicenseAgreementDlg; +import static jdk.jpackage.internal.wixui.WixDialog.ProgressDlg; +import static jdk.jpackage.internal.wixui.WixDialog.VerifyReadyDlg; +import static jdk.jpackage.internal.wixui.WixDialog.WelcomeDlg; +import static jdk.jpackage.internal.wixui.WixDialog.WelcomeEulaDlg; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * UI spec. + *

+ * UI is based on one of the standard WiX UIs with optional alterations. + */ +public record UISpec( + WixUI wixUI, + Map wixVariables, + Map customDialogSequence, + Collection hideDialogs) { + + public UISpec { + Objects.requireNonNull(wixUI); + Objects.requireNonNull(wixVariables); + Objects.requireNonNull(customDialogSequence); + Objects.requireNonNull(hideDialogs); + } + + static Builder build(WixUI wixUI) { + return new Builder().wixUI(wixUI); + } + + static final class Builder { + + private Builder() { + } + + UISpec create() { + return new UISpec( + wixUI, + Optional.ofNullable(wixVariables).map(Collections::unmodifiableMap).orElseGet(Map::of), + Optional.ofNullable(customDialogSequence).map(Collections::unmodifiableMap).orElseGet(Map::of), + Optional.ofNullable(hideDialogs).map(List::copyOf).orElseGet(List::of)); + } + + Builder wixUI(WixUI v) { + wixUI = v; + return this; + } + + Builder setWixVariable(String name, String value) { + wixVariables.put(Objects.requireNonNull(name), Objects.requireNonNull(value)); + return this; + } + + Builder customDialogSequence(Map v) { + customDialogSequence = v; + return this; + } + + Builder hideDialogs(Collection v) { + hideDialogs = v; + return this; + } + + Builder hideDialogs(ShowActionSuppresser... v) { + return hideDialogs(List.of(v)); + } + + private WixUI wixUI; + private final Map wixVariables = new HashMap<>(); + private Map customDialogSequence; + private Collection hideDialogs; + } + + public static UISpec create(UIConfig cfg) { + Objects.requireNonNull(cfg); + return Optional.ofNullable(DEFAULT_SPECS.get(cfg)).map(Supplier::get).orElseGet(() -> { + return createCustom(cfg); + }); + } + + private static UISpec createCustom(UIConfig cfg) { + Objects.requireNonNull(cfg); + + var dialogs = installDirUiDialogs(cfg); + var dialogPairs = toDialogPairs(dialogs); + + var customDialogSequence = overrideInstallDirDialogSequence().stream().filter(e -> { + return dialogPairs.contains(e.getKey()); + }).collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)); + + var uiSpec = build(WixUI.INSTALL_DIR).customDialogSequence(customDialogSequence); + + var it = dialogs.iterator(); + do { + if (it.next().equals(InstallDirDlg)) { + uiSpec.setWixVariable("JpAfterInstallDirDlg", it.next().id()); + } + } while (it.hasNext()); + + return uiSpec.create(); + } + + private static void createPairs( + BiConsumer sink, + Dialog first, + Dialog second, + Publish publishNext, + Publish publishPrev) { + + createPairNext(sink, first, second, publishNext); + createPairBack(sink, second, first, publishPrev); + } + + private static void createPairs( + BiConsumer sink, + Dialog first, + Dialog second, + Publish publish) { + createPairs(sink, first, second, publish, publish); + } + + private static void createPairNext( + BiConsumer sink, + Dialog first, + Dialog second, + Publish publish) { + + var pair = new DialogPair(first, second); + + sink.accept(pair, publish.toBuilder().next().create()); + } + + private static void createPairBack( + BiConsumer sink, + Dialog first, + Dialog second, + Publish publish) { + + var pair = new DialogPair(first, second); + + sink.accept(pair, publish.toBuilder().back().create()); + } + + private static Collection toDialogPairs(List

dialogs) { + if (dialogs.size() < 2) { + throw new IllegalArgumentException(); + } + + var pairs = new ArrayList(); + var it = dialogs.listIterator(); + var prev = it.next(); + do { + var next = it.next(); + var pair = new DialogPair(prev, next); + pairs.add(pair); + pairs.add(pair.flip()); + prev = next; + } while (it.hasNext()); + + return pairs; + } + + private static List installDirUiDialogs(UIConfig cfg) { + var dialogs = new ArrayList(); + + dialogs.add(WelcomeDlg); + + if (cfg.isWithLicenseDlg()) { + dialogs.add(LicenseAgreementDlg); + } + + if (cfg.isWithInstallDirChooserDlg()) { + dialogs.add(InstallDirDlg); + } + + if (cfg.isWithShortcutPromptDlg()) { + dialogs.add(ShortcutPromptDlg); + } + + dialogs.add(VerifyReadyDlg); + + return dialogs; + } + + private static Collection> overrideInstallDirDialogSequence() { + + List> entries = new ArrayList<>(); + + BiConsumer acc = (pair, publish) -> { + entries.add(Map.entry(pair, publish)); + }; + + // Order is a "weight" of action. If there are multiple + // "NewDialog" action for the same dialog Id, MSI would pick the one + // with higher order value. In WixUI_InstallDir dialog sequence the + // highest order value is 4. InstallDirNotEmptyDlg adds NewDialog + // action with order 5. Setting order to 6 for all + // actions configured in this function would make them executed + // instead of corresponding default actions defined in + // WixUI_InstallDir dialog sequence. + var order = 6; + + // Based on WixUI_InstallDir.wxs + var backFromVerifyReadyDlg = Publish.build().condition(CONDITION_NOT_INSTALLED).order(order).create(); + var uncondinal = Publish.build().condition(CONDITION_ALWAYS).create(); + var ifNotIstalled = Publish.build().condition(CONDITION_NOT_INSTALLED).order(order).create(); + var ifLicenseAccepted = Publish.build().condition("LicenseAccepted = \"1\"").order(order).create(); + + // Define all alternative transitions: + // - Skip standard license dialog + // - Insert shortcut prompt dialog after the standard install dir dialog + // - Replace the standard install dir dialog with the shortcut prompt dialog + + createPairs(acc, WelcomeDlg, InstallDirDlg, ifNotIstalled); + createPairs(acc, WelcomeDlg, VerifyReadyDlg, ifNotIstalled); + createPairs(acc, WelcomeDlg, ShortcutPromptDlg, ifNotIstalled); + + createPairs(acc, LicenseAgreementDlg, ShortcutPromptDlg, ifLicenseAccepted, uncondinal); + createPairs(acc, LicenseAgreementDlg, VerifyReadyDlg, ifLicenseAccepted, backFromVerifyReadyDlg); + + createPairs(acc, InstallDirDlg, ShortcutPromptDlg, uncondinal); + + createPairs(acc, ShortcutPromptDlg, VerifyReadyDlg, uncondinal, backFromVerifyReadyDlg); + + return entries; + } + + private static final Map> DEFAULT_SPECS; + + private static final String CONDITION_ALWAYS = "1"; + private static final String CONDITION_NOT_INSTALLED = "NOT Installed"; + + static { + + var specs = new HashMap>(); + + // Verbatim WiX "Minimal" dialog set. + specs.put(UIConfig.build() + .withLicenseDlg() + .create(), () -> { + return build(WixUI.MINIMAL).create(); + }); + + // Standard WiX "Minimal" dialog set without the license dialog. + // The license dialog is removed by overriding the default "Show" + // action with the condition that always evaluates to "FALSE". + specs.put(UIConfig.build() + .create(), () -> { + return build(WixUI.MINIMAL).hideDialogs(suppressShowAction(WelcomeEulaDlg).after(ProgressDlg)).create(); + }); + + DEFAULT_SPECS = Collections.unmodifiableMap(specs); + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixDialog.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixDialog.java new file mode 100644 index 00000000000..95c415dcaa0 --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixDialog.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.wixui; + +/** + * Standard WiX dialogs. + */ +enum WixDialog implements Dialog { + InstallDirDlg, + LicenseAgreementDlg, + ProgressDlg, + VerifyReadyDlg, + WelcomeDlg, + WelcomeEulaDlg, + ; + + @Override + public String id() { + return name(); + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixUI.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixUI.java new file mode 100644 index 00000000000..1b87a7fee2d --- /dev/null +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/wixui/WixUI.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.wixui; + +import java.util.Objects; + +/** + * Standard WiX UI. + */ +public enum WixUI { + MINIMAL("WixUI_Minimal"), + INSTALL_DIR("WixUI_InstallDir"), + ; + + WixUI(String id) { + this.id = Objects.requireNonNull(id); + } + + public String id() { + return id; + } + + private final String id; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java index 6b3fae07d41..6c92e820f2e 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MsiDatabase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,9 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -42,9 +44,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.Stream; -final class MsiDatabase { +public final class MsiDatabase { static MsiDatabase load(Path msiFile, Path idtFileOutputDir, Set tableNames) { try { @@ -61,7 +64,7 @@ final class MsiDatabase { .execute(0); var tables = orderedTableNames.stream().map(tableName -> { - return Map.entry(tableName, idtFileOutputDir.resolve(tableName + ".idt")); + return Map.entry(tableName, idtFileOutputDir.resolve(tableName.tableName() + ".idt")); }).filter(e -> { return Files.exists(e.getValue()); }).collect(Collectors.toMap(Map.Entry::getKey, e -> { @@ -81,6 +84,8 @@ final class MsiDatabase { FILE("File"), PROPERTY("Property"), SHORTCUT("Shortcut"), + CONTROL_EVENT("ControlEvent"), + INSTALL_UI_SEQUENCE("InstallUISequence"), ; Table(String name) { @@ -95,6 +100,7 @@ final class MsiDatabase { static final Set
FIND_PROPERTY_REQUIRED_TABLES = Set.of(PROPERTY); static final Set
LIST_SHORTCUTS_REQUIRED_TABLES = Set.of(COMPONENT, DIRECTORY, FILE, SHORTCUT); + static final Set
UI_ALTERATIONS_REQUIRED_TABLES = Set.of(CONTROL_EVENT, INSTALL_UI_SEQUENCE); } @@ -120,12 +126,7 @@ final class MsiDatabase { } Collection listShortcuts() { - var shortcuts = tables.get(Table.SHORTCUT); - if (shortcuts == null) { - return List.of(); - } - return IntStream.range(0, shortcuts.rowCount()).mapToObj(i -> { - var row = shortcuts.row(i); + return rows(Table.SHORTCUT).map(row -> { var shortcutPath = directoryPath(row.apply("Directory_")).resolve(fileNameFromFieldValue(row.apply("Name"))); var workDir = directoryPath(row.apply("WkDir")); var shortcutTarget = Path.of(expandFormattedString(row.apply("Target"))); @@ -133,6 +134,53 @@ final class MsiDatabase { }).toList(); } + UIAlterations uiAlterations() { + + var includeActions = Set.of("WelcomeEulaDlg", "WelcomeDlg"); + var actions = actionSequence(Table.INSTALL_UI_SEQUENCE).filter(action -> { + return includeActions.contains(action.action()); + }).sorted(Comparator.comparing(Action::sequence)).toList(); + + // Custom jpackage dialogs. + var jpackageDialogs = Set.of("InstallDirNotEmptyDlg", "ShortcutPromptDlg"); + + var includeControls = Set.of("Next", "Back"); + var controlEvents = rows(Table.CONTROL_EVENT).map(row -> { + return new ControlEvent( + row.apply("Dialog_"), + row.apply("Control_"), + row.apply("Event"), + row.apply("Argument"), + row.apply("Condition"), + Integer.parseInt(row.apply("Ordering"))); + }).filter(controlEvent -> { + if (jpackageDialogs.contains(controlEvent.dialog())) { + // Include controls of all custom jpackage dialogs. + return true; + } + + if (controlEvent.ordering() >= 6) { + // jpackage assumes the standard WiX UI doesn't define control events + // for dialog sequences it alters with the order higher than 6. + // Include all such items. + + if (includeControls.contains(controlEvent.control())) { + // Include only specific controls that jpackage alters. + return true; + } + } + + return false; + }).sorted(Comparator.comparing(ControlEvent::dialog) + .thenComparing(ControlEvent::control) + .thenComparing(ControlEvent::event) + .thenComparing(ControlEvent::argument) + .thenComparing(ControlEvent::condition) + .thenComparing(Comparator.comparingInt(ControlEvent::ordering))).toList(); + + return new UIAlterations(actions, controlEvents); + } + record Shortcut(Path path, Path target, Path workDir) { Shortcut { @@ -148,6 +196,49 @@ final class MsiDatabase { } } + public record Action(String action, String condition, int sequence) { + public Action { + Objects.requireNonNull(action); + Objects.requireNonNull(condition); + } + } + + public record ControlEvent( + String dialog, + String control, + String event, + String argument, + String condition, + int ordering) { + + public ControlEvent { + Objects.requireNonNull(dialog); + Objects.requireNonNull(control); + Objects.requireNonNull(event); + Objects.requireNonNull(argument); + Objects.requireNonNull(condition); + } + } + + public record UIAlterations(Collection installUISequence, Collection controlEvents) { + + public UIAlterations { + Objects.requireNonNull(installUISequence); + } + } + + private Stream actionSequence(Table tableName) { + return rows(tableName).map(row -> { + return new Action(row.apply("Action"), row.apply("Condition"), Integer.parseInt(row.apply("Sequence"))); + }); + } + + private Stream> rows(Table tableName) { + return Optional.ofNullable(tables.get(tableName)).stream().flatMap(table -> { + return IntStream.range(0, table.rowCount()).mapToObj(table::row); + }); + } + private Path directoryPath(String directoryId) { var table = tables.get(Table.DIRECTORY); Path result = null; @@ -339,20 +430,28 @@ final class MsiDatabase { var columns = headerLines.get(0).split("\t"); - var header = headerLines.get(2).split("\t", 4); - if (header.length == 3) { - if (Pattern.matches("^[1-9]\\d+$", header[0])) { - charset = Charset.forName(header[0]); - } else { - throw new IllegalArgumentException(String.format( - "Unexpected charset name [%s] in [%s] file", header[0], idtFile)); - } - } else if (header.length != 2) { + int tableNameIdx; + + var header = headerLines.get(2).split("\t"); + if (Pattern.matches("^[1-9]\\d+$", header[0])) { + charset = Charset.forName(header[0]); + tableNameIdx = 1; + } else { + tableNameIdx = 0; + } + + if (header.length < (tableNameIdx + 2)) { throw new IllegalArgumentException(String.format( "Unexpected number of fields (%d) in the 3rd line of [%s] file", header.length, idtFile)); } + var tableName = header[tableNameIdx]; + List primaryKeys = Arrays.asList(header).subList(tableNameIdx + 1, header.length); + + TKit.trace(String.format("Table in [%s] file: charset=%s; name=%s; primary keys=%s", + idtFile, charset, tableName, primaryKeys)); + return new IdtFileHeader(charset, List.of(columns)); } catch (IOException ex) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java index c7b55ed1de7..98e9c4bfe61 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java @@ -276,6 +276,11 @@ public class WindowsHelper { return MsiDatabaseCache.INSTANCE.findProperty(cmd.outputBundle(), propertyName).orElseThrow(); } + public static MsiDatabase.UIAlterations getUIAlterations(JPackageCommand cmd) { + cmd.verifyIsOfType(PackageType.WIN_MSI); + return MsiDatabaseCache.INSTANCE.uiAlterations(cmd.outputBundle()); + } + static Collection getMsiShortcuts(JPackageCommand cmd) { cmd.verifyIsOfType(PackageType.WIN_MSI); return MsiDatabaseCache.INSTANCE.listShortcuts(cmd.outputBundle()); @@ -572,6 +577,10 @@ public class WindowsHelper { return ensureTables(msiPath, MsiDatabase.Table.LIST_SHORTCUTS_REQUIRED_TABLES).listShortcuts(); } + MsiDatabase.UIAlterations uiAlterations(Path msiPath) { + return ensureTables(msiPath, MsiDatabase.Table.UI_ALTERATIONS_REQUIRED_TABLES).uiAlterations(); + } + MsiDatabase ensureTables(Path msiPath, Set tableNames) { Objects.requireNonNull(msiPath); try { diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/help-windows.txt b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/help-windows.txt index b72ba310f3f..7a098b847ad 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/help-windows.txt +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/help-windows.txt @@ -203,3 +203,5 @@ Platform dependent options for creating the application package: URL of available application update information --win-upgrade-uuid UUID associated with upgrades for this package + --win-with-ui + Enforces the installer to have UI diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/jpackage-options.md b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/jpackage-options.md index 60b79451547..64d3c6c075b 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/jpackage-options.md +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/jpackage-options.md @@ -61,3 +61,4 @@ | --win-shortcut-prompt | win-exe, win-msi | x | x | | USE_LAST | | --win-update-url | win-exe, win-msi | x | x | | USE_LAST | | --win-upgrade-uuid | win-exe, win-msi | x | x | | USE_LAST | +| --win-with-ui | win-exe, win-msi | x | x | | USE_LAST | diff --git a/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/WixVariablesTest.java b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/WixVariablesTest.java new file mode 100644 index 00000000000..a9d83c8d544 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/WixVariablesTest.java @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal; + +import static jdk.jpackage.internal.WixToolset.WixToolsetType.Wix4; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import jdk.jpackage.internal.WixToolset.WixToolsetType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; + +class WixVariablesTest { + + @Test + void test_define() { + assertEquals(List.of("-d", "foo=yes"), new WixVariables().define("foo").toWixCommandLine(Wix4)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void test_define_null(boolean immutable) { + assertThrows(NullPointerException.class, vars -> { + vars.define(null); + }, create(immutable)); + } + + @Test + void test_put() { + assertEquals(List.of("-d", "foo=bar"), new WixVariables().put("foo", "bar").toWixCommandLine(Wix4)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void test_put_null(boolean immutable) { + assertThrows(NullPointerException.class, vars -> { + vars.put("foo", null); + }, create(immutable)); + + assertThrows(NullPointerException.class, vars -> { + vars.put(null, "foo"); + }, create(immutable)); + } + + @Test + void test_putAll() { + assertEquals(List.of("-d", "foo=bar"), new WixVariables().putAll(Map.of("foo", "bar")).toWixCommandLine(Wix4)); + assertEquals(List.of("-d", "foo=yes"), new WixVariables().putAll(new WixVariables().define("foo")).toWixCommandLine(Wix4)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void test_putAll_null(boolean immutable) { + + assertThrows(NullPointerException.class, vars -> { + vars.putAll((Map)null); + }, create(immutable)); + + assertThrows(NullPointerException.class, vars -> { + vars.putAll((WixVariables)null); + }, create(immutable)); + + final var expectedExceptionType = immutable ? IllegalStateException.class : NullPointerException.class; + + var other = new HashMap(); + + other.clear(); + other.put("foo", null); + assertThrows(expectedExceptionType, vars -> { + vars.putAll(other); + }, create(immutable)); + + other.clear(); + other.put(null, "foo"); + assertThrows(expectedExceptionType, vars -> { + vars.putAll(other); + }, create(immutable)); + } + + @Test + void testImmutable() { + var vars = new WixVariables().define("foo").createdImmutableCopy(); + + assertThrows(IllegalStateException.class, _ -> { + vars.putAll(Map.of()); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.putAll(new WixVariables()); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.define("foo"); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.put("foo", "bar"); + }, vars); + + for (var allowOverrides : List.of(true, false)) { + assertThrows(IllegalStateException.class, _ -> { + vars.allowOverrides(allowOverrides); + }, vars); + } + } + + @Test + void testDefaultOverridable() { + var vars = new WixVariables().define("foo"); + + assertThrows(IllegalStateException.class, _ -> { + vars.define("foo"); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.put("foo", "no"); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.put("foo", "yes"); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.putAll(Map.of("foo", "A", "bar", "B")); + }, vars); + + assertThrows(IllegalStateException.class, _ -> { + vars.putAll(new WixVariables().putAll(Map.of("foo", "A", "bar", "B"))); + }, vars); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testOverridable_define(boolean overridable) { + var vars = new WixVariables().allowOverrides(overridable).define("foo"); + + if (overridable) { + vars.define("foo"); + } else { + assertThrows(IllegalStateException.class, _ -> { + vars.define("foo"); + }, vars); + vars.allowOverrides(true); + vars.define("foo"); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testOverridable_put(boolean overridable) { + var vars = new WixVariables().allowOverrides(overridable).define("foo"); + + if (overridable) { + vars.put("foo", "bar"); + assertEquals(List.of("-d", "foo=bar"), vars.toWixCommandLine(Wix4)); + } else { + assertThrows(IllegalStateException.class, _ -> { + vars.put("foo", "bar"); + }, vars); + vars.allowOverrides(true); + vars.put("foo", "bar"); + assertEquals(List.of("-d", "foo=bar"), vars.toWixCommandLine(Wix4)); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testOverridable_putAll(boolean overridable) { + var vars = new WixVariables().allowOverrides(overridable).define("foo"); + + var other = Map.of("foo", "A", "bar", "B"); + + if (overridable) { + vars.putAll(other); + assertEquals(List.of("-d", "bar=B", "-d", "foo=A"), vars.toWixCommandLine(Wix4)); + } else { + assertThrows(IllegalStateException.class, _ -> { + vars.putAll(other); + }, vars); + vars.allowOverrides(true); + vars.putAll(other); + assertEquals(List.of("-d", "bar=B", "-d", "foo=A"), vars.toWixCommandLine(Wix4)); + } + } + + @Test + void test_createdImmutableCopy() { + var vars = new WixVariables().define("foo"); + + var copy = vars.createdImmutableCopy(); + + assertNotSame(vars, copy); + + assertSame(copy, copy.createdImmutableCopy()); + + assertEquals(List.of("-d", "foo=yes"), copy.toWixCommandLine(Wix4)); + + vars.allowOverrides(true).put("foo", "bar"); + assertEquals(List.of("-d", "foo=bar"), vars.toWixCommandLine(Wix4)); + assertEquals(List.of("-d", "foo=yes"), copy.toWixCommandLine(Wix4)); + } + + @ParameterizedTest + @EnumSource(WixToolsetType.class) + void test_toWixCommandLine(WixToolsetType wixType) { + var args = new WixVariables().define("foo").put("bar", "a").toWixCommandLine(wixType); + + var expectedArgs = switch (wixType) { + case Wix3 -> { + yield List.of("-dbar=a", "-dfoo=yes"); + } + case Wix4 -> { + yield List.of("-d", "bar=a", "-d", "foo=yes"); + } + }; + + assertEquals(expectedArgs, args); + } + + private static WixVariables create(boolean immutable) { + var vars = new WixVariables(); + if (immutable) { + return vars.createdImmutableCopy(); + } else { + return vars; + } + } + + private static void assertThrows( + Class expectedExceptionType, Consumer mutator, WixVariables vars) { + + var content = vars.toWixCommandLine(Wix4); + + assertThrowsExactly(expectedExceptionType, () -> { + mutator.accept(vars); + }); + + assertEquals(content, vars.toWixCommandLine(Wix4)); + } +} diff --git a/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/wixui/UISpecTest.java b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/wixui/UISpecTest.java new file mode 100644 index 00000000000..08e7796ca50 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/windows/jdk.jpackage/jdk/jpackage/internal/wixui/UISpecTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal.wixui; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class UISpecTest { + + @ParameterizedTest + @MethodSource + void test(UIConfig cfg) { + var uiSpec = UISpec.create(cfg); + + validateCustomDialogSequence(uiSpec.customDialogSequence()); + } + + private static Collection test() { + + var testCases = new ArrayList(); + + for (boolean withInstallDirChooserDlg : List.of(true, false)) { + for (boolean withShortcutPromptDlg : List.of(true, false)) { + for (boolean withLicenseDlg : List.of(true, false)) { + testCases.add(UIConfig.build() + .withInstallDirChooserDlg(withInstallDirChooserDlg) + .withShortcutPromptDlg(withShortcutPromptDlg) + .withLicenseDlg(withLicenseDlg) + .create()); + } + } + } + + return testCases; + } + + static void validateCustomDialogSequence(Map seq) { + seq.entrySet().stream().map(DialogControl::new).collect(Collectors.toMap(x -> x, x -> x, (a, b) -> { + throw new AssertionError(String.format( + "Dialog [%s] has multiple Publish elements associated with [%s] control", a.host(), a.hostedControl())); + })); + } + + record DialogControl(Dialog host, Control hostedControl) { + DialogControl { + Objects.requireNonNull(host); + Objects.requireNonNull(hostedControl); + } + + DialogControl(DialogPair pair, Publish publish) { + this(pair.first(), publish.control()); + } + + DialogControl(Map.Entry e) { + this(e.getKey(), e.getValue()); + } + } +} diff --git a/test/jdk/tools/jpackage/junit/windows/junit.java b/test/jdk/tools/jpackage/junit/windows/junit.java index d27a282f0dc..1a1d0d58f7e 100644 --- a/test/jdk/tools/jpackage/junit/windows/junit.java +++ b/test/jdk/tools/jpackage/junit/windows/junit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,3 +28,20 @@ * jdk/jpackage/internal/ExecutableOSVersionTest.java * @run junit jdk.jpackage/jdk.jpackage.internal.ExecutableOSVersionTest */ + +/* @test + * @summary WixVariables unit tests + * @requires (os.family == "windows") + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/WixVariablesTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.WixVariablesTest + */ + +/* @test + * @summary UiSpec unit tests + * @requires (os.family == "windows") + * @modules jdk.jpackage/jdk.jpackage.internal.wixui:open + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/wixui/UISpecTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.wixui.UISpecTest + */ diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/ControlEvents.md new file mode 100644 index 00000000000..39e6b535e69 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/ControlEvents.md @@ -0,0 +1,8 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | +| InstallDirNotEmptyDlg | No | NewDialog | InstallDirDlg | 1 | 1 | +| InstallDirNotEmptyDlg | Yes | NewDialog | ShortcutPromptDlg | 1 | 1 | +| ShortcutPromptDlg | Back | NewDialog | InstallDirDlg | 1 | 1 | +| ShortcutPromptDlg | Cancel | SpawnDialog | CancelDlg | 1 | 1 | +| ShortcutPromptDlg | Next | NewDialog | VerifyReadyDlg | 1 | 1 | +| VerifyReadyDlg | Back | NewDialog | ShortcutPromptDlg | NOT Installed | 6 | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/InstallUISequence.md new file mode 100644 index 00000000000..e583dfa4982 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license+shortcut_prompt/InstallUISequence.md @@ -0,0 +1,3 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | NOT Installed OR PATCH | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/ControlEvents.md new file mode 100644 index 00000000000..1d908b0508a --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/ControlEvents.md @@ -0,0 +1,4 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | +| InstallDirNotEmptyDlg | No | NewDialog | InstallDirDlg | 1 | 1 | +| InstallDirNotEmptyDlg | Yes | NewDialog | VerifyReadyDlg | 1 | 1 | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/InstallUISequence.md new file mode 100644 index 00000000000..e583dfa4982 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+license/InstallUISequence.md @@ -0,0 +1,3 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | NOT Installed OR PATCH | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/ControlEvents.md new file mode 100644 index 00000000000..93d11c944e0 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/ControlEvents.md @@ -0,0 +1,10 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | +| InstallDirDlg | Back | NewDialog | WelcomeDlg | NOT Installed | 6 | +| InstallDirNotEmptyDlg | No | NewDialog | InstallDirDlg | 1 | 1 | +| InstallDirNotEmptyDlg | Yes | NewDialog | ShortcutPromptDlg | 1 | 1 | +| ShortcutPromptDlg | Back | NewDialog | InstallDirDlg | 1 | 1 | +| ShortcutPromptDlg | Cancel | SpawnDialog | CancelDlg | 1 | 1 | +| ShortcutPromptDlg | Next | NewDialog | VerifyReadyDlg | 1 | 1 | +| VerifyReadyDlg | Back | NewDialog | ShortcutPromptDlg | NOT Installed | 6 | +| WelcomeDlg | Next | NewDialog | InstallDirDlg | NOT Installed | 6 | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/InstallUISequence.md new file mode 100644 index 00000000000..e583dfa4982 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser+shortcut_prompt/InstallUISequence.md @@ -0,0 +1,3 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | NOT Installed OR PATCH | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/ControlEvents.md new file mode 100644 index 00000000000..2fcc387732a --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/ControlEvents.md @@ -0,0 +1,6 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | +| InstallDirDlg | Back | NewDialog | WelcomeDlg | NOT Installed | 6 | +| InstallDirNotEmptyDlg | No | NewDialog | InstallDirDlg | 1 | 1 | +| InstallDirNotEmptyDlg | Yes | NewDialog | VerifyReadyDlg | 1 | 1 | +| WelcomeDlg | Next | NewDialog | InstallDirDlg | NOT Installed | 6 | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/InstallUISequence.md new file mode 100644 index 00000000000..e583dfa4982 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/dir_chooser/InstallUISequence.md @@ -0,0 +1,3 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | NOT Installed OR PATCH | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/ControlEvents.md new file mode 100644 index 00000000000..72e8b80ca08 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/ControlEvents.md @@ -0,0 +1,7 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | +| LicenseAgreementDlg | Next | NewDialog | ShortcutPromptDlg | LicenseAccepted = "1" | 6 | +| ShortcutPromptDlg | Back | NewDialog | LicenseAgreementDlg | 1 | 1 | +| ShortcutPromptDlg | Cancel | SpawnDialog | CancelDlg | 1 | 1 | +| ShortcutPromptDlg | Next | NewDialog | VerifyReadyDlg | 1 | 1 | +| VerifyReadyDlg | Back | NewDialog | ShortcutPromptDlg | NOT Installed | 6 | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/InstallUISequence.md new file mode 100644 index 00000000000..e583dfa4982 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license+shortcut_prompt/InstallUISequence.md @@ -0,0 +1,3 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | NOT Installed OR PATCH | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/ControlEvents.md new file mode 100644 index 00000000000..7a9bcca86f3 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/ControlEvents.md @@ -0,0 +1,2 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/InstallUISequence.md new file mode 100644 index 00000000000..3008e0db54b --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/license/InstallUISequence.md @@ -0,0 +1,4 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | Installed AND PATCH | +| WelcomeEulaDlg | NOT Installed | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/ControlEvents.md new file mode 100644 index 00000000000..a93b0ecbff1 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/ControlEvents.md @@ -0,0 +1,7 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | +| ShortcutPromptDlg | Back | NewDialog | WelcomeDlg | NOT Installed | 6 | +| ShortcutPromptDlg | Cancel | SpawnDialog | CancelDlg | 1 | 1 | +| ShortcutPromptDlg | Next | NewDialog | VerifyReadyDlg | 1 | 1 | +| VerifyReadyDlg | Back | NewDialog | ShortcutPromptDlg | NOT Installed | 6 | +| WelcomeDlg | Next | NewDialog | ShortcutPromptDlg | NOT Installed | 6 | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/InstallUISequence.md new file mode 100644 index 00000000000..e583dfa4982 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/shortcut_prompt/InstallUISequence.md @@ -0,0 +1,3 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | NOT Installed OR PATCH | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/ControlEvents.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/ControlEvents.md new file mode 100644 index 00000000000..7a9bcca86f3 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/ControlEvents.md @@ -0,0 +1,2 @@ +| Dialog | Control | Event | Argument | Condition | Ordering | +| --- | --- | --- | --- | --- | --- | diff --git a/test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/InstallUISequence.md b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/InstallUISequence.md new file mode 100644 index 00000000000..6e3eef39f43 --- /dev/null +++ b/test/jdk/tools/jpackage/resources/WinInstallerUiTest/ui/InstallUISequence.md @@ -0,0 +1,4 @@ +| Action | Condition | +| --- | --- | +| WelcomeDlg | Installed AND PATCH | +| WelcomeEulaDlg | 0 | diff --git a/test/jdk/tools/jpackage/resources/msi-export.js b/test/jdk/tools/jpackage/resources/msi-export.js index d639f19ca44..6cb5ab5a781 100644 --- a/test/jdk/tools/jpackage/resources/msi-export.js +++ b/test/jdk/tools/jpackage/resources/msi-export.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,7 +71,7 @@ function exportTables(db, outputDir, requestedTableNames) { var msi = WScript.arguments(0) var outputDir = WScript.arguments(1) var tables = {} - for (var i = 0; i !== WScript.arguments.Count(); i++) { + for (var i = 2; i !== WScript.arguments.Count(); i++) { tables[WScript.arguments(i)] = true } diff --git a/test/jdk/tools/jpackage/windows/WinInstallerUiTest.java b/test/jdk/tools/jpackage/windows/WinInstallerUiTest.java index d6cebde6444..e9238c1a6f7 100644 --- a/test/jdk/tools/jpackage/windows/WinInstallerUiTest.java +++ b/test/jdk/tools/jpackage/windows/WinInstallerUiTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,114 +21,299 @@ * questions. */ +import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import jdk.jpackage.internal.util.Slot; -import jdk.jpackage.test.Annotations.Parameters; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.MsiDatabase; +import jdk.jpackage.test.MsiDatabase.UIAlterations; +import jdk.jpackage.test.MsiDatabase.ControlEvent; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.RunnablePackageTest.Action; import jdk.jpackage.test.TKit; +import jdk.jpackage.test.WindowsHelper; /** - * Test all possible combinations of --win-dir-chooser, --win-shortcut-prompt + * Test combinations of --win-dir-chooser, --win-shortcut-prompt, --win-with-ui, * and --license parameters. */ /* * @test - * @summary jpackage with --win-dir-chooser, --win-shortcut-prompt and --license parameters + * @summary jpackage with --win-dir-chooser, --win-shortcut-prompt, --with-with-ui and --license parameters * @library /test/jdk/tools/jpackage/helpers * @key jpackagePlatformPackage * @build jdk.jpackage.test.* * @build WinInstallerUiTest * @requires (os.family == "windows") + * @requires (jpackage.test.SQETest != null) + * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=WinInstallerUiTest + * --jpt-exclude=(dir_chooser) + * --jpt-exclude=(license) + * --jpt-exclude=+ui + */ + +/* + * @test + * @summary jpackage with --win-dir-chooser, --win-shortcut-prompt, --with-with-ui and --license parameters + * @library /test/jdk/tools/jpackage/helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @build WinInstallerUiTest + * @requires (os.family == "windows") + * @requires (jpackage.test.SQETest == null) * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=WinInstallerUiTest */ + public class WinInstallerUiTest { - public WinInstallerUiTest(Boolean withDirChooser, Boolean withLicense, - Boolean withShortcutPrompt) { - this.withShortcutPrompt = withShortcutPrompt; - this.withDirChooser = withDirChooser; - this.withLicense = withLicense; + @Test + @ParameterSupplier + public void test(TestSpec spec) { + spec.run(); } - @Parameters - public static List data() { - List data = new ArrayList<>(); - for (var withDirChooser : List.of(Boolean.TRUE, Boolean.FALSE)) { - for (var withLicense : List.of(Boolean.TRUE, Boolean.FALSE)) { - for (var withShortcutPrompt : List.of(Boolean.TRUE, Boolean.FALSE)) { + public static void updateExpectedMsiTables() { + for (var spec : testCases()) { + spec.createTest(true).addBundleVerifier(cmd -> { + spec.save(WindowsHelper.getUIAlterations(cmd)); + }).run(Action.CREATE); + } + } + + record TestSpec( + boolean withDirChooser, + boolean withLicense, + boolean withShortcutPrompt, + boolean withUi) { + + TestSpec { + if (!withDirChooser && !withLicense && !withShortcutPrompt && !withUi) { + throw new IllegalArgumentException(); + } + } + + @Override + public String toString() { + var tokens = new ArrayList(); + if (withDirChooser) { + tokens.add("dir_chooser"); + } + + if (withShortcutPrompt) { + tokens.add("shortcut_prompt"); + } + + if (withLicense) { + tokens.add("license"); + } + + if (withUi) { + tokens.add("ui"); + } + + return tokens.stream().sorted().collect(Collectors.joining("+")); + } + + TestSpec copyWithUi(boolean withUi) { + return new TestSpec(withDirChooser, withLicense, withShortcutPrompt, withUi); + } + + TestSpec copyWithUi() { + return copyWithUi(true); + } + + TestSpec copyWithoutUi() { + return copyWithUi(false); + } + + void run() { + createTest(false).forTypes(PackageType.WIN_MSI).addBundleVerifier(cmd -> { + var expectedFilesDir = expectedFilesDir(); + + var expectedInstallUISequence = Files.readAllLines(expectedFilesDir.resolve(INSTALL_UI_SEQUENCE_FILE)); + var expectedControlEvents = Files.readAllLines(expectedFilesDir.resolve(CONTROL_EVENTS_FILE)); + + var uiAlterations = WindowsHelper.getUIAlterations(cmd); + + var actualInstallUISequence = actionSequenceToMarkdownTable(uiAlterations.installUISequence()); + var actualControlEvents = controlEventsToMarkdownTable(uiAlterations.controlEvents()); + + TKit.assertStringListEquals(expectedInstallUISequence, actualInstallUISequence, + String.format("Check alterations to the `InstallUISequence` MSI table match the contents of [%s] file", + expectedFilesDir.resolve(INSTALL_UI_SEQUENCE_FILE))); + + TKit.assertStringListEquals(expectedControlEvents, actualControlEvents, + String.format("Check alterations to the `ControlEvents` MSI table match the contents of [%s] file", + expectedFilesDir.resolve(CONTROL_EVENTS_FILE))); + }).run(); + } + + PackageTest createTest(boolean onlyMsi) { + return new PackageTest() + .forTypes(onlyMsi ? Set.of(PackageType.WIN_MSI) : PackageType.WINDOWS) + .configureHelloApp() + .addInitializer(JPackageCommand::setFakeRuntime) + .addInitializer(this::setPackageName) + .mutate(test -> { + if (withDirChooser) { + test.addInitializer(cmd -> cmd.addArgument("--win-dir-chooser")); + } + + if (withShortcutPrompt) { + test.addInitializer(cmd -> { + cmd.addArgument("--win-shortcut-prompt"); + cmd.addArgument("--win-menu"); + cmd.addArgument("--win-shortcut"); + }); + } + + if (withLicense) { + setLicenseFile(test); + } + + if (withUi) { + test.addInitializer(cmd -> cmd.addArgument("--win-with-ui")); + } + }); + } + + private void setPackageName(JPackageCommand cmd) { + StringBuilder sb = new StringBuilder(cmd.name()); + sb.append("With"); + if (withDirChooser) { + sb.append("Dc"); // DirChooser + } + if (withShortcutPrompt) { + sb.append("Sp"); // ShortcutPrompt + } + if (withLicense) { + sb.append("L"); // License + } + if (withUi) { + sb.append("Ui"); // UI + } + cmd.setArgumentValue("--name", sb.toString()); + } + + void save(UIAlterations uiAlterations) { + var expectedFilesDir = expectedFilesDir(); + + write(expectedFilesDir.resolve(INSTALL_UI_SEQUENCE_FILE), + actionSequenceToMarkdownTable(uiAlterations.installUISequence())); + + write(expectedFilesDir.resolve(CONTROL_EVENTS_FILE), + controlEventsToMarkdownTable(uiAlterations.controlEvents())); + } + + private Path expectedFilesDir() { + if ((withDirChooser || withShortcutPrompt || withLicense) && withUi) { + return copyWithoutUi().expectedFilesDir(); + } else { + return EXPECTED_MSI_TABLES_ROOT.resolve(toString()); + } + } + + private void write(Path file, List lines) { + try { + Files.createDirectories(file.getParent()); + Files.write(file, lines, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static List toMarkdownTable(List header, Stream content) { + return Stream.of( + Stream.of(header.toArray(String[]::new)), + Stream.of(Collections.nCopies(header.size(), "---").toArray(String[]::new)), + content + ).flatMap(x -> x).map(row -> { + return Stream.of(row).map(v -> { + // Escape the pipe (|) character. + return v.replaceAll(Pattern.quote("|"), "|"); + }).collect(Collectors.joining(" | ", "| ", " |")); + }).toList(); + } + + private static List actionSequenceToMarkdownTable(Collection actions) { + return toMarkdownTable( + List.of("Action", "Condition"), + actions.stream().map(action -> { + return toStringArray(action.action(), action.condition()); + }) + ); + } + + private static List controlEventsToMarkdownTable(Collection controlEvents) { + return toMarkdownTable( + List.of("Dialog", "Control", "Event", "Argument", "Condition", "Ordering"), + controlEvents.stream().map(controlEvent -> { + return toStringArray( + controlEvent.dialog(), + controlEvent.control(), + controlEvent.event(), + controlEvent.argument(), + controlEvent.condition(), + Integer.toString(controlEvent.ordering())); + }) + ); + } + + private static String[] toStringArray(String... items) { + return items; + } + + private static final String CONTROL_EVENTS_FILE = "ControlEvents.md"; + private static final String INSTALL_UI_SEQUENCE_FILE = "InstallUISequence.md"; + } + + public static Collection test() { + return Stream.concat( + testCases().stream().filter(Predicate.not(TestSpec::withUi)).map(TestSpec::copyWithUi), + testCases().stream() + ).map(v -> { + return new Object[] {v}; + }).toList(); + } + + private static Collection testCases() { + var testCases = new ArrayList(); + + for (var withDirChooser : List.of(true, false)) { + for (var withLicense : List.of(true, false)) { + for (var withShortcutPrompt : List.of(true, false)) { if (!withDirChooser && !withLicense && !withShortcutPrompt) { // Duplicates SimplePackageTest continue; } - if (withDirChooser && !withLicense && !withShortcutPrompt) { - // Duplicates WinDirChooserTest - continue; - } - - if (!withDirChooser && withLicense && !withShortcutPrompt) { - // Duplicates LicenseTest - continue; - } - - data.add(new Object[]{withDirChooser, withLicense, - withShortcutPrompt}); + testCases.add(new TestSpec(withDirChooser, withLicense, withShortcutPrompt, false)); } } } - return data; - } + // Enforce UI + testCases.add(new TestSpec(false, false, false, true)); - @Test - public void test() { - PackageTest test = new PackageTest() - .forTypes(PackageType.WINDOWS) - .configureHelloApp(); - - test.addInitializer(JPackageCommand::setFakeRuntime); - test.addInitializer(this::setPackageName); - - if (withDirChooser) { - test.addInitializer(cmd -> cmd.addArgument("--win-dir-chooser")); - } - - if (withShortcutPrompt) { - test.addInitializer(cmd -> { - cmd.addArgument("--win-shortcut-prompt"); - cmd.addArgument("--win-menu"); - cmd.addArgument("--win-shortcut"); - }); - } - - if (withLicense) { - setLicenseFile(test); - } - - test.run(); - } - - private void setPackageName(JPackageCommand cmd) { - StringBuilder sb = new StringBuilder(cmd.name()); - sb.append("With"); - if (withDirChooser) { - sb.append("Dc"); // DirChooser - } - if (withShortcutPrompt) { - sb.append("Sp"); // ShortcutPrompt - } - if (withLicense) { - sb.append("L"); // License - } - cmd.setArgumentValue("--name", sb.toString()); + return testCases; } private static void setLicenseFile(PackageTest test) { @@ -143,9 +328,8 @@ public class WinInstallerUiTest { }); } - private final boolean withDirChooser; - private final boolean withLicense; - private final boolean withShortcutPrompt; - private static final Path LICENSE_FILE = TKit.TEST_SRC_ROOT.resolve(Path.of("resources", "license.txt")); + + private static final Path EXPECTED_MSI_TABLES_ROOT = TKit.TEST_SRC_ROOT.resolve( + Path.of("resources", WinInstallerUiTest.class.getSimpleName())); } diff --git a/test/jdk/tools/jpackage/windows/WinL10nTest.java b/test/jdk/tools/jpackage/windows/WinL10nTest.java index 14139013318..4d983df2598 100644 --- a/test/jdk/tools/jpackage/windows/WinL10nTest.java +++ b/test/jdk/tools/jpackage/windows/WinL10nTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,22 +21,22 @@ * questions. */ +import static jdk.jpackage.test.WindowsHelper.getWixTypeFromVerboseJPackageOutput; +import static jdk.jpackage.test.WindowsHelper.WixType.WIX3; + import java.io.IOException; import java.nio.file.Path; -import jdk.jpackage.test.TKit; -import jdk.jpackage.test.PackageTest; -import jdk.jpackage.test.PackageType; -import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.Annotations.Parameters; - import java.util.Arrays; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.jpackage.test.Annotations.Parameters; +import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Executor; -import static jdk.jpackage.test.WindowsHelper.WixType.WIX3; -import static jdk.jpackage.test.WindowsHelper.getWixTypeFromVerboseJPackageOutput; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.TKit; /* * @test @@ -124,8 +124,17 @@ public class WinL10nTest { var toolFileName = wixToolName + ".exe"; return (s) -> { s = s.trim(); - return s.startsWith(toolFileName) || ((s.contains(String.format("\\%s ", toolFileName)) && s. - contains(" -out "))); + + if (s.startsWith(toolFileName)) { + return true; + } + + // Accommodate for: + // 'C:\Program Files (x86)\WiX Toolset v3.14\bin\light.exe' ... + // light.exe ... + return Stream.of("\\%s ", "\\%s' ").map(format -> { + return String.format(format, toolFileName); + }).anyMatch(s::contains) && s.contains(" -out "); }; } From 3c09c2cd2d8e93844f366a3c9e2e3790ecc0d029 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Fri, 20 Mar 2026 02:38:41 +0000 Subject: [PATCH 040/160] 8267961: JInternalFrame.getNormalBounds() returns normalBounds when maximized state is false instead of bounds Reviewed-by: tr, dnguyen --- .../classes/javax/swing/JInternalFrame.java | 6 ++ .../TestNonMaximizedNormalBounds.java | 73 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 test/jdk/javax/swing/JInternalFrame/TestNonMaximizedNormalBounds.java diff --git a/src/java.desktop/share/classes/javax/swing/JInternalFrame.java b/src/java.desktop/share/classes/javax/swing/JInternalFrame.java index f3295cd6ad8..dec53d29e75 100644 --- a/src/java.desktop/share/classes/javax/swing/JInternalFrame.java +++ b/src/java.desktop/share/classes/javax/swing/JInternalFrame.java @@ -987,6 +987,9 @@ public class JInternalFrame extends JComponent implements = "Indicates whether this internal frame is maximized.") public void setMaximum(boolean b) throws PropertyVetoException { if (isMaximum == b) { + if (!b) { + normalBounds = null; + } return; } @@ -998,6 +1001,9 @@ public class JInternalFrame extends JComponent implements get it wrong... See, for example, getNormalBounds() */ isMaximum = b; firePropertyChange(IS_MAXIMUM_PROPERTY, oldValue, newValue); + if (!b) { + normalBounds = null; + } } /** diff --git a/test/jdk/javax/swing/JInternalFrame/TestNonMaximizedNormalBounds.java b/test/jdk/javax/swing/JInternalFrame/TestNonMaximizedNormalBounds.java new file mode 100644 index 00000000000..67faeeb5bb1 --- /dev/null +++ b/test/jdk/javax/swing/JInternalFrame/TestNonMaximizedNormalBounds.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 + * @key headful + * @bug 8267961 + * @summary Verify JInternalFrame.getNormalBounds() + * returns getBounds() value in non-maximized state + * @run main TestNonMaximizedNormalBounds + */ + +import java.awt.Rectangle; +import javax.swing.JDesktopPane; +import javax.swing.JInternalFrame; +import javax.swing.SwingUtilities; +import java.beans.PropertyVetoException; + +public class TestNonMaximizedNormalBounds { + + private static volatile Rectangle bounds; + private static volatile Rectangle normalBounds; + private static JInternalFrame jif; + + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(() -> { + Rectangle bounds = new Rectangle(96, 97, 98, 99); + Rectangle nbounds = new Rectangle(196, 197, 198, 199); + JDesktopPane p = new JDesktopPane(); + jif = new JInternalFrame(); + p.add(jif); + jif.setBounds(bounds); + jif.setNormalBounds(nbounds); + }); + Thread.sleep(100); + SwingUtilities.invokeAndWait(() -> { + try { + jif.setMaximum(false); + } catch (PropertyVetoException e) { + throw new RuntimeException(e); + } + }); + Thread.sleep(100); + SwingUtilities.invokeAndWait(() -> { + normalBounds = jif.getNormalBounds(); + bounds = jif.getBounds(); + }); + if (!normalBounds.equals(bounds)) { + System.out.println("normalBounds " + normalBounds + " getBounds " + bounds); + throw new RuntimeException("normalBounds not equal to getBounds in non-maximized state"); + } + } +} From b79449f0938c2abd63d44d613a0de6720913699f Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 20 Mar 2026 03:01:03 +0000 Subject: [PATCH 041/160] 8379431: [macos] jpackage issues unexpected warning when bundling an unsigned runtime package from the signed predefined runtime bundle Reviewed-by: almatvee --- .../jpackage/internal/MacPackageBuilder.java | 4 +- .../test/JPackageOutputValidator.java | 47 +++++++++++++------ .../jpackage/test/JPackageStringBundle.java | 14 ++++++ .../SigningRuntimeImagePackageTest.java | 9 ++++ .../tools/jpackage/share/AppVersionTest.java | 11 ++--- 5 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java index e19f234d0d6..1049496146f 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackageBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,7 +65,7 @@ final class MacPackageBuilder { } private static void validatePredefinedAppImage(MacPackage pkg) { - if (pkg.predefinedAppImageSigned().orElse(false)) { + if (pkg.predefinedAppImageSigned().orElse(false) && !pkg.isRuntimeInstaller()) { pkg.predefinedAppImage().ifPresent(predefinedAppImage -> { var thePackageFile = PackageFile.getPathInAppImage(APPLICATION_LAYOUT); if (!Files.exists(predefinedAppImage.resolve(thePackageFile))) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageOutputValidator.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageOutputValidator.java index c5c9c018b6a..ed14f40f65c 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageOutputValidator.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageOutputValidator.java @@ -43,7 +43,7 @@ public final class JPackageOutputValidator { } public JPackageOutputValidator(JPackageOutputValidator other) { - stdout = other.stdout; + stream = other.stream; match = other.match; matchTimestamps = other.matchTimestamps; stripTimestamps = other.stripTimestamps; @@ -59,7 +59,7 @@ public final class JPackageOutputValidator { * @return this */ public JPackageOutputValidator stdout() { - stdout = true; + stream = StdStream.OUT; return this; } @@ -68,7 +68,16 @@ public final class JPackageOutputValidator { * @return this */ public JPackageOutputValidator stderr() { - stdout = false; + stream = StdStream.ERR; + return this; + } + + /** + * Configures this validator to validate both stdout and stderr. + * @return this + */ + public JPackageOutputValidator stdoutAndStderr() { + stream = StdStream.OUT_AND_ERR; return this; } @@ -171,7 +180,7 @@ public final class JPackageOutputValidator { } public JPackageOutputValidator compose(JPackageOutputValidator other) { - if (stdout != other.stdout) { + if (stream != other.stream) { throw new IllegalArgumentException(); } if (match != other.match) { @@ -188,7 +197,7 @@ public final class JPackageOutputValidator { private Optional> toResultConsumer(JPackageCommand cmd) { return toStringIteratorConsumer(cmd).map(validator -> { - return toResultConsumer(validator, stdout, match, label()); + return toResultConsumer(validator, stream, match, label()); }); } @@ -244,7 +253,11 @@ public final class JPackageOutputValidator { } private String label() { - return stdout ? "'stdout'" : "'stderr'"; + return switch (stream) { + case OUT -> "'stdout'"; + case ERR -> "'stderr'"; + case OUT_AND_ERR -> "'stdout+stderr'"; + }; } private Consumer> decorate(TKit.TextStreamVerifier validator) { @@ -256,17 +269,16 @@ public final class JPackageOutputValidator { } private static Consumer toResultConsumer( - Consumer> validator, boolean stdout, boolean match, String label) { + Consumer> validator, StdStream stream, boolean match, String label) { Objects.requireNonNull(validator); Objects.requireNonNull(label); return result -> { - List content; - if (stdout) { - content = result.stdout(); - } else { - content = result.stderr(); - } + List content = switch (stream) { + case OUT -> result.stdout(); + case ERR -> result.stderr(); + case OUT_AND_ERR -> result.getOutput(); + }; if (match) { TKit.trace(String.format("Checking %s for exact match against defined validators...", label)); @@ -371,7 +383,14 @@ public final class JPackageOutputValidator { } } - boolean stdout = true; + private enum StdStream { + OUT, + ERR, + OUT_AND_ERR, + ; + } + + StdStream stream = StdStream.OUT; boolean match; boolean matchTimestamps; boolean stripTimestamps; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java index dcf994a0e93..688b16a8e09 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java @@ -78,6 +78,10 @@ public enum JPackageStringBundle { }); } + public Pattern cannedFormattedStringAsPattern(String key, Object ... args) { + return cannedFormattedStringAsPattern(key, MATCH_ANY, args); + } + static Pattern toPattern(MessageFormat mf, Function formatArgMapper, Object ... args) { Objects.requireNonNull(mf); Objects.requireNonNull(formatArgMapper); @@ -154,4 +158,14 @@ public enum JPackageStringBundle { private final Class i18nClass; private final Method i18nClass_getString; private final BiFunction formatter; + + private static final Function MATCH_ANY = new Function<>() { + + @Override + public Pattern apply(Object v) { + return PATTERN; + } + + private static final Pattern PATTERN = Pattern.compile(".*"); + }; } diff --git a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java index 11b466ed715..42a0e6fd664 100644 --- a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java @@ -37,6 +37,8 @@ import jdk.jpackage.internal.util.Slot; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageOutputValidator; +import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.MacHelper; import jdk.jpackage.test.MacHelper.ResolvableCertificateRequest; import jdk.jpackage.test.MacHelper.SignKeyOption; @@ -98,6 +100,13 @@ public class SigningRuntimeImagePackageTest { cmd.ignoreDefaultRuntime(true); cmd.removeArgumentWithValue("--input"); cmd.setArgumentValue("--runtime-image", predefinedRuntime.get()); + + // `warning.per.user.app.image.signed` warning doesn't apply to runtime bundling. + // Ensure the warning is not in the output. + new JPackageOutputValidator().add(TKit.assertTextStream( + JPackageStringBundle.MAIN.cannedFormattedStringAsPattern("warning.per.user.app.image.signed", "file") + ).negate()).stdoutAndStderr().applyTo(cmd); + }).addInstallVerifier(cmd -> { MacSignVerify.verifyAppImageSigned(cmd, signRuntime.certRequest()); }).run(); diff --git a/test/jdk/tools/jpackage/share/AppVersionTest.java b/test/jdk/tools/jpackage/share/AppVersionTest.java index 3b6ac5a9241..7ba1870def1 100644 --- a/test/jdk/tools/jpackage/share/AppVersionTest.java +++ b/test/jdk/tools/jpackage/share/AppVersionTest.java @@ -46,7 +46,6 @@ import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.regex.Pattern; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.util.MacBundle; @@ -287,9 +286,7 @@ public final class AppVersionTest { } TKit.TextStreamVerifier negateFind() { - var pattern = JPackageStringBundle.MAIN.cannedFormattedStringAsPattern(key, _ -> { - return Pattern.compile(".*"); - }, args); + var pattern = JPackageStringBundle.MAIN.cannedFormattedStringAsPattern(key, args); return TKit.assertTextStream(pattern).negate(); } @@ -400,7 +397,7 @@ public final class AppVersionTest { Stream.of(Message.values()).filter(message -> { return !expectMessageKeys.contains(message.key()); }).map(Message::negateFind).forEach(validator -> { - new JPackageOutputValidator().add(validator).applyTo(cmd); + new JPackageOutputValidator().add(validator).stdoutAndStderr().applyTo(cmd); }); } @@ -760,7 +757,7 @@ public final class AppVersionTest { private final List versions = new ArrayList<>(); private final Map expected = new HashMap<>(); - private final static Set ALL_TYPES = Set.of(PackageType.values()); + private static final Set ALL_TYPES = Set.of(PackageType.values()); } } @@ -1019,7 +1016,7 @@ public final class AppVersionTest { return predefinedRuntimeDir; } - final static String MAC_PREDEFINED_RUNTIME_BUNDLE_VERSION = "1.22.333"; + static final String MAC_PREDEFINED_RUNTIME_BUNDLE_VERSION = "1.22.333"; } private static Consumer skipImagePackageType(Consumer consumer) { From 7e943e7d41ee8175660e236c4b7fe39604fdae2f Mon Sep 17 00:00:00 2001 From: David Holmes Date: Fri, 20 Mar 2026 03:57:48 +0000 Subject: [PATCH 042/160] 8380474: Crash SEGV in ThreadIdTable::lazy_initialize after JDK-8323792 Reviewed-by: kvn, iklam --- src/hotspot/share/services/threadIdTable.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/services/threadIdTable.cpp b/src/hotspot/share/services/threadIdTable.cpp index d5141441ccd..585cc6377ec 100644 --- a/src/hotspot/share/services/threadIdTable.cpp +++ b/src/hotspot/share/services/threadIdTable.cpp @@ -23,6 +23,7 @@ */ #include "classfile/javaClasses.inline.hpp" +#include "runtime/handles.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.inline.hpp" #include "runtime/threadSMR.hpp" @@ -94,11 +95,11 @@ void ThreadIdTable::lazy_initialize(const ThreadsList *threads) { for (uint i = 0; i < threads->length(); i++) { JavaThread* thread = threads->thread_at(i); - oop tobj = thread->threadObj(); + Handle tobj = Handle(JavaThread::current(), thread->threadObj()); if (tobj != nullptr) { MutexLocker ml(Threads_lock); if (!thread->is_exiting()) { - jlong java_tid = java_lang_Thread::thread_id(tobj); + jlong java_tid = java_lang_Thread::thread_id(tobj()); // Must be inside the lock to ensure that we don't add a thread to the table // that has just passed the removal point in Threads::remove(). add_thread(java_tid, thread); From f2550e89c7d382b3256327d7a720c9059de1e4b5 Mon Sep 17 00:00:00 2001 From: Dusan Balek Date: Fri, 20 Mar 2026 07:25:45 +0000 Subject: [PATCH 043/160] 8378906: NPE from javac thrown when encountering abstract synthetic bridge method Reviewed-by: mcimadamore --- .../com/sun/tools/javac/code/Types.java | 6 +- .../types/IsFunctionalInterfaceTest.java | 160 ++++++++++++++++++ 2 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 test/langtools/tools/javac/types/IsFunctionalInterfaceTest.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 9ffab9fd961..f1c6676d087 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -993,10 +993,12 @@ public class Types { @Override public boolean test(Symbol sym) { + List msyms; return sym.kind == MTH && (sym.flags() & (ABSTRACT | DEFAULT)) == ABSTRACT && !overridesObjectMethod(origin, sym) && - (interfaceCandidates(origin.type, (MethodSymbol)sym).head.flags() & DEFAULT) == 0; + (msyms = interfaceCandidates(origin.type, (MethodSymbol)sym)).nonEmpty() && + (msyms.head.flags() & DEFAULT) == 0; } } diff --git a/test/langtools/tools/javac/types/IsFunctionalInterfaceTest.java b/test/langtools/tools/javac/types/IsFunctionalInterfaceTest.java new file mode 100644 index 00000000000..a4fe4e470c7 --- /dev/null +++ b/test/langtools/tools/javac/types/IsFunctionalInterfaceTest.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 8378906 + * @summary Check that Types.isFunctionalInterface() works for interface methods + * with ACC_PUBLIC, ACC_BRIDGE, ACC_ABSTRACT, ACC_SYNTHETIC flags. + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit IsFunctionalInterfaceTest + */ + +import java.lang.classfile.ClassFile; +import java.lang.classfile.MethodElement; +import java.lang.classfile.MethodModel; +import java.lang.reflect.AccessFlag; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; + +import toolbox.JavacTask; +import toolbox.ToolBox; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import toolbox.Task; + +public class IsFunctionalInterfaceTest { + + Path base; + ToolBox tb = new ToolBox(); + + @Test + void test8378906() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + interface I { + @Deprecated + Object test(); + } + + final class Sub implements I { + public static final Sub INSTANCE = new Sub(); + public Object test() { return null; } + } + + class Util { + public static boolean foo(I a) { return true; } + public static boolean foo(Sub a) { return false; } + } + """) + .run() + .writeAll(); + + Path path = classes.resolve("I.class"); + ClassFile classFile = ClassFile.of(); + byte[] bytes = classFile.transformClass(classFile.parse(path), + (classBuilder, classElement) -> { + if (classElement instanceof MethodModel mm + && mm.methodName().equalsString("test")) { + int flags = mm.flags().flagsMask() | AccessFlag.BRIDGE.mask() | AccessFlag.SYNTHETIC.mask(); + classBuilder.withMethod(mm.methodName(), mm.methodType(), flags, (methodBuilder) -> { + mm.attributes().forEach(attr -> { + if (attr instanceof MethodElement me) { + methodBuilder.with(me); + } + }); + }); + } else { + classBuilder.with(classElement); + } + }); + Files.write(path, bytes); + + new JavacTask(tb) + .options("-d", classes.toString(), "-cp", classes.toString()) + .sources(""" + public class Test { + public void main() { Util.foo(Sub.INSTANCE); } + } + """) + .run() + .writeAll(); + + List out1 = new JavacTask(tb) + .options("-d", classes.toString(), "-cp", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + public class Test { + public void main() { t(() -> null); } + private void t(I i) {} + } + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out1, List.of( + "Test.java:2:25: compiler.err.cant.apply.symbol: kindname.method, t, I, @27, kindname.class, Test, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.not.a.functional.intf.1: I, (compiler.misc.no.abstracts: kindname.interface, I)))", + "1 error")); + + List out2 = new JavacTask(tb) + .options("-d", classes.toString(), "-cp", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + public class Impl implements I { + } + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out2, List.of( + "Impl.java:1:8: compiler.err.does.not.override.abstract: Impl, test(), I", + "1 error")); + + new JavacTask(tb) + .options("-d", classes.toString(), "-cp", classes.toString()) + .sources(""" + public class Impl implements I { + public Object test() { return null; } + } + """) + .run() + .writeAll(); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} From f5fbb6a237f022b485dbc79b6ec819df65535ffe Mon Sep 17 00:00:00 2001 From: Arno Zeller Date: Fri, 20 Mar 2026 09:01:59 +0000 Subject: [PATCH 044/160] 8380428: ProblemList containers/docker/TestJcmdWithSideCar.java on linux-all Reviewed-by: goetz, mbaesken, dholmes, ysuenaga --- test/hotspot/jtreg/ProblemList.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index a9211bed62e..dec9682f0cf 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -116,7 +116,7 @@ runtime/NMT/VirtualAllocCommitMerge.java 8309698 linux-s390x applications/jcstress/copy.java 8229852 linux-all containers/docker/TestJFREvents.java 8327723 linux-x64 -containers/docker/TestJcmdWithSideCar.java 8341518 linux-x64 +containers/docker/TestJcmdWithSideCar.java 8341518 linux-all ############################################################################# From f7c427fedaf7406560918ee568e6861e666f5aa4 Mon Sep 17 00:00:00 2001 From: Roger Riggs Date: Fri, 20 Mar 2026 14:14:59 +0000 Subject: [PATCH 045/160] 8379182: [test] java/lang/ProcessBuilder/PipelineLeaksFD.java fails Reviewed-by: stuefe --- test/jdk/java/lang/ProcessBuilder/Basic.java | 1 - .../java/lang/ProcessBuilder/LinuxFDInfo.java | 62 +++++ .../lang/ProcessBuilder/PipelineLeaksFD.java | 248 +++++------------- .../java/lang/ProcessBuilder/libLinuxFDInfo.c | 37 +++ 4 files changed, 159 insertions(+), 189 deletions(-) create mode 100644 test/jdk/java/lang/ProcessBuilder/LinuxFDInfo.java create mode 100644 test/jdk/java/lang/ProcessBuilder/libLinuxFDInfo.c diff --git a/test/jdk/java/lang/ProcessBuilder/Basic.java b/test/jdk/java/lang/ProcessBuilder/Basic.java index 401a1e2bd8a..70d6101c1a5 100644 --- a/test/jdk/java/lang/ProcessBuilder/Basic.java +++ b/test/jdk/java/lang/ProcessBuilder/Basic.java @@ -45,7 +45,6 @@ * @summary Basic tests for Process and Environment Variable code * @modules java.base/java.lang:open * java.base/java.io:open - * @requires !vm.musl * @requires vm.flagless * @library /test/lib * @run main/othervm/native/timeout=360 -Djdk.lang.Process.launchMechanism=fork Basic diff --git a/test/jdk/java/lang/ProcessBuilder/LinuxFDInfo.java b/test/jdk/java/lang/ProcessBuilder/LinuxFDInfo.java new file mode 100644 index 00000000000..6e0b08c5879 --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/LinuxFDInfo.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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. + */ + +/** + * Accessor for Native calls to get information about File Descriptors. + * If it's a pipe and its Inode number. + */ +public class LinuxFDInfo { + + static { + System.loadLibrary("LinuxFDInfo"); + } + + // Maximum file descriptor to probe for being a pipe, + private final static int MAX_FD = 100; + + // A simple main to print the pipes in this process and their Inode value + public static void main() { + for (int fd = 0; fd < MAX_FD; fd++) { + long inode = getPipeInodeNum(fd); + if (inode != 0) { + System.out.printf("fd: %d, inode: 0x%08x\n", fd, inode); + } + } + } + + // Parse the output from main into a long array of the fd, and Inode. + public static FdAndInode parseFdAndInode(String s) { + String[] args = s.split(","); + return new FdAndInode(Integer.parseUnsignedInt(args[0].split(":")[1].trim()), + Long.parseUnsignedLong(args[1].split(":")[1].trim().substring(2), 16)); + } + + /** + * Return the inode number for the FD, if it is a pipe. + * @param fd file descriptor + * @return the Inode number. + */ + public static native long getPipeInodeNum(int fd); + + public record FdAndInode(int fd, long inode) {} +} diff --git a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java index 18dde6e95d7..d1213a5ee11 100644 --- a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java +++ b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java @@ -21,34 +21,26 @@ * questions. */ -import org.junit.jupiter.api.condition.EnabledIf; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import static org.junit.jupiter.api.Assertions.*; -import jdk.test.lib.Utils; import java.io.BufferedReader; import java.io.IOException; import java.io.Writer; -import java.lang.ProcessHandle; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.TimeUnit; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; -import java.util.Optional; import java.util.Set; -import java.util.StringJoiner; import java.util.stream.Collectors; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + /* * @test id=FORK * @bug 8289643 8291760 8291986 - * @requires os.family == "mac" | (os.family == "linux" & !vm.musl) + * @requires os.family == "mac" | os.family == "linux" * @summary File descriptor leak detection with ProcessBuilder.startPipeline * @library /test/lib * @run junit/othervm/timeout=240 -Djdk.lang.Process.launchMechanism=FORK PipelineLeaksFD @@ -56,20 +48,23 @@ import java.util.stream.Collectors; /* * @test id=POSIX_SPAWN - * @bug 8289643 8291760 8291986 - * @requires os.family == "mac" | (os.family == "linux" & !vm.musl) + * @bug 8289643 8291760 8291986 8379182 + * @requires os.family == "mac" | os.family == "linux" * @summary File descriptor leak detection with ProcessBuilder.startPipeline * @library /test/lib - * @run junit/othervm/timeout=240 -Djdk.lang.Process.launchMechanism=POSIX_SPAWN PipelineLeaksFD + * @run junit/othervm/native/timeout=240 -Djdk.lang.Process.launchMechanism=POSIX_SPAWN PipelineLeaksFD */ public class PipelineLeaksFD { - private static final String OS_NAME = System.getProperty("os.name", "Unknown"); + private static final String TEST_JDK = System.getProperty("test.jdk"); + + private static final String JAVA_LIBRARY_PATH = System.getProperty("java.library.path"); private static final long MY_PID = ProcessHandle.current().pid(); - private static final boolean LSOF_AVAILABLE = checkForLSOF(); + // Maximum file descriptor to probe for being a pipe, + private static final int MAX_FD = 100; // Test cases for pipelines with a number of pipeline sequences public static Object[][] builders() { @@ -85,39 +80,17 @@ public class PipelineLeaksFD { }; } - // True if lsof is runnable and collect pipe usage. - private static boolean lsofAvailable() { - return LSOF_AVAILABLE; - } - - // Check if lsof is available - private static boolean checkForLSOF() { - try { - lsofForPids(MY_PID); - return true; - } catch (IOException ioe) { - System.err.println("Skipping: " + ioe); - return false; - } - } - - @EnabledIf("lsofAvailable") @ParameterizedTest @MethodSource("builders") void checkForLeaks(List builders) throws IOException { System.out.println("Using:" + System.getProperty("jdk.lang.Process.launchMechanism")); - - List lsofLines = lsofForPids(MY_PID); - Set pipesBefore = pipesFromLSOF(lsofLines, MY_PID); - if (pipesBefore.size() < 3) { - // Dump all lsof output to aid debugging - System.out.println("Output from lsof"); - lsofLines.forEach(System.err::println); - System.err.println(pipesBefore); - fail("There should be at least 3 pipes before, (0, 1, 2)"); + Set beforePipes = pipesFromSelf(); + if (beforePipes.size() < 3) { + fail("There should be at least 3 pipes before, (0, 1, 2): is " + + beforePipes.size()); } - printPipes(pipesBefore, "Before start"); + printPipes(beforePipes, "Before start"); List processes = ProcessBuilder.startPipeline(builders); @@ -144,18 +117,14 @@ public class PipelineLeaksFD { processes.forEach(PipelineLeaksFD::waitForQuiet); - lsofLines = lsofForPids(MY_PID); - Set pipesAfter = pipesFromLSOF(lsofLines, MY_PID); - if (!pipesBefore.equals(pipesAfter)) { - Set missing = new HashSet<>(pipesBefore); - missing.removeAll(pipesAfter); - printPipes(missing, "Missing from pipesAfter"); - Set extra = new HashSet<>(pipesAfter); - extra.removeAll(pipesBefore); - printPipes(extra, "Extra pipes in pipesAfter"); - // Dump all lsof output to aid debugging - System.out.println("\nOutput from lsof"); - lsofLines.forEach(System.err::println); + Set afterPipes = pipesFromSelf(); + if (!beforePipes.equals(afterPipes)) { + Set missing = new HashSet<>(beforePipes); + missing.removeAll(afterPipes); + printPipes(missing, "Missing from beforePipes()"); + Set extra = new HashSet<>(afterPipes); + extra.removeAll(beforePipes); + printPipes(extra, "Extra pipes in afterPipes()"); fail("More or fewer pipes than expected"); } } @@ -168,43 +137,46 @@ public class PipelineLeaksFD { }; } - // Test redirectErrorStream (true/false) has the right number of pipes in use - @EnabledIf("lsofAvailable") + // Test redirectErrorStream (true/false) has the right number of pipes in use. + // Spawn the child to report its pipe inode info. @ParameterizedTest() @MethodSource("redirectCases") void checkRedirectErrorStream(boolean redirectError) { System.out.println("Using:" + System.getProperty("jdk.lang.Process.launchMechanism")); - try (Process p = new ProcessBuilder("cat") + try (Process p = new ProcessBuilder(TEST_JDK + "/bin/java", + "--enable-preview", + "-Djava.library.path=" + JAVA_LIBRARY_PATH, + "--enable-native-access=ALL-UNNAMED", "LinuxFDInfo") .redirectErrorStream(redirectError) .start()) { - System.err.printf("Parent PID; %d, Child Pid: %d\n", MY_PID, p.pid()); - List lsofLines = lsofForPids(MY_PID, p.pid()); - final Set pipes = pipesFromLSOF(lsofLines, MY_PID, p.pid()); - printPipes(pipes, "Parent and child pipes"); - // For each of the child standard pipes, check the parent and child uses of the pipe - var pIn = PipeRecord.pipeFor(pipes, p.pid(), 0).orElseThrow(); - Set pIns = PipeRecord.allSamePipes(pipes, pIn); - assertEquals(2, pIns.size(), "StdIn pipe count"); + final Set pipes = pipesFromSelf(); + printPipes(pipes, "Parent pipes"); + List lines = p.inputReader().readAllLines(); + Set childPipes = lines.stream().map(s -> { + var fdAndInode = LinuxFDInfo.parseFdAndInode(s); + return PipeRecord.lookup(fdAndInode.fd(), "0x%08x".formatted(fdAndInode.inode()), p.pid()); + }) + .collect(Collectors.toCollection(LinkedHashSet::new)); + printPipes(childPipes, "child pipes"); + int uniquePipes = childPipes.stream().map(PipeRecord::myKey).collect(Collectors.toSet()).size(); // Number of pipe references depending on whether stderr is redirected to stdout - long expected = redirectError ? 3 : 2; - - var pOut = PipeRecord.pipeFor(pipes, p.pid(), 1).orElseThrow(); - Set pOuts = PipeRecord.allSamePipes(pipes, pOut); - assertEquals(expected, pOuts.size(), "StdOut pipe count"); - - var pErr = PipeRecord.pipeFor(pipes, p.pid(), 2).orElseThrow(); - Set pErrs = PipeRecord.allSamePipes(pipes, pErr); - assertEquals(expected, pErrs.size(), "StdErr pipe count"); + long expected = redirectError ? 2 : 3; + assertEquals(expected, uniquePipes, "Wrong number of unique pipes"); String expectedTypeName = redirectError ? "java.lang.ProcessBuilder$NullInputStream" : "java.lang.ProcessImpl$ProcessPipeInputStream"; assertEquals(expectedTypeName, p.getErrorStream().getClass().getName(), "errorStream type is incorrect"); + + final Set afterPipes = pipesFromSelf(); + if (!pipes.equals(afterPipes)) { + printPipes(afterPipes, "Parent pipes after are different"); + } } catch (IOException ioe) { fail("Process start", ioe); } @@ -221,122 +193,26 @@ public class PipelineLeaksFD { if (st != 0) { System.err.println("non-zero exit status: " + p); } - } catch (InterruptedException ie) { + } catch (InterruptedException ignore) { } } - /** - * Extract the PipeRecords from the `lsof` output for a list of processes - * - * @param lines lines of lsof output - * @param pids varargs list of pids of interest - * @return a Set of PipeRecords for those pids - */ - private static LinkedHashSet pipesFromLSOF(List lines, long... pids) { - Arrays.sort(pids); - return lines.stream() - .map(PipelineLeaksFD::pipeFromLSOF) - .filter(pr -> pr != null && - Arrays.binarySearch(pids, pr.pid()) >= 0) - .collect(Collectors.toCollection(LinkedHashSet::new)); - } - - /** - * Collect the output of `lsof` for all files. - * Files are used for `lsof` input and output to avoid creating pipes. - * @param pids zero or more pids to request data for; none -> all - * @return a List of lines output from `lsof`. - */ - private static List lsofForPids(long... pids) throws IOException { - Path tmpDir = Path.of("."); - String tmpPrefix = "lsof-"; - Path lsofEmptyInput = Files.createTempFile(tmpDir, tmpPrefix, ".empty"); - Path lsofOutput = Files.createTempFile(tmpDir, tmpPrefix, ".tmp"); - - List lsofArgs = new ArrayList<>(); - lsofArgs.add("lsof"); - if (pids.length > 0) { - StringJoiner pidsArg = new StringJoiner(","); - for (long p : pids) { - pidsArg.add(Long.toString(p)); + static Set pipesFromSelf() { + Set pipes = new LinkedHashSet<>(MAX_FD); + for (int fd = 0; fd < MAX_FD; fd++) { + long inode = LinuxFDInfo.getPipeInodeNum(fd); + if (inode != 0) { + pipes.add(PipeRecord.lookup(fd, "0x%08x".formatted(inode), MY_PID)); } - lsofArgs.add("-p"); - lsofArgs.add(pidsArg.toString()); } - - try (Process p = new ProcessBuilder(lsofArgs) - .redirectOutput(lsofOutput.toFile()) - .redirectInput(lsofEmptyInput.toFile()) // empty input - .redirectError(ProcessBuilder.Redirect.DISCARD) // ignored output - .start()) { - boolean status = p.waitFor(Utils.adjustTimeout(120), TimeUnit.SECONDS); - if (!status) { - p.destroyForcibly(); - } - assertTrue(status, "Process 'lsof' failed"); - - return Files.readAllLines(lsofOutput); - } catch (InterruptedException ie) { - throw new IOException("Waiting for lsof exit interrupted", ie); - } - } - - // Return pipe records by parsing the appropriate platform specific `lsof` output. - static PipeRecord pipeFromLSOF(String s) { - return switch (OS_NAME) { - case "Linux" -> pipeFromLinuxLSOF(s); - case "Mac OS X" -> pipeFromMacLSOF(s); - default -> throw new RuntimeException("lsof not supported on platform: " + OS_NAME); - }; - } - - // Return Pipe from lsof output put, or null (on Mac OS X) - // lsof 55221 xxxx 0 PIPE 0xc76402237956a5cb 16384 ->0xfcb0c07ae447908c - // lsof 55221 xxxx 1 PIPE 0xb486e02f86da463e 16384 ->0xf94eacc85896b4e6 - static PipeRecord pipeFromMacLSOF(String s) { - String[] fields = s.split("\\s+"); - if ("PIPE".equals(fields[4])) { - final int pid = Integer.parseInt(fields[1]); - final String myKey = (fields.length > 5) ? fields[5] : ""; - final String otherKey = (fields.length > 7) ? fields[7].substring(2) : ""; - return PipeRecord.lookup(Integer.parseInt(fields[3]), myKey, otherKey, pid); - } - return null; - } - - // Return Pipe from lsof output put, or null (on Linux) - // java 7612 xxxx 14w FIFO 0,12 0t0 117662267 pipe - // java 7612 xxxx 15r FIFO 0,12 0t0 117662268 pipe - static PipeRecord pipeFromLinuxLSOF(String s) { - String[] fields = s.split("\\s+"); - if ("FIFO".equals(fields[4])) { - final int pid = Integer.parseInt(fields[1]); - final String key = (fields.length > 7) ? fields[7] : ""; - final int fdNum = Integer.parseInt(fields[3].substring(0, fields[3].length() - 1)); - return PipeRecord.lookup(fdNum, key, null, pid); - } - return null; + return pipes; } // Identify a pipe by pid, fd, and a key (unique across processes) // Mac OS X has separate keys for read and write sides, both are matched to the same "name" record PipeRecord(long pid, int fd, KeyedString myKey) { - static PipeRecord lookup(int fd, String myKey, String otherKey, int pid) { - return new PipeRecord(pid, fd, KeyedString.getKey(myKey, otherKey)); - } - - // Return the PipeRecord matching the fd and pid - static Optional pipeFor(Set pipes, long pid, int fd) { - return pipes.stream() - .filter(p -> p.fd() == fd && p.pid() == pid) - .findFirst(); - } - - // Return all the PipeRecords with the same key (the same OS pipe identification) - static Set allSamePipes(Set pipes, PipeRecord p) { - return pipes.stream() - .filter(p1 -> p1.myKey().key.equals(p.myKey().key)) - .collect(Collectors.toSet()); + static PipeRecord lookup(int fd, String myKey, long pid) { + return new PipeRecord(pid, fd, KeyedString.getKey(myKey)); } } @@ -357,12 +233,8 @@ public class PipelineLeaksFD { this(s, k); } - static KeyedString getKey(String key, String otherKey) { - var k = map.computeIfAbsent(key, KeyedString::new); - if (otherKey != null) { - map.putIfAbsent(otherKey, k); - } - return k; + static KeyedString getKey(String key) { + return map.computeIfAbsent(key, KeyedString::new); } public String toString() { diff --git a/test/jdk/java/lang/ProcessBuilder/libLinuxFDInfo.c b/test/jdk/java/lang/ProcessBuilder/libLinuxFDInfo.c new file mode 100644 index 00000000000..3eb4bd15aac --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/libLinuxFDInfo.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jni.h" +#include + +// If the file descriptor is a PIPE, return the INode. +JNIEXPORT jlong JNICALL +Java_LinuxFDInfo_getPipeInodeNum(JNIEnv *env, jclass cls, jint fd) { +#ifdef S_ISFIFO + struct stat buf; + if (fstat(fd, &buf) != -1 && S_ISFIFO(buf.st_mode)) { + return buf.st_ino; + } +#endif + return 0; +} From 396cc0ab6a8a6d2e3a0d9b6c3cedafef6a427094 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Fri, 20 Mar 2026 15:58:45 +0000 Subject: [PATCH 046/160] 8380534: Use loopback address in java/net/DatagramPacket/ReuseBuf.java test Reviewed-by: dfuchs --- .../jdk/java/net/DatagramPacket/ReuseBuf.java | 161 ++++++++++++------ 1 file changed, 111 insertions(+), 50 deletions(-) diff --git a/test/jdk/java/net/DatagramPacket/ReuseBuf.java b/test/jdk/java/net/DatagramPacket/ReuseBuf.java index 232bd07dded..2213f306a8a 100644 --- a/test/jdk/java/net/DatagramPacket/ReuseBuf.java +++ b/test/jdk/java/net/DatagramPacket/ReuseBuf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,71 +21,132 @@ * questions. */ -/** + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketException; +import java.util.concurrent.atomic.AtomicReference; + +/* * @test - * * @bug 4424096 - * - * @summary DatagramPacket spec needs clarification (reuse buf) + * @summary Verify the specification of DatagramPacket.getData() */ -import java.net.*; - public class ReuseBuf { - static String msgs[] = {"Hello World", "Java", "Good Bye"}; - static int port; + private static final String[] msgs = {"Hello World", "Java", "Good Bye"}; - static class ServerThread extends Thread{ - DatagramSocket ds; - public ServerThread() { + static class Server implements Runnable, AutoCloseable { + + private final DatagramSocket ds; + private volatile boolean closed; + private final AtomicReference serverFailure = new AtomicReference<>(); + + public Server(final InetSocketAddress bindAddr) { try { - InetAddress local = InetAddress.getLocalHost(); - InetSocketAddress bindaddr = new InetSocketAddress(local, 0); - ds = new DatagramSocket(bindaddr); - port = ds.getLocalPort(); + this.ds = new DatagramSocket(bindAddr); + } catch (SocketException e) { + throw new RuntimeException(e); + } + System.out.println("Server bound to address: " + this.ds.getLocalSocketAddress()); + } + + private InetSocketAddress getServerAddress() { + return (InetSocketAddress) this.ds.getLocalSocketAddress(); + } + + private static void serverLog(final String msg) { + System.out.println("[server] " + msg); + } + + @Override + public void run() { + serverLog("server processing started"); + try { + doRun(); } catch (Exception e) { - throw new RuntimeException(e.getMessage()); + // no need to be concerned with the exception + // if the server is already closed + if (!closed) { + this.serverFailure.set(e); + System.err.println("server exception: " + e); + e.printStackTrace(); + } + } finally { + close(); } } - public void run() { - byte b[] = new byte[100]; - DatagramPacket dp = new DatagramPacket(b,b.length); - while (true) { - try { - ds.receive(dp); - String reply = new String(dp.getData(), dp.getOffset(), dp.getLength()); - ds.send(new DatagramPacket(reply.getBytes(),reply.length(), - dp.getAddress(),dp.getPort())); - if (reply.equals(msgs[msgs.length-1])) { - break; - } - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); + private void doRun() throws Exception { + byte[] b = new byte[100]; + DatagramPacket dp = new DatagramPacket(b, b.length); + while (!closed) { + serverLog("waiting to receive a message at " + ds.getLocalSocketAddress()); + ds.receive(dp); + String reply = new String(dp.getData(), dp.getOffset(), dp.getLength()); + serverLog("replying to " + dp.getAddress() + ":" + dp.getPort()); + ds.send(new DatagramPacket(reply.getBytes(), reply.length(), + dp.getAddress(), dp.getPort())); + if (reply.equals(msgs[msgs.length - 1])) { + break; } } - ds.close(); + } + + @Override + public void close() { + if (this.closed) { + return; + } + synchronized (this) { + if (this.closed) { + return; + } + this.closed = true; + } + System.out.println("Server closing " + ds.getLocalSocketAddress()); + this.ds.close(); } } - public static void main(String args[]) throws Exception { - ServerThread st = new ServerThread(); - st.start(); - InetAddress local = InetAddress.getLocalHost(); - InetSocketAddress bindaddr = new InetSocketAddress(local, 0); - DatagramSocket ds = new DatagramSocket(bindaddr); - byte b[] = new byte[100]; - DatagramPacket dp = new DatagramPacket(b,b.length); - for (int i = 0; i < msgs.length; i++) { - ds.send(new DatagramPacket(msgs[i].getBytes(),msgs[i].length(), - InetAddress.getLocalHost(), - port)); - ds.receive(dp); - if (!msgs[i].equals(new String(dp.getData(), dp.getOffset(), dp.getLength()))) { - throw new RuntimeException("Msg expected: "+msgs[i] +msgs[i].length()+ - "msg received: "+new String(dp.getData(), dp.getOffset(), dp.getLength())+dp.getLength()); + public static void main(String[] args) throws Exception { + InetSocketAddress loopbackEphemeral = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); + Server server; + Thread serverThread; + try (var _ = server = new Server(loopbackEphemeral); + DatagramSocket ds = new DatagramSocket(loopbackEphemeral)) { + + InetSocketAddress destAddr = server.getServerAddress(); + // start the server + serverThread = new Thread(server); + serverThread.start(); + + byte[] b = new byte[100]; + DatagramPacket dp = new DatagramPacket(b, b.length); + for (String msg : msgs) { + System.out.println("sending message from " + ds.getLocalSocketAddress() + + " to " + destAddr); + ds.send(new DatagramPacket(msg.getBytes(), msg.length(), destAddr)); + // wait for a reply from the server + ds.receive(dp); + System.out.println("received message from: " + dp.getAddress() + ":" + dp.getPort()); + String actual = new String(dp.getData(), dp.getOffset(), dp.getLength()); + if (!msg.equals(actual)) { + throw new RuntimeException("Msg expected: " + msg + + " of length: " + msg.length() + + ", actual received: " + actual + " of length: " + dp.getLength()); + } } + System.out.println("All " + msgs.length + " replies received from the server"); + } + // wait for the server thread to complete + System.out.println("awaiting server thread " + serverThread + " to complete"); + serverThread.join(); + Exception serverFailure = server.serverFailure.get(); + if (serverFailure != null) { + System.err.println("Unexpected failure on server: " + serverFailure); + throw serverFailure; } - ds.close(); - System.out.println("Test Passed!!!"); } } From b32e9b0f9be7349f797ced4a260e1e10418af3bb Mon Sep 17 00:00:00 2001 From: William Kemper Date: Fri, 20 Mar 2026 16:20:40 +0000 Subject: [PATCH 047/160] 8378806: Genshen: Add scope of collection to end of cycle message in JMX notification Reviewed-by: kdnilsen, xpeng --- .../share/gc/shenandoah/shenandoahUtils.cpp | 18 +++- .../share/gc/shenandoah/shenandoahUtils.hpp | 2 + .../mxbeans/TestCycleEndMessage.java | 83 +++++++++++++++++++ .../TestStringDeduplicationTools.java | 3 +- 4 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 test/hotspot/jtreg/gc/shenandoah/mxbeans/TestCycleEndMessage.java diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp index 176baa133c8..dea47fcbf4f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp @@ -40,6 +40,22 @@ ShenandoahPhaseTimings::Phase ShenandoahTimingsTracker::_current_phase = ShenandoahPhaseTimings::_invalid_phase; +const char* ShenandoahGCSession::cycle_end_message(ShenandoahGenerationType type) { + switch (type) { + case NON_GEN: + return "end of GC cycle"; + case GLOBAL: + return "end of Global GC cycle"; + case YOUNG: + return "end of Young GC cycle"; + case OLD: + return "end of Old GC cycle"; + default: + ShouldNotReachHere(); + return "end of GC cycle"; + } +} + ShenandoahGCSession::ShenandoahGCSession(GCCause::Cause cause, ShenandoahGeneration* generation) : _heap(ShenandoahHeap::heap()), _generation(generation), @@ -54,7 +70,7 @@ ShenandoahGCSession::ShenandoahGCSession(GCCause::Cause cause, ShenandoahGenerat _heap->trace_heap_before_gc(_tracer); _trace_cycle.initialize(_heap->cycle_memory_manager(), cause, - "end of GC cycle", + cycle_end_message(_generation->type()), /* allMemoryPoolsAffected */ true, /* recordGCBeginTime = */ true, /* recordPreGCUsage = */ true, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp index 8a508c4afd8..6ef4cd7c702 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp @@ -67,6 +67,8 @@ private: GCTracer* const _tracer; TraceMemoryManagerStats _trace_cycle; + + static const char* cycle_end_message(ShenandoahGenerationType type); public: ShenandoahGCSession(GCCause::Cause cause, ShenandoahGeneration* generation); ~ShenandoahGCSession(); diff --git a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestCycleEndMessage.java b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestCycleEndMessage.java new file mode 100644 index 00000000000..e7a4cfc9605 --- /dev/null +++ b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestCycleEndMessage.java @@ -0,0 +1,83 @@ +/* + * 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 + * @summary Check that GC cycle end message contains generation name + * @library /test/lib / + * @requires vm.gc.Shenandoah + * + * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational + * TestCycleEndMessage + */ + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; +import javax.management.openmbean.CompositeData; + +import com.sun.management.GarbageCollectionNotificationInfo; + +public class TestCycleEndMessage { + + public static void main(String[] args) throws Exception { + final AtomicBoolean foundGenerationInCycle = new AtomicBoolean(false); + + NotificationListener listener = new NotificationListener() { + @Override + public void handleNotification(Notification n, Object o) { + if (n.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { + GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from((CompositeData) n.getUserData()); + + String name = info.getGcName(); + String action = info.getGcAction(); + + System.out.println("Received: " + name + " / " + action); + + if (name.equals("Shenandoah Cycles") && + (action.contains("Global") || action.contains("Young") || action.contains("Old"))) { + foundGenerationInCycle.set(true); + } + } + } + }; + + for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) { + ((NotificationEmitter) bean).addNotificationListener(listener, null, null); + } + + System.gc(); + Thread.sleep(2000); + + if (!foundGenerationInCycle.get()) { + throw new IllegalStateException("Expected to find generation name (Global/Young/Old) in Shenandoah Cycles action message"); + } + + System.out.println("Test passed: Found generation name in cycle end message"); + } +} diff --git a/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java b/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java index 3dbedd61d12..e4185dd0f12 100644 --- a/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java +++ b/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java @@ -129,7 +129,8 @@ class TestStringDeduplicationTools { GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from((CompositeData) n.getUserData()); // Shenandoah and Z GC also report GC pauses, skip them if (info.getGcName().startsWith("Shenandoah")) { - if ("end of GC cycle".equals(info.getGcAction())) { + String action = info.getGcAction(); + if (action != null && action.contains("GC cycle")) { gcCount++; } } else if (info.getGcName().startsWith("ZGC")) { From 26c4b5f5a1ad623ef7d3db06e4903867b03b5443 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Fri, 20 Mar 2026 16:25:39 +0000 Subject: [PATCH 048/160] 8379367: GenShen: Replace atomic promotion failure counters with thread local variables Reviewed-by: kdnilsen --- .../gc/shenandoah/shenandoahBarrierSet.cpp | 7 +- .../shenandoah/shenandoahGenerationalHeap.cpp | 207 ++-------------- .../shenandoah/shenandoahGenerationalHeap.hpp | 7 - .../share/gc/shenandoah/shenandoahHeap.cpp | 18 +- .../gc/shenandoah/shenandoahOldGeneration.cpp | 85 +++++-- .../gc/shenandoah/shenandoahOldGeneration.hpp | 12 +- .../share/gc/shenandoah/shenandoahPLAB.cpp | 221 ++++++++++++++++++ .../share/gc/shenandoah/shenandoahPLAB.hpp | 130 +++++++++++ .../shenandoah/shenandoahThreadLocalData.cpp | 13 +- .../shenandoah/shenandoahThreadLocalData.hpp | 84 +------ .../test_shenandoahOldGeneration.cpp | 20 +- 11 files changed, 470 insertions(+), 334 deletions(-) create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp create mode 100644 src/hotspot/share/gc/shenandoah/shenandoahPLAB.hpp diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index 004558a9fa8..cb6ff795c07 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -166,10 +166,9 @@ void ShenandoahBarrierSet::on_thread_detach(Thread *thread) { gclab->retire(); } - PLAB* plab = ShenandoahThreadLocalData::plab(thread); - if (plab != nullptr) { - // This will assert if plab is not null in non-generational mode - ShenandoahGenerationalHeap::heap()->retire_plab(plab); + ShenandoahPLAB* shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + if (shenandoah_plab != nullptr) { + shenandoah_plab->retire(); } // SATB protocol requires to keep alive reachable oops from roots at the beginning of GC diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index c731221adeb..27e374a0f85 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -269,27 +269,25 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint break; } case OLD_GENERATION: { - PLAB* plab = ShenandoahThreadLocalData::plab(thread); - if (plab != nullptr) { + ShenandoahPLAB* shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + if (shenandoah_plab != nullptr) { has_plab = true; - copy = allocate_from_plab(thread, size, is_promotion); - if ((copy == nullptr) && (size < ShenandoahThreadLocalData::plab_size(thread)) && - ShenandoahThreadLocalData::plab_retries_enabled(thread)) { + copy = shenandoah_plab->allocate(size, is_promotion); + if (copy == nullptr && size < shenandoah_plab->desired_size() && shenandoah_plab->retries_enabled()) { // PLAB allocation failed because we are bumping up against the limit on old evacuation reserve or because // the requested object does not fit within the current plab but the plab still has an "abundance" of memory, // where abundance is defined as >= ShenGenHeap::plab_min_size(). In the former case, we try shrinking the // desired PLAB size to the minimum and retry PLAB allocation to avoid cascading of shared memory allocations. // Shrinking the desired PLAB size may allow us to eke out a small PLAB while staying beneath evacuation reserve. - if (plab->words_remaining() < plab_min_size()) { - ShenandoahThreadLocalData::set_plab_size(thread, plab_min_size()); - copy = allocate_from_plab(thread, size, is_promotion); - // If we still get nullptr, we'll try a shared allocation below. + if (shenandoah_plab->plab()->words_remaining() < plab_min_size()) { + shenandoah_plab->set_desired_size(plab_min_size()); + copy = shenandoah_plab->allocate(size, is_promotion); if (copy == nullptr) { - // If retry fails, don't continue to retry until we have success (probably in next GC pass) - ShenandoahThreadLocalData::disable_plab_retries(thread); + // If we still get nullptr, we'll try a shared allocation below. + // However, don't continue to retry until we have success (probably in next GC pass) + shenandoah_plab->disable_retries(); } } - // else, copy still equals nullptr. this causes shared allocation below, preserving this plab for future needs. } } break; @@ -384,9 +382,9 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint break; } case OLD_GENERATION: { - ShenandoahThreadLocalData::plab(thread)->undo_allocation(copy, size); + ShenandoahThreadLocalData::shenandoah_plab(thread)->plab()->undo_allocation(copy, size); if (is_promotion) { - ShenandoahThreadLocalData::subtract_from_plab_promoted(thread, size * HeapWordSize); + ShenandoahThreadLocalData::shenandoah_plab(thread)->subtract_from_promoted(size * HeapWordSize); } break; } @@ -411,179 +409,6 @@ template oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint from_region_age); template oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint from_region_age); -inline HeapWord* ShenandoahGenerationalHeap::allocate_from_plab(Thread* thread, size_t size, bool is_promotion) { - assert(UseTLAB, "TLABs should be enabled"); - - PLAB* plab = ShenandoahThreadLocalData::plab(thread); - HeapWord* obj; - - if (plab == nullptr) { - assert(!thread->is_Java_thread() && !thread->is_Worker_thread(), "Performance: thread should have PLAB: %s", thread->name()); - // No PLABs in this thread, fallback to shared allocation - return nullptr; - } else if (is_promotion && !ShenandoahThreadLocalData::allow_plab_promotions(thread)) { - return nullptr; - } - // if plab->word_size() <= 0, thread's plab not yet initialized for this pass, so allow_plab_promotions() is not trustworthy - obj = plab->allocate(size); - if ((obj == nullptr) && (plab->words_remaining() < plab_min_size())) { - // allocate_from_plab_slow will establish allow_plab_promotions(thread) for future invocations - obj = allocate_from_plab_slow(thread, size, is_promotion); - } - // if plab->words_remaining() >= ShenGenHeap::heap()->plab_min_size(), just return nullptr so we can use a shared allocation - if (obj == nullptr) { - return nullptr; - } - - if (is_promotion) { - ShenandoahThreadLocalData::add_to_plab_promoted(thread, size * HeapWordSize); - } - return obj; -} - -// Establish a new PLAB and allocate size HeapWords within it. -HeapWord* ShenandoahGenerationalHeap::allocate_from_plab_slow(Thread* thread, size_t size, bool is_promotion) { - assert(mode()->is_generational(), "PLABs only relevant to generational GC"); - - const size_t plab_min_size = this->plab_min_size(); - // PLABs are aligned to card boundaries to avoid synchronization with concurrent - // allocations in other PLABs. - const size_t min_size = (size > plab_min_size)? align_up(size, CardTable::card_size_in_words()): plab_min_size; - - // Figure out size of new PLAB, using value determined at last refill. - size_t cur_size = ShenandoahThreadLocalData::plab_size(thread); - if (cur_size == 0) { - cur_size = plab_min_size; - } - - // Expand aggressively, doubling at each refill in this epoch, ceiling at plab_max_size() - const size_t future_size = MIN2(cur_size * 2, plab_max_size()); - // Doubling, starting at a card-multiple, should give us a card-multiple. (Ceiling and floor - // are card multiples.) - assert(is_aligned(future_size, CardTable::card_size_in_words()), "Card multiple by construction, future_size: %zu" - ", card_size: %u, cur_size: %zu, max: %zu", - future_size, CardTable::card_size_in_words(), cur_size, plab_max_size()); - - // Record new heuristic value even if we take any shortcut. This captures - // the case when moderately-sized objects always take a shortcut. At some point, - // heuristics should catch up with them. Note that the requested cur_size may - // not be honored, but we remember that this is the preferred size. - log_debug(gc, plab)("Set next PLAB refill size: %zu bytes", future_size * HeapWordSize); - ShenandoahThreadLocalData::set_plab_size(thread, future_size); - - if (cur_size < size) { - // The PLAB to be allocated is still not large enough to hold the object. Fall back to shared allocation. - // This avoids retiring perfectly good PLABs in order to represent a single large object allocation. - log_debug(gc, plab)("Current PLAB size (%zu) is too small for %zu", cur_size * HeapWordSize, size * HeapWordSize); - return nullptr; - } - - // Retire current PLAB, and allocate a new one. - PLAB* plab = ShenandoahThreadLocalData::plab(thread); - if (plab->words_remaining() < plab_min_size) { - // Retire current PLAB. This takes care of any PLAB book-keeping. - // retire_plab() registers the remnant filler object with the remembered set scanner without a lock. - // Since PLABs are card-aligned, concurrent registrations in other PLABs don't interfere. - retire_plab(plab, thread); - - size_t actual_size = 0; - HeapWord* plab_buf = allocate_new_plab(min_size, cur_size, &actual_size); - if (plab_buf == nullptr) { - if (min_size == plab_min_size) { - // Disable PLAB promotions for this thread because we cannot even allocate a minimal PLAB. This allows us - // to fail faster on subsequent promotion attempts. - ShenandoahThreadLocalData::disable_plab_promotions(thread); - } - return nullptr; - } else { - ShenandoahThreadLocalData::enable_plab_retries(thread); - } - // Since the allocated PLAB may have been down-sized for alignment, plab->allocate(size) below may still fail. - if (ZeroTLAB) { - // ... and clear it. - Copy::zero_to_words(plab_buf, actual_size); - } else { - // ...and zap just allocated object. -#ifdef ASSERT - // Skip mangling the space corresponding to the object header to - // ensure that the returned space is not considered parsable by - // any concurrent GC thread. - size_t hdr_size = oopDesc::header_size(); - Copy::fill_to_words(plab_buf + hdr_size, actual_size - hdr_size, badHeapWordVal); -#endif // ASSERT - } - assert(is_aligned(actual_size, CardTable::card_size_in_words()), "Align by design"); - plab->set_buf(plab_buf, actual_size); - if (is_promotion && !ShenandoahThreadLocalData::allow_plab_promotions(thread)) { - return nullptr; - } - return plab->allocate(size); - } else { - // If there's still at least min_size() words available within the current plab, don't retire it. Let's nibble - // away on this plab as long as we can. Meanwhile, return nullptr to force this particular allocation request - // to be satisfied with a shared allocation. By packing more promotions into the previously allocated PLAB, we - // reduce the likelihood of evacuation failures, and we reduce the need for downsizing our PLABs. - return nullptr; - } -} - -HeapWord* ShenandoahGenerationalHeap::allocate_new_plab(size_t min_size, size_t word_size, size_t* actual_size) { - // Align requested sizes to card-sized multiples. Align down so that we don't violate max size of TLAB. - assert(is_aligned(min_size, CardTable::card_size_in_words()), "Align by design"); - assert(word_size >= min_size, "Requested PLAB is too small"); - - ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(min_size, word_size); - // Note that allocate_memory() sets a thread-local flag to prohibit further promotions by this thread - // if we are at risk of infringing on the old-gen evacuation budget. - HeapWord* res = allocate_memory(req); - if (res != nullptr) { - *actual_size = req.actual_size(); - } else { - *actual_size = 0; - } - assert(is_aligned(res, CardTable::card_size_in_words()), "Align by design"); - return res; -} - -void ShenandoahGenerationalHeap::retire_plab(PLAB* plab, Thread* thread) { - // We don't enforce limits on plab evacuations. We let it consume all available old-gen memory in order to reduce - // probability of an evacuation failure. We do enforce limits on promotion, to make sure that excessive promotion - // does not result in an old-gen evacuation failure. Note that a failed promotion is relatively harmless. Any - // object that fails to promote in the current cycle will be eligible for promotion in a subsequent cycle. - - // When the plab was instantiated, its entirety was treated as if the entire buffer was going to be dedicated to - // promotions. Now that we are retiring the buffer, we adjust for the reality that the plab is not entirely promotions. - // 1. Some of the plab may have been dedicated to evacuations. - // 2. Some of the plab may have been abandoned due to waste (at the end of the plab). - size_t not_promoted = - ShenandoahThreadLocalData::get_plab_actual_size(thread) - ShenandoahThreadLocalData::get_plab_promoted(thread); - ShenandoahThreadLocalData::reset_plab_promoted(thread); - ShenandoahThreadLocalData::set_plab_actual_size(thread, 0); - if (not_promoted > 0) { - log_debug(gc, plab)("Retire PLAB, unexpend unpromoted: %zu", not_promoted * HeapWordSize); - old_generation()->unexpend_promoted(not_promoted); - } - const size_t original_waste = plab->waste(); - HeapWord* const top = plab->top(); - - // plab->retire() overwrites unused memory between plab->top() and plab->hard_end() with a dummy object to make memory parsable. - // It adds the size of this unused memory, in words, to plab->waste(). - plab->retire(); - if (top != nullptr && plab->waste() > original_waste && is_in_old(top)) { - // If retiring the plab created a filler object, then we need to register it with our card scanner so it can - // safely walk the region backing the plab. - log_debug(gc, plab)("retire_plab() is registering remnant of size %zu at " PTR_FORMAT, - (plab->waste() - original_waste) * HeapWordSize, p2i(top)); - // No lock is necessary because the PLAB memory is aligned on card boundaries. - old_generation()->card_scan()->register_object_without_lock(top); - } -} - -void ShenandoahGenerationalHeap::retire_plab(PLAB* plab) { - Thread* thread = Thread::current(); - retire_plab(plab, thread); -} - // Make sure old-generation is large enough, but no larger than is necessary, to hold mixed evacuations // and promotions, if we anticipate either. Any deficit is provided by the young generation, subject to // mutator_xfer_limit, and any surplus is transferred to the young generation. mutator_xfer_limit is @@ -1164,9 +989,7 @@ void ShenandoahGenerationalHeap::complete_degenerated_cycle() { coalesce_and_fill_old_regions(false); } - log_info(gc, cset)("Degenerated cycle complete, promotions reserved: %zu, promotions expended: %zu, failed count: %zu, failed bytes: %zu", - old_generation()->get_promoted_reserve(), old_generation()->get_promoted_expended(), - old_generation()->get_promotion_failed_count(), old_generation()->get_promotion_failed_words() * HeapWordSize); + old_generation()->maybe_log_promotion_failure_stats(false); } void ShenandoahGenerationalHeap::complete_concurrent_cycle() { @@ -1181,9 +1004,7 @@ void ShenandoahGenerationalHeap::complete_concurrent_cycle() { entry_global_coalesce_and_fill(); } - log_info(gc, cset)("Concurrent cycle complete, promotions reserved: %zu, promotions expended: %zu, failed count: %zu, failed bytes: %zu", - old_generation()->get_promoted_reserve(), old_generation()->get_promoted_expended(), - old_generation()->get_promotion_failed_count(), old_generation()->get_promotion_failed_words() * HeapWordSize); + old_generation()->maybe_log_promotion_failure_stats(true); } void ShenandoahGenerationalHeap::entry_global_coalesce_and_fill() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp index 7fe0362aa3f..d6893dc011e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp @@ -102,9 +102,6 @@ public: size_t plab_min_size() const { return _min_plab_size; } size_t plab_max_size() const { return _max_plab_size; } - void retire_plab(PLAB* plab); - void retire_plab(PLAB* plab, Thread* thread); - // ---------- Update References // // In the generational mode, we will use this function for young, mixed, and global collections. @@ -113,10 +110,6 @@ public: void final_update_refs_update_region_states() override; private: - HeapWord* allocate_from_plab(Thread* thread, size_t size, bool is_promotion); - HeapWord* allocate_from_plab_slow(Thread* thread, size_t size, bool is_promotion); - HeapWord* allocate_new_plab(size_t min_size, size_t word_size, size_t* actual_size); - const size_t _min_plab_size; const size_t _max_plab_size; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index c0a4cfc34fb..9cd22e6ee25 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1182,20 +1182,20 @@ public: } if (ShenandoahHeap::heap()->mode()->is_generational()) { - PLAB* plab = ShenandoahThreadLocalData::plab(thread); - assert(plab != nullptr, "PLAB should be initialized for %s", thread->name()); + ShenandoahPLAB* shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + assert(shenandoah_plab != nullptr, "PLAB should be initialized for %s", thread->name()); // There are two reasons to retire all plabs between old-gen evacuation passes. // 1. We need to make the plab memory parsable by remembered-set scanning. // 2. We need to establish a trustworthy UpdateWaterMark value within each old-gen heap region - ShenandoahGenerationalHeap::heap()->retire_plab(plab, thread); + shenandoah_plab->retire(); // Re-enable promotions for the next evacuation phase. - ShenandoahThreadLocalData::enable_plab_promotions(thread); + shenandoah_plab->enable_promotions(); // Reset the fill size for next evacuation phase. - if (_resize && ShenandoahThreadLocalData::plab_size(thread) > 0) { - ShenandoahThreadLocalData::set_plab_size(thread, 0); + if (_resize && shenandoah_plab->desired_size() > 0) { + shenandoah_plab->set_desired_size(0); } } } @@ -1465,9 +1465,9 @@ public: assert(gclab->words_remaining() == 0, "GCLAB should not need retirement"); if (ShenandoahHeap::heap()->mode()->is_generational()) { - PLAB* plab = ShenandoahThreadLocalData::plab(thread); - assert(plab != nullptr, "PLAB should be initialized for %s", thread->name()); - assert(plab->words_remaining() == 0, "PLAB should not need retirement"); + ShenandoahPLAB* shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + assert(shenandoah_plab != nullptr, "PLAB should be initialized for %s", thread->name()); + assert(shenandoah_plab->plab()->words_remaining() == 0, "PLAB should not need retirement"); } } }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index 1b12909bcaf..16a24da8b1c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -38,6 +38,7 @@ #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahReferenceProcessor.hpp" #include "gc/shenandoah/shenandoahScanRemembered.inline.hpp" +#include "gc/shenandoah/shenandoahThreadLocalData.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shenandoah/shenandoahWorkerPolicy.hpp" #include "gc/shenandoah/shenandoahYoungGeneration.hpp" @@ -109,8 +110,6 @@ ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues) _promoted_expended(0), _promotion_potential(0), _pad_for_promote_in_place(0), - _promotion_failure_count(0), - _promotion_failure_words(0), _promotable_humongous_regions(0), _promotable_regular_regions(0), _is_parsable(true), @@ -148,8 +147,50 @@ void ShenandoahOldGeneration::augment_promoted_reserve(size_t increment) { void ShenandoahOldGeneration::reset_promoted_expended() { shenandoah_assert_heaplocked_or_safepoint(); _promoted_expended.store_relaxed(0); - _promotion_failure_count.store_relaxed(0); - _promotion_failure_words.store_relaxed(0); +} + +void ShenandoahOldGeneration::maybe_log_promotion_failure_stats(bool concurrent) const { + LogTarget(Info, gc, plab) plab_info; + if (plab_info.is_enabled()) { + size_t failed_count = 0; + size_t failed_words = 0; + + class AggregatePromotionFailuresClosure : public ThreadClosure { + private: + size_t _total_count; + size_t _total_words; + public: + AggregatePromotionFailuresClosure() : _total_count(0), _total_words(0) {} + + void do_thread(Thread* thread) override { + ShenandoahPLAB* plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + if (plab != nullptr) { + _total_count += plab->get_promotion_failure_count(); + _total_words += plab->get_promotion_failure_words(); + plab->reset_promotion_failures(); + } + } + + size_t total_count() const { return _total_count; } + size_t total_words() const { return _total_words; } + }; + + AggregatePromotionFailuresClosure cl; + if (concurrent) { + MutexLocker lock(Threads_lock); + Threads::threads_do(&cl); + } else { + Threads::threads_do(&cl); + } + + failed_count = cl.total_count(); + failed_words = cl.total_words(); + + LogStream ls(plab_info); + ls.print_cr("Cycle complete, promotions reserved: %zu, promotions expended: %zu, failed count: %zu, failed bytes: %zu", + get_promoted_reserve(), get_promoted_expended(), + failed_count, failed_words * HeapWordSize); + } } size_t ShenandoahOldGeneration::expend_promoted(size_t increment) { @@ -199,7 +240,8 @@ ShenandoahOldGeneration::configure_plab_for_current_thread(const ShenandoahAlloc // We've created a new plab. Now we configure it whether it will be used for promotions // and evacuations - or just evacuations. Thread* thread = Thread::current(); - ShenandoahThreadLocalData::reset_plab_promoted(thread); + ShenandoahPLAB* shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + shenandoah_plab->reset_promoted(); // The actual size of the allocation may be larger than the requested bytes (due to alignment on card boundaries). // If this puts us over our promotion budget, we need to disable future PLAB promotions for this thread. @@ -209,12 +251,12 @@ ShenandoahOldGeneration::configure_plab_for_current_thread(const ShenandoahAlloc log_debug(gc, plab)("Thread can promote using PLAB of %zu bytes. Expended: %zu, available: %zu", actual_size, get_promoted_expended(), get_promoted_reserve()); expend_promoted(actual_size); - ShenandoahThreadLocalData::enable_plab_promotions(thread); - ShenandoahThreadLocalData::set_plab_actual_size(thread, actual_size); + shenandoah_plab->enable_promotions(); + shenandoah_plab->set_actual_size(actual_size); } else { // Disable promotions in this thread because entirety of this PLAB must be available to hold old-gen evacuations. - ShenandoahThreadLocalData::disable_plab_promotions(thread); - ShenandoahThreadLocalData::set_plab_actual_size(thread, 0); + shenandoah_plab->disable_promotions(); + shenandoah_plab->set_actual_size(0); log_debug(gc, plab)("Thread cannot promote using PLAB of %zu bytes. Expended: %zu, available: %zu, mixed evacuations? %s", actual_size, get_promoted_expended(), get_promoted_reserve(), BOOL_TO_STR(ShenandoahHeap::heap()->collection_set()->has_old_regions())); } @@ -582,13 +624,21 @@ void ShenandoahOldGeneration::handle_failed_evacuation() { } } -void ShenandoahOldGeneration::handle_failed_promotion(Thread* thread, size_t size) { - _promotion_failure_count.add_then_fetch(1UL); - _promotion_failure_words.add_then_fetch(size); +void ShenandoahOldGeneration::handle_failed_promotion(Thread* thread, size_t size) const { + LogTarget(Info, gc, plab) plab_info; + if (plab_info.is_enabled()) { + ShenandoahPLAB* plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + if (plab != nullptr) { + plab->record_promotion_failure(size); + } else { + ResourceMark for_thread_name; + log_debug(gc, plab)("Thread: %s has no plab", thread->name()); + } + } - LogTarget(Debug, gc, plab) lt; - LogStream ls(lt); - if (lt.is_enabled()) { + LogTarget(Debug, gc, plab) plab_debug; + if (plab_debug.is_enabled()) { + LogStream ls(plab_debug); log_failed_promotion(ls, thread, size); } } @@ -603,9 +653,10 @@ void ShenandoahOldGeneration::log_failed_promotion(LogStream& ls, Thread* thread const size_t gc_id = heap->control_thread()->get_gc_id(); if ((gc_id != last_report_epoch) || (epoch_report_count++ < MaxReportsPerEpoch)) { // Promotion failures should be very rare. Invest in providing useful diagnostic info. - PLAB* const plab = ShenandoahThreadLocalData::plab(thread); + ShenandoahPLAB* const shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + PLAB* const plab = (shenandoah_plab == nullptr)? nullptr: shenandoah_plab->plab(); const size_t words_remaining = (plab == nullptr)? 0: plab->words_remaining(); - const char* promote_enabled = ShenandoahThreadLocalData::allow_plab_promotions(thread)? "enabled": "disabled"; + const char* promote_enabled = (shenandoah_plab != nullptr && shenandoah_plab->allows_promotion())? "enabled": "disabled"; // Promoted reserve is only changed by vm or control thread. Promoted expended is always accessed atomically. const size_t promotion_reserve = get_promoted_reserve(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 5ebad461f3c..12e046a1afc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -76,11 +76,6 @@ private: // objects). This field records the total amount of padding used for such regions. size_t _pad_for_promote_in_place; - // Keep track of the number and size of promotions that failed. Perhaps we should use this to increase - // the size of the old generation for the next collection cycle. - Atomic _promotion_failure_count; - Atomic _promotion_failure_words; - // During construction of the collection set, we keep track of regions that are eligible // for promotion in place. These fields track the count of those humongous and regular regions. // This data is used to force the evacuation phase even when the collection set is otherwise @@ -125,9 +120,8 @@ public: // This is used on the allocation path to gate promotions that would exceed the reserve size_t get_promoted_expended() const; - // Return the count and size (in words) of failed promotions since the last reset - size_t get_promotion_failed_count() const { return _promotion_failure_count.load_relaxed(); } - size_t get_promotion_failed_words() const { return _promotion_failure_words.load_relaxed(); } + // Aggregate and log promotion failure stats if logging is enabled + void maybe_log_promotion_failure_stats(bool concurrent) const; // Test if there is enough memory reserved for this promotion bool can_promote(size_t requested_bytes) const { @@ -175,7 +169,7 @@ public: void handle_failed_evacuation(); // Increment promotion failure counters, optionally log a more detailed message - void handle_failed_promotion(Thread* thread, size_t size); + void handle_failed_promotion(Thread* thread, size_t size) const; void log_failed_promotion(LogStream& ls, Thread* thread, size_t size) const; // A successful evacuation re-dirties the cards and registers the object with the remembered set diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp new file mode 100644 index 00000000000..412cfa9447e --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.cpp @@ -0,0 +1,221 @@ +/* + * 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. + * + */ + +#include "gc/shared/cardTable.hpp" +#include "gc/shenandoah/shenandoahAllocRequest.hpp" +#include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" +#include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahPLAB.hpp" +#include "logging/log.hpp" +#include "runtime/globals.hpp" +#include "runtime/javaThread.hpp" +#include "utilities/copy.hpp" + +ShenandoahPLAB::ShenandoahPLAB() : + _plab(nullptr), + _desired_size(0), + _actual_size(0), + _promoted(0), + _promotion_failure_count(0), + _promotion_failure_words(0), + _allows_promotion(false), + _retries_enabled(false), + _heap(ShenandoahGenerationalHeap::heap()) { + _plab = new PLAB(align_up(PLAB::min_size(), CardTable::card_size_in_words())); +} + +ShenandoahPLAB::~ShenandoahPLAB() { + if (_plab != nullptr) { + delete _plab; + } +} + +void ShenandoahPLAB::subtract_from_promoted(size_t increment) { + assert(_promoted >= increment, "Cannot subtract more than remaining promoted"); + _promoted -= increment; +} + +HeapWord* ShenandoahPLAB::allocate(size_t size, bool is_promotion) { + assert(UseTLAB, "TLABs should be enabled"); + + if (_plab == nullptr) { + // No PLABs in this thread, fallback to shared allocation + return nullptr; + } + + if (is_promotion && !_allows_promotion) { + // Thread is not allowed to promote + return nullptr; + } + + HeapWord* obj = _plab->allocate(size); + if (obj == nullptr) { + if (_plab->words_remaining() < _heap->plab_min_size()) { + // allocate_slow will establish _allows_promotion for future invocations + obj = allocate_slow(size, is_promotion); + } + } + + // if plab->words_remaining() >= ShenGenHeap::heap()->plab_min_size(), just return nullptr so we can use a shared allocation + if (obj == nullptr) { + return nullptr; + } + + if (is_promotion) { + add_to_promoted(size * HeapWordSize); + } + return obj; +} + +// Establish a new PLAB and allocate size HeapWords within it. +HeapWord* ShenandoahPLAB::allocate_slow(size_t size, bool is_promotion) { + assert(_heap->mode()->is_generational(), "PLABs only relevant to generational GC"); + + // PLABs are aligned to card boundaries to avoid synchronization with concurrent + // allocations in other PLABs. + const size_t plab_min_size = _heap->plab_min_size(); + const size_t min_size = (size > plab_min_size)? align_up(size, CardTable::card_size_in_words()): plab_min_size; + + // Figure out size of new PLAB, using value determined at last refill. + size_t cur_size = _desired_size; + if (cur_size == 0) { + cur_size = plab_min_size; + } + + // Expand aggressively, doubling at each refill in this epoch, ceiling at plab_max_size() + // Doubling, starting at a card-multiple, should give us a card-multiple. (Ceiling and floor + // are card multiples.) + const size_t future_size = MIN2(cur_size * 2, _heap->plab_max_size()); + assert(is_aligned(future_size, CardTable::card_size_in_words()), "Card multiple by construction, future_size: %zu" + ", card_size: %u, cur_size: %zu, max: %zu", + future_size, CardTable::card_size_in_words(), cur_size, _heap->plab_max_size()); + + // Record new heuristic value even if we take any shortcut. This captures + // the case when moderately-sized objects always take a shortcut. At some point, + // heuristics should catch up with them. Note that the requested cur_size may + // not be honored, but we remember that this is the preferred size. + log_debug(gc, plab)("Set next PLAB refill size: %zu bytes", future_size * HeapWordSize); + set_desired_size(future_size); + + if (cur_size < size) { + // The PLAB to be allocated is still not large enough to hold the object. Fall back to shared allocation. + // This avoids retiring perfectly good PLABs in order to represent a single large object allocation. + log_debug(gc, plab)("Current PLAB size (%zu) is too small for %zu", cur_size * HeapWordSize, size * HeapWordSize); + return nullptr; + } + + if (_plab->words_remaining() < plab_min_size) { + // Retire current PLAB. This takes care of any PLAB book-keeping. + // retire_plab() registers the remnant filler object with the remembered set scanner without a lock. + // Since PLABs are card-aligned, concurrent registrations in other PLABs don't interfere. + retire(); + + size_t actual_size = 0; + HeapWord* plab_buf = allocate_new_plab(min_size, cur_size, &actual_size); + if (plab_buf == nullptr) { + if (min_size == plab_min_size) { + // Disable PLAB promotions for this thread because we cannot even allocate a minimal PLAB. This allows us + // to fail faster on subsequent promotion attempts. + disable_promotions(); + } + return nullptr; + } + + enable_retries(); + + // Since the allocated PLAB may have been down-sized for alignment, plab->allocate(size) below may still fail. + if (ZeroTLAB) { + // Skip mangling the space corresponding to the object header to + // ensure that the returned space is not considered parsable by + // any concurrent GC thread. + Copy::zero_to_words(plab_buf, actual_size); + } else { +#ifdef ASSERT + size_t hdr_size = oopDesc::header_size(); + Copy::fill_to_words(plab_buf + hdr_size, actual_size - hdr_size, badHeapWordVal); +#endif + } + assert(is_aligned(actual_size, CardTable::card_size_in_words()), "Align by design"); + _plab->set_buf(plab_buf, actual_size); + if (is_promotion && !_allows_promotion) { + return nullptr; + } + return _plab->allocate(size); + } + + // If there's still at least min_size() words available within the current plab, don't retire it. Let's nibble + // away on this plab as long as we can. Meanwhile, return nullptr to force this particular allocation request + // to be satisfied with a shared allocation. By packing more promotions into the previously allocated PLAB, we + // reduce the likelihood of evacuation failures, and we reduce the need for downsizing our PLABs. + return nullptr; +} + +HeapWord* ShenandoahPLAB::allocate_new_plab(size_t min_size, size_t word_size, size_t* actual_size) { + assert(is_aligned(min_size, CardTable::card_size_in_words()), "Align by design"); + assert(word_size >= min_size, "Requested PLAB is too small"); + + ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(min_size, word_size); + HeapWord* res = _heap->allocate_memory(req); + if (res != nullptr) { + *actual_size = req.actual_size(); + } else { + *actual_size = 0; + } + assert(is_aligned(res, CardTable::card_size_in_words()), "Align by design"); + return res; +} + +void ShenandoahPLAB::retire() { + // We don't enforce limits on plab evacuations. We let it consume all available old-gen memory in order to reduce + // probability of an evacuation failure. We do enforce limits on promotion, to make sure that excessive promotion + // does not result in an old-gen evacuation failure. Note that a failed promotion is relatively harmless. Any + // object that fails to promote in the current cycle will be eligible for promotion in a subsequent cycle. + + // When the plab was instantiated, its entirety was treated as if the entire buffer was going to be dedicated to + // promotions. Now that we are retiring the buffer, we adjust for the reality that the plab is not entirely promotions. + // 1. Some of the plab may have been dedicated to evacuations. + // 2. Some of the plab may have been abandoned due to waste (at the end of the plab). + size_t not_promoted = _actual_size - _promoted; + reset_promoted(); + set_actual_size(0); + if (not_promoted > 0) { + log_debug(gc, plab)("Retire PLAB, unexpend unpromoted: %zu", not_promoted * HeapWordSize); + _heap->old_generation()->unexpend_promoted(not_promoted); + } + const size_t original_waste = _plab->waste(); + HeapWord* const top = _plab->top(); + + // plab->retire() overwrites unused memory between plab->top() and plab->hard_end() with a dummy object to make memory parsable. + // It adds the size of this unused memory, in words, to plab->waste(). + _plab->retire(); + if (top != nullptr && _plab->waste() > original_waste && _heap->is_in_old(top)) { + // If retiring the plab created a filler object, then we need to register it with our card scanner so it can + // safely walk the region backing the plab. + log_debug(gc, plab)("retire_plab() is registering remnant of size %zu at " PTR_FORMAT, + (_plab->waste() - original_waste) * HeapWordSize, p2i(top)); + // No lock is necessary because the PLAB memory is aligned on card boundaries. + _heap->old_generation()->card_scan()->register_object_without_lock(top); + } +} diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPLAB.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.hpp new file mode 100644 index 00000000000..4278eb65b10 --- /dev/null +++ b/src/hotspot/share/gc/shenandoah/shenandoahPLAB.hpp @@ -0,0 +1,130 @@ +/* + * 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. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHPLAB_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHPLAB_HPP + +#include "gc/shared/plab.hpp" +#include "memory/allocation.hpp" + +class ShenandoahGenerationalHeap; + +class ShenandoahPLAB : public CHeapObj { +private: + // The actual allocation buffer + PLAB* _plab; + + // Heuristics will grow the desired size of plabs. + size_t _desired_size; + + // Once the plab has been allocated, and we know the actual size, we record it here. + size_t _actual_size; + + // As the plab is used for promotions, this value is incremented. When the plab is + // retired, the difference between 'actual_size' and 'promoted' will be returned to + // the old generation's promotion reserve (i.e., it will be 'unexpended'). + size_t _promoted; + + // Track failed promotion attempts per thread + size_t _promotion_failure_count; + size_t _promotion_failure_words; + + // If false, no more promotion by this thread during this evacuation phase. + bool _allows_promotion; + + // If true, evacuations may attempt to allocate a smaller plab if the original size fails. + bool _retries_enabled; + + // Use for allocations, min/max plab sizes + ShenandoahGenerationalHeap* _heap; + + // Enable retry logic for PLAB allocation failures + void enable_retries() { _retries_enabled = true; } + + // Establish a new PLAB and allocate from it + HeapWord* allocate_slow(size_t size, bool is_promotion); + // Allocate a new PLAB buffer from the heap + HeapWord* allocate_new_plab(size_t min_size, size_t word_size, size_t* actual_size); + +public: + ShenandoahPLAB(); + ~ShenandoahPLAB(); + + // Access the underlying PLAB buffer + PLAB* plab() const { return _plab; } + + // Heuristic size for next PLAB allocation + size_t desired_size() const { return _desired_size; } + // Update heuristic size for next PLAB allocation + void set_desired_size(size_t v) { _desired_size = v; } + + // Check if retry logic is enabled + bool retries_enabled() const { return _retries_enabled; } + // Disable retry logic for PLAB allocation failures + void disable_retries() { _retries_enabled = false; } + + // Allow this thread to promote objects + void enable_promotions() { _allows_promotion = true; } + // Prevent this thread from promoting objects + void disable_promotions() { _allows_promotion = false; } + // Check if this thread is allowed to promote objects + bool allows_promotion() const { return _allows_promotion; } + + // Reset promotion tracking for new evacuation phase + void reset_promoted() { _promoted = 0; } + // When a plab is retired, subtract from the expended promotion budget + void subtract_from_promoted(size_t increment); + // Bytes promoted through this PLAB + size_t get_promoted() const { return _promoted; } + // Track promoted bytes in this PLAB + void add_to_promoted(size_t increment) { _promoted += increment; } + + // Track failed promotion attempts + void record_promotion_failure(size_t size) { + _promotion_failure_count++; + _promotion_failure_words += size; + } + // Get failed promotion count for aggregation + size_t get_promotion_failure_count() const { return _promotion_failure_count; } + // Get failed promotion words for aggregation + size_t get_promotion_failure_words() const { return _promotion_failure_words; } + // Reset failure tracking for new evacuation phase + void reset_promotion_failures() { + _promotion_failure_count = 0; + _promotion_failure_words = 0; + } + + // Record actual allocated PLAB size + void set_actual_size(size_t value) { _actual_size = value; } + // Actual allocated PLAB size + size_t get_actual_size() const { return _actual_size; } + + // Allocate from this PLAB + HeapWord* allocate(size_t size, bool is_promotion); + + // Retire this PLAB and return unused promotion budget + void retire(); +}; + +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHPLAB_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp index ace5ab5e69a..1f3ce76cc1c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.cpp @@ -37,12 +37,7 @@ ShenandoahThreadLocalData::ShenandoahThreadLocalData() : _card_table(nullptr), _gclab(nullptr), _gclab_size(0), - _plab(nullptr), - _plab_desired_size(0), - _plab_actual_size(0), - _plab_promoted(0), - _plab_allows_promotion(true), - _plab_retries_enabled(true), + _shenandoah_plab(nullptr), _evacuation_stats(new ShenandoahEvacuationStats()) { } @@ -50,9 +45,9 @@ ShenandoahThreadLocalData::~ShenandoahThreadLocalData() { if (_gclab != nullptr) { delete _gclab; } - if (_plab != nullptr) { - ShenandoahGenerationalHeap::heap()->retire_plab(_plab); - delete _plab; + if (_shenandoah_plab != nullptr) { + _shenandoah_plab->retire(); + delete _shenandoah_plab; } delete _evacuation_stats; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp index f54a65b0785..b1b923bbfce 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahThreadLocalData.hpp @@ -36,6 +36,7 @@ #include "gc/shenandoah/shenandoahCodeRoots.hpp" #include "gc/shenandoah/shenandoahEvacTracker.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.hpp" +#include "gc/shenandoah/shenandoahPLAB.hpp" #include "gc/shenandoah/shenandoahSATBMarkQueueSet.hpp" #include "runtime/javaThread.hpp" #include "utilities/debug.hpp" @@ -62,24 +63,7 @@ private: // Used both by mutator threads and by GC worker threads // for evacuations within the old generation and // for promotions from the young generation into the old generation. - PLAB* _plab; - - // Heuristics will grow the desired size of plabs. - size_t _plab_desired_size; - - // Once the plab has been allocated, and we know the actual size, we record it here. - size_t _plab_actual_size; - - // As the plab is used for promotions, this value is incremented. When the plab is - // retired, the difference between 'actual_size' and 'promoted' will be returned to - // the old generation's promotion reserve (i.e., it will be 'unexpended'). - size_t _plab_promoted; - - // If false, no more promotion by this thread during this evacuation phase. - bool _plab_allows_promotion; - - // If true, evacuations may attempt to allocate a smaller plab if the original size fails. - bool _plab_retries_enabled; + ShenandoahPLAB* _shenandoah_plab; ShenandoahEvacuationStats* _evacuation_stats; @@ -141,8 +125,7 @@ public: data(thread)->_gclab_size = 0; if (ShenandoahHeap::heap()->mode()->is_generational()) { - data(thread)->_plab = new PLAB(align_up(PLAB::min_size(), CardTable::card_size_in_words())); - data(thread)->_plab_desired_size = 0; + data(thread)->_shenandoah_plab = new ShenandoahPLAB(); } } @@ -170,65 +153,8 @@ public: return data(thread)->_evacuation_stats; } - static PLAB* plab(Thread* thread) { - return data(thread)->_plab; - } - - static size_t plab_size(Thread* thread) { - return data(thread)->_plab_desired_size; - } - - static void set_plab_size(Thread* thread, size_t v) { - data(thread)->_plab_desired_size = v; - } - - static void enable_plab_retries(Thread* thread) { - data(thread)->_plab_retries_enabled = true; - } - - static void disable_plab_retries(Thread* thread) { - data(thread)->_plab_retries_enabled = false; - } - - static bool plab_retries_enabled(Thread* thread) { - return data(thread)->_plab_retries_enabled; - } - - static void enable_plab_promotions(Thread* thread) { - data(thread)->_plab_allows_promotion = true; - } - - static void disable_plab_promotions(Thread* thread) { - data(thread)->_plab_allows_promotion = false; - } - - static bool allow_plab_promotions(Thread* thread) { - return data(thread)->_plab_allows_promotion; - } - - static void reset_plab_promoted(Thread* thread) { - data(thread)->_plab_promoted = 0; - } - - static void add_to_plab_promoted(Thread* thread, size_t increment) { - data(thread)->_plab_promoted += increment; - } - - static void subtract_from_plab_promoted(Thread* thread, size_t increment) { - assert(data(thread)->_plab_promoted >= increment, "Cannot subtract more than remaining promoted"); - data(thread)->_plab_promoted -= increment; - } - - static size_t get_plab_promoted(Thread* thread) { - return data(thread)->_plab_promoted; - } - - static void set_plab_actual_size(Thread* thread, size_t value) { - data(thread)->_plab_actual_size = value; - } - - static size_t get_plab_actual_size(Thread* thread) { - return data(thread)->_plab_actual_size; + static ShenandoahPLAB* shenandoah_plab(Thread* thread) { + return data(thread)->_shenandoah_plab; } // Evacuation OOM handling diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp index 4167e33b706..4633d8588d3 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahOldGeneration.cpp @@ -26,6 +26,7 @@ #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/mode/shenandoahMode.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" +#include "gc/shenandoah/shenandoahPLAB.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #define SKIP_IF_NOT_SHENANDOAH() \ @@ -58,10 +59,15 @@ protected: old->set_evacuation_reserve(512 * HeapWordSize); Thread* thread = Thread::current(); - ShenandoahThreadLocalData::reset_plab_promoted(thread); - ShenandoahThreadLocalData::disable_plab_promotions(thread); - ShenandoahThreadLocalData::set_plab_actual_size(thread, INITIAL_PLAB_SIZE); - ShenandoahThreadLocalData::add_to_plab_promoted(thread, INITIAL_PLAB_PROMOTED); + ShenandoahPLAB* shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + if (shenandoah_plab == nullptr) { + ShenandoahThreadLocalData::initialize_gclab(thread); + shenandoah_plab = ShenandoahThreadLocalData::shenandoah_plab(thread); + } + shenandoah_plab->reset_promoted(); + shenandoah_plab->disable_promotions(); + shenandoah_plab->set_actual_size(INITIAL_PLAB_SIZE); + shenandoah_plab->add_to_promoted(INITIAL_PLAB_PROMOTED); } void TearDown() override { @@ -72,15 +78,15 @@ protected: } static bool promotions_enabled() { - return ShenandoahThreadLocalData::allow_plab_promotions(Thread::current()); + return ShenandoahThreadLocalData::shenandoah_plab(Thread::current())->allows_promotion(); } static size_t plab_size() { - return ShenandoahThreadLocalData::get_plab_actual_size(Thread::current()); + return ShenandoahThreadLocalData::shenandoah_plab(Thread::current())->get_actual_size(); } static size_t plab_promoted() { - return ShenandoahThreadLocalData::get_plab_promoted(Thread::current()); + return ShenandoahThreadLocalData::shenandoah_plab(Thread::current())->get_promoted(); } }; From 54c5e41cbaabcd21d7bbf8f5667b2e35cfe12dd7 Mon Sep 17 00:00:00 2001 From: Xu Jiawei Date: Fri, 20 Mar 2026 16:45:43 +0000 Subject: [PATCH 049/160] 8380085: jpackage (Linux) launcher crashes with SIGSEGV when input contains large number of JAR files Reviewed-by: asemenyuk, almatvee --- .../linux/native/applauncher/LinuxLauncher.c | 66 +++++++++++++++---- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/src/jdk.jpackage/linux/native/applauncher/LinuxLauncher.c b/src/jdk.jpackage/linux/native/applauncher/LinuxLauncher.c index 7fb8d9f53e9..7db42bc2dbd 100644 --- a/src/jdk.jpackage/linux/native/applauncher/LinuxLauncher.c +++ b/src/jdk.jpackage/linux/native/applauncher/LinuxLauncher.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -133,6 +133,43 @@ cleanup: } +static ssize_t readFully(const int fd, void* buf, const size_t len) { + size_t nRead = 0; + ssize_t n; + + while (nRead < len) { + n = read(fd, (char*)buf + nRead, len - nRead); + if (n == -1) { + if (errno == EINTR) { + continue; + } + return -1; + } + if (n == 0) { + break; + } + nRead += (size_t)n; + } + return (ssize_t)nRead; +} + +static ssize_t writeFully(const int fd, const void* buf, const size_t len) { + size_t nWritten = 0; + ssize_t n; + + while (nWritten < len) { + n = write(fd, (const char*)buf + nWritten, len - nWritten); + if (n == -1) { + if (errno == EINTR) { + continue; + } + return -1; + } + nWritten += (size_t)n; + } + return (ssize_t)nWritten; +} + static void closePipeEnd(int* pipefd, int idx) { if (pipefd[idx] >= 0) { close(pipefd[idx]); @@ -190,21 +227,24 @@ int main(int argc, char *argv[]) { jvmLauncherData = initJvmlLauncherData(&jvmLauncherDataBufferSize); if (jvmLauncherData) { /* Buffer size */ - if (write(pipefd[1], &jvmLauncherDataBufferSize, - sizeof(jvmLauncherDataBufferSize)) == -1) { + if (writeFully(pipefd[1], &jvmLauncherDataBufferSize, + sizeof(jvmLauncherDataBufferSize)) != + sizeof(jvmLauncherDataBufferSize)) { JP_LOG_ERRNO; goto cleanup; } if (jvmLauncherDataBufferSize) { /* Buffer address */ - if (write(pipefd[1], &jvmLauncherData, - sizeof(jvmLauncherData)) == -1) { + if (writeFully(pipefd[1], &jvmLauncherData, + sizeof(jvmLauncherData)) != + sizeof(jvmLauncherData)) { JP_LOG_ERRNO; goto cleanup; } /* Buffer data */ - if (write(pipefd[1], jvmLauncherData, - jvmLauncherDataBufferSize) == -1) { + if (writeFully(pipefd[1], jvmLauncherData, + jvmLauncherDataBufferSize) != + jvmLauncherDataBufferSize) { JP_LOG_ERRNO; goto cleanup; } @@ -218,8 +258,9 @@ int main(int argc, char *argv[]) { /* Close unused write end */ closePipeEnd(pipefd, 1); - if (read(pipefd[0], &jvmLauncherDataBufferSize, - sizeof(jvmLauncherDataBufferSize)) == -1) { + if (readFully(pipefd[0], &jvmLauncherDataBufferSize, + sizeof(jvmLauncherDataBufferSize)) != + sizeof(jvmLauncherDataBufferSize)) { JP_LOG_ERRNO; goto cleanup; } @@ -229,7 +270,8 @@ int main(int argc, char *argv[]) { goto cleanup; } - if (read(pipefd[0], &baseAddress, sizeof(baseAddress)) == -1) { + if (readFully(pipefd[0], &baseAddress, sizeof(baseAddress)) != + sizeof(baseAddress)) { JP_LOG_ERRNO; goto cleanup; } @@ -240,8 +282,8 @@ int main(int argc, char *argv[]) { goto cleanup; } - if (read(pipefd[0], jvmLauncherData, - jvmLauncherDataBufferSize) == -1) { + if (readFully(pipefd[0], jvmLauncherData, jvmLauncherDataBufferSize) != + jvmLauncherDataBufferSize) { JP_LOG_ERRNO; goto cleanup; } From d0841c6bde28183ab39feb8242982bcefed3f1f9 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Fri, 20 Mar 2026 16:46:12 +0000 Subject: [PATCH 050/160] 8379688: GenShen: Skip promotions when marking finds enough immediate garbage Reviewed-by: kdnilsen --- .../shenandoahGenerationalHeuristics.cpp | 59 ++-- .../shenandoahGenerationalHeuristics.hpp | 2 +- .../heuristics/shenandoahGlobalHeuristics.cpp | 293 ++++++++-------- .../heuristics/shenandoahGlobalHeuristics.hpp | 135 ++++++- .../heuristics/shenandoahOldHeuristics.cpp | 18 +- .../heuristics/shenandoahOldHeuristics.hpp | 5 +- .../heuristics/shenandoahYoungHeuristics.cpp | 7 +- .../shenandoah/shenandoahInPlacePromoter.cpp | 15 +- .../shenandoah/shenandoahInPlacePromoter.hpp | 1 + .../test_shenandoahGlobalHeuristic.cpp | 332 ++++++++++++++++++ 10 files changed, 661 insertions(+), 206 deletions(-) create mode 100644 test/hotspot/gtest/gc/shenandoah/test_shenandoahGlobalHeuristic.cpp diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp index bcf4197f86d..08d628e67ff 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -80,19 +80,11 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio assert(collection_set->is_empty(), "Collection set must be empty here"); _add_regions_to_old = 0; - ShenandoahInPlacePromotionPlanner in_place_promotions(heap); - // Find the amount that will be promoted, regions that will be promoted in - // place, and preselected older regions that will be promoted by evacuation. - compute_evacuation_budgets(in_place_promotions, heap); + // Choose the collection set + filter_regions(collection_set); - // Choose the collection set, including the regions preselected above for promotion into the old generation. - filter_regions(in_place_promotions, collection_set); - - // Even if collection_set->is_empty(), we want to adjust budgets, making reserves available to mutator. - adjust_evacuation_budgets(heap, collection_set); - - if (_generation->is_global()) { + if (!collection_set->is_empty() && _generation->is_global()) { // We have just chosen a collection set for a global cycle. The mark bitmap covering old regions is complete, so // the remembered set scan can use that to avoid walking into garbage. When the next old mark begins, we will // use the mark bitmap to make the old regions parsable by coalescing and filling any unmarked objects. Thus, @@ -229,8 +221,7 @@ void ShenandoahGenerationalHeuristics::compute_evacuation_budgets(ShenandoahInPl // case of a GLOBAL gc. During choose_collection_set() of GLOBAL, old will be expanded on demand. } -void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahInPlacePromotionPlanner& in_place_promotions, - ShenandoahCollectionSet* collection_set) { +void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahCollectionSet* collection_set) { auto heap = ShenandoahGenerationalHeap::heap(); const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); @@ -270,21 +261,12 @@ void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahInPlacePromotion immediate_garbage += garbage; region->make_trash_immediate(); } else { - if (collection_set->is_in(i)) { - assert(heap->is_tenurable(region), "Preselected region %zu must be tenurable", i); - } else if (region->is_young() && heap->is_tenurable(region)) { - // Note that for GLOBAL GC, region may be OLD, and OLD regions do not qualify for pre-selection - - // This region is old enough to be promoted, but it was not preselected, either because its garbage is below - // old garbage threshold so it will be promoted in place, or because there is insufficient room - // in old gen to hold the evacuated copies of this region's live data. In either case, we choose not to - // place this region into the collection set. - } else { - // This is our candidate for later consideration. - assert(region->get_top_before_promote() == nullptr, "Cannot add region %zu scheduled for in-place-promotion to the collection set", i); - candidates[cand_idx].set_region_and_garbage(region, garbage); - cand_idx++; - } + // This is our candidate for later consideration. Note that this region + // could still be promoted in place and may not necessarily end up in the + // collection set. + assert(region->get_top_before_promote() == nullptr, "Cannot add region %zu scheduled for in-place-promotion to the collection set", i); + candidates[cand_idx].set_region_and_garbage(region, garbage); + cand_idx++; } } else if (region->is_humongous_start()) { // Reclaim humongous regions here, and count them as the immediate garbage @@ -310,18 +292,25 @@ void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahInPlacePromotion PROPERFMTARGS(immediate_garbage), PROPERFMTARGS(total_garbage)); const size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage); - const bool has_preselected_regions = !collection_set->is_empty(); - if (has_preselected_regions || (immediate_percent <= ShenandoahImmediateThreshold)) { + ShenandoahInPlacePromotionPlanner in_place_promotions(heap); + if (immediate_percent <= ShenandoahImmediateThreshold) { + + // Find the amount that will be promoted, regions that will be promoted in + // place, and preselected older regions that will be promoted by evacuation. + compute_evacuation_budgets(in_place_promotions, heap); + // Call the subclasses to add young-gen regions into the collection set. choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); - } - if (collection_set->has_old_regions()) { - heap->shenandoah_policy()->record_mixed_cycle(); + // Even if collection_set->is_empty(), we want to adjust budgets, making reserves available to mutator. + adjust_evacuation_budgets(heap, collection_set); + + if (collection_set->has_old_regions()) { + heap->shenandoah_policy()->record_mixed_cycle(); + } } collection_set->summarize(total_garbage, immediate_garbage, immediate_regions); - ShenandoahTracer::report_evacuation_info(collection_set, free_regions, in_place_promotions.humongous_region_stats().count, @@ -375,7 +364,7 @@ size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePr } if (!r->is_regular()) { - if (r->is_humongous() && heap->is_tenurable(r)) { + if (r->is_humongous_start() && heap->is_tenurable(r)) { in_place_promotions.prepare(r); } // Nothing else to be done for humongous regions diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp index 248d0de5a26..c418e8c24ec 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp @@ -74,7 +74,7 @@ private: size_t select_aged_regions(ShenandoahInPlacePromotionPlanner& in_place_promotions, const size_t old_promotion_reserve); // Filter and sort remaining regions before adding to collection set. - void filter_regions(ShenandoahInPlacePromotionPlanner& in_place_promotions, ShenandoahCollectionSet* collection_set); + void filter_regions(ShenandoahCollectionSet* collection_set); // Adjust evacuation budgets after choosing collection set. The argument regions_to_xfer // represents regions to be transferred to old based on decisions made in top_off_collection_set() diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp index 142a6c3f047..ed25cd2e1a9 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp @@ -31,21 +31,94 @@ #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "utilities/quickSort.hpp" +bool ShenandoahEvacuationBudget::try_reserve(size_t bytes) { + size_t new_consumption = _consumed + bytes; + if (new_consumption <= _reserve) { + return true; + } + // Try expanding from shared pool + size_t new_reserve = _reserve; + size_t new_committed = _shared->committed; + while ((new_consumption > new_reserve) && (new_committed < _shared->limit)) { + new_committed += _region_size_bytes; + new_reserve += _region_size_bytes; + } + if (new_consumption <= new_reserve) { + _reserve = new_reserve; + _shared->committed = new_committed; + return true; + } + return false; +} + +void ShenandoahEvacuationBudget::commit(size_t consumption, size_t live) { + _consumed += consumption; + _live_bytes += live; + _region_count++; +} + +ShenandoahGlobalRegionDisposition ShenandoahGlobalCSetBudget::try_add_region( + const ShenandoahGlobalRegionAttributes& region) { + + size_t region_garbage = region.garbage; + size_t new_garbage = _cur_garbage + region_garbage; + bool add_regardless = (region_garbage > _ignore_threshold) && (new_garbage < _min_garbage); + + if (!add_regardless && (region_garbage < _garbage_threshold)) { + return ShenandoahGlobalRegionDisposition::SKIP; + } + + size_t live_bytes = region.live_data_bytes; + + if (region.is_old) { + size_t evac_need = old_evac.anticipated_consumption(live_bytes); + size_t promo_loss = region.free_bytes; + + // Snapshot state for rollback โ€” old branch does two reservations + size_t saved_committed = _shared.committed; + size_t saved_old_reserve = old_evac.reserve(); + size_t saved_promo_reserve = promo.reserve(); + + if (old_evac.try_reserve(evac_need) && promo.try_reserve(promo_loss)) { + old_evac.commit(evac_need, live_bytes); + promo.commit_raw(promo_loss); + _cur_garbage = new_garbage; + return ShenandoahGlobalRegionDisposition::ADD_OLD_EVAC; + } + _shared.committed = saved_committed; + old_evac.set_reserve(saved_old_reserve); + promo.set_reserve(saved_promo_reserve); + return ShenandoahGlobalRegionDisposition::SKIP; + } else if (region.is_tenurable) { + size_t promo_need = promo.anticipated_consumption(live_bytes); + if (promo.try_reserve(promo_need)) { + promo.commit(promo_need, live_bytes); + _cur_garbage = new_garbage; + return ShenandoahGlobalRegionDisposition::ADD_PROMO; + } + return ShenandoahGlobalRegionDisposition::SKIP; + } else { + size_t evac_need = young_evac.anticipated_consumption(live_bytes); + if (young_evac.try_reserve(evac_need)) { + young_evac.commit(evac_need, live_bytes); + _cur_garbage = new_garbage; + return ShenandoahGlobalRegionDisposition::ADD_YOUNG_EVAC; + } + return ShenandoahGlobalRegionDisposition::SKIP; + } +} + ShenandoahGlobalHeuristics::ShenandoahGlobalHeuristics(ShenandoahGlobalGeneration* generation) : ShenandoahGenerationalHeuristics(generation) { } - void ShenandoahGlobalHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, RegionData* data, size_t size, size_t actual_free) { - // Better select garbage-first regions QuickSort::sort(data, size, compare_by_garbage); - - choose_global_collection_set(cset, data, size, actual_free, 0 /* cur_young_garbage */); + choose_global_collection_set(cset, data, size, actual_free, 0); } - void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollectionSet* cset, const ShenandoahHeuristics::RegionData* data, size_t size, size_t actual_free, @@ -80,8 +153,7 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti if (young_evac_reserve > unaffiliated_young_memory) { shared_reserve_regions += unaffiliated_young_regions; } else { - size_t delta_regions = young_evac_reserve / region_size_bytes; - shared_reserve_regions += delta_regions; + shared_reserve_regions += young_evac_reserve / region_size_bytes; } young_evac_reserve = 0; size_t total_old_reserve = old_evac_reserve + old_promo_reserve; @@ -90,24 +162,15 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti shared_reserve_regions += unaffiliated_old_regions; old_promo_reserve = total_old_reserve - unaffiliated_old_memory; } else { - size_t delta_regions = old_evac_reserve / region_size_bytes; - shared_reserve_regions += delta_regions; + shared_reserve_regions += old_evac_reserve / region_size_bytes; } old_evac_reserve = 0; assert(shared_reserve_regions <= (heap->young_generation()->free_unaffiliated_regions() + heap->old_generation()->free_unaffiliated_regions()), - "simple math"); - - size_t shared_reserves = shared_reserve_regions * region_size_bytes; - size_t committed_from_shared_reserves = 0; - - size_t promo_bytes = 0; - size_t old_evac_bytes = 0; - size_t young_evac_bytes = 0; - - size_t consumed_by_promo = 0; // promo_bytes * ShenandoahPromoEvacWaste - size_t consumed_by_old_evac = 0; // old_evac_bytes * ShenandoahOldEvacWaste - size_t consumed_by_young_evac = 0; // young_evac_bytes * ShenandoahEvacWaste + "Shared reserve regions (%zu) should not exceed total unaffiliated regions (young: %zu, old: %zu)", + shared_reserve_regions, + heap->young_generation()->free_unaffiliated_regions(), + heap->old_generation()->free_unaffiliated_regions()); // Of the memory reclaimed by GC, some of this will need to be reserved for the next GC collection. Use the current // young reserve as an approximation of the future Collector reserve requirement. Try to end with at least @@ -115,147 +178,93 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti size_t free_target = (capacity * ShenandoahMinFreeThreshold) / 100 + original_young_evac_reserve; size_t min_garbage = (free_target > actual_free) ? (free_target - actual_free) : 0; - size_t aged_regions_promoted = 0; - size_t young_regions_evacuated = 0; - size_t old_regions_evacuated = 0; + ShenandoahGlobalCSetBudget budget(region_size_bytes, + shared_reserve_regions * region_size_bytes, + garbage_threshold, ignore_threshold, min_garbage, + young_evac_reserve, ShenandoahEvacWaste, + old_evac_reserve, ShenandoahOldEvacWaste, + old_promo_reserve, ShenandoahPromoEvacWaste); + budget.set_cur_garbage(cur_young_garbage); - log_info(gc, ergo)("Adaptive CSet Selection for GLOBAL. Discretionary evacuation budget (for either old or young): %zu%s" - ", Actual Free: %zu%s.", - byte_size_in_proper_unit(shared_reserves), proper_unit_for_byte_size(shared_reserves), - byte_size_in_proper_unit(actual_free), proper_unit_for_byte_size(actual_free)); + log_info(gc, ergo)("Adaptive CSet Selection for global cycle. Discretionary evacuation budget (for either old or young): " PROPERFMT ", Actual Free: " PROPERFMT, + PROPERFMTARGS(budget.shared_reserves()), PROPERFMTARGS(actual_free)); - size_t cur_garbage = cur_young_garbage; for (size_t idx = 0; idx < size; idx++) { ShenandoahHeapRegion* r = data[idx].get_region(); - assert(!cset->is_in(r->index()), "Region (%zu) should not be in the collection set", r->index()); - bool add_region = false; - size_t region_garbage = r->garbage(); - size_t new_garbage = cur_garbage + region_garbage; - bool add_regardless = (region_garbage > ignore_threshold) && (new_garbage < min_garbage); - size_t live_bytes = r->get_live_data_bytes(); - if (add_regardless || (region_garbage >= garbage_threshold)) { - if (r->is_old()) { - size_t anticipated_consumption = (size_t) (live_bytes * ShenandoahOldEvacWaste); - size_t new_old_consumption = consumed_by_old_evac + anticipated_consumption; - size_t new_old_evac_reserve = old_evac_reserve; - size_t proposed_old_region_expansion = 0; - while ((new_old_consumption > new_old_evac_reserve) && (committed_from_shared_reserves < shared_reserves)) { - committed_from_shared_reserves += region_size_bytes; - proposed_old_region_expansion++; - new_old_evac_reserve += region_size_bytes; - } - // If this region has free memory and we choose to place it in the collection set, its free memory is no longer - // available to hold promotion results. So we behave as if its free memory is consumed within the promotion reserve. - size_t anticipated_loss_from_promo_reserve = r->free(); - size_t new_promo_consumption = consumed_by_promo + anticipated_loss_from_promo_reserve; - size_t new_promo_reserve = old_promo_reserve; - while ((new_promo_consumption > new_promo_reserve) && (committed_from_shared_reserves < shared_reserves)) { - committed_from_shared_reserves += region_size_bytes; - proposed_old_region_expansion++; - new_promo_reserve += region_size_bytes; - } - if ((new_old_consumption <= new_old_evac_reserve) && (new_promo_consumption <= new_promo_reserve)) { - add_region = true; - old_evac_reserve = new_old_evac_reserve; - old_promo_reserve = new_promo_reserve; - old_evac_bytes += live_bytes; - consumed_by_old_evac = new_old_consumption; - consumed_by_promo = new_promo_consumption; - cur_garbage = new_garbage; - old_regions_evacuated++; - } else { - // We failed to sufficiently expand old so unwind proposed expansion - committed_from_shared_reserves -= proposed_old_region_expansion * region_size_bytes; - } - } else if (heap->is_tenurable(r)) { - size_t anticipated_consumption = (size_t) (live_bytes * ShenandoahPromoEvacWaste); - size_t new_promo_consumption = consumed_by_promo + anticipated_consumption; - size_t new_promo_reserve = old_promo_reserve; - size_t proposed_old_region_expansion = 0; - while ((new_promo_consumption > new_promo_reserve) && (committed_from_shared_reserves < shared_reserves)) { - committed_from_shared_reserves += region_size_bytes; - proposed_old_region_expansion++; - new_promo_reserve += region_size_bytes; - } - if (new_promo_consumption <= new_promo_reserve) { - add_region = true; - old_promo_reserve = new_promo_reserve; - promo_bytes += live_bytes; - consumed_by_promo = new_promo_consumption; - cur_garbage = new_garbage; - aged_regions_promoted++; - } else { - // We failed to sufficiently expand old so unwind proposed expansion - committed_from_shared_reserves -= proposed_old_region_expansion * region_size_bytes; - } - } else { - assert(r->is_young() && !heap->is_tenurable(r), "DeMorgan's law (assuming r->is_affiliated)"); - size_t anticipated_consumption = (size_t) (live_bytes * ShenandoahEvacWaste); - size_t new_young_evac_consumption = consumed_by_young_evac + anticipated_consumption; - size_t new_young_evac_reserve = young_evac_reserve; - size_t proposed_young_region_expansion = 0; - while ((new_young_evac_consumption > new_young_evac_reserve) && (committed_from_shared_reserves < shared_reserves)) { - committed_from_shared_reserves += region_size_bytes; - proposed_young_region_expansion++; - new_young_evac_reserve += region_size_bytes; - } - if (new_young_evac_consumption <= new_young_evac_reserve) { - add_region = true; - young_evac_reserve = new_young_evac_reserve; - young_evac_bytes += live_bytes; - consumed_by_young_evac = new_young_evac_consumption; - cur_garbage = new_garbage; - young_regions_evacuated++; - } else { - // We failed to sufficiently expand old so unwind proposed expansion - committed_from_shared_reserves -= proposed_young_region_expansion * region_size_bytes; - } - } + if (cset->is_in(r) || r->get_top_before_promote() != nullptr) { + assert(heap->is_tenurable(r), "Region %zu already selected for promotion must be tenurable", idx); + continue; } - if (add_region) { + + ShenandoahGlobalRegionAttributes attrs; + attrs.garbage = r->garbage(); + attrs.live_data_bytes = r->get_live_data_bytes(); + attrs.free_bytes = r->free(); + attrs.is_old = r->is_old(); + attrs.is_tenurable = !r->is_old() && heap->is_tenurable(r); + + if (budget.try_add_region(attrs) != ShenandoahGlobalRegionDisposition::SKIP) { cset->add_region(r); } } - if (committed_from_shared_reserves < shared_reserves) { - // Give all the rest to promotion - old_promo_reserve += (shared_reserves - committed_from_shared_reserves); - // dead code: committed_from_shared_reserves = shared_reserves; - } + budget.finish(); - // Consider the effects of round-off: - // 1. We know that the sum over each evacuation mutiplied by Evacuation Waste is <= total evacuation reserve - // 2. However, the reserve for each individual evacuation may be rounded down. In the worst case, we will be over budget - // by the number of regions evacuated, since each region's reserve might be under-estimated by at most 1 - // 3. Likewise, if we take the sum of bytes evacuated and multiply this by the Evacuation Waste and then round down - // to nearest integer, the calculated reserve will underestimate the true reserve needs by at most 1. - // 4. This explains the adjustments to subtotals in the assert statements below. - assert(young_evac_bytes * ShenandoahEvacWaste <= young_evac_reserve + young_regions_evacuated, - "budget: %zu <= %zu", (size_t) (young_evac_bytes * ShenandoahEvacWaste), young_evac_reserve); - assert(old_evac_bytes * ShenandoahOldEvacWaste <= old_evac_reserve + old_regions_evacuated, - "budget: %zu <= %zu", (size_t) (old_evac_bytes * ShenandoahOldEvacWaste), old_evac_reserve); - assert(promo_bytes * ShenandoahPromoEvacWaste <= old_promo_reserve + aged_regions_promoted, - "budget: %zu <= %zu", (size_t) (promo_bytes * ShenandoahPromoEvacWaste), old_promo_reserve); - assert(young_evac_reserve + old_evac_reserve + old_promo_reserve <= - heap->young_generation()->get_evacuation_reserve() + heap->old_generation()->get_evacuation_reserve() + - heap->old_generation()->get_promoted_reserve(), "Exceeded budget"); + DEBUG_ONLY(budget.assert_budget_constraints_hold( + heap->young_generation()->get_evacuation_reserve() + + heap->old_generation()->get_evacuation_reserve() + + heap->old_generation()->get_promoted_reserve())); - if (heap->young_generation()->get_evacuation_reserve() < young_evac_reserve) { - size_t delta_bytes = young_evac_reserve - heap->young_generation()->get_evacuation_reserve(); + if (heap->young_generation()->get_evacuation_reserve() < budget.young_evac.reserve()) { + size_t delta_bytes = budget.young_evac.reserve() - heap->young_generation()->get_evacuation_reserve(); size_t delta_regions = delta_bytes / region_size_bytes; size_t regions_to_transfer = MIN2(unaffiliated_old_regions, delta_regions); log_info(gc)("Global GC moves %zu unaffiliated regions from old collector to young collector reserves", regions_to_transfer); ssize_t negated_regions = -regions_to_transfer; heap->free_set()->move_unaffiliated_regions_from_collector_to_old_collector(negated_regions); - } else if (heap->young_generation()->get_evacuation_reserve() > young_evac_reserve) { - size_t delta_bytes = heap->young_generation()->get_evacuation_reserve() - young_evac_reserve; + } else if (heap->young_generation()->get_evacuation_reserve() > budget.young_evac.reserve()) { + size_t delta_bytes = heap->young_generation()->get_evacuation_reserve() - budget.young_evac.reserve(); size_t delta_regions = delta_bytes / region_size_bytes; size_t regions_to_transfer = MIN2(unaffiliated_young_regions, delta_regions); log_info(gc)("Global GC moves %zu unaffiliated regions from young collector to old collector reserves", regions_to_transfer); heap->free_set()->move_unaffiliated_regions_from_collector_to_old_collector(regions_to_transfer); } - heap->young_generation()->set_evacuation_reserve(young_evac_reserve); - heap->old_generation()->set_evacuation_reserve(old_evac_reserve); - heap->old_generation()->set_promoted_reserve(old_promo_reserve); + heap->young_generation()->set_evacuation_reserve(budget.young_evac.reserve()); + heap->old_generation()->set_evacuation_reserve(budget.old_evac.reserve()); + heap->old_generation()->set_promoted_reserve(budget.promo.reserve()); } + +#ifdef ASSERT +void ShenandoahGlobalCSetBudget::assert_budget_constraints_hold(size_t original_total_reserves) const { + // Consider the effects of round-off: + // 1. We know that the sum over each evacuation multiplied by Evacuation Waste is <= total evacuation reserve + // 2. However, the reserve for each individual evacuation may be rounded down. In the worst case, we will be + // over budget by the number of regions evacuated, since each region's reserve might be under-estimated by + // at most 1. + // 3. Likewise, if we take the sum of bytes evacuated and multiply this by the Evacuation Waste and then round + // down to nearest integer, the calculated reserve will underestimate the true reserve needs by at most 1. + // 4. This explains the adjustments to subtotals in the assert statements below. + assert(young_evac.live_bytes() * young_evac.waste_factor() <= + young_evac.reserve() + young_evac.region_count(), + "Young evac consumption (%zu) exceeds reserve (%zu) + region count (%zu)", + (size_t)(young_evac.live_bytes() * young_evac.waste_factor()), + young_evac.reserve(), young_evac.region_count()); + assert(old_evac.live_bytes() * old_evac.waste_factor() <= + old_evac.reserve() + old_evac.region_count(), + "Old evac consumption (%zu) exceeds reserve (%zu) + region count (%zu)", + (size_t)(old_evac.live_bytes() * old_evac.waste_factor()), + old_evac.reserve(), old_evac.region_count()); + assert(promo.live_bytes() * promo.waste_factor() <= + promo.reserve() + promo.region_count(), + "Promo consumption (%zu) exceeds reserve (%zu) + region count (%zu)", + (size_t)(promo.live_bytes() * promo.waste_factor()), + promo.reserve(), promo.region_count()); + + size_t total_post_reserves = young_evac.reserve() + old_evac.reserve() + promo.reserve(); + assert(total_post_reserves <= original_total_reserves, + "Total post-cset reserves (%zu + %zu + %zu = %zu) exceed original reserves (%zu)", + young_evac.reserve(), old_evac.reserve(), promo.reserve(), + total_post_reserves, original_total_reserves); +} +#endif diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp index 1f95f75c521..8102fa24d14 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp @@ -25,16 +25,138 @@ #ifndef SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGLOBALHEURISTICS_HPP #define SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGLOBALHEURISTICS_HPP - #include "gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp" class ShenandoahGlobalGeneration; -/* - * This is a specialization of the generational heuristics which is aware - * of old and young regions and respects the configured evacuation parameters - * for such regions during a global collection of a generational heap. - */ +enum class ShenandoahGlobalRegionDisposition { + SKIP, + ADD_OLD_EVAC, + ADD_PROMO, + ADD_YOUNG_EVAC +}; + +// A shared pool of evacuation reserves that can be drawn from by any +// evacuation category. Owned by ShenandoahGlobalCSetBudget; each +// ShenandoahEvacuationBudget holds a pointer to it. +struct ShenandoahSharedEvacReserve { + size_t limit; + size_t committed; + + ShenandoahSharedEvacReserve(size_t limit) : limit(limit), committed(0) {} +}; + +// Tracks the budget for a single evacuation category. +class ShenandoahEvacuationBudget { + size_t _reserve; + size_t _consumed; + size_t _live_bytes; + size_t _region_count; + size_t _region_size_bytes; + double _waste_factor; + ShenandoahSharedEvacReserve* _shared; + +public: + ShenandoahEvacuationBudget(size_t reserve, double waste_factor, + size_t region_size_bytes, + ShenandoahSharedEvacReserve* shared) + : _reserve(reserve), _consumed(0), _live_bytes(0), + _region_count(0), _region_size_bytes(region_size_bytes), + _waste_factor(waste_factor), _shared(shared) {} + + size_t anticipated_consumption(size_t live_bytes) const { + return (size_t)(live_bytes * _waste_factor); + } + + // Try to reserve 'bytes' from this budget, expanding from the shared + // pool if necessary. On success, updates _reserve and shared->committed + // and returns true. On failure, nothing is modified. + bool try_reserve(size_t bytes); + + // Record that a region was accepted. + void commit(size_t consumption, size_t live_bytes); + + // Record a raw consumption (e.g. free bytes lost from promo reserve). + void commit_raw(size_t bytes) { _consumed += bytes; } + + size_t reserve() const { return _reserve; } + size_t consumed() const { return _consumed; } + size_t live_bytes() const { return _live_bytes; } + size_t region_count() const { return _region_count; } + double waste_factor() const { return _waste_factor; } + + void add_to_reserve(size_t bytes) { _reserve += bytes; } + void set_reserve(size_t bytes) { _reserve = bytes; } +}; + +// These are the attributes of a region required to decide if it can be +// added to the collection set or not. +struct ShenandoahGlobalRegionAttributes { + size_t garbage; + size_t live_data_bytes; + size_t free_bytes; + bool is_old; + bool is_tenurable; +}; + +// This class consolidates all of the data required to build a global +// collection set. Critically, it takes no dependencies on any classes +// that themselves depend on ShenandoahHeap. This makes it possible to +// write extensive unit tests for this complex code. +class ShenandoahGlobalCSetBudget { + size_t _region_size_bytes; + size_t _garbage_threshold; + size_t _ignore_threshold; + size_t _min_garbage; + size_t _cur_garbage; + + ShenandoahSharedEvacReserve _shared; + +public: + ShenandoahEvacuationBudget young_evac; + ShenandoahEvacuationBudget old_evac; + ShenandoahEvacuationBudget promo; + + ShenandoahGlobalCSetBudget(size_t region_size_bytes, + size_t shared_reserves, + size_t garbage_threshold, + size_t ignore_threshold, + size_t min_garbage, + size_t young_evac_reserve, double young_waste, + size_t old_evac_reserve, double old_waste, + size_t promo_reserve, double promo_waste) + : _region_size_bytes(region_size_bytes), + _garbage_threshold(garbage_threshold), + _ignore_threshold(ignore_threshold), + _min_garbage(min_garbage), + _cur_garbage(0), + _shared(shared_reserves), + young_evac(young_evac_reserve, young_waste, region_size_bytes, &_shared), + old_evac(old_evac_reserve, old_waste, region_size_bytes, &_shared), + promo(promo_reserve, promo_waste, region_size_bytes, &_shared) {} + + ShenandoahGlobalRegionDisposition try_add_region(const ShenandoahGlobalRegionAttributes& region); + + // Any remaining shared budget is given to the promotion reserve. + void finish() { + if (_shared.committed < _shared.limit) { + promo.add_to_reserve(_shared.limit - _shared.committed); + } + } + + // Verify that the budget invariants hold after collection set selection. + // original_total_reserves is the sum of the young, old, and promo evacuation + // reserves as they were before the budget was constructed. + DEBUG_ONLY(void assert_budget_constraints_hold(size_t original_total_reserves) const;) + + size_t region_size_bytes() const { return _region_size_bytes; } + size_t shared_reserves() const { return _shared.limit; } + size_t committed_from_shared() const { return _shared.committed; } + size_t cur_garbage() const { return _cur_garbage; } + + void set_cur_garbage(size_t g) { _cur_garbage = g; } +}; + class ShenandoahGlobalHeuristics : public ShenandoahGenerationalHeuristics { public: ShenandoahGlobalHeuristics(ShenandoahGlobalGeneration* generation); @@ -50,5 +172,4 @@ private: size_t cur_young_garbage) const; }; - #endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHGLOBALHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index e0cab781674..4f71562f4f6 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -79,7 +79,6 @@ ShenandoahOldHeuristics::ShenandoahOldHeuristics(ShenandoahOldGeneration* genera } bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* collection_set) { - _mixed_evac_cset = collection_set; _included_old_regions = 0; _evacuated_old_bytes = 0; _collected_old_bytes = 0; @@ -106,10 +105,6 @@ bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* coll _first_pinned_candidate = NOT_FOUND; - uint included_old_regions = 0; - size_t evacuated_old_bytes = 0; - size_t collected_old_bytes = 0; - // If a region is put into the collection set, then this region's free (not yet used) bytes are no longer // "available" to hold the results of other evacuations. This may cause a decrease in the remaining amount // of memory that can still be evacuated. We address this by reducing the evacuation budget by the amount @@ -152,7 +147,7 @@ bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* coll log_debug(gc)("Choose old regions for mixed collection: old evacuation budget: " PROPERFMT ", candidates: %u", PROPERFMTARGS(_old_evacuation_budget), unprocessed_old_collection_candidates()); - return add_old_regions_to_cset(); + return add_old_regions_to_cset(collection_set); } bool ShenandoahOldHeuristics::all_candidates_are_pinned() { @@ -226,7 +221,7 @@ void ShenandoahOldHeuristics::slide_pinned_regions_to_front() { _next_old_collection_candidate = write_index + 1; } -bool ShenandoahOldHeuristics::add_old_regions_to_cset() { +bool ShenandoahOldHeuristics::add_old_regions_to_cset(ShenandoahCollectionSet* collection_set) { if (unprocessed_old_collection_candidates() == 0) { return false; } @@ -310,7 +305,7 @@ bool ShenandoahOldHeuristics::add_old_regions_to_cset() { break; } } - _mixed_evac_cset->add_region(r); + collection_set->add_region(r); _included_old_regions++; _evacuated_old_bytes += live_data_for_evacuation; _collected_old_bytes += r->garbage(); @@ -356,7 +351,7 @@ bool ShenandoahOldHeuristics::finalize_mixed_evacs() { return (_included_old_regions > 0); } -bool ShenandoahOldHeuristics::top_off_collection_set(size_t &add_regions_to_old) { +bool ShenandoahOldHeuristics::top_off_collection_set(ShenandoahCollectionSet* collection_set, size_t &add_regions_to_old) { if (unprocessed_old_collection_candidates() == 0) { add_regions_to_old = 0; return false; @@ -367,11 +362,10 @@ bool ShenandoahOldHeuristics::top_off_collection_set(size_t &add_regions_to_old) // We have budgeted to assure the live_bytes_in_tenurable_regions() get evacuated into old generation. Young reserves // only for untenurable region evacuations. - size_t planned_young_evac = _mixed_evac_cset->get_live_bytes_in_untenurable_regions(); + size_t planned_young_evac = collection_set->get_live_bytes_in_untenurable_regions(); size_t consumed_from_young_cset = (size_t) (planned_young_evac * ShenandoahEvacWaste); size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); - size_t regions_required_for_collector_reserve = (consumed_from_young_cset + region_size_bytes - 1) / region_size_bytes; assert(consumed_from_young_cset <= max_young_cset, "sanity"); assert(max_young_cset <= young_unaffiliated_regions * region_size_bytes, "sanity"); @@ -399,7 +393,7 @@ bool ShenandoahOldHeuristics::top_off_collection_set(size_t &add_regions_to_old) _old_generation->augment_evacuation_reserve(budget_supplement); young_generation->set_evacuation_reserve(max_young_cset - budget_supplement); - return add_old_regions_to_cset(); + return add_old_regions_to_cset(collection_set); } else { add_regions_to_old = 0; return false; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp index e657ac58ae4..04a92d28248 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp @@ -106,7 +106,6 @@ private: // when client code invokes prime_collection_set(). They are consulted, and sometimes modified, when client code // calls top_off_collection_set() to possibly expand the number of old-gen regions in a mixed evacuation cset, and by // finalize_mixed_evacs(), which prepares the way for mixed evacuations to begin. - ShenandoahCollectionSet* _mixed_evac_cset; size_t _evacuated_old_bytes; size_t _collected_old_bytes; size_t _included_old_regions; @@ -163,7 +162,7 @@ private: // a conservative old evacuation budget, and the second time with a larger more aggressive old evacuation budget. Returns // true iff we need to finalize mixed evacs. (If no regions are added to the collection set, there is no need to finalize // mixed evacuations.) - bool add_old_regions_to_cset(); + bool add_old_regions_to_cset(ShenandoahCollectionSet* collection_set); public: explicit ShenandoahOldHeuristics(ShenandoahOldGeneration* generation, ShenandoahGenerationalHeap* gen_heap); @@ -180,7 +179,7 @@ public: // evacuation candidate regions into the collection set as will fit within this excess repurposed reserved. // Returns true iff we need to finalize mixed evacs. Upon return, the var parameter regions_to_xfer holds the // number of regions to transfer from young to old. - bool top_off_collection_set(size_t &add_regions_to_old); + bool top_off_collection_set(ShenandoahCollectionSet* collection_set, size_t &add_regions_to_old); // Having added all eligible mixed-evacuation candidates to the collection set, this function updates the total count // of how much old-gen memory remains to be evacuated and adjusts the representation of old-gen regions that remain to diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp index 68935aba5a8..68ffb6592db 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp @@ -60,7 +60,7 @@ void ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(Shenandoah // enough consolidated garbage to make effective use of young-gen evacuation reserve. If there is still // young-gen reserve available following selection of the young-gen collection set, see if we can use // this memory to expand the old-gen evacuation collection set. - need_to_finalize_mixed |= heap->old_generation()->heuristics()->top_off_collection_set(_add_regions_to_old); + need_to_finalize_mixed |= heap->old_generation()->heuristics()->top_off_collection_set(cset, _add_regions_to_old); if (need_to_finalize_mixed) { heap->old_generation()->heuristics()->finalize_mixed_evacs(); } @@ -90,7 +90,10 @@ void ShenandoahYoungHeuristics::choose_young_collection_set(ShenandoahCollection for (size_t idx = 0; idx < size; idx++) { ShenandoahHeapRegion* r = data[idx].get_region(); - assert(!cset->is_in(r), "Region %zu should not already be in the collection set", idx); + if (cset->is_in(r) || r->get_top_before_promote() != nullptr) { + assert(heap->is_tenurable(r), "Region %zu already selected for promotion must be tenurable", idx); + continue; + } // Note that we do not add tenurable regions if they were not pre-selected. They were not selected // because there is insufficient room in old-gen to hold their to-be-promoted live objects or because diff --git a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp index 4a15401250c..10d61221e87 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.cpp @@ -48,6 +48,7 @@ bool ShenandoahInPlacePromotionPlanner::is_eligible(const ShenandoahHeapRegion* } void ShenandoahInPlacePromotionPlanner::prepare(ShenandoahHeapRegion* r) { + assert(!r->is_humongous_continuation(), "Should not call for humongous continuations"); HeapWord* tams = _marking_context->top_at_mark_start(r); HeapWord* original_top = r->top(); @@ -59,10 +60,16 @@ void ShenandoahInPlacePromotionPlanner::prepare(ShenandoahHeapRegion* r) { return; } - if (r->is_humongous()) { - // Nothing else to do for humongous, we just update the stats and move on. The humongous regions - // themselves will be discovered and promoted by gc workers during evacuation. - _pip_humongous_stats.update(r); + if (r->is_humongous_start()) { + if (const oop obj = cast_to_oop(r->bottom()); !obj->is_typeArray()) { + // Nothing else to do for humongous, we just update the stats and move on. The humongous regions + // themselves will be discovered and promoted by gc workers during evacuation. Note that humongous + // primitive arrays are not promoted. + const size_t num_regions = ShenandoahHeapRegion::required_regions(obj->size() * HeapWordSize); + for (size_t i = r->index(); i < r->index() + num_regions; i++) { + _pip_humongous_stats.update(_heap->get_region(i)); + } + } return; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp index 2e7e19ee26e..d2cb644a59e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahInPlacePromoter.hpp @@ -142,3 +142,4 @@ private: }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHINPLACEPROMOTER_HPP + diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahGlobalHeuristic.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahGlobalHeuristic.cpp new file mode 100644 index 00000000000..7b3e4227b5c --- /dev/null +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahGlobalHeuristic.cpp @@ -0,0 +1,332 @@ +/* + * 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. + */ + +#include "unittest.hpp" +#include "gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp" + +// Region size of 256K is the minimum Shenandoah region size. +static const size_t REGION_SIZE = 256 * 1024; + +// Waste factors matching the default JVM flag values. +static const double OLD_EVAC_WASTE = ShenandoahOldEvacWaste; +static const double PROMO_EVAC_WASTE = ShenandoahPromoEvacWaste; +static const double YOUNG_EVAC_WASTE = ShenandoahEvacWaste; + +// Default thresholds as percentages of region size. +static const size_t GARBAGE_THRESHOLD = REGION_SIZE * 25 / 100; +static const size_t IGNORE_THRESHOLD = REGION_SIZE * 5 / 100; + +static ShenandoahGlobalCSetBudget make_budget(size_t shared_reserves, + size_t min_garbage = 0, + size_t young_evac_reserve = 0, + size_t old_evac_reserve = 0, + size_t promo_reserve = 0) { + return ShenandoahGlobalCSetBudget(REGION_SIZE, shared_reserves, + GARBAGE_THRESHOLD, IGNORE_THRESHOLD, min_garbage, + young_evac_reserve, YOUNG_EVAC_WASTE, + old_evac_reserve, OLD_EVAC_WASTE, + promo_reserve, PROMO_EVAC_WASTE); +} + +// ---- Threshold tests ---- + +// A region whose garbage is 1 byte below the 25% garbage threshold should be +// skipped, even when there is plenty of shared reserve available. +TEST(ShenandoahGlobalCSet, skip_below_garbage_threshold) { + auto budget = make_budget(10 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD - 1, REGION_SIZE - (GARBAGE_THRESHOLD - 1), 0, false, false + }; + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::SKIP); +} + +// When the cumulative garbage collected so far is below min_garbage, regions +// with garbage above the ignore threshold (5%) but below the garbage threshold +// (25%) are added anyway ("add_regardless" path). This young non-tenurable +// region should be accepted as a young evacuation. +TEST(ShenandoahGlobalCSet, add_regardless_when_below_min_garbage) { + auto budget = make_budget(10 * REGION_SIZE, /*min_garbage=*/REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + IGNORE_THRESHOLD + 1, 1000, 0, false, false + }; + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_YOUNG_EVAC); +} + +// A region whose garbage is exactly at the ignore threshold (not above it) +// should be skipped even when min_garbage has not been met, because the +// add_regardless condition requires garbage strictly above the ignore threshold. +TEST(ShenandoahGlobalCSet, skip_at_ignore_threshold) { + auto budget = make_budget(10 * REGION_SIZE, /*min_garbage=*/REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + IGNORE_THRESHOLD, 1000, 0, false, false + }; + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::SKIP); +} + +// ---- Old region evacuation ---- + +// An old region at the garbage threshold with 50K live and 10K free, backed by +// ample shared reserves. Should be accepted as old evacuation. The old_evac +// budget consumes 50K * 1.4 = 70K for evacuation, and the promo budget absorbs +// the 10K of free bytes that are lost when this region enters the collection set. +TEST(ShenandoahGlobalCSet, old_region_accepted) { + auto budget = make_budget(5 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 50000, 10000, true, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_OLD_EVAC); + EXPECT_EQ(budget.old_evac.region_count(), (size_t)1); + EXPECT_EQ(budget.old_evac.live_bytes(), (size_t)50000); + EXPECT_EQ(budget.old_evac.consumed(), (size_t)(50000 * OLD_EVAC_WASTE)); + EXPECT_EQ(budget.promo.consumed(), (size_t)10000); + EXPECT_EQ(budget.cur_garbage(), GARBAGE_THRESHOLD); +} + +// An old region with zero shared reserves and no per-category reserves. Both +// the old_evac and promo reservations will fail, so the region is skipped. +// Verify that all budget state remains at zero (clean rollback). +TEST(ShenandoahGlobalCSet, old_region_rejected_no_budget) { + auto budget = make_budget(0); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 50000, 10000, true, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::SKIP); + EXPECT_EQ(budget.old_evac.region_count(), (size_t)0); + EXPECT_EQ(budget.committed_from_shared(), (size_t)0); + EXPECT_EQ(budget.old_evac.consumed(), (size_t)0); + EXPECT_EQ(budget.promo.consumed(), (size_t)0); +} + +// An old region with no per-category reserves but sufficient shared reserves. +// The old_evac budget must expand from the shared pool to accommodate the +// evacuation. Since old_evac starts at zero, all of its reserve comes from +// shared, so committed_from_shared should equal old_evac.reserve(). +TEST(ShenandoahGlobalCSet, old_region_expands_shared) { + auto budget = make_budget(5 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 50000, 0, true, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_OLD_EVAC); + EXPECT_GT(budget.committed_from_shared(), (size_t)0); + EXPECT_EQ(budget.committed_from_shared(), budget.old_evac.reserve()); + EXPECT_GE(budget.old_evac.reserve(), budget.old_evac.consumed()); +} + +// An old region with enough old_evac reserve for the live data but zero promo +// reserve, and a full region's worth of free bytes. The free bytes represent +// promotion capacity that is lost when this region enters the cset, so the +// promo budget must expand from shared reserves to absorb that loss. +TEST(ShenandoahGlobalCSet, old_region_free_bytes_consume_promo_reserve) { + size_t live_data = 10000; + size_t evac_needed = (size_t)(live_data * OLD_EVAC_WASTE); + auto budget = make_budget(5 * REGION_SIZE, 0, 0, evac_needed, 0); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, live_data, REGION_SIZE, true, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_OLD_EVAC); + EXPECT_GT(budget.committed_from_shared(), (size_t)0); + EXPECT_GE(budget.promo.reserve(), (size_t)REGION_SIZE); + EXPECT_EQ(budget.promo.consumed(), (size_t)REGION_SIZE); +} + +// ---- Promotion ---- + +// A young tenurable region at the garbage threshold with 40K live data and +// ample shared reserves. Should be accepted as a promotion. The promo budget +// consumes 40K * 1.2 = 48K. +TEST(ShenandoahGlobalCSet, tenurable_region_promoted) { + auto budget = make_budget(5 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 40000, 0, false, true + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_PROMO); + EXPECT_EQ(budget.promo.region_count(), (size_t)1); + EXPECT_EQ(budget.promo.live_bytes(), (size_t)40000); + EXPECT_EQ(budget.promo.consumed(), (size_t)(40000 * PROMO_EVAC_WASTE)); +} + +// A young tenurable region with zero reserves. The promo reservation fails, +// so the region is skipped. +TEST(ShenandoahGlobalCSet, tenurable_region_rejected) { + auto budget = make_budget(0); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 40000, 0, false, true + }; + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::SKIP); +} + +// ---- Young evacuation ---- + +// A young non-tenurable region at the garbage threshold with 30K live data. +// Should be accepted as a young evacuation. The young_evac budget consumes +// 30K * 1.2 = 36K. +TEST(ShenandoahGlobalCSet, young_region_evacuated) { + auto budget = make_budget(5 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 30000, 0, false, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_YOUNG_EVAC); + EXPECT_EQ(budget.young_evac.region_count(), (size_t)1); + EXPECT_EQ(budget.young_evac.consumed(), (size_t)(30000 * YOUNG_EVAC_WASTE)); +} + +// A young non-tenurable region with zero reserves. The young_evac reservation +// fails, so the region is skipped. +TEST(ShenandoahGlobalCSet, young_region_rejected) { + auto budget = make_budget(0); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 30000, 0, false, false + }; + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::SKIP); +} + +// ---- Multi-region and budget interaction tests ---- + +// Evaluate two identical young regions against a budget with only 1 region's +// worth of shared reserves. Each region has 150K live data, so anticipated +// consumption is 150K * 1.2 = 180K per region. The first region expands the +// young_evac reserve by one region (256K) from shared, consuming 180K of it. +// The second region needs another 180K, but only 76K remains in the reserve +// and the shared pool is exhausted, so it must be skipped. +TEST(ShenandoahGlobalCSet, shared_exhausted_across_regions) { + auto budget = make_budget(1 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 150000, 0, false, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_YOUNG_EVAC); + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::SKIP); + EXPECT_EQ(budget.young_evac.region_count(), (size_t)1); +} + +// An old region where the old_evac reserve is pre-sized to exactly cover the +// evacuation need, and the promo reserve covers the free bytes loss. No shared +// reserves should be drawn because the per-category reserves are sufficient. +TEST(ShenandoahGlobalCSet, existing_reserve_used_before_shared) { + size_t live_data = 50000; + size_t evac_needed = (size_t)(live_data * OLD_EVAC_WASTE); + auto budget = make_budget(5 * REGION_SIZE, 0, 0, evac_needed, REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, live_data, 1000, true, false + }; + + EXPECT_EQ(budget.try_add_region(region), ShenandoahGlobalRegionDisposition::ADD_OLD_EVAC); + EXPECT_EQ(budget.committed_from_shared(), (size_t)0); +} + +// Evaluate two identical young regions and verify that cur_garbage accumulates +// the garbage from each accepted region. +TEST(ShenandoahGlobalCSet, garbage_accumulates) { + auto budget = make_budget(10 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 10000, 0, false, false + }; + + budget.try_add_region(region); + EXPECT_EQ(budget.cur_garbage(), GARBAGE_THRESHOLD); + budget.try_add_region(region); + EXPECT_EQ(budget.cur_garbage(), 2 * GARBAGE_THRESHOLD); +} + +// When no regions are evaluated, finish() should donate the entire shared +// reserve pool to the promo budget. +TEST(ShenandoahGlobalCSet, finish_donates_remaining_to_promo) { + auto budget = make_budget(5 * REGION_SIZE); + size_t promo_before = budget.promo.reserve(); + budget.finish(); + EXPECT_EQ(budget.promo.reserve(), promo_before + 5 * REGION_SIZE); +} + +// After evaluating one region that draws from the shared pool, finish() should +// donate only the remaining (unconsumed) shared reserves to promo. +TEST(ShenandoahGlobalCSet, finish_donates_remainder_after_use) { + auto budget = make_budget(5 * REGION_SIZE); + ShenandoahGlobalRegionAttributes region = { + GARBAGE_THRESHOLD, 10000, 0, false, false + }; + budget.try_add_region(region); + size_t shared_used = budget.committed_from_shared(); + size_t promo_before = budget.promo.reserve(); + budget.finish(); + EXPECT_EQ(budget.promo.reserve(), promo_before + (5 * REGION_SIZE - shared_used)); +} + +// ---- ShenandoahEvacuationBudget unit tests ---- + +// A reservation that fits entirely within the existing reserve should succeed +// without drawing from the shared pool. +TEST(ShenandoahEvacuationBudget, try_reserve_fits_without_expansion) { + ShenandoahSharedEvacReserve shared(5 * REGION_SIZE); + ShenandoahEvacuationBudget evac_budget(REGION_SIZE, 1.0, REGION_SIZE, &shared); + EXPECT_TRUE(evac_budget.try_reserve(REGION_SIZE)); + EXPECT_EQ(shared.committed, (size_t)0); + EXPECT_EQ(evac_budget.reserve(), (size_t)REGION_SIZE); +} + +// A reservation of REGION_SIZE+1 bytes starting from a zero reserve requires +// two region-sized expansions from the shared pool (one region isn't enough). +TEST(ShenandoahEvacuationBudget, try_reserve_expands_from_shared) { + ShenandoahSharedEvacReserve shared(5 * REGION_SIZE); + ShenandoahEvacuationBudget evac_budget(0, 1.0, REGION_SIZE, &shared); + EXPECT_TRUE(evac_budget.try_reserve(REGION_SIZE + 1)); + EXPECT_EQ(shared.committed, 2 * REGION_SIZE); + EXPECT_EQ(evac_budget.reserve(), 2 * REGION_SIZE); +} + +// A reservation with an empty shared pool should fail, leaving both the +// budget's reserve and the shared pool's committed count unchanged. +TEST(ShenandoahEvacuationBudget, try_reserve_fails_no_shared) { + ShenandoahSharedEvacReserve shared(0); + ShenandoahEvacuationBudget evac_budget(0, 1.0, REGION_SIZE, &shared); + EXPECT_FALSE(evac_budget.try_reserve(REGION_SIZE)); + EXPECT_EQ(shared.committed, (size_t)0); + EXPECT_EQ(evac_budget.reserve(), (size_t)0); +} + +// A reservation of 3 regions against a shared pool of only 2 regions should +// fail. On failure, neither the budget's reserve nor the shared pool's +// committed count should be modified. +TEST(ShenandoahEvacuationBudget, try_reserve_fails_insufficient_shared) { + ShenandoahSharedEvacReserve shared(2 * REGION_SIZE); + ShenandoahEvacuationBudget evac_budget(0, 1.0, REGION_SIZE, &shared); + EXPECT_FALSE(evac_budget.try_reserve(3 * REGION_SIZE)); + EXPECT_EQ(shared.committed, (size_t)0); + EXPECT_EQ(evac_budget.reserve(), (size_t)0); +} + +// Committing a consumption of 1200 bytes with 1000 live bytes should update +// all three tracking fields: consumed, live_bytes, and region_count. +TEST(ShenandoahEvacuationBudget, commit_updates_fields) { + ShenandoahSharedEvacReserve shared(5 * REGION_SIZE); + ShenandoahEvacuationBudget evac_budget(REGION_SIZE, 1.2, REGION_SIZE, &shared); + evac_budget.commit(1200, 1000); + EXPECT_EQ(evac_budget.consumed(), (size_t)1200); + EXPECT_EQ(evac_budget.live_bytes(), (size_t)1000); + EXPECT_EQ(evac_budget.region_count(), (size_t)1); +} From 137d97131bc044fa090b88eab767913d690bb2e2 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Fri, 20 Mar 2026 20:18:12 +0000 Subject: [PATCH 051/160] 8378935: C2 crash in PhaseOutput::fill_buffer: wrong size of mach node Reviewed-by: iklam, asmehra --- src/hotspot/share/cds/aotMetaspace.cpp | 4 +-- src/hotspot/share/cds/cdsConfig.cpp | 4 +-- src/hotspot/share/code/aotCodeCache.cpp | 45 +++---------------------- src/hotspot/share/code/aotCodeCache.hpp | 9 ++--- 4 files changed, 10 insertions(+), 52 deletions(-) diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index b75d7628aa9..5e75cd72778 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -1187,8 +1187,8 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS CDSConfig::enable_dumping_aot_code(); { builder.start_ac_region(); - // Write the contents to AOT code region and close AOTCodeCache before packing the region - AOTCodeCache::close(); + // Write the contents to AOT code region before packing the region + AOTCodeCache::dump(); builder.end_ac_region(); } CDSConfig::disable_dumping_aot_code(); diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index f4ef3c66f7a..d8ae50517fe 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -108,6 +108,8 @@ void CDSConfig::ergo_initialize() { } AOTMapLogger::ergo_initialize(); + + setup_compiler_args(); } const char* CDSConfig::default_archive_path() { @@ -635,8 +637,6 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla FLAG_SET_ERGO_IF_DEFAULT(AOTClassLinking, true); } - setup_compiler_args(); - if (AOTClassLinking) { // If AOTClassLinking is specified, enable all AOT optimizations by default. FLAG_SET_ERGO_IF_DEFAULT(AOTInvokeDynamicLinking, true); diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index e5f68afc51d..b29cf906736 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -284,11 +284,11 @@ bool AOTCodeCache::open_cache(bool is_dumping, bool is_using) { return true; } -void AOTCodeCache::close() { +void AOTCodeCache::dump() { if (is_on()) { - delete _cache; // Free memory - _cache = nullptr; - opened_cache = nullptr; + assert(is_on_for_dump(), "should be called only when dumping AOT code"); + MutexLocker ml(Compile_lock); + _cache->finish_write(); } } @@ -304,7 +304,6 @@ AOTCodeCache::AOTCodeCache(bool is_dumping, bool is_using) : _store_size(0), _for_use(is_using), _for_dump(is_dumping), - _closing(false), _failed(false), _lookup_failed(false), _table(nullptr), @@ -381,30 +380,6 @@ void AOTCodeCache::init_early_c1_table() { } } -AOTCodeCache::~AOTCodeCache() { - if (_closing) { - return; // Already closed - } - // Stop any further access to cache. - _closing = true; - - MutexLocker ml(Compile_lock); - if (for_dump()) { // Finalize cache - finish_write(); - } - _load_buffer = nullptr; - if (_C_store_buffer != nullptr) { - FREE_C_HEAP_ARRAY(char, _C_store_buffer); - _C_store_buffer = nullptr; - _store_buffer = nullptr; - } - if (_table != nullptr) { - MutexLocker ml(AOTCodeCStrings_lock, Mutex::_no_safepoint_check_flag); - delete _table; - _table = nullptr; - } -} - void AOTCodeCache::Config::record(uint cpu_features_offset) { _flags = 0; #ifdef ASSERT @@ -1545,18 +1520,6 @@ void AOTCodeAddressTable::init_early_c1() { #undef SET_ADDRESS -AOTCodeAddressTable::~AOTCodeAddressTable() { - if (_extrs_addr != nullptr) { - FREE_C_HEAP_ARRAY(address, _extrs_addr); - } - if (_stubs_addr != nullptr) { - FREE_C_HEAP_ARRAY(address, _stubs_addr); - } - if (_shared_blobs_addr != nullptr) { - FREE_C_HEAP_ARRAY(address, _shared_blobs_addr); - } -} - #ifdef PRODUCT #define MAX_STR_COUNT 200 #else diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 85f8b47920f..bd8721fea8c 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -152,7 +152,6 @@ public: _early_c1_complete(false), _complete(false) { } - ~AOTCodeAddressTable(); void init_extrs(); void init_early_stubs(); void init_shared_blobs(); @@ -260,7 +259,6 @@ private: uint _store_size; // Used when writing cache bool _for_use; // AOT cache is open for using AOT code bool _for_dump; // AOT cache is open for dumping AOT code - bool _closing; // Closing cache file bool _failed; // Failed read/write to/from cache (cache is broken?) bool _lookup_failed; // Failed to lookup for info (skip only this code load) @@ -290,7 +288,6 @@ private: public: AOTCodeCache(bool is_dumping, bool is_using); - ~AOTCodeCache(); const char* cache_buffer() const { return _load_buffer; } bool failed() const { return _failed; } @@ -314,8 +311,6 @@ public: bool for_use() const { return _for_use && !_failed; } bool for_dump() const { return _for_dump && !_failed; } - bool closing() const { return _closing; } - AOTCodeEntry* add_entry() { _store_entries_cnt++; _store_entries -= 1; @@ -375,8 +370,8 @@ public: static AOTCodeCache* cache() { assert(_passed_init2, "Too early to ask"); return _cache; } static void initialize() NOT_CDS_RETURN; static void init2() NOT_CDS_RETURN; - static void close() NOT_CDS_RETURN; - static bool is_on() CDS_ONLY({ return cache() != nullptr && !_cache->closing(); }) NOT_CDS_RETURN_(false); + static void dump() NOT_CDS_RETURN; + static bool is_on() CDS_ONLY({ return cache() != nullptr; }) NOT_CDS_RETURN_(false); static bool is_on_for_use() CDS_ONLY({ return is_on() && _cache->for_use(); }) NOT_CDS_RETURN_(false); static bool is_on_for_dump() CDS_ONLY({ return is_on() && _cache->for_dump(); }) NOT_CDS_RETURN_(false); static bool is_dumping_stub() NOT_CDS_RETURN_(false); From d610aceed477e0e17d3cdc56df719706ae9eccc5 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Fri, 20 Mar 2026 23:42:08 +0000 Subject: [PATCH 052/160] 8380562: GenShen: GC notification event may see invalid values Reviewed-by: cslucas, kdnilsen --- .../share/gc/shenandoah/shenandoahHeap.cpp | 5 +-- .../gc/shenandoah/shenandoahMemoryPool.cpp | 31 ++----------------- .../gc/shenandoah/shenandoahMemoryPool.hpp | 11 +++++++ 3 files changed, 14 insertions(+), 33 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 9cd22e6ee25..817f5e8dc47 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -2700,10 +2700,7 @@ GrowableArray ShenandoahHeap::memory_pools() { } MemoryUsage ShenandoahHeap::memory_usage() { - assert(_initial_size <= ShenandoahHeap::heap()->max_capacity(), "sanity"); - assert(used() <= ShenandoahHeap::heap()->max_capacity(), "sanity"); - assert(committed() <= ShenandoahHeap::heap()->max_capacity(), "sanity"); - return MemoryUsage(_initial_size, used(), committed(), max_capacity()); + return shenandoah_memory_usage(_initial_size, used(), committed(), max_capacity()); } ShenandoahRegionIterator::ShenandoahRegionIterator() : diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp index d55d1bd8147..dfc34dcc3c3 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.cpp @@ -48,25 +48,7 @@ ShenandoahMemoryPool::ShenandoahMemoryPool(ShenandoahHeap* heap, MemoryUsage ShenandoahMemoryPool::get_memory_usage() { - size_t initial = initial_size(); - size_t max = max_size(); - size_t used = used_in_bytes(); - size_t committed = _heap->committed(); - - // These asserts can never fail: max is stable, and all updates to other values never overflow max. - assert(initial <= max, "initial: %zu, max: %zu", initial, max); - assert(used <= max, "used: %zu, max: %zu", used, max); - assert(committed <= max, "committed: %zu, max: %zu", committed, max); - - // Committed and used are updated concurrently and independently. They can momentarily break - // the assert below, which would also fail in downstream code. To avoid that, adjust values - // to make sense under the race. See JDK-8207200. - committed = MAX2(used, committed); - assert(used <= committed, "used: %zu, committed: %zu", used, committed); - assert(initial <= _heap->max_capacity(), "sanity"); - assert(committed <= _heap->max_capacity(), "sanity"); - assert(max <= _heap->max_capacity(), "sanity"); - return MemoryUsage(initial, used, committed, max); + return shenandoah_memory_usage(initial_size(), used_in_bytes(), _heap->committed(), max_size()); } size_t ShenandoahMemoryPool::used_in_bytes() { @@ -83,16 +65,7 @@ ShenandoahGenerationalMemoryPool::ShenandoahGenerationalMemoryPool(ShenandoahHea _generation(generation) { } MemoryUsage ShenandoahGenerationalMemoryPool::get_memory_usage() { - size_t initial = initial_size(); - size_t max = max_size(); - size_t used = used_in_bytes(); - size_t committed = _generation->used_regions_size(); - - assert(initial <= _heap->max_capacity(), "sanity"); - assert(used <= _heap->max_capacity(), "sanity"); - assert(committed <= _heap->max_capacity(), "sanity"); - assert(max <= _heap->max_capacity(), "sanity"); - return MemoryUsage(initial, used, committed, max); + return shenandoah_memory_usage(initial_size(), used_in_bytes(), _generation->used_regions_size(), max_size()); } size_t ShenandoahGenerationalMemoryPool::used_in_bytes() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.hpp index ccdfdddede9..d466087b9b7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMemoryPool.hpp @@ -30,6 +30,17 @@ #include "services/memoryPool.hpp" #include "services/memoryUsage.hpp" +// Constructs a MemoryUsage from concurrently sampled values, clamping committed +// to be at least as large as used to account for concurrent updates. See JDK-8207200. +inline MemoryUsage shenandoah_memory_usage(size_t initial, size_t used, size_t committed, size_t max) { + assert(initial <= max, "initial: %zu, max: %zu", initial, max); + assert(used <= max, "used: %zu, max: %zu", used, max); + assert(committed <= max, "committed: %zu, max: %zu", committed, max); + committed = MAX2(used, committed); + assert(used <= committed, "used: %zu, committed: %zu", used, committed); + return MemoryUsage(initial, used, committed, max); +} + class ShenandoahMemoryPool : public CollectedMemoryPool { protected: ShenandoahHeap* _heap; From e58e576f80cd35728bac6738474c4090737c16a8 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Sat, 21 Mar 2026 02:21:09 +0000 Subject: [PATCH 053/160] 8369181: InflaterOutputStream: writing after finish() results in IllegalStateException instead of an IOException Reviewed-by: alanb, sherman, lancea --- .../java/util/zip/InflaterOutputStream.java | 87 +++++++--- .../util/zip/InflaterOutputStreamTest.java | 157 ++++++++++++++++++ 2 files changed, 218 insertions(+), 26 deletions(-) create mode 100644 test/jdk/java/util/zip/InflaterOutputStreamTest.java diff --git a/src/java.base/share/classes/java/util/zip/InflaterOutputStream.java b/src/java.base/share/classes/java/util/zip/InflaterOutputStream.java index 31c51509a76..abe4e069915 100644 --- a/src/java.base/share/classes/java/util/zip/InflaterOutputStream.java +++ b/src/java.base/share/classes/java/util/zip/InflaterOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,14 +31,14 @@ import java.io.OutputStream; import java.util.Objects; /** - * Implements an output stream filter for uncompressing data stored in the + * Implements an output stream filter for decompressing data stored in the * "deflate" compression format. * *

Decompressor Usage

* An {@code InflaterOutputStream} created without * specifying a {@linkplain Inflater decompressor} will create a decompressor * at construction time, and close the decompressor when the output stream - * is {@linkplain #close closed}. + * is {@linkplain #close closed} or when {@link #finish()} is called. *

* If a decompressor is specified when creating a {@code InflaterOutputStream}, it is the * responsibility of the caller to {@linkplain Inflater#close close} the @@ -49,7 +49,6 @@ import java.util.Objects; * stream, either directly, or with the {@code try}-with-resources statement. * * @since 1.6 - * @author David R Tribble (david@tribble.com) * * @see InflaterInputStream * @see DeflaterInputStream @@ -60,7 +59,7 @@ public class InflaterOutputStream extends FilterOutputStream { /** Decompressor for this stream. */ protected final Inflater inf; - /** Output buffer for writing uncompressed data. */ + /** Output buffer for writing decompressed data. */ protected final byte[] buf; /** Temporary write buffer. */ @@ -72,6 +71,10 @@ public class InflaterOutputStream extends FilterOutputStream { /** true iff {@link #close()} has been called. */ private boolean closed = false; + // set to true if finish() was called and this InflaterOutputStream + // had created its own Inflater at construction time. + private boolean defaultInflaterClosed; + /** * Checks to make sure that this stream has not been closed. */ @@ -88,7 +91,7 @@ public class InflaterOutputStream extends FilterOutputStream { * The decompressor will be closed when this output stream * is {@linkplain #close() closed}. * - * @param out output stream to write the uncompressed data to + * @param out output stream to write the decompressed data to * @throws NullPointerException if {@code out} is null */ public InflaterOutputStream(OutputStream out) { @@ -104,7 +107,7 @@ public class InflaterOutputStream extends FilterOutputStream { * {@linkplain ##decompressor-usage will not close} the given * {@linkplain Inflater decompressor}. * - * @param out output stream to write the uncompressed data to + * @param out output stream to write the decompressed data to * @param infl decompressor ("inflater") for this stream * @throws NullPointerException if {@code out} or {@code infl} is null */ @@ -120,7 +123,7 @@ public class InflaterOutputStream extends FilterOutputStream { * {@linkplain ##decompressor-usage will not close} the given * {@linkplain Inflater decompressor}. * - * @param out output stream to write the uncompressed data to + * @param out output stream to write the decompressed data to * @param infl decompressor ("inflater") for this stream * @param bufLen decompression buffer size * @throws IllegalArgumentException if {@code bufLen <= 0} @@ -143,27 +146,45 @@ public class InflaterOutputStream extends FilterOutputStream { } /** - * Writes any remaining uncompressed data to the output stream and closes + * Writes any remaining decompressed data to the output stream and closes * the underlying output stream. * + * @implSpec If not already closed, this method calls {@link #finish()} before + * closing the underlying output stream. + * * @throws IOException if an I/O error occurs */ @Override public void close() throws IOException { - if (!closed) { - // Complete the uncompressed output + if (closed) { + return; + } + IOException toThrow = null; + // Complete the decompressed output + try { + finish(); + } catch (IOException ioe) { + toThrow = ioe; + } finally { try { - finish(); - } finally { out.close(); - closed = true; + } catch (IOException ioe) { + if (toThrow == null) { + toThrow = ioe; + } else if (toThrow != ioe) { + toThrow.addSuppressed(ioe); + } } + closed = true; + } + if (toThrow != null) { + throw toThrow; } } /** - * Flushes this output stream, forcing any pending buffered output bytes to be - * written. + * Flushes this output stream, writing any pending buffered decompressed data to + * the underlying output stream. * * @throws IOException if an I/O error occurs or this stream is already * closed @@ -184,7 +205,7 @@ public class InflaterOutputStream extends FilterOutputStream { break; } - // Write the uncompressed output data block + // Write the decompressed output data block out.write(buf, 0, n); } super.flush(); @@ -200,12 +221,18 @@ public class InflaterOutputStream extends FilterOutputStream { } /** - * Finishes writing uncompressed data to the output stream without closing - * the underlying stream. Use this method when applying multiple filters in - * succession to the same output stream. + * Writes any pending buffered decompressed data to the underlying output stream, + * without closing the underlying stream. * - * @throws IOException if an I/O error occurs or this stream is already - * closed + * @implSpec This method calls {@link #flush()} to write any pending buffered + * decompressed data. + *

+ * If this {@code InflaterOutputStream} was created without specifying + * a {@linkplain Inflater decompressor}, then this method closes the decompressor + * that was created at construction time. The {@code InflaterOutputStream} cannot + * then be used for any further writes. + * + * @throws IOException if an I/O error occurs or this stream is already closed */ public void finish() throws IOException { ensureOpen(); @@ -214,11 +241,12 @@ public class InflaterOutputStream extends FilterOutputStream { flush(); if (usesDefaultInflater) { inf.end(); + this.defaultInflaterClosed = true; } } /** - * Writes a byte to the uncompressed output stream. + * Writes a byte to the decompressed output stream. * * @param b a single byte of compressed data to decompress and write to * the output stream @@ -234,7 +262,7 @@ public class InflaterOutputStream extends FilterOutputStream { } /** - * Writes an array of bytes to the uncompressed output stream. + * Writes an array of bytes to the decompressed output stream. * * @param b buffer containing compressed data to decompress and write to * the output stream @@ -251,15 +279,22 @@ public class InflaterOutputStream extends FilterOutputStream { public void write(byte[] b, int off, int len) throws IOException { // Sanity checks ensureOpen(); + // check if this InflaterOutputStream had constructed its own + // Inflater at construction time and has been + // rendered unusable for writes due to finish() being called + // on it. + if (usesDefaultInflater && defaultInflaterClosed) { + throw new IOException("Inflater closed"); + } if (b == null) { - throw new NullPointerException("Null buffer for read"); + throw new NullPointerException("Null input buffer"); } Objects.checkFromIndexSize(off, len, b.length); if (len == 0) { return; } - // Write uncompressed data to the output stream + // Write decompressed data to the output stream try { for (;;) { int n; diff --git a/test/jdk/java/util/zip/InflaterOutputStreamTest.java b/test/jdk/java/util/zip/InflaterOutputStreamTest.java new file mode 100644 index 00000000000..3f588d95c0c --- /dev/null +++ b/test/jdk/java/util/zip/InflaterOutputStreamTest.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.Deflater; +import java.util.zip.Inflater; +import java.util.zip.InflaterOutputStream; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + +/* + * @test + * @bug 8369181 + * @summary Verify the behaviour of basic operations on InflaterOutputStream + * @run junit ${test.main.class} + */ +class InflaterOutputStreamTest { + + private static final byte[] INPUT_CONTENT = "hello world".getBytes(US_ASCII); + private static byte[] COMPRESSED_CONTENT; + + @BeforeAll + static void beforeAll() throws Exception { + // created the compressed content + final ByteArrayOutputStream compressedBaos = new ByteArrayOutputStream(); + try (Deflater compressor = new Deflater()) { + compressor.setInput(INPUT_CONTENT); + compressor.finish(); + while (!compressor.finished()) { + final byte[] tmpBuffer = new byte[100]; + final int numCompressed = compressor.deflate(tmpBuffer); + compressedBaos.write(tmpBuffer, 0, numCompressed); + } + } + COMPRESSED_CONTENT = compressedBaos.toByteArray(); + } + + static List inflaterOutputStreams() { + final List args = new ArrayList<>(); + args.add(Arguments.of(new InflaterOutputStream(new ByteArrayOutputStream()))); + args.add(Arguments.of(new InflaterOutputStream(new ByteArrayOutputStream(), + new Inflater()))); + args.add(Arguments.of(new InflaterOutputStream(new ByteArrayOutputStream(), + new Inflater(), 1024))); + return args; + } + + /* + * Verify that write() methods throw an IOException when the InflaterOutputStream + * is already closed. + */ + @ParameterizedTest + @MethodSource("inflaterOutputStreams") + void testWriteAfterClose(final InflaterOutputStream ios) throws Exception { + ios.close(); + IOException ioe = assertThrows(IOException.class, () -> ios.write(COMPRESSED_CONTENT)); + // verify it failed for right reason + assertStreamClosedIOE(ioe); + } + + /* + * Verify that flush() throws an IOException when the InflaterOutputStream + * is already closed + */ + @ParameterizedTest + @MethodSource("inflaterOutputStreams") + void testFlushAfterClose(final InflaterOutputStream ios) throws Exception { + ios.close(); + final IOException ioe = assertThrows(IOException.class, ios::flush); + // verify it failed for right reason + assertStreamClosedIOE(ioe); + } + + /* + * Verify that finish() throws an IOException when the InflaterOutputStream + * is already closed. + */ + @ParameterizedTest + @MethodSource("inflaterOutputStreams") + void testFinishAfterClose(final InflaterOutputStream ios) throws Exception { + ios.close(); + final IOException ioe = assertThrows(IOException.class, ios::finish); + // verify it failed for right reason + assertStreamClosedIOE(ioe); + } + + /* + * Verify that after finish() is called on an InflaterOutputStream that was constructed + * without specifying a Inflater, any subsequent writes will throw an IOException. + */ + @Test + void testWriteAfterFinish() throws IOException { + // InflaterOutputStream that constructs an internal default Inflater + final InflaterOutputStream ios = new InflaterOutputStream(new ByteArrayOutputStream()); + ios.finish(); + final IOException ioe = assertThrows(IOException.class, () -> ios.write(COMPRESSED_CONTENT)); + final String msg = ioe.getMessage(); + // verify that it's the right IOException + if (msg == null || !msg.contains("Inflater closed")) { + // propagate the original exception + fail("unexpected exception message in IOException", ioe); + } + + // repeat with a InflaterOutputStream that is passed a Inflater + try (Inflater intf = new Inflater(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InflaterOutputStream stream = new InflaterOutputStream(baos, intf)) { + + stream.finish(); + // not expected to throw any exception + stream.write(COMPRESSED_CONTENT); + stream.flush(); + // verify the decompressed content is as expected + final byte[] decompressed = baos.toByteArray(); + assertArrayEquals(INPUT_CONTENT, decompressed, "unexpected decompressed content"); + } + } + + private void assertStreamClosedIOE(final IOException ioe) { + final String msg = ioe.getMessage(); + if (msg == null || !msg.contains("Stream closed")) { + // propagate the original exception + fail("unexpected exception message in IOException", ioe); + } + } +} From 9a3b850af6178abd7aa29f42d74c4f7fd3f2bc4c Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Sat, 21 Mar 2026 05:36:27 +0000 Subject: [PATCH 054/160] 8380416: Enable CmpUNodeValueTests.java IR tests for RISC-V Reviewed-by: fyang --- .../compiler/c2/gvn/CmpUNodeValueTests.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/gvn/CmpUNodeValueTests.java b/test/hotspot/jtreg/compiler/c2/gvn/CmpUNodeValueTests.java index cbcbda3422f..2df74e52971 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/CmpUNodeValueTests.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/CmpUNodeValueTests.java @@ -99,7 +99,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, counts = {IRNode.CMP_U, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {IRNode.CMP_U, "1"}) int testIntControl(boolean b1, boolean b2) { int v1 = b1 ? 1 : -1; int v2 = b2 ? 1 : 0; @@ -107,7 +107,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) int testIntLT(int x) { int v1 = x & Integer.MAX_VALUE; // Unset the highest bit, make v1 non-negative int v2 = Integer.MIN_VALUE; @@ -115,7 +115,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) int testIntLE(boolean b1, boolean b2) { int v1 = b1 ? 2 : 0; int v2 = b2 ? -1 : 2; @@ -123,7 +123,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) int testIntGT(boolean b1, boolean b2) { int v1 = b1 ? Integer.MIN_VALUE : Integer.MAX_VALUE; int v2 = b2 ? 0 : 2; @@ -131,7 +131,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) int testIntGE(int x, int y) { int v1 = x | Integer.MIN_VALUE; // Set the highest bit, make v1 negative int v2 = y & Integer.MAX_VALUE; // Unset the highest bit, make v2 non-negative @@ -139,13 +139,13 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) int testIntEQ() { return Integer.compareUnsigned(oneInline(), 1) == 0 ? 0 : one(); } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) int testIntNE(boolean b1, boolean b2) { int v1 = b1 ? 1 : -1; int v2 = b2 ? 0 : 2; @@ -153,7 +153,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, counts = {IRNode.CMP_UL, "1"}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, counts = {IRNode.CMP_UL, "1"}) int testLongControl(boolean b1, boolean b2) { long v1 = b1 ? 1 : -1; long v2 = b2 ? 1 : 0; @@ -162,7 +162,7 @@ public class CmpUNodeValueTests { @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) int testLongLT(int x, boolean b2) { long v1 = Integer.toUnsignedLong(x); long v2 = Integer.MIN_VALUE; @@ -170,7 +170,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) int testLongLE(boolean b1, boolean b2) { long v1 = b1 ? 2 : 0; long v2 = b2 ? -1 : 2; @@ -178,7 +178,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) int testLongGT(boolean b1, boolean b2) { long v1 = b1 ? Long.MIN_VALUE : Long.MAX_VALUE; long v2 = b2 ? 0 : 2; @@ -186,7 +186,7 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) int testLongGE(int x, int y) { long v1 = x | Long.MIN_VALUE; // Set the highest bit, make v1 negative long v2 = y & Long.MAX_VALUE; // Unset the highest bit, make v2 non-negative @@ -194,13 +194,13 @@ public class CmpUNodeValueTests { } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) int testLongEQ() { return Long.compareUnsigned(oneInline(), 1L) == 0 ? 0 : one(); } @Test - @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) int testLongNE(boolean b1, boolean b2) { long v1 = b1 ? 1 : -1; long v2 = b2 ? 0 : 2; From 2cdf61795206675fdfd056d720f181ad45929d4e Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Sat, 21 Mar 2026 07:50:03 +0000 Subject: [PATCH 055/160] 8372649: C2 compilation fails with "there should be an oop in OopMap instead of a live raw oop at safepoint" Reviewed-by: qamai, chagedorn --- src/hotspot/share/opto/output.cpp | 25 ++++- ...heckCastPPRawOopSchedulingAtSafepoint.java | 103 ++++++++++++++++++ 2 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/codegen/TestCheckCastPPRawOopSchedulingAtSafepoint.java diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index edfd6189fb9..a70620fac5b 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2914,17 +2914,30 @@ void Scheduling::ComputeRegisterAntidependencies(Block *b) { Node *m = b->get_node(i); - // Add precedence edge from following safepoint to use of derived pointer - if( last_safept_node != end_node && + if (last_safept_node != end_node && m != last_safept_node) { + bool need_safept_prec = false; + // Add precedence edge from following safepoint to use of derived pointer for (uint k = 1; k < m->req(); k++) { const Type *t = m->in(k)->bottom_type(); - if( t->isa_oop_ptr() && - t->is_ptr()->offset() != 0 ) { - last_safept_node->add_prec( m ); + if (t->isa_oop_ptr() && + t->is_ptr()->offset() != 0) { + need_safept_prec = true; break; } } + // A CheckCastPP whose input is still RawPtr must stay above the following safepoint. + // Otherwise post-regalloc block-local scheduling can leave a live raw oop at the safepoint. + if (!need_safept_prec && m->is_Mach() && + m->as_Mach()->ideal_Opcode() == Op_CheckCastPP) { + Node* def = m->in(1); + if (def != nullptr && def->bottom_type()->base() == Type::RawPtr) { + need_safept_prec = true; + } + } + if (need_safept_prec) { + last_safept_node->add_prec(m); + } } if( n->jvms() ) { // Precedence edge from derived to safept diff --git a/test/hotspot/jtreg/compiler/codegen/TestCheckCastPPRawOopSchedulingAtSafepoint.java b/test/hotspot/jtreg/compiler/codegen/TestCheckCastPPRawOopSchedulingAtSafepoint.java new file mode 100644 index 00000000000..00ba4be76f8 --- /dev/null +++ b/test/hotspot/jtreg/compiler/codegen/TestCheckCastPPRawOopSchedulingAtSafepoint.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 8372649 8376189 + * @summary CheckCastPP with RawPtr input can be scheduled below a safepoint during C2 + * post-regalloc block-local scheduling, causing a live raw oop at the safepoint + * instead of a corresponding oop in the OopMap. + * @library /test/lib + * @modules jdk.incubator.vector + * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:TrackedInitializationLimit=0 + * -XX:CompileCommand=compileonly,${test.main.class}::test8372649 + * -XX:+OptoScheduling ${test.main.class} 8372649 + * @run main/othervm --add-modules=jdk.incubator.vector + * -XX:+OptoScheduling ${test.main.class} 8376189 + */ + +package compiler.codegen; + +import jdk.incubator.vector.*; +import jdk.test.lib.Asserts; + +public class TestCheckCastPPRawOopSchedulingAtSafepoint { + private static final VectorSpecies SPECIES_I = IntVector.SPECIES_64; + + static public void main(String[] args) { + String mode = args[0]; + if ("8372649".equals(mode)) { + run8372649(); + } else if ("8376189".equals(mode)) { + run8376189(); + } + } + + // JDK-8372649 + static void run8372649(){ + for (int j = 6; 116 > j; ++j) { + test8372649(); + } + Asserts.assertEQ(Test.c, 43560L); + } + + static class Test { + static int a = 256; + float[] b = new float[256]; + static long c; + } + + static void test8372649() { + float[][] g = new float[Test.a][Test.a]; + for (int d = 7; d < 16; d++) { + long e = 1; + do { + g[d][(int) e] = d; + synchronized (new Test()) {} + } while (++e < 5); + } + for (int i = 0; i < Test.a; ++i) { + for (int j = 0; j < Test.a ; ++j) { + Test.c += g[i][j]; + } + } + } + + // JDK-8376189 + static void run8376189(){ + int[] a = new int[10_000]; + int r = 0; + for (int i = 0; i < 10_000; i++) { + r = test8376189(a); + } + Asserts.assertEQ(r, 0); + } + + public static int test8376189(int[] a) { + var mins = IntVector.broadcast(SPECIES_I, a[0]); + for (int i = 0; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + mins = IntVector.fromArray(SPECIES_I, a, 0); + } + return mins.reduceLanes(VectorOperators.MIN); + } +} \ No newline at end of file From e042467ed2afd6c70fb5fa1ce98cae27f7d9e85b Mon Sep 17 00:00:00 2001 From: Afshin Zafari Date: Sun, 22 Mar 2026 12:56:27 +0000 Subject: [PATCH 056/160] 8378440: ASAN build fails on Windows due to missing dlfcn.h file Reviewed-by: stuefe --- src/hotspot/share/sanitizers/address.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/hotspot/share/sanitizers/address.cpp b/src/hotspot/share/sanitizers/address.cpp index 7d129feab0a..26de106ebcb 100644 --- a/src/hotspot/share/sanitizers/address.cpp +++ b/src/hotspot/share/sanitizers/address.cpp @@ -30,6 +30,7 @@ #include "utilities/globalDefinitions.hpp" #include "utilities/vmError.hpp" +#ifndef _WINDOWS #include #include @@ -118,4 +119,11 @@ void Asan::report(outputStream* st) { } } +#else // defined windows + +void Asan::initialize() {} +bool Asan::had_error() { return false; } +void Asan::report(outputStream* st) {} +#endif // ifndef _WINDOWS + #endif // ADDRESS_SANITIZER From aba5d31ae73c9ffc9f7f9ab192f91e7e33f5bc66 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Sun, 22 Mar 2026 19:51:45 +0000 Subject: [PATCH 057/160] 8378605: Remove AppContext from the Swing TimerQueue implementation Reviewed-by: psadhukhan, dnguyen --- .../share/classes/javax/swing/TimerQueue.java | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/TimerQueue.java b/src/java.desktop/share/classes/javax/swing/TimerQueue.java index 4ef2769652a..39c06e57479 100644 --- a/src/java.desktop/share/classes/javax/swing/TimerQueue.java +++ b/src/java.desktop/share/classes/javax/swing/TimerQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ package javax.swing; import java.util.concurrent.*; import java.util.concurrent.locks.*; import java.util.concurrent.atomic.AtomicLong; -import sun.awt.AppContext; +import sun.awt.util.ThreadGroupUtils; /** * Internal class to manage all Timers using one thread. @@ -40,8 +40,7 @@ import sun.awt.AppContext; */ class TimerQueue implements Runnable { - private static final Object sharedInstanceKey = - new StringBuffer("TimerQueue.sharedInstanceKey"); + private static volatile TimerQueue sharedInstance; private final DelayQueue queue; private volatile boolean running; @@ -69,14 +68,10 @@ class TimerQueue implements Runnable public static TimerQueue sharedInstance() { synchronized (classLock) { - TimerQueue sharedInst = (TimerQueue) - SwingUtilities.appContextGet( - sharedInstanceKey); - if (sharedInst == null) { - sharedInst = new TimerQueue(); - SwingUtilities.appContextPut(sharedInstanceKey, sharedInst); + if (sharedInstance == null) { + sharedInstance = new TimerQueue(); } - return sharedInst; + return sharedInstance; } } @@ -88,9 +83,10 @@ class TimerQueue implements Runnable return; } try { - final ThreadGroup threadGroup = AppContext.getAppContext().getThreadGroup(); + final ThreadGroup threadGroup = ThreadGroupUtils.getRootThreadGroup(); String name = "TimerQueue"; Thread timerThread = new Thread(threadGroup, this, name, 0, false); + timerThread.setContextClassLoader(null); timerThread.setDaemon(true); timerThread.setPriority(Thread.NORM_PRIORITY); timerThread.start(); @@ -183,11 +179,6 @@ class TimerQueue implements Runnable timer.getLock().unlock(); } } catch (InterruptedException ie) { - // Shouldn't ignore InterruptedExceptions here, so AppContext - // is disposed gracefully, see 6799345 for details - if (AppContext.getAppContext().isDisposed()) { - break; - } } } } finally { From fae345c6cf513d39d21f35826f8a82d125da2053 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Sun, 22 Mar 2026 19:53:43 +0000 Subject: [PATCH 058/160] 8378918: Remove AppContext from some DnD classes Reviewed-by: serb, dnguyen --- .../plaf/basic/DragRecognitionSupport.java | 17 +++++------------ .../sun/awt/dnd/SunDragSourceContextPeer.java | 11 ++++------- .../sun/awt/dnd/SunDropTargetContextPeer.java | 6 ++---- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/DragRecognitionSupport.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/DragRecognitionSupport.java index abdd460e44b..517dad35d45 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/DragRecognitionSupport.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/DragRecognitionSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,14 +29,12 @@ import java.awt.event.*; import java.awt.dnd.DragSource; import javax.swing.*; import sun.awt.dnd.SunDragSourceContextPeer; -import sun.awt.AppContext; /** * Drag gesture recognition support for classes that have a * TransferHandler. The gesture for a drag in this class is a mouse * press followed by movement by DragSource.getDragThreshold() - * pixels. An instance of this class is maintained per AppContext, and the - * public static methods call into the appropriate instance. + * pixels. * * @author Shannon Hickey */ @@ -53,19 +51,14 @@ class DragRecognitionSupport { public void dragStarting(MouseEvent me); } + private static DragRecognitionSupport support; /** - * Returns the DragRecognitionSupport for the caller's AppContext. + * Returns the DragRecognitionSupport instance. */ - private static DragRecognitionSupport getDragRecognitionSupport() { - DragRecognitionSupport support = - (DragRecognitionSupport)AppContext.getAppContext(). - get(DragRecognitionSupport.class); - + private static synchronized DragRecognitionSupport getDragRecognitionSupport() { if (support == null) { support = new DragRecognitionSupport(); - AppContext.getAppContext().put(DragRecognitionSupport.class, support); } - return support; } diff --git a/src/java.desktop/share/classes/sun/awt/dnd/SunDragSourceContextPeer.java b/src/java.desktop/share/classes/sun/awt/dnd/SunDragSourceContextPeer.java index 258cebd6616..4aad70961bc 100644 --- a/src/java.desktop/share/classes/sun/awt/dnd/SunDragSourceContextPeer.java +++ b/src/java.desktop/share/classes/sun/awt/dnd/SunDragSourceContextPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -264,8 +264,7 @@ public abstract class SunDragSourceContextPeer implements DragSourceContextPeer modifiers, x, y); EventDispatcher dispatcher = new EventDispatcher(dispatchType, event); - SunToolkit.invokeLaterOnAppContext( - SunToolkit.targetToAppContext(getComponent()), dispatcher); + SunToolkit.invokeLater(dispatcher); startSecondaryEventLoop(); } @@ -310,8 +309,7 @@ public abstract class SunDragSourceContextPeer implements DragSourceContextPeer EventDispatcher dispatcher = new EventDispatcher(DISPATCH_EXIT, event); - SunToolkit.invokeLaterOnAppContext( - SunToolkit.targetToAppContext(getComponent()), dispatcher); + SunToolkit.invokeLater(dispatcher); startSecondaryEventLoop(); } @@ -341,8 +339,7 @@ public abstract class SunDragSourceContextPeer implements DragSourceContextPeer EventDispatcher dispatcher = new EventDispatcher(DISPATCH_FINISH, event); - SunToolkit.invokeLaterOnAppContext( - SunToolkit.targetToAppContext(getComponent()), dispatcher); + SunToolkit.invokeLater(dispatcher); startSecondaryEventLoop(); setNativeContext(0); diff --git a/src/java.desktop/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java b/src/java.desktop/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java index 686c3166441..70d1bda9cd0 100644 --- a/src/java.desktop/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java +++ b/src/java.desktop/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,7 +55,6 @@ import sun.util.logging.PlatformLogger; import java.io.IOException; import java.io.InputStream; -import sun.awt.AppContext; import sun.awt.SunToolkit; import sun.awt.datatransfer.DataTransferer; import sun.awt.datatransfer.ToolkitThreadBlockedHandler; @@ -558,7 +557,6 @@ public abstract class SunDropTargetContextPeer implements DropTargetContextPeer, final long nativeCtxt, final int eventID, final boolean dispatchType) { - AppContext appContext = SunToolkit.targetToAppContext(component); EventDispatcher dispatcher = new EventDispatcher(this, dropAction, actions, formats, nativeCtxt, @@ -572,7 +570,7 @@ public abstract class SunDropTargetContextPeer implements DropTargetContextPeer, } // schedule callback - SunToolkit.postEvent(appContext, event); + SunToolkit.postEvent(event); eventPosted(event); From d64ea294f716fbbe09f8dea32b7512f48484d539 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Sun, 22 Mar 2026 19:54:01 +0000 Subject: [PATCH 059/160] 8378919: Remove AppContext from the java.awt.Desktop class Reviewed-by: serb, dnguyen --- src/java.desktop/share/classes/java/awt/Desktop.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/java.desktop/share/classes/java/awt/Desktop.java b/src/java.desktop/share/classes/java/awt/Desktop.java index 3f73fa6cd81..43bd1f9c11c 100644 --- a/src/java.desktop/share/classes/java/awt/Desktop.java +++ b/src/java.desktop/share/classes/java/awt/Desktop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -272,6 +272,8 @@ public class Desktop { } } + private static Desktop desktop; + /** * Returns the {@code Desktop} instance of the current * desktop context. On some platforms the Desktop API may not be @@ -292,12 +294,8 @@ public class Desktop { "supported on the current platform"); } - sun.awt.AppContext context = sun.awt.AppContext.getAppContext(); - Desktop desktop = (Desktop)context.get(Desktop.class); - if (desktop == null) { desktop = new Desktop(); - context.put(Desktop.class, desktop); } return desktop; From a26d61312eaa3faf20db86d7378c4bdec42275ac Mon Sep 17 00:00:00 2001 From: Phil Race Date: Sun, 22 Mar 2026 19:54:22 +0000 Subject: [PATCH 060/160] 8378898: Remove AppContext from sun/awt/windows/WWindowPeer.java Reviewed-by: dnguyen, serb --- .../classes/sun/awt/windows/WWindowPeer.java | 112 +++--------------- 1 file changed, 18 insertions(+), 94 deletions(-) diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java index f678be9f1ab..f028e0fbec5 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,7 +56,6 @@ import java.util.LinkedList; import java.util.List; import sun.awt.AWTAccessor; -import sun.awt.AppContext; import sun.awt.DisplayChangedListener; import sun.awt.SunToolkit; import sun.awt.TimedWindowEvent; @@ -84,26 +83,12 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, private TranslucentWindowPainter painter; /* - * A key used for storing a list of active windows in AppContext. The value - * is a list of windows, sorted by the time of activation: later a window is - * activated, greater its index is in the list. - */ - private static final StringBuffer ACTIVE_WINDOWS_KEY = - new StringBuffer("active_windows_list"); - - /* - * Listener for 'activeWindow' KFM property changes. It is added to each - * AppContext KFM. See ActiveWindowListener inner class below. + * Listener for 'activeWindow' KFM property changes. + * See ActiveWindowListener inner class below. */ private static PropertyChangeListener activeWindowListener = new ActiveWindowListener(); - /* - * The object is a listener for the AppContext.GUI_DISPOSED property. - */ - private static final PropertyChangeListener guiDisposedListener = - new GuiDisposedListener(); - /* * Called (on the Toolkit thread) before the appropriate * WindowStateEvent is posted to the EventQueue. @@ -116,18 +101,17 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, private static native void initIDs(); static { initIDs(); + KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + kfm.addPropertyChangeListener("activeWindow", activeWindowListener); } + static List activeWindows = new LinkedList(); // WComponentPeer overrides @Override @SuppressWarnings("unchecked") protected void disposeImpl() { - AppContext appContext = SunToolkit.targetToAppContext(target); - synchronized (appContext) { - List l = (List)appContext.get(ACTIVE_WINDOWS_KEY); - if (l != null) { - l.remove(this); - } + synchronized (activeWindows) { + activeWindows.remove(this); } // Remove ourself from the Map of DisplayChangeListeners @@ -222,8 +206,6 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, Win32GraphicsDevice gd = (Win32GraphicsDevice) gc.getDevice(); gd.addDisplayChangedListener(this); - initActiveWindowsTracking((Window)target); - updateIconImages(); Shape shape = ((Window)target).getShape(); @@ -530,22 +512,15 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, native void modalEnable(Dialog blocker); /* - * Returns all the ever active windows from the current AppContext. + * Returns all the active windows. * The list is sorted by the time of activation, so the latest * active window is always at the end. */ - @SuppressWarnings("unchecked") public static long[] getActiveWindowHandles(Component target) { - AppContext appContext = SunToolkit.targetToAppContext(target); - if (appContext == null) return null; - synchronized (appContext) { - List l = (List)appContext.get(ACTIVE_WINDOWS_KEY); - if (l == null) { - return null; - } - long[] result = new long[l.size()]; - for (int j = 0; j < l.size(); j++) { - result[j] = l.get(j).getHWnd(); + synchronized (activeWindows) { + long[] result = new long[activeWindows.size()]; + for (int j = 0; j < activeWindows.size(); j++) { + result[j] = activeWindows.get(j).getHWnd(); } return result; } @@ -823,58 +798,11 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, } } - /* - * The method maps the list of the active windows to the window's AppContext, - * then the method registers ActiveWindowListener, GuiDisposedListener listeners; - * it executes the initilialization only once per AppContext. - */ - @SuppressWarnings("unchecked") - private static void initActiveWindowsTracking(Window w) { - AppContext appContext = AppContext.getAppContext(); - synchronized (appContext) { - List l = (List)appContext.get(ACTIVE_WINDOWS_KEY); - if (l == null) { - l = new LinkedList(); - appContext.put(ACTIVE_WINDOWS_KEY, l); - appContext.addPropertyChangeListener(AppContext.GUI_DISPOSED, guiDisposedListener); - - KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - kfm.addPropertyChangeListener("activeWindow", activeWindowListener); - } - } - } - - /* - * The GuiDisposedListener class listens for the AppContext.GUI_DISPOSED property, - * it removes the list of the active windows from the disposed AppContext and - * unregisters ActiveWindowListener listener. - */ - private static final class GuiDisposedListener implements PropertyChangeListener { - @Override - public void propertyChange(PropertyChangeEvent e) { - boolean isDisposed = (Boolean)e.getNewValue(); - if (isDisposed != true) { - if (log.isLoggable(PlatformLogger.Level.FINE)) { - log.fine(" Assertion (newValue != true) failed for AppContext.GUI_DISPOSED "); - } - } - AppContext appContext = AppContext.getAppContext(); - synchronized (appContext) { - appContext.remove(ACTIVE_WINDOWS_KEY); - appContext.removePropertyChangeListener(AppContext.GUI_DISPOSED, this); - - KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - kfm.removePropertyChangeListener("activeWindow", activeWindowListener); - } - } - } - /* * Static inner class, listens for 'activeWindow' KFM property changes and - * updates the list of active windows per AppContext, so the latest active - * window is always at the end of the list. The list is stored in AppContext. + * updates the list of active windows so the latest active + * window is always at the end of the list. */ - @SuppressWarnings("unchecked") private static final class ActiveWindowListener implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent e) { @@ -882,15 +810,11 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, if (w == null) { return; } - AppContext appContext = SunToolkit.targetToAppContext(w); - synchronized (appContext) { + synchronized (activeWindows) { WWindowPeer wp = AWTAccessor.getComponentAccessor().getPeer(w); // add/move wp to the end of the list - List l = (List)appContext.get(ACTIVE_WINDOWS_KEY); - if (l != null) { - l.remove(wp); - l.add(wp); - } + activeWindows.remove(wp); + activeWindows.add(wp); } } } From ea464b1405f05963e5a1fa2fb27427935cad7f70 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Sun, 22 Mar 2026 19:55:47 +0000 Subject: [PATCH 061/160] 8379018: Remove AppContext from two input method related classes Reviewed-by: serb, dnguyen, kizune --- .../java/awt/event/InputMethodEvent.java | 6 +- .../awt/im/ExecutableInputMethodManager.java | 6 +- test/jdk/java/awt/im/8041990/bug8041990.java | 106 ------------------ 3 files changed, 4 insertions(+), 114 deletions(-) delete mode 100644 test/jdk/java/awt/im/8041990/bug8041990.java diff --git a/src/java.desktop/share/classes/java/awt/event/InputMethodEvent.java b/src/java.desktop/share/classes/java/awt/event/InputMethodEvent.java index 12ad1a03171..429d0b819f4 100644 --- a/src/java.desktop/share/classes/java/awt/event/InputMethodEvent.java +++ b/src/java.desktop/share/classes/java/awt/event/InputMethodEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,6 @@ import java.text.AttributedCharacterIterator; import java.text.CharacterIterator; import sun.awt.AWTAccessor; -import sun.awt.AppContext; import sun.awt.SunToolkit; /** @@ -444,8 +443,7 @@ public class InputMethodEvent extends AWTEvent { // throw the IllegalArgumentException to conform to EventObject spec throw new IllegalArgumentException("null source"); } - AppContext appContext = SunToolkit.targetToAppContext(source); - EventQueue eventQueue = SunToolkit.getSystemEventQueueImplPP(appContext); + EventQueue eventQueue = SunToolkit.getSystemEventQueueImplPP(); return AWTAccessor.getEventQueueAccessor().getMostRecentEventTime(eventQueue); } } diff --git a/src/java.desktop/share/classes/sun/awt/im/ExecutableInputMethodManager.java b/src/java.desktop/share/classes/sun/awt/im/ExecutableInputMethodManager.java index d8246a4947c..f67f560e004 100644 --- a/src/java.desktop/share/classes/sun/awt/im/ExecutableInputMethodManager.java +++ b/src/java.desktop/share/classes/sun/awt/im/ExecutableInputMethodManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,6 @@ import java.awt.PopupMenu; import java.awt.Menu; import java.awt.MenuItem; import java.awt.Toolkit; -import sun.awt.AppContext; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InvocationEvent; @@ -177,9 +176,8 @@ class ExecutableInputMethodManager extends InputMethodManager lock, true); - AppContext requesterAppContext = SunToolkit.targetToAppContext(requester); synchronized (lock) { - SunToolkit.postEvent(requesterAppContext, event); + SunToolkit.postEvent(event); while (!event.isDispatched()) { lock.wait(); } diff --git a/test/jdk/java/awt/im/8041990/bug8041990.java b/test/jdk/java/awt/im/8041990/bug8041990.java deleted file mode 100644 index c5ddbac42ba..00000000000 --- a/test/jdk/java/awt/im/8041990/bug8041990.java +++ /dev/null @@ -1,106 +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. - */ - - -/** - * @test - * @key headful - * @bug 8041990 - * @summary Language specific keys does not work in non-default AppContexts of top-level windows - * @author Petr Pchelko - * @modules java.desktop/sun.awt - */ - -import sun.awt.SunToolkit; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.InputMethodEvent; -import java.awt.font.TextHitInfo; -import java.text.AttributedString; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -public class bug8041990 { - private static JFrame frame; - private static JComponent component; - - public static void main(String[] args) throws Exception { - ThreadGroup stubTG = new ThreadGroup(getRootThreadGroup(), "Stub Thread Group"); - ThreadGroup swingTG = new ThreadGroup(getRootThreadGroup(), "SwingTG"); - try { - Thread stubThread = new Thread(stubTG, SunToolkit::createNewAppContext); - stubThread.start(); - stubThread.join(); - - CountDownLatch startSwingLatch = new CountDownLatch(1); - new Thread(swingTG, () -> { - SunToolkit.createNewAppContext(); - SwingUtilities.invokeLater(() -> { - frame = new JFrame(); - component = new JLabel("Test Text"); - frame.add(component); - frame.setBounds(100, 100, 100, 100); - frame.setVisible(true); - startSwingLatch.countDown(); - }); - }).start(); - startSwingLatch.await(); - - AtomicReference caughtException = new AtomicReference<>(); - Thread checkThread = new Thread(getRootThreadGroup(), () -> { - try { - // If the bug is present this will throw exception - new InputMethodEvent(component, - InputMethodEvent.CARET_POSITION_CHANGED, - TextHitInfo.leading(0), - TextHitInfo.trailing(0)); - } catch (Exception e) { - caughtException.set(e); - } - }); - checkThread.start(); - checkThread.join(); - - if (caughtException.get() != null) { - throw new RuntimeException("Failed. Caught exception!", caughtException.get()); - } - } finally { - new Thread(swingTG, () -> SwingUtilities.invokeLater(() -> { - if (frame != null) { - frame.dispose(); - } - })).start(); - } - } - - private static ThreadGroup getRootThreadGroup() { - ThreadGroup currentTG = Thread.currentThread().getThreadGroup(); - ThreadGroup parentTG = currentTG.getParent(); - while (parentTG != null) { - currentTG = parentTG; - parentTG = currentTG.getParent(); - } - return currentTG; - } -} From 88972399e5e940bdd76f9f30fdcca5757b205349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Arias=20de=20Reyna=20Dom=C3=ADnguez?= Date: Mon, 23 Mar 2026 02:13:38 +0000 Subject: [PATCH 062/160] 8380427: Revert JDK-8380292Confusing "reverted *" messages during training Reviewed-by: adinn, iklam --- src/hotspot/share/oops/constantPool.cpp | 2 +- src/hotspot/share/oops/cpCache.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 8d817178d1a..456333efad0 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -564,7 +564,7 @@ void ConstantPool::remove_resolved_klass_if_non_deterministic(int cp_index) { } LogStreamHandle(Trace, aot, resolve) log; - if (log.is_enabled() && !CDSConfig::is_dumping_preimage_static_archive()) { + if (log.is_enabled()) { ResourceMark rm; log.print("%s klass CP entry [%3d]: %s %s", (can_archive ? "archived" : "reverted"), diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index 4177e4601ef..edb5f6714c0 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -450,7 +450,7 @@ void ConstantPoolCache::remove_resolved_field_entries_if_non_deterministic() { Symbol* klass_name = cp->klass_name_at(klass_cp_index); Symbol* name = cp->uncached_name_ref_at(cp_index); Symbol* signature = cp->uncached_signature_ref_at(cp_index); - if (resolved && !CDSConfig::is_dumping_preimage_static_archive()) { + if (resolved) { log.print("%s field CP entry [%3d]: %s => %s.%s:%s%s%s", (archived ? "archived" : "reverted"), cp_index, @@ -493,7 +493,7 @@ void ConstantPoolCache::remove_resolved_method_entries_if_non_deterministic() { Symbol* name = cp->uncached_name_ref_at(cp_index); Symbol* signature = cp->uncached_signature_ref_at(cp_index); LogStream ls(lt); - if (resolved && !CDSConfig::is_dumping_preimage_static_archive()) { + if (resolved) { ls.print("%s%s method CP entry [%3d]: %s %s.%s:%s", (archived ? "archived" : "reverted"), (rme->is_resolved(Bytecodes::_invokeinterface) ? " interface" : ""), @@ -539,7 +539,7 @@ void ConstantPoolCache::remove_resolved_indy_entries_if_non_deterministic() { Symbol* bsm_name = cp->uncached_name_ref_at(bsm_ref); Symbol* bsm_signature = cp->uncached_signature_ref_at(bsm_ref); Symbol* bsm_klass = cp->klass_name_at(cp->uncached_klass_ref_index_at(bsm_ref)); - if (resolved && !CDSConfig::is_dumping_preimage_static_archive()) { + if (resolved) { log.print("%s indy CP entry [%3d]: %s (%d)", (archived ? "archived" : "reverted"), cp_index, cp->pool_holder()->name()->as_C_string(), i); From a6f59ba1d16a3e6032db393d94c1f6ab9b1c8657 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Mon, 23 Mar 2026 04:35:56 +0000 Subject: [PATCH 063/160] 8378749: Early GC crash with unresolved AOT FMG subgraph classes Reviewed-by: kvn, eosterlund --- src/hotspot/share/cds/aotMetaspace.cpp | 13 +-- src/hotspot/share/cds/cdsConfig.cpp | 22 +++- src/hotspot/share/cds/dumpTimeClassInfo.hpp | 9 +- src/hotspot/share/cds/filemap.cpp | 28 ++++- src/hotspot/share/cds/heapShared.cpp | 104 +++--------------- src/hotspot/share/cds/heapShared.hpp | 25 +---- .../classfile/systemDictionaryShared.cpp | 5 - .../classfile/systemDictionaryShared.hpp | 3 +- test/hotspot/jtreg/TEST.groups | 1 + .../cds/appcds/TestZGCWithAOTHeap.java | 4 +- .../appcds/jigsaw/addmods/AddmodsOption.java | 13 +-- .../jigsaw/addopens/AddopensOption.java | 12 +- .../appcds/jigsaw/module/ModuleOption.java | 13 +-- .../ReplaceCriticalClasses.java | 3 +- 14 files changed, 85 insertions(+), 170 deletions(-) diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 5e75cd72778..76c37194882 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -1812,15 +1812,10 @@ MapArchiveResult AOTMetaspace::map_archives(FileMapInfo* static_mapinfo, FileMap } else { FileMapRegion* r = static_mapinfo->region_at(AOTMetaspace::hp); if (r->used() > 0) { - if (static_mapinfo->object_streaming_mode()) { - AOTMetaspace::report_loading_error("Cannot use CDS heap data."); - } else { - if (!UseCompressedOops && !AOTMappedHeapLoader::can_map()) { - AOTMetaspace::report_loading_error("Cannot use CDS heap data. Selected GC not compatible -XX:-UseCompressedOops"); - } else { - AOTMetaspace::report_loading_error("Cannot use CDS heap data. UseEpsilonGC, UseG1GC, UseSerialGC, UseParallelGC, or UseShenandoahGC are required."); - } - } + AOTMetaspace::report_loading_error("Cannot use CDS heap data."); + } + if (!CDSConfig::is_dumping_static_archive()) { + CDSConfig::stop_using_full_module_graph("No CDS heap data"); } } } diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index d8ae50517fe..9d191bf0121 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -972,17 +972,27 @@ bool CDSConfig::is_loading_heap() { } bool CDSConfig::is_dumping_klass_subgraphs() { - if (is_dumping_classic_static_archive() || is_dumping_final_static_archive()) { + if (is_dumping_aot_linked_classes()) { // KlassSubGraphs (see heapShared.cpp) is a legacy mechanism for archiving oops. It // has been superceded by AOT class linking. This feature is used only when // AOT class linking is disabled. - // - // KlassSubGraphs are disabled in the preimage static archive, which contains a very - // limited set of oops. - return is_dumping_heap() && !is_dumping_aot_linked_classes(); - } else { return false; } + + if (is_dumping_preimage_static_archive()) { + // KlassSubGraphs are disabled in the preimage static archive, which contains a very + // limited set of oops. + return false; + } + + if (!is_dumping_full_module_graph()) { + // KlassSubGraphs cannot be partially disabled. Since some of the KlassSubGraphs + // are used for (legacy support) of the archived full module graph, if + // is_dumping_full_module_graph() is calse, we must disable all KlassSubGraphs. + return false; + } + + return is_dumping_heap(); } bool CDSConfig::is_using_klass_subgraphs() { diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.hpp index c2f83b22337..d2de8148bea 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.hpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,6 @@ class Symbol; class DumpTimeClassInfo: public CHeapObj { bool _excluded; bool _is_aot_tooling_class; - bool _is_early_klass; bool _has_checked_exclusion; class DTLoaderConstraint { @@ -143,7 +142,6 @@ public: _clsfile_crc32 = -1; _excluded = false; _is_aot_tooling_class = false; - _is_early_klass = JvmtiExport::is_early_phase(); _verifier_constraints = nullptr; _verifier_constraint_flags = nullptr; _loader_constraints = nullptr; @@ -219,11 +217,6 @@ public: _is_aot_tooling_class = true; } - // Was this class loaded while JvmtiExport::is_early_phase()==true - bool is_early_klass() { - return _is_early_klass; - } - // simple accessors void set_excluded() { _excluded = true; } bool has_checked_exclusion() const { return _has_checked_exclusion; } diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 91fbce701e5..a07f13cefca 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -1535,10 +1535,34 @@ bool FileMapInfo::can_use_heap_region() { if (!has_heap_region()) { return false; } - if (!object_streaming_mode() && !Universe::heap()->can_load_archived_objects() && !UseG1GC) { - // Incompatible object format + + if (!object_streaming_mode() && !AOTMappedHeapLoader::can_use()) { + // Currently this happens only when using ZGC with an AOT cache generated with -XX:-AOTStreamableObjects + AOTMetaspace::report_loading_error("CDS heap data cannot be used by the selected GC. " + "Please choose a different GC or rebuild AOT cache " + "with -XX:+AOTStreamableObjects"); return false; } + + if (CDSConfig::is_using_aot_linked_classes()) { + assert(!JvmtiExport::should_post_class_file_load_hook(), "already checked"); + assert(CDSConfig::is_using_full_module_graph(), "already checked"); + } else { + if (JvmtiExport::should_post_class_file_load_hook()) { + AOTMetaspace::report_loading_error("CDS heap data is disabled because JVMTI ClassFileLoadHook is in use."); + return false; + } + if (!CDSConfig::is_using_full_module_graph()) { + if (CDSConfig::is_dumping_final_static_archive()) { + // We are loading the preimage static archive, which has no KlassSubGraphs. + // See CDSConfig::is_dumping_klass_subgraphs() + } else { + AOTMetaspace::report_loading_error("CDS heap data is disabled because archived full module graph is not used."); + return false; + } + } + } + if (JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()) { ShouldNotReachHere(); // CDS should have been disabled. // The archived objects are mapped at JVM start-up, but we don't know if diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 8cab56dda8d..708a19e9a7d 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -131,17 +131,14 @@ static ArchivableStaticFieldInfo archive_subgraph_entry_fields[] = { {"java/lang/module/Configuration", "EMPTY_CONFIGURATION"}, {"jdk/internal/math/FDBigInteger", "archivedCaches"}, -#ifndef PRODUCT - {nullptr, nullptr}, // Extra slot for -XX:ArchiveHeapTestClass -#endif - {nullptr, nullptr}, -}; - -// full module graph -static ArchivableStaticFieldInfo fmg_archive_subgraph_entry_fields[] = { + // full module graph support {"jdk/internal/loader/ArchivedClassLoaders", "archivedClassLoaders"}, {ARCHIVED_BOOT_LAYER_CLASS, ARCHIVED_BOOT_LAYER_FIELD}, {"java/lang/Module$ArchivedData", "archivedData"}, + +#ifndef PRODUCT + {nullptr, nullptr}, // Extra slot for -XX:ArchiveHeapTestClass +#endif {nullptr, nullptr}, }; @@ -164,8 +161,7 @@ bool HeapShared::is_subgraph_root_class(InstanceKlass* ik) { assert(CDSConfig::is_dumping_heap(), "dump-time only"); if (CDSConfig::is_dumping_klass_subgraphs()) { // Legacy CDS archive support (to be deprecated) - return is_subgraph_root_class_of(archive_subgraph_entry_fields, ik) || - is_subgraph_root_class_of(fmg_archive_subgraph_entry_fields, ik); + return is_subgraph_root_class_of(archive_subgraph_entry_fields, ik); } else { return false; } @@ -925,7 +921,7 @@ void HeapShared::start_scanning_for_oops() { // The special subgraph doesn't belong to any class. We use Object_klass() here just // for convenience. - _dump_time_special_subgraph = init_subgraph_info(vmClasses::Object_klass(), false); + _dump_time_special_subgraph = init_subgraph_info(vmClasses::Object_klass()); // Cache for recording where the archived objects are copied to create_archived_object_cache(); @@ -1003,12 +999,7 @@ void HeapShared::archive_subgraphs() { assert(CDSConfig::is_dumping_heap(), "must be"); if (CDSConfig::is_dumping_klass_subgraphs()) { - archive_object_subgraphs(archive_subgraph_entry_fields, - false /* is_full_module_graph */); - if (CDSConfig::is_dumping_full_module_graph()) { - archive_object_subgraphs(fmg_archive_subgraph_entry_fields, - true /* is_full_module_graph */); - } + archive_object_subgraphs(archive_subgraph_entry_fields); } } @@ -1021,12 +1012,11 @@ HeapShared::RunTimeKlassSubGraphInfoTable HeapShared::_run_time_subgraph_info_ // Get the subgraph_info for Klass k. A new subgraph_info is created if // there is no existing one for k. The subgraph_info records the "buffered" // address of the class. -KlassSubGraphInfo* HeapShared::init_subgraph_info(Klass* k, bool is_full_module_graph) { +KlassSubGraphInfo* HeapShared::init_subgraph_info(Klass* k) { assert(CDSConfig::is_dumping_heap(), "dump time only"); bool created; KlassSubGraphInfo* info = - _dump_time_subgraph_info_table->put_if_absent(k, KlassSubGraphInfo(k, is_full_module_graph), - &created); + _dump_time_subgraph_info_table->put_if_absent(k, KlassSubGraphInfo(k), &created); assert(created, "must not initialize twice"); return info; } @@ -1114,7 +1104,6 @@ void KlassSubGraphInfo::add_subgraph_object_klass(Klass* orig_k) { } _subgraph_object_klasses->append_if_missing(orig_k); - _has_non_early_klasses |= is_non_early_klass(orig_k); } void KlassSubGraphInfo::check_allowed_klass(InstanceKlass* ik) { @@ -1157,45 +1146,11 @@ void KlassSubGraphInfo::check_allowed_klass(InstanceKlass* ik) { AOTMetaspace::unrecoverable_writing_error(); } -bool KlassSubGraphInfo::is_non_early_klass(Klass* k) { - if (k->is_objArray_klass()) { - k = ObjArrayKlass::cast(k)->bottom_klass(); - } - if (k->is_instance_klass()) { - if (!SystemDictionaryShared::is_early_klass(InstanceKlass::cast(k))) { - ResourceMark rm; - log_info(aot, heap)("non-early: %s", k->external_name()); - return true; - } else { - return false; - } - } else { - return false; - } -} - // Initialize an archived subgraph_info_record from the given KlassSubGraphInfo. void ArchivedKlassSubGraphInfoRecord::init(KlassSubGraphInfo* info) { _k = ArchiveBuilder::get_buffered_klass(info->klass()); _entry_field_records = nullptr; _subgraph_object_klasses = nullptr; - _is_full_module_graph = info->is_full_module_graph(); - - if (_is_full_module_graph) { - // Consider all classes referenced by the full module graph as early -- we will be - // allocating objects of these classes during JVMTI early phase, so they cannot - // be processed by (non-early) JVMTI ClassFileLoadHook - _has_non_early_klasses = false; - } else { - _has_non_early_klasses = info->has_non_early_klasses(); - } - - if (_has_non_early_klasses) { - ResourceMark rm; - log_info(aot, heap)( - "Subgraph of klass %s has non-early klasses and cannot be used when JVMTI ClassFileLoadHook is enabled", - _k->external_name()); - } // populate the entry fields GrowableArray* entry_fields = info->subgraph_entry_fields(); @@ -1353,15 +1308,10 @@ static void verify_the_heap(Klass* k, const char* which) { // Before GC can execute, we must ensure that all oops reachable from HeapShared::roots() // have a valid klass. I.e., oopDesc::klass() must have already been resolved. -// -// Note: if a ArchivedKlassSubGraphInfoRecord contains non-early classes, and JVMTI -// ClassFileLoadHook is enabled, it's possible for this class to be dynamically replaced. In -// this case, we will not load the ArchivedKlassSubGraphInfoRecord and will clear its roots. void HeapShared::resolve_classes(JavaThread* current) { assert(CDSConfig::is_using_archive(), "runtime only!"); if (CDSConfig::is_using_klass_subgraphs()) { resolve_classes_for_subgraphs(current, archive_subgraph_entry_fields); - resolve_classes_for_subgraphs(current, fmg_archive_subgraph_entry_fields); } } @@ -1509,24 +1459,6 @@ HeapShared::resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAP } return nullptr; } else { - if (record->is_full_module_graph() && !CDSConfig::is_using_full_module_graph()) { - if (log_is_enabled(Info, aot, heap)) { - ResourceMark rm(THREAD); - log_info(aot, heap)("subgraph %s cannot be used because full module graph is disabled", - k->external_name()); - } - return nullptr; - } - - if (record->has_non_early_klasses() && JvmtiExport::should_post_class_file_load_hook()) { - if (log_is_enabled(Info, aot, heap)) { - ResourceMark rm(THREAD); - log_info(aot, heap)("subgraph %s cannot be used because JVMTI ClassFileLoadHook is enabled", - k->external_name()); - } - return nullptr; - } - if (log_is_enabled(Info, aot, heap)) { ResourceMark rm; log_info(aot, heap)("%s subgraph %s ", do_init ? "init" : "resolve", k->external_name()); @@ -1608,8 +1540,8 @@ void HeapShared::init_archived_fields_for(Klass* k, const ArchivedKlassSubGraphI // mirror after this point. if (log_is_enabled(Info, aot, heap)) { ResourceMark rm; - log_info(aot, heap)("initialize_from_archived_subgraph %s " PTR_FORMAT "%s%s", - k->external_name(), p2i(k), JvmtiExport::is_early_phase() ? " (early)" : "", + log_info(aot, heap)("initialize_from_archived_subgraph %s " PTR_FORMAT "%s", + k->external_name(), p2i(k), k->has_aot_initialized_mirror() ? " (aot-inited)" : ""); } } @@ -2075,9 +2007,9 @@ void HeapShared::set_has_been_seen_during_subgraph_recording(oop obj) { ++ _num_new_walked_objs; } -void HeapShared::start_recording_subgraph(InstanceKlass *k, const char* class_name, bool is_full_module_graph) { +void HeapShared::start_recording_subgraph(InstanceKlass *k, const char* class_name) { log_info(aot, heap)("Start recording subgraph(s) for archived fields in %s", class_name); - init_subgraph_info(k, is_full_module_graph); + init_subgraph_info(k); init_seen_objects_table(); _num_new_walked_objs = 0; _num_new_archived_objs = 0; @@ -2209,9 +2141,6 @@ void HeapShared::init_subgraph_entry_fields(TRAPS) { _dump_time_subgraph_info_table = new (mtClass)DumpTimeKlassSubGraphInfoTable(); if (CDSConfig::is_dumping_klass_subgraphs()) { init_subgraph_entry_fields(archive_subgraph_entry_fields, CHECK); - if (CDSConfig::is_dumping_full_module_graph()) { - init_subgraph_entry_fields(fmg_archive_subgraph_entry_fields, CHECK); - } } } @@ -2310,8 +2239,7 @@ void HeapShared::init_heap_writer() { } } -void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[], - bool is_full_module_graph) { +void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[]) { _num_total_subgraph_recordings = 0; _num_total_walked_objs = 0; _num_total_archived_objs = 0; @@ -2327,7 +2255,7 @@ void HeapShared::archive_object_subgraphs(ArchivableStaticFieldInfo fields[], for (int i = 0; fields[i].valid(); ) { ArchivableStaticFieldInfo* info = &fields[i]; const char* klass_name = info->klass_name; - start_recording_subgraph(info->klass, klass_name, is_full_module_graph); + start_recording_subgraph(info->klass, klass_name); // If you have specified consecutive fields of the same klass in // fields[], these will be archived in the same diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index 92f41784268..c3ad1f666b1 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -66,21 +66,12 @@ class KlassSubGraphInfo: public CHeapObj { // For each entry field, it is a tuple of field_offset, field_value GrowableArray* _subgraph_entry_fields; - // Does this KlassSubGraphInfo belong to the archived full module graph - bool _is_full_module_graph; - - // Does this KlassSubGraphInfo references any classes that were loaded while - // JvmtiExport::is_early_phase()!=true. If so, this KlassSubGraphInfo cannot be - // used at runtime if JVMTI ClassFileLoadHook is enabled. - bool _has_non_early_klasses; static bool is_non_early_klass(Klass* k); static void check_allowed_klass(InstanceKlass* ik); public: - KlassSubGraphInfo(Klass* k, bool is_full_module_graph) : + KlassSubGraphInfo(Klass* k) : _k(k), _subgraph_object_klasses(nullptr), - _subgraph_entry_fields(nullptr), - _is_full_module_graph(is_full_module_graph), - _has_non_early_klasses(false) {} + _subgraph_entry_fields(nullptr) {} ~KlassSubGraphInfo() { if (_subgraph_object_klasses != nullptr) { @@ -104,8 +95,6 @@ class KlassSubGraphInfo: public CHeapObj { return _subgraph_object_klasses == nullptr ? 0 : _subgraph_object_klasses->length(); } - bool is_full_module_graph() const { return _is_full_module_graph; } - bool has_non_early_klasses() const { return _has_non_early_klasses; } }; // An archived record of object sub-graphs reachable from static @@ -114,7 +103,6 @@ class KlassSubGraphInfo: public CHeapObj { class ArchivedKlassSubGraphInfoRecord { private: Klass* _k; - bool _is_full_module_graph; bool _has_non_early_klasses; // contains pairs of field offset and value for each subgraph entry field @@ -130,7 +118,6 @@ class ArchivedKlassSubGraphInfoRecord { Klass* klass() const { return _k; } Array* entry_field_records() const { return _entry_field_records; } Array* subgraph_object_klasses() const { return _subgraph_object_klasses; } - bool is_full_module_graph() const { return _is_full_module_graph; } bool has_non_early_klasses() const { return _has_non_early_klasses; } }; #endif // INCLUDE_CDS_JAVA_HEAP @@ -270,8 +257,7 @@ private: static CachedOopInfo make_cached_oop_info(oop obj, oop referrer); static ArchivedKlassSubGraphInfoRecord* archive_subgraph_info(KlassSubGraphInfo* info); - static void archive_object_subgraphs(ArchivableStaticFieldInfo fields[], - bool is_full_module_graph); + static void archive_object_subgraphs(ArchivableStaticFieldInfo fields[]); // Archive object sub-graph starting from the given static field // in Klass k's mirror. @@ -285,7 +271,7 @@ private: static void verify_subgraph_from(oop orig_obj) PRODUCT_RETURN; static void check_special_subgraph_classes(); - static KlassSubGraphInfo* init_subgraph_info(Klass *k, bool is_full_module_graph); + static KlassSubGraphInfo* init_subgraph_info(Klass *k); static KlassSubGraphInfo* get_subgraph_info(Klass *k); static void init_subgraph_entry_fields(TRAPS) NOT_CDS_JAVA_HEAP_RETURN; @@ -340,8 +326,7 @@ private: static size_t _num_total_recorded_klasses; static size_t _num_total_verifications; - static void start_recording_subgraph(InstanceKlass *k, const char* klass_name, - bool is_full_module_graph); + static void start_recording_subgraph(InstanceKlass *k, const char* klass_name); static void done_recording_subgraph(InstanceKlass *k, const char* klass_name); static bool has_been_seen_during_subgraph_recording(oop obj); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index cfb20412ab8..5947050e31a 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -373,11 +373,6 @@ bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) { return false; } -bool SystemDictionaryShared::is_early_klass(InstanceKlass* ik) { - DumpTimeClassInfo* info = _dumptime_table->get(ik); - return (info != nullptr) ? info->is_early_klass() : false; -} - bool SystemDictionaryShared::check_self_exclusion(InstanceKlass* k) { bool log_warning = false; const char* error = check_self_exclusion_helper(k, log_warning); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index 2619a642fd1..33b245e26fc 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -199,7 +199,6 @@ private: 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); static void set_has_archived_enum_objs(InstanceKlass* ik); diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index 32cff812975..6e9421e5c09 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -458,6 +458,7 @@ hotspot_appcds_dynamic = \ -runtime/cds/appcds/TestEpsilonGCWithCDS.java \ -runtime/cds/appcds/TestParallelGCWithCDS.java \ -runtime/cds/appcds/TestSerialGCWithCDS.java \ + -runtime/cds/appcds/TestZGCWithAOTHeap.java \ -runtime/cds/appcds/TestZGCWithCDS.java \ -runtime/cds/appcds/UnusedCPDuringDump.java \ -runtime/cds/appcds/VerifierTest_1B.java diff --git a/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithAOTHeap.java b/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithAOTHeap.java index 567c8da73cb..719a4ee83f7 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithAOTHeap.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithAOTHeap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,7 +65,7 @@ public class TestZGCWithAOTHeap { String dumpGC = dumpWithZ ? Z : G1; String execGC = execWithZ ? Z : G1; String generalErrMsg = "Cannot use CDS heap data."; - String coopsErrMsg = generalErrMsg + " Selected GC not compatible -XX:-UseCompressedOops"; + String coopsErrMsg = "CDS heap data cannot be used by the selected GC. Please choose a different GC or rebuild AOT cache with -XX:+AOTStreamableObjects"; String coops = "-XX:-UseCompressedOops"; String coh = shouldUseCOH ? "-XX:+UseCompactObjectHeaders" : "-XX:-UseCompactObjectHeaders"; String stream = shouldStream ? "-XX:+AOTStreamableObjects" : "-XX:-AOTStreamableObjects"; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java index 10e17f2072f..cf9ffb2403a 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,7 +47,6 @@ public class AddmodsOption { final String allModulePath = "ALL-MODULE-PATH"; final String loggingOption = "-Xlog:aot=debug,aot+module=debug,aot+heap=info,cds=debug,module=trace"; final String versionPattern = "java.[0-9][0-9].*"; - final String subgraphCannotBeUsed = "subgraph jdk.internal.module.ArchivedBootLayer cannot be used because full module graph is disabled"; final String warningIncubator = "WARNING: Using incubator modules: jdk.incubator.vector"; String archiveName = TestCommon.getNewArchiveName("addmods-option"); TestCommon.setCurrentArchiveName(archiveName); @@ -81,16 +80,14 @@ public class AddmodsOption { "-version"); oa.shouldHaveExitValue(0) .shouldContain("Mismatched values for property jdk.module.addmods") - .shouldContain("runtime jdk.incubator.vector dump time jdk.jconsole") - .shouldContain(subgraphCannotBeUsed); + .shouldContain("runtime jdk.incubator.vector dump time jdk.jconsole"); // no module specified during runtime oa = TestCommon.execCommon( loggingOption, "-version"); oa.shouldHaveExitValue(0) - .shouldContain("jdk.httpserver specified during dump time but not during runtime") - .shouldContain(subgraphCannotBeUsed); + .shouldContain("jdk.httpserver specified during dump time but not during runtime"); // dump an archive without the --add-modules option archiveName = TestCommon.getNewArchiveName("no-addmods-option"); @@ -111,8 +108,7 @@ public class AddmodsOption { oa.shouldHaveExitValue(0) .shouldContain("jdk.jconsole specified during runtime but not during dump time") // version of the jdk.httpserver module, e.g. java 22-ea - .shouldMatch(versionPattern) - .shouldContain(subgraphCannotBeUsed); + .shouldMatch(versionPattern); // dump an archive with an incubator module, -add-modules jdk.incubator.vector archiveName = TestCommon.getNewArchiveName("incubator-module"); @@ -137,7 +133,6 @@ public class AddmodsOption { // module is not restored from archive .shouldContain("define_module(): creation of module: jdk.incubator.vector") .shouldContain("WARNING: Using incubator modules: jdk.incubator.vector") - .shouldContain("subgraph jdk.internal.module.ArchivedBootLayer is not recorde") .shouldHaveExitValue(0); if (Compiler.isJVMCIEnabled()) { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addopens/AddopensOption.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addopens/AddopensOption.java index 636169ef3cb..6f1864f90bd 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addopens/AddopensOption.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addopens/AddopensOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,6 @@ public class AddopensOption { final String addOpensTimeFormat = "java.base/java.time.format=ALL-UNNAMED"; final String loggingOption = "-Xlog:aot=debug,aot+module=debug,aot+heap=info,cds=debug,module=trace"; final String versionPattern = "java.[0-9][0-9].*"; - final String subgraphCannotBeUsed = "subgraph jdk.internal.module.ArchivedBootLayer cannot be used because full module graph is disabled"; final String warningIncubator = "WARNING: Using incubator modules: jdk.incubator.vector"; String archiveName = TestCommon.getNewArchiveName("addopens-option"); TestCommon.setCurrentArchiveName(archiveName); @@ -76,16 +75,14 @@ public class AddopensOption { "-version"); oa.shouldHaveExitValue(0) .shouldContain("Mismatched values for property jdk.module.addopens") - .shouldContain("runtime java.base/java.nio=ALL-UNNAMED dump time java.base/java.time.format=ALL-UNNAMED") - .shouldContain(subgraphCannotBeUsed); + .shouldContain("runtime java.base/java.nio=ALL-UNNAMED dump time java.base/java.time.format=ALL-UNNAMED"); // no module specified during runtime oa = TestCommon.execCommon( loggingOption, "-version"); oa.shouldHaveExitValue(0) - .shouldContain("jdk.httpserver specified during dump time but not during runtime") - .shouldContain(subgraphCannotBeUsed); + .shouldContain("jdk.httpserver specified during dump time but not during runtime"); // dump an archive without the --add-opens option archiveName = TestCommon.getNewArchiveName("no-addopens-option"); @@ -106,8 +103,7 @@ public class AddopensOption { oa.shouldHaveExitValue(0) .shouldContain("java.base/java.time.format=ALL-UNNAMED specified during runtime but not during dump time") // version of the jdk.httpserver module, e.g. java 22-ea - .shouldMatch(versionPattern) - .shouldContain(subgraphCannotBeUsed); + .shouldMatch(versionPattern); // dump an archive with -add-opens java.base/java.nio=ALL-UNNAMED archiveName = TestCommon.getNewArchiveName("addopens-java-nio"); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java index 3001b8dd2ca..6339538bbb2 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,6 @@ public class ModuleOption { // e.g. JDK 22: "java 22" // JDK 22.0.1: "java 22.0.1" final String versionPattern = "java.[0-9][0-9].*"; - final String subgraphCannotBeUsed = "subgraph jdk.internal.module.ArchivedBootLayer cannot be used because full module graph is disabled"; String archiveName = TestCommon.getNewArchiveName("module-option"); TestCommon.setCurrentArchiveName(archiveName); @@ -70,16 +69,14 @@ public class ModuleOption { "-m", "jdk.compiler/com.sun.tools.javac.Main", "-version"); oa.shouldHaveExitValue(0) - .shouldContain("Mismatched values for property jdk.module.main: runtime jdk.compiler dump time jdk.httpserver") - .shouldContain(subgraphCannotBeUsed); + .shouldContain("Mismatched values for property jdk.module.main: runtime jdk.compiler dump time jdk.httpserver"); // no module specified during runtime oa = TestCommon.execCommon( loggingOption, "-version"); oa.shouldHaveExitValue(0) - .shouldContain("Mismatched values for property jdk.module.main: jdk.httpserver specified during dump time but not during runtime") - .shouldContain(subgraphCannotBeUsed); + .shouldContain("Mismatched values for property jdk.module.main: jdk.httpserver specified during dump time but not during runtime"); // dump an archive without the module option archiveName = TestCommon.getNewArchiveName("no-module-option"); @@ -98,8 +95,7 @@ public class ModuleOption { oa.shouldHaveExitValue(0) .shouldContain("Mismatched values for property jdk.module.main: jdk.httpserver specified during runtime but not during dump time") // version of the jdk.httpserver module, e.g. java 22-ea - .shouldMatch(versionPattern) - .shouldContain(subgraphCannotBeUsed); + .shouldMatch(versionPattern); // dump an archive with an incubator module, -m jdk.incubator.vector archiveName = TestCommon.getNewArchiveName("incubator-module"); @@ -122,7 +118,6 @@ public class ModuleOption { // module is not restored from archive .shouldContain("define_module(): creation of module: jdk.incubator.vector") .shouldContain("WARNING: Using incubator modules: jdk.incubator.vector") - .shouldContain("subgraph jdk.internal.module.ArchivedBootLayer is not recorde") .shouldContain("module jdk.incubator.vector does not have a ModuleMainClass attribute, use -m /") .shouldHaveExitValue(1); } diff --git a/test/hotspot/jtreg/runtime/cds/serviceability/ReplaceCriticalClasses.java b/test/hotspot/jtreg/runtime/cds/serviceability/ReplaceCriticalClasses.java index 408ab0582b1..99b2714a08f 100644 --- a/test/hotspot/jtreg/runtime/cds/serviceability/ReplaceCriticalClasses.java +++ b/test/hotspot/jtreg/runtime/cds/serviceability/ReplaceCriticalClasses.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -190,7 +190,6 @@ public class ReplaceCriticalClasses { if (checkSubgraph) { if (expectShared) { if (!out.getOutput().contains("Unable to map at required address in java heap")) { - out.shouldContain(subgraphInit); // If the subgraph is successfully initialized, the specified shared class must not be rewritten. out.shouldNotContain("Rewriting done."); } From f1cb3b41f1f498f7a1542cd247fe6564294b572e Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Mon, 23 Mar 2026 04:41:14 +0000 Subject: [PATCH 064/160] 8379792: UBSAN runtime error: signed integer overflow during compress expand value transform Co-authored-by: Kim Barrett Reviewed-by: kbarrett, fyang --- src/hotspot/cpu/riscv/vm_version_riscv.hpp | 2 +- .../linux_riscv/vm_version_linux_riscv.cpp | 20 +++++++------ src/hotspot/share/opto/intrinsicnode.cpp | 4 +-- .../share/utilities/globalDefinitions.hpp | 30 ++++++++++++------- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index 03c843efc69..11a88dfedd7 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -55,7 +55,7 @@ class VM_Version : public Abstract_VM_Version { public: RVFeatureValue(const char* pretty, int linux_bit_num, bool fstring) : - _pretty(pretty), _feature_string(fstring), _linux_feature_bit(nth_bit(linux_bit_num)) { + _pretty(pretty), _feature_string(fstring), _linux_feature_bit(nth_bit(linux_bit_num)) { } virtual void enable_feature(int64_t value = 0) = 0; virtual void disable_feature() = 0; diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp index 35cbb75e8ff..648131b94a3 100644 --- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp @@ -36,40 +36,42 @@ #include #include +static constexpr uint64_t feature_bit(int n) { return nth_bit(n); } + #ifndef HWCAP_ISA_I -#define HWCAP_ISA_I nth_bit('I' - 'A') +#define HWCAP_ISA_I feature_bit('I' - 'A') #endif #ifndef HWCAP_ISA_M -#define HWCAP_ISA_M nth_bit('M' - 'A') +#define HWCAP_ISA_M feature_bit('M' - 'A') #endif #ifndef HWCAP_ISA_A -#define HWCAP_ISA_A nth_bit('A' - 'A') +#define HWCAP_ISA_A feature_bit('A' - 'A') #endif #ifndef HWCAP_ISA_F -#define HWCAP_ISA_F nth_bit('F' - 'A') +#define HWCAP_ISA_F feature_bit('F' - 'A') #endif #ifndef HWCAP_ISA_D -#define HWCAP_ISA_D nth_bit('D' - 'A') +#define HWCAP_ISA_D feature_bit('D' - 'A') #endif #ifndef HWCAP_ISA_C -#define HWCAP_ISA_C nth_bit('C' - 'A') +#define HWCAP_ISA_C feature_bit('C' - 'A') #endif #ifndef HWCAP_ISA_Q -#define HWCAP_ISA_Q nth_bit('Q' - 'A') +#define HWCAP_ISA_Q feature_bit('Q' - 'A') #endif #ifndef HWCAP_ISA_H -#define HWCAP_ISA_H nth_bit('H' - 'A') +#define HWCAP_ISA_H feature_bit('H' - 'A') #endif #ifndef HWCAP_ISA_V -#define HWCAP_ISA_V nth_bit('V' - 'A') +#define HWCAP_ISA_V feature_bit('V' - 'A') #endif #define read_csr(csr) \ diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp index c3e003ad8d3..3e235738e0f 100644 --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.cpp @@ -273,7 +273,7 @@ static const Type* bitshuffle_value(const TypeInteger* src_type, const TypeInteg // result.lo = 0 if (maskcon != -1L) { int bitcount = population_count(static_cast(bt == T_INT ? maskcon & 0xFFFFFFFFL : maskcon)); - hi = right_n_bits_typed(bitcount); + hi = right_n_bits(bitcount); lo = 0L; } else { // preserve originally assigned hi (MAX_INT/LONG) and lo (MIN_INT/LONG) values @@ -376,7 +376,7 @@ static const Type* bitshuffle_value(const TypeInteger* src_type, const TypeInteg // Rule 3: // We can further constrain the upper bound of bit compression if the number of bits // which can be set(one) is less than the maximum number of bits of integral type. - hi = MIN2(right_n_bits_typed(result_bit_width), hi); + hi = MIN2(right_n_bits(result_bit_width), hi); } } else { assert(opc == Op_ExpandBits, ""); diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 9560d863a2c..40691de518e 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -1072,18 +1072,26 @@ const intptr_t NoBits = 0; // no bits set in a word const jlong NoLongBits = 0; // no bits set in a long const intptr_t OneBit = 1; // only right_most bit set in a word -// get a word with the n.th or the right-most or left-most n bits set -// (note: #define used only so that they can be used in enum constant definitions) -#define nth_bit(n) (((n) >= BitsPerWord) ? 0 : (OneBit << (n))) -#define right_n_bits(n) (nth_bit(n) - 1) - -// same as nth_bit(n), but allows handing in a type as template parameter. Allows -// us to use nth_bit with 64-bit types on 32-bit platforms -template inline T nth_bit_typed(int n) { - return ((T)1) << n; +// Return a value of type T with the n.th bit set and all other bits zero. +// T must be an integral or enum type. n must be non-negative. If n is at +// least the bitwise size of T then all bits in the result are zero. +template +constexpr T nth_bit(int n) { + assert(n >= 0, "n must be non-negative"); + using U = std::make_unsigned_t; + constexpr size_t size = sizeof(U) * BitsPerByte; + return T((size_t(n) >= size) ? U(0) : (U(1) << n)); } -template inline T right_n_bits_typed(int n) { - return nth_bit_typed(n) - 1; + +// Return a value of type T with all bits below the n.th bit set and all +// other bits zero. T must be an integral or enum type. n must be +// non-negative. If n is at least the bitwise size of T then all bits in +// the result are set. +template +constexpr T right_n_bits(int n) { + assert(n >= 0, "n must be non-negative"); + using U = std::make_unsigned_t; + return T(nth_bit(n) - 1); } // bit-operations using a mask m From 8e906ddad6e8019718a916e02082b2badf0c0ff2 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Mon, 23 Mar 2026 05:29:22 +0000 Subject: [PATCH 065/160] 8379019: Remove AppContext from WindowEvent Reviewed-by: serb, azvegint --- .../share/classes/java/awt/event/WindowEvent.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/java.desktop/share/classes/java/awt/event/WindowEvent.java b/src/java.desktop/share/classes/java/awt/event/WindowEvent.java index d898860f153..b2dbdb4f13f 100644 --- a/src/java.desktop/share/classes/java/awt/event/WindowEvent.java +++ b/src/java.desktop/share/classes/java/awt/event/WindowEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ import java.awt.Window; import java.io.Serial; import java.lang.annotation.Native; -import sun.awt.AppContext; import sun.awt.SunToolkit; /** @@ -324,21 +323,14 @@ public class WindowEvent extends ComponentEvent { * WINDOW_LOST_FOCUS event, this is the Window that gained activation or * focus. For any other type of WindowEvent, or if the focus or activation * change occurs with a native application, with a Java application in a - * different VM or context, or with no other Window, null is returned. + * different VM, or with no other Window, null is returned. * * @return the other Window involved in the focus or activation change, or * null * @since 1.4 */ public Window getOppositeWindow() { - if (opposite == null) { - return null; - } - - return (SunToolkit.targetToAppContext(opposite) == - AppContext.getAppContext()) - ? opposite - : null; + return opposite; } /** From 206c9cd6e18cf3229c51d3932a79c6f6258a0f04 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Mon, 23 Mar 2026 05:59:16 +0000 Subject: [PATCH 066/160] 8378923: Remove AppContext from datatransfer classes Reviewed-by: psadhukhan, tr, serb --- .../classes/javax/swing/TransferHandler.java | 31 ++--- .../DesktopDatatransferServiceImpl.java | 11 +- .../sun/awt/datatransfer/SunClipboard.java | 3 - .../classes/sun/swing/SwingUtilities2.java | 9 -- .../SystemClipboardTest.java | 111 ------------------ 5 files changed, 13 insertions(+), 152 deletions(-) delete mode 100644 test/jdk/java/awt/Clipboard/FlavorChangeNotificationTest/SystemClipboardTest.java diff --git a/src/java.desktop/share/classes/javax/swing/TransferHandler.java b/src/java.desktop/share/classes/javax/swing/TransferHandler.java index f412314b28f..0f2229482bc 100644 --- a/src/java.desktop/share/classes/javax/swing/TransferHandler.java +++ b/src/java.desktop/share/classes/javax/swing/TransferHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,6 @@ import javax.swing.text.JTextComponent; import sun.reflect.misc.MethodUtil; import sun.swing.SwingUtilities2; -import sun.awt.AppContext; import sun.swing.*; import sun.awt.SunToolkit; @@ -1096,15 +1095,12 @@ public class TransferHandler implements Serializable { private String propertyName; private static SwingDragGestureRecognizer recognizer = null; + private static DropHandler handler; private static DropTargetListener getDropTargetListener() { synchronized(DropHandler.class) { - DropHandler handler = - (DropHandler)AppContext.getAppContext().get(DropHandler.class); - if (handler == null) { handler = new DropHandler(); - AppContext.getAppContext().put(DropHandler.class, handler); } return handler; @@ -1725,29 +1721,22 @@ public class TransferHandler implements Serializable { } } + private static Clipboard clipboard; /** * Returns the clipboard to use for cut/copy/paste. */ private Clipboard getClipboard(JComponent c) { - if (SwingUtilities2.canAccessSystemClipboard()) { + if (!GraphicsEnvironment.isHeadless()) { return c.getToolkit().getSystemClipboard(); } - Clipboard clipboard = (Clipboard)sun.awt.AppContext.getAppContext(). - get(SandboxClipboardKey); - if (clipboard == null) { - clipboard = new Clipboard("Sandboxed Component Clipboard"); - sun.awt.AppContext.getAppContext().put(SandboxClipboardKey, - clipboard); + // Likely it is impossible to be here in headless. + synchronized (TransferHandler.class) { + if (clipboard == null) { + clipboard = new Clipboard("Headless clipboard"); + } + return clipboard; } - return clipboard; } - - /** - * Key used in app context to lookup Clipboard to use if access to - * System clipboard is denied. - */ - private static Object SandboxClipboardKey = new Object(); - } } diff --git a/src/java.desktop/share/classes/sun/awt/datatransfer/DesktopDatatransferServiceImpl.java b/src/java.desktop/share/classes/sun/awt/datatransfer/DesktopDatatransferServiceImpl.java index e96ca888ee3..4234dbad59a 100644 --- a/src/java.desktop/share/classes/sun/awt/datatransfer/DesktopDatatransferServiceImpl.java +++ b/src/java.desktop/share/classes/sun/awt/datatransfer/DesktopDatatransferServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,6 @@ package sun.awt.datatransfer; -import sun.awt.AppContext; import sun.datatransfer.DesktopDatatransferService; import java.awt.EventQueue; @@ -43,8 +42,6 @@ import java.util.function.Supplier; */ public class DesktopDatatransferServiceImpl implements DesktopDatatransferService { - private static final Object FLAVOR_MAP_KEY = new Object(); - @Override public void invokeOnEventThread(Runnable r) { EventQueue.invokeLater(r); @@ -59,13 +56,11 @@ public class DesktopDatatransferServiceImpl implements DesktopDatatransferServic return null; } + private FlavorMap fm; @Override - public FlavorMap getFlavorMap(Supplier supplier) { - AppContext context = AppContext.getAppContext(); - FlavorMap fm = (FlavorMap) context.get(FLAVOR_MAP_KEY); + public synchronized FlavorMap getFlavorMap(Supplier supplier) { if (fm == null) { fm = supplier.get(); - context.put(FLAVOR_MAP_KEY, fm); } return fm; } diff --git a/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java b/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java index bc8071a798b..4ccee481a9e 100644 --- a/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java +++ b/src/java.desktop/share/classes/sun/awt/datatransfer/SunClipboard.java @@ -226,9 +226,6 @@ public abstract class SunClipboard extends Clipboard { * argument is not {@code null} and is not equal to the current * contents context. * - * @param disposedContext the AppContext that is disposed or - * {@code null} if the ownership is lost because another - * application acquired ownership. */ protected void lostOwnershipLater() { SunToolkit.postEvent(new PeerEvent(this, () -> lostOwnershipNow(), diff --git a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java index 399ef3e531d..8c6d6ab9850 100644 --- a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java +++ b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java @@ -1435,15 +1435,6 @@ public class SwingUtilities2 { } } - /** - * checks if the system clipboard can be accessed. - * This is true in a headful environment, false in a headless one - * - */ - public static boolean canAccessSystemClipboard() { - return !GraphicsEnvironment.isHeadless(); - } - public static String displayPropertiesToCSS(Font font, Color fg) { StringBuilder rule = new StringBuilder("body {"); if (font != null) { diff --git a/test/jdk/java/awt/Clipboard/FlavorChangeNotificationTest/SystemClipboardTest.java b/test/jdk/java/awt/Clipboard/FlavorChangeNotificationTest/SystemClipboardTest.java deleted file mode 100644 index ff19005b8aa..00000000000 --- a/test/jdk/java/awt/Clipboard/FlavorChangeNotificationTest/SystemClipboardTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - @test - @bug 4259272 - @summary tests that notifications on changes to the set of DataFlavors - available on the system clipboard are delivered properly - @key headful - @modules java.desktop/sun.awt - @build Common - @run main SystemClipboardTest -*/ - -import java.awt.Toolkit; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.StringSelection; - -import sun.awt.SunToolkit; - -public class SystemClipboardTest { - - private final Clipboard clipboard = - Toolkit.getDefaultToolkit().getSystemClipboard(); - - private final FlavorListenerImpl listener1 = new FlavorListenerImpl(); - - private final FlavorListenerImpl listener2 = new FlavorListenerImpl(); - - private boolean isListener2Added; - - - public static void main(String[] args) { - new SystemClipboardTest().start(); - } - - public void start() { - Util.setClipboardContents(clipboard, - new StringSelection("text3"), null); - - clipboard.addFlavorListener(listener1); - - final ThreadGroup threadGroup = new ThreadGroup("Test thread group"); - final Object lock = new Object(); - final Runnable runnable = new Runnable() { - public void run() { - SunToolkit.createNewAppContext(); - clipboard.addFlavorListener(listener2); - synchronized (lock) { - isListener2Added = true; - lock.notifyAll(); - } - } - }; - final Thread thread = new Thread(threadGroup, runnable, "Test thread"); - synchronized (lock) { - thread.start(); - while (!isListener2Added) { - try { - lock.wait(); - } catch (InterruptedException ie) { - ie.printStackTrace(); - } - } - } - - Util.setClipboardContents(clipboard, - new TransferableUnion(new StringSelection("text2"), - new ImageSelection(Util.createImage())), - null); - Util.sleep(3000); - - clipboard.removeFlavorListener(listener1); - // must not remove listener2 from this AppContext - - Util.setClipboardContents(clipboard, - new StringSelection("text3"), null); - Util.sleep(3000); - - System.err.println("listener1: " + listener1 - + "\nlistener2: " + listener2); - - if (!(listener1.notified1 - && listener2.notified1 - && !listener1.notified2 - && listener2.notified2)) { - throw new RuntimeException("notifications about flavor " + - "changes delivered incorrectly!"); - } - } -} \ No newline at end of file From db49e8d083118709c01eb00e659bbab8f98e95e2 Mon Sep 17 00:00:00 2001 From: Christian Hagedorn Date: Mon, 23 Mar 2026 07:11:33 +0000 Subject: [PATCH 067/160] 8378243: [IR Framework] Create separate VMInfo and ApplicableIRRules JavaMessage classes Reviewed-by: mchevalier, dfenacci, thartmann --- .../compiler/lib/ir_framework/IRNode.java | 2 +- .../driver/irmatching/irmethod/IRMethod.java | 2 +- .../driver/irmatching/irrule/IRRule.java | 2 +- .../checkattribute/parsing/RawIRNode.java | 6 +- .../irrule/constraint/raw/RawConstraint.java | 4 +- .../constraint/raw/RawCountsConstraint.java | 3 +- .../constraint/raw/RawFailOnConstraint.java | 2 +- .../phase/CompilePhaseIRRuleBuilder.java | 2 +- .../DefaultPhaseRawConstraintParser.java | 6 +- .../parser/ApplicableIRRulesParser.java | 75 ++-------------- .../irmatching/parser/IRMethodBuilder.java | 1 + .../irmatching/parser/TestClassParser.java | 20 +++-- .../irmatching/parser/VMInfoParser.java | 67 -------------- .../driver/network/TestVMData.java | 6 +- .../testvm/java/ApplicableIRRules.java | 81 +++++++++++++++++ .../driver/network/testvm/java/IRRuleIds.java | 4 + .../testvm/java/JavaMessageParser.java | 33 +++---- .../network/testvm/java/JavaMessages.java | 24 ++--- .../testvm/java}/VMInfo.java | 87 ++++++++++--------- .../multiline/ApplicableIRRulesStrategy.java | 78 +++++++++++++++++ .../java/multiline/MultiLineParser.java | 67 ++++++++++++++ .../multiline/MultiLineParsingStrategy.java | 32 +++++++ .../testvm/java/multiline/VMInfoStrategy.java | 57 ++++++++++++ .../test/ApplicableIRRulesPrinter.java | 25 +++--- .../ir_framework/tests/TestIRMatching.java | 15 +++- 25 files changed, 453 insertions(+), 248 deletions(-) delete mode 100644 test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfoParser.java create mode 100644 test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/ApplicableIRRules.java rename test/hotspot/jtreg/compiler/lib/ir_framework/driver/{irmatching/parser => network/testvm/java}/VMInfo.java (74%) create mode 100644 test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/ApplicableIRRulesStrategy.java create mode 100644 test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParser.java create mode 100644 test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParsingStrategy.java create mode 100644 test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/VMInfoStrategy.java diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 1c59a03b4b0..ebc95527344 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -24,7 +24,7 @@ package compiler.lib.ir_framework; import compiler.lib.ir_framework.driver.irmatching.mapping.*; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.CheckedTestFrameworkException; import compiler.lib.ir_framework.shared.TestFormat; import compiler.lib.ir_framework.shared.TestFormatException; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java index 5fbe90f0e72..137766b0011 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java @@ -31,8 +31,8 @@ import compiler.lib.ir_framework.driver.irmatching.MatchResult; import compiler.lib.ir_framework.driver.irmatching.Matchable; import compiler.lib.ir_framework.driver.irmatching.MatchableMatcher; import compiler.lib.ir_framework.driver.irmatching.irrule.IRRule; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; import compiler.lib.ir_framework.driver.network.testvm.java.IRRuleIds; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.TestFormat; import compiler.lib.ir_framework.shared.TestFormatException; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/IRRule.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/IRRule.java index 749942eabd6..5c48394851b 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/IRRule.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/IRRule.java @@ -31,7 +31,7 @@ import compiler.lib.ir_framework.driver.irmatching.Matchable; import compiler.lib.ir_framework.driver.irmatching.MatchableMatcher; import compiler.lib.ir_framework.driver.irmatching.irrule.phase.CompilePhaseIRRule; import compiler.lib.ir_framework.driver.irmatching.irrule.phase.CompilePhaseIRRuleBuilder; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; /** * This class represents a generic {@link IR @IR} rule of an IR method. It contains a list of compile phase specific diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java index b86ae47e186..d8be8add2a2 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/checkattribute/parsing/RawIRNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,10 +25,10 @@ package compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.parsin import compiler.lib.ir_framework.CompilePhase; import compiler.lib.ir_framework.IRNode; +import compiler.lib.ir_framework.driver.SuccessOnlyConstraintException; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.Comparison; import compiler.lib.ir_framework.shared.TestFormat; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; -import compiler.lib.ir_framework.driver.SuccessOnlyConstraintException; import java.util.regex.Matcher; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawConstraint.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawConstraint.java index 1c2fba218db..174136c7c28 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawConstraint.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawConstraint.java @@ -27,12 +27,12 @@ import compiler.lib.ir_framework.CompilePhase; import compiler.lib.ir_framework.IR; import compiler.lib.ir_framework.IRNode; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.Constraint; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; /** * Interface to represent a single raw constraint as found in the {@link IR @IR} annotation (i.e. {@link IRNode} * placeholder strings are not replaced by regexes, yet). A raw constraint can be parsed into a {@link Constraint} by - * calling {@link #parse(CompilePhase, String)}. This replaces the IR node placeholder strings by actual regexes and + * calling {@link #parse(CompilePhase, String, VMInfo)}. This replaces the IR node placeholder strings by actual regexes and * merges composite nodes together. * * @see Constraint diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawCountsConstraint.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawCountsConstraint.java index 9934a830a06..a616f1a0455 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawCountsConstraint.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawCountsConstraint.java @@ -26,12 +26,11 @@ package compiler.lib.ir_framework.driver.irmatching.irrule.constraint.raw; import compiler.lib.ir_framework.CompilePhase; import compiler.lib.ir_framework.IR; -import compiler.lib.ir_framework.IRNode; import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.parsing.RawIRNode; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.Constraint; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; import compiler.lib.ir_framework.driver.SuccessOnlyConstraintException; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.Comparison; import compiler.lib.ir_framework.shared.TestFormat; import compiler.lib.ir_framework.shared.TestFormatException; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawFailOnConstraint.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawFailOnConstraint.java index 4bd334f1f08..6bd3f8f3448 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawFailOnConstraint.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/constraint/raw/RawFailOnConstraint.java @@ -29,7 +29,7 @@ import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.driver.SuccessOnlyConstraintException; import compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.parsing.RawIRNode; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.Constraint; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.Comparison; /** diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/CompilePhaseIRRuleBuilder.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/CompilePhaseIRRuleBuilder.java index 5d327135c22..4d46a53c46b 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/CompilePhaseIRRuleBuilder.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/CompilePhaseIRRuleBuilder.java @@ -34,7 +34,7 @@ import compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.parsing import compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.parsing.RawFailOn; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.Constraint; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.raw.RawConstraint; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.TestFormat; import java.util.*; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/DefaultPhaseRawConstraintParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/DefaultPhaseRawConstraintParser.java index 1f837883978..5f8f597a4fe 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/DefaultPhaseRawConstraintParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irrule/phase/DefaultPhaseRawConstraintParser.java @@ -31,7 +31,7 @@ import compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.Counts; import compiler.lib.ir_framework.driver.irmatching.irrule.checkattribute.FailOn; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.Constraint; import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.raw.RawConstraint; -import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.TestFrameworkException; import java.util.ArrayList; @@ -71,7 +71,7 @@ class DefaultPhaseRawConstraintParser { for (RawConstraint rawConstraint : rawConstraints) { CompilePhase compilePhase = rawConstraint.defaultCompilePhase(); List checkAttribute = - matchableForCompilePhase.computeIfAbsent(compilePhase, k -> new ArrayList<>()); + matchableForCompilePhase.computeIfAbsent(compilePhase, _ -> new ArrayList<>()); checkAttribute.add(rawConstraint.parse(compilePhase, compilation.output(compilePhase), vmInfo)); } return replaceConstraintsWithCheckAttribute(matchableForCompilePhase, checkAttributeType); @@ -113,7 +113,7 @@ class DefaultPhaseRawConstraintParser { private static void addCheckAttribute(Map failOnForCompilePhase, Map> result) { failOnForCompilePhase.forEach((compilePhase, matchable) -> { - List list = result.computeIfAbsent(compilePhase, k -> new ArrayList<>()); + List list = result.computeIfAbsent(compilePhase, _ -> new ArrayList<>()); list.add(matchable); }); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java index d251c574e47..67a04df2b58 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java @@ -25,16 +25,12 @@ package compiler.lib.ir_framework.driver.irmatching.parser; import compiler.lib.ir_framework.IR; import compiler.lib.ir_framework.TestFramework; -import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.HotSpotPidFileParser; +import compiler.lib.ir_framework.driver.network.testvm.java.ApplicableIRRules; import compiler.lib.ir_framework.driver.network.testvm.java.IRRuleIds; import compiler.lib.ir_framework.shared.TestFormat; -import compiler.lib.ir_framework.shared.TestFrameworkException; -import compiler.lib.ir_framework.test.ApplicableIRRulesPrinter; import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -55,73 +51,19 @@ public class ApplicableIRRulesParser { * Parse the Applicable IR rules passed as parameter and return a "test name" -> TestMethod map that contains an * entry for each method that needs to be IR matched on. */ - public TestMethods parse(String applicableIRRules) { - createTestMethodMap(applicableIRRules, testClass); + public TestMethods parse(ApplicableIRRules applicableIRRules) { + createTestMethodMap(applicableIRRules); // We could have found format errors in @IR annotations. Report them now with an exception. TestFormat.throwIfAnyFailures(); return new TestMethods(testMethods); } - /** - * Sets up a map testname -> TestMethod map. The TestMethod object will later be filled with the ideal and opto - * assembly output in {@link HotSpotPidFileParser}. - */ - private void createTestMethodMap(String applicableIRRules, Class testClass) { - Map irRulesMap = parseApplicableIRRules(applicableIRRules); - createTestMethodsWithApplicableIRRules(testClass, irRulesMap); - } - - /** - * Read the Applicable IR Rules emitted by the Test VM to decide if an @IR rule must be checked for a method. - */ - private Map parseApplicableIRRules(String applicableIRRules) { - Map irRulesMap = new HashMap<>(); - String[] applicableIRRulesLines = getApplicableIRRulesLines(applicableIRRules); - for (String s : applicableIRRulesLines) { - String line = s.trim(); - String[] splitLine = line.split(","); - if (splitLine.length < 2) { - throw new TestFrameworkException("Invalid Applicable IR Rules format. No comma found: " + splitLine[0]); - } - String testName = splitLine[0]; - IRRuleIds irRuleIds = parseIrRulesIds(splitLine); - irRulesMap.put(testName, irRuleIds); - } - return irRulesMap; - } - - /** - * Parse the Applicable IR Rules lines without header, explanation line and footer and return them in an array. - */ - private String[] getApplicableIRRulesLines(String applicableIRRules) { - if (applicableIRRules.isEmpty()) { - // Nothing to IR match. - return new String[0]; - } - return applicableIRRules.split("\\R"); - } - - /** - * Parse rule indexes from a single line of the Applicable IR Rules in the format: - */ - private IRRuleIds parseIrRulesIds(String[] splitLine) { - List irRuleIds = new ArrayList<>(); - for (int i = 1; i < splitLine.length; i++) { - try { - irRuleIds.add(Integer.parseInt(splitLine[i])); - } catch (NumberFormatException e) { - throw new TestFrameworkException("Invalid Applicable IR Rules format. No number found: " + splitLine[i]); - } - } - return new IRRuleIds(irRuleIds); - } - - private void createTestMethodsWithApplicableIRRules(Class testClass, Map irRulesMap) { + private void createTestMethodMap(ApplicableIRRules applicableIRRules) { for (Method m : testClass.getDeclaredMethods()) { IR[] irAnnos = m.getAnnotationsByType(IR.class); if (irAnnos.length > 0) { // Validation of legal @IR attributes and placement of the annotation was already done in Test VM. - IRRuleIds irRuleIds = irRulesMap.get(m.getName()); + IRRuleIds irRuleIds = applicableIRRules.ruleIds(m.getName()); validateIRRuleIds(m, irAnnos, irRuleIds); if (hasAnyApplicableIRRules(irRuleIds)) { testMethods.put(m.getName(), new TestMethod(m, irAnnos, irRuleIds)); @@ -131,10 +73,7 @@ public class ApplicableIRRulesParser { } private void validateIRRuleIds(Method m, IR[] irAnnos, IRRuleIds irRuleIds) { - TestFramework.check(irRuleIds != null, "Should find method name in validIrRulesMap for " + m); - TestFramework.check(!irRuleIds.isEmpty(), "Did not find any rule indices for " + m); - TestFramework.check((irRuleIds.first() >= 1 || irRuleIds.first() == ApplicableIRRulesPrinter.NO_RULES) - && irRuleIds.last() <= irAnnos.length, + TestFramework.check((irRuleIds.isEmpty() || (irRuleIds.first() >= 1 && irRuleIds.last() <= irAnnos.length)), "Invalid IR rule index found in validIrRulesMap for " + m); } @@ -142,6 +81,6 @@ public class ApplicableIRRulesParser { * Does the list of IR rules contain any applicable IR rules for the given conditions? */ private boolean hasAnyApplicableIRRules(IRRuleIds irRuleIds) { - return irRuleIds.first() != ApplicableIRRulesPrinter.NO_RULES; + return !irRuleIds.isEmpty(); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java index 46a237576e6..5ce93df434c 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java @@ -32,6 +32,7 @@ import compiler.lib.ir_framework.driver.irmatching.irmethod.NotCompilableIRMetho import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.HotSpotPidFileParser; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.LoggedMethod; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.LoggedMethods; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import java.util.Map; import java.util.SortedSet; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java index ca36a3e9f72..30a766ddd4e 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java @@ -23,6 +23,7 @@ package compiler.lib.ir_framework.driver.irmatching.parser; +import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.driver.irmatching.Matchable; import compiler.lib.ir_framework.driver.irmatching.NonIRTestClass; import compiler.lib.ir_framework.driver.irmatching.TestClass; @@ -31,6 +32,8 @@ import compiler.lib.ir_framework.driver.irmatching.irmethod.IRMethodMatchable; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.HotSpotPidFileParser; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.LoggedMethods; import compiler.lib.ir_framework.driver.network.TestVMData; +import compiler.lib.ir_framework.driver.network.testvm.java.ApplicableIRRules; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.TestFormat; import java.util.SortedSet; @@ -55,19 +58,20 @@ public class TestClassParser { * Return a default/empty TestClass object if there are no applicable @IR rules in any method of the test class. */ public Matchable parse(TestVMData testVmData) { + ApplicableIRRules applicableIrRules = testVmData.applicableIRRules(); + if (applicableIrRules.hasNoMethods()) { + return new NonIRTestClass(); + } ApplicableIRRulesParser applicableIRRulesParser = new ApplicableIRRulesParser(testClass); TestMethods testMethods = applicableIRRulesParser.parse(testVmData.applicableIRRules()); - VMInfo vmInfo = VMInfoParser.parseVMInfo(testVmData.vmInfo()); - if (testMethods.hasTestMethods()) { - HotSpotPidFileParser hotSpotPidFileParser = new HotSpotPidFileParser(testClass.getName(), testMethods); - LoggedMethods loggedMethods = hotSpotPidFileParser.parse(testVmData.hotspotPidFileName()); - return createTestClass(testMethods, loggedMethods, vmInfo); - } - return new NonIRTestClass(); + TestFramework.check(testMethods.hasTestMethods(), "must have at least one"); + HotSpotPidFileParser hotSpotPidFileParser = new HotSpotPidFileParser(testClass.getName(), testMethods); + LoggedMethods loggedMethods = hotSpotPidFileParser.parse(testVmData.hotspotPidFileName()); + return createTestClass(testMethods, loggedMethods, testVmData.vmInfo()); } /** - * Create test class with IR methods for all test methods identified by {@link ApplicableIRRulesParser} by combining them + * Create test class with IR methods for all test methods found in {@link ApplicableIRRules} by combining them * with the parsed compilation output from {@link HotSpotPidFileParser}. */ private Matchable createTestClass(TestMethods testMethods, LoggedMethods loggedMethods, VMInfo vmInfo) { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfoParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfoParser.java deleted file mode 100644 index 44013839754..00000000000 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfoParser.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package compiler.lib.ir_framework.driver.irmatching.parser; - -import compiler.lib.ir_framework.shared.TestFrameworkException; - -import java.util.HashMap; -import java.util.Map; - -/** - * Class to parse the VMInfo emitted by the Test VM and creating {@link VMInfo} objects for each entry. - * - * @see VMInfo - */ -public class VMInfoParser { - - /** - * Create a new VMInfo object from the vmInfo string. - */ - public static VMInfo parseVMInfo(String vmInfo) { - Map map = new HashMap<>(); - String[] lines = getVMInfoLines(vmInfo); - for (String s : lines) { - String line = s.trim(); - String[] splitLine = line.split(":", 2); - if (splitLine.length != 2) { - throw new TestFrameworkException("Invalid VMInfo key:value encoding. Found: " + splitLine[0]); - } - String key = splitLine[0]; - String value = splitLine[1]; - map.put(key, value); - } - return new VMInfo(map); - } - - /** - * Extract the VMInfo from the applicableIRRules string, strip away the header and return the individual key-value lines. - */ - private static String[] getVMInfoLines(String vmInfo) { - if (vmInfo.isEmpty()) { - // Nothing to IR match. - return new String[0]; - } - return vmInfo.split("\\R"); - } -} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java index 413cf3347d8..59632ef1a48 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java @@ -24,7 +24,9 @@ package compiler.lib.ir_framework.driver.network; import compiler.lib.ir_framework.driver.irmatching.IRMatcher; +import compiler.lib.ir_framework.driver.network.testvm.java.ApplicableIRRules; import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessages; +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; import compiler.lib.ir_framework.shared.TestFrameworkSocket; /** @@ -42,11 +44,11 @@ public class TestVMData { this.allowNotCompilable = allowNotCompilable; } - public String applicableIRRules() { + public ApplicableIRRules applicableIRRules() { return javaMessages.applicableIRRules(); } - public String vmInfo() { + public VMInfo vmInfo() { return javaMessages.vmInfo(); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/ApplicableIRRules.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/ApplicableIRRules.java new file mode 100644 index 00000000000..eb41472057e --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/ApplicableIRRules.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.ir_framework.driver.network.testvm.java; + +import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.TestFramework; +import compiler.lib.ir_framework.driver.irmatching.IRMatcher; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Class to hold the Applicable IR Rules sent by the Test VM. It specifies which {@link IR @IR} rules the + * {@link IRMatcher} need to check. This can be different depending on the used VM flags or the machine the test is run + * on itself. + */ +public class ApplicableIRRules implements JavaMessage { + private static final boolean PRINT_APPLICABLE_IR_RULES = + Boolean.parseBoolean(System.getProperty("PrintApplicableIRRules", "false")) + || TestFramework.VERBOSE; + + private final Map methods; + + public ApplicableIRRules() { + this.methods = new LinkedHashMap<>(); + } + + public void add(String method, IRRuleIds irRuleIds) { + methods.put(method, irRuleIds); + } + + public IRRuleIds ruleIds(String methodName) { + return methods.computeIfAbsent(methodName, _ -> IRRuleIds.createEmpty()); + } + + public boolean hasNoMethods() { + return methods.isEmpty(); + } + + @Override + public void print() { + if (!PRINT_APPLICABLE_IR_RULES) { + return; + } + + System.out.println(); + System.out.println("Applicable IR Rules"); + System.out.println("-------------------"); + if (methods.isEmpty()) { + System.out.println(""); + return; + } + for (var entry : methods.entrySet()) { + String method = entry.getKey(); + String ruleIds = entry.getValue().stream().map(String::valueOf).collect(Collectors.joining(", ")); + System.out.println("- " + method + ": " + ruleIds); + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/IRRuleIds.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/IRRuleIds.java index b8ea1765b4f..f5bc084fbce 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/IRRuleIds.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/IRRuleIds.java @@ -49,6 +49,10 @@ public class IRRuleIds implements Iterable { return ruleIds.getLast(); } + public static IRRuleIds createEmpty() { + return new IRRuleIds(new ArrayList<>()); + } + public boolean isEmpty() { return ruleIds.isEmpty(); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessageParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessageParser.java index 896aef38f1f..d419a06c8de 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessageParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessageParser.java @@ -24,6 +24,9 @@ package compiler.lib.ir_framework.driver.network.testvm.java; import compiler.lib.ir_framework.TestFramework; +import compiler.lib.ir_framework.driver.network.testvm.java.multiline.ApplicableIRRulesStrategy; +import compiler.lib.ir_framework.driver.network.testvm.java.multiline.MultiLineParser; +import compiler.lib.ir_framework.driver.network.testvm.java.multiline.VMInfoStrategy; import compiler.lib.ir_framework.shared.TestFrameworkException; import compiler.lib.ir_framework.test.network.MessageTag; @@ -43,18 +46,18 @@ public class JavaMessageParser { private final List stdoutMessages; private final List executedTests; private final Map methodTimes; - private final StringBuilder vmInfoBuilder; - private final StringBuilder applicableIrRules; + private final MultiLineParser vmInfoParser; + private final MultiLineParser applicableIRRulesParser; - private StringBuilder currentBuilder; + private MultiLineParser currentMultiLineParser; public JavaMessageParser() { this.stdoutMessages = new ArrayList<>(); this.methodTimes = new HashMap<>(); this.executedTests = new ArrayList<>(); - this.vmInfoBuilder = new StringBuilder(); - this.applicableIrRules = new StringBuilder(); - this.currentBuilder = null; + this.vmInfoParser = new MultiLineParser<>(new VMInfoStrategy()); + this.applicableIRRulesParser = new MultiLineParser<>(new ApplicableIRRulesStrategy()); + this.currentMultiLineParser = null; } public void parseLine(String line) { @@ -74,12 +77,11 @@ public class JavaMessageParser { return; } - // Multi-line message for single tag. - currentBuilder.append(line).append(System.lineSeparator()); + currentMultiLineParser.parseLine(line); } private void assertNoActiveParser() { - TestFramework.check(currentBuilder == null, "Unexpected new tag while parsing block"); + TestFramework.check(currentMultiLineParser == null, "Unexpected new tag while parsing block"); } private void parseTagLine(Matcher tagLineMatcher) { @@ -89,8 +91,8 @@ public class JavaMessageParser { case STDOUT -> stdoutMessages.add(message); case TEST_LIST -> executedTests.add(message); case PRINT_TIMES -> parsePrintTimes(message); - case VM_INFO -> currentBuilder = vmInfoBuilder; - case APPLICABLE_IR_RULES -> currentBuilder = applicableIrRules; + case VM_INFO -> currentMultiLineParser = vmInfoParser; + case APPLICABLE_IR_RULES -> currentMultiLineParser = applicableIRRulesParser; default -> throw new TestFrameworkException("unknown tag"); } } @@ -108,18 +110,19 @@ public class JavaMessageParser { } private void assertActiveParser() { - TestFramework.check(currentBuilder != null, "Received non-tag line outside of any tag block"); + TestFramework.check(currentMultiLineParser != null, "Received non-tag line outside of any tag block"); } private void parseEndTag() { - currentBuilder = null; + currentMultiLineParser.markFinished(); + currentMultiLineParser = null; } public JavaMessages output() { return new JavaMessages(new StdoutMessages(stdoutMessages), new ExecutedTests(executedTests), new MethodTimes(methodTimes), - applicableIrRules.toString(), - vmInfoBuilder.toString()); + applicableIRRulesParser.output(), + vmInfoParser.output()); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java index e47ecff4b2a..e817610f441 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java @@ -23,22 +23,18 @@ package compiler.lib.ir_framework.driver.network.testvm.java; -import compiler.lib.ir_framework.TestFramework; - /** * Class to collect all Java messages sent from the Test VM to the Driver VM. */ public class JavaMessages { - private static final boolean PRINT_APPLICABLE_IR_RULES = Boolean.parseBoolean(System.getProperty("PrintApplicableIRRules", "false")); - private final StdoutMessages stdoutMessages; private final ExecutedTests executedTests; private final MethodTimes methodTimes; - private final String applicableIrRules; - private final String vmInfo; + private final ApplicableIRRules applicableIrRules; + private final VMInfo vmInfo; JavaMessages(StdoutMessages stdoutMessages, ExecutedTests executedTests, MethodTimes methodTimes, - String applicableIrRules, String vmInfo) { + ApplicableIRRules applicableIrRules, VMInfo vmInfo) { this.stdoutMessages = stdoutMessages; this.executedTests = executedTests; this.methodTimes = methodTimes; @@ -46,21 +42,19 @@ public class JavaMessages { this.vmInfo = vmInfo; } - public String applicableIRRules() { - return applicableIrRules; + public VMInfo vmInfo() { + return vmInfo; } - public String vmInfo() { - return vmInfo; + public ApplicableIRRules applicableIRRules() { + return applicableIrRules; } public void print() { stdoutMessages.print(); methodTimes.print(); executedTests.print(); - if (TestFramework.VERBOSE || PRINT_APPLICABLE_IR_RULES) { - System.out.println("Read Applicable IR Rules from Test VM:"); - System.out.println(applicableIrRules); - } + vmInfo.print(); + applicableIrRules.print(); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/VMInfo.java similarity index 74% rename from test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.java rename to test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/VMInfo.java index 89b6d610496..a948dbc22ef 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/VMInfo.java @@ -21,7 +21,7 @@ * questions. */ -package compiler.lib.ir_framework.driver.irmatching.parser; +package compiler.lib.ir_framework.driver.network.testvm.java; import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.shared.TestFrameworkException; @@ -32,31 +32,27 @@ import java.util.regex.Pattern; /** * This class stores the key value mapping from the VMInfo. - * - * @see ApplicableIRRulesParser */ -public class VMInfo { +public class VMInfo implements JavaMessage { + private static final Pattern CPU_SKYLAKE_PATTERN = + Pattern.compile("family 6 model 85 stepping (\\d+) "); + /** * Stores the key-value mapping. */ private final Map keyValueMap; - private static final Pattern CPU_SKYLAKE_PATTERN = - Pattern.compile("family 6 model 85 stepping (\\d+) "); - - public VMInfo(Map map) { - this.keyValueMap = map; - - TestFramework.check(isKey("cpuFeatures"), "VMInfo does not contain cpuFeatures"); - TestFramework.check(isKey("MaxVectorSize"), "VMInfo does not contain MaxVectorSize"); - TestFramework.check(isKey("MaxVectorSizeIsDefault"), "VMInfo does not contain MaxVectorSizeIsDefault"); - TestFramework.check(isKey("LoopMaxUnroll"), "VMInfo does not contain LoopMaxUnroll"); - TestFramework.check(isKey("UseAVX"), "VMInfo does not contain UseAVX"); - TestFramework.check(isKey("UseAVXIsDefault"), "VMInfo does not contain UseAVXIsDefault"); + public VMInfo(Map keyValueMap) { + this.keyValueMap = keyValueMap; } - public String getStringValue(String key) { - TestFramework.check(isKey(key), "VMInfo does not contain \"" + key + "\""); + public boolean hasCPUFeature(String feature) { + String features = getStringValue("cpuFeatures") + ","; + return features.contains(" " + feature + ","); + } + + private String getStringValue(String key) { + TestFramework.check(keyValueMap.containsKey(key), "VMInfo does not contain \"" + key + "\""); return keyValueMap.get(key); } @@ -68,28 +64,6 @@ public class VMInfo { } } - public boolean hasCPUFeature(String feature) { - String features = getStringValue("cpuFeatures") + ","; - return features.contains(" " + feature + ","); - } - - public boolean isCascadeLake() { - Matcher matcher = CPU_SKYLAKE_PATTERN.matcher(getStringValue("cpuFeatures")); - if (!matcher.find()) { - return false; // skylake pattern not found - } - String stepping = matcher.group(1).trim(); - return Long.parseLong(stepping) >= 5; // this makes it Cascade Lake - } - - public boolean isDefaultCascadeLake() { - // See VM_Version::is_default_intel_cascade_lake - return isCascadeLake() && - getLongValue("MaxVectorSizeIsDefault") == 1 && - getLongValue("UseAVXIsDefault") == 1 && - getLongValue("UseAVX") > 2; - } - /** * Some platforms do not behave as expected, and one cannot trust that the vectors * make use of the full MaxVectorSize. For Cascade Lake, we only use 32 bytes for @@ -101,7 +75,36 @@ public class VMInfo { return !isDefaultCascadeLake(); } - public boolean isKey(String key) { - return keyValueMap.containsKey(key); + private boolean isDefaultCascadeLake() { + // See VM_Version::is_default_intel_cascade_lake + return isCascadeLake() && + getLongValue("MaxVectorSizeIsDefault") == 1 && + getLongValue("UseAVXIsDefault") == 1 && + getLongValue("UseAVX") > 2; + } + + private boolean isCascadeLake() { + Matcher matcher = CPU_SKYLAKE_PATTERN.matcher(getStringValue("cpuFeatures")); + if (!matcher.find()) { + return false; // skylake pattern not found + } + String stepping = matcher.group(1).trim(); + return Long.parseLong(stepping) >= 5; // this makes it Cascade Lake + } + + @Override + public void print() { + if (!TestFramework.VERBOSE) { + return; + } + + System.out.println(); + System.out.println("VM Info"); + System.out.println("--------"); + for (var entry : keyValueMap.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + System.out.println("- Key: " + key + ", Value: " + value); + } } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/ApplicableIRRulesStrategy.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/ApplicableIRRulesStrategy.java new file mode 100644 index 00000000000..3728e489ba7 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/ApplicableIRRulesStrategy.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.ir_framework.driver.network.testvm.java.multiline; + +import compiler.lib.ir_framework.driver.network.testvm.java.ApplicableIRRules; +import compiler.lib.ir_framework.driver.network.testvm.java.IRRuleIds; +import compiler.lib.ir_framework.shared.TestFrameworkException; +import compiler.lib.ir_framework.test.ApplicableIRRulesPrinter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Dedicated strategy to parse the multi-line Applicable IR Rules message into a new {@link ApplicableIRRules} object. + */ +public class ApplicableIRRulesStrategy implements MultiLineParsingStrategy { + private final ApplicableIRRules applicableIrRules; + + public ApplicableIRRulesStrategy() { + this.applicableIrRules = new ApplicableIRRules(); + } + + @Override + public void parseLine(String line) { + if (line.equals(ApplicableIRRulesPrinter.NO_RULES)) { + return; + } + + String[] splitLine = line.split(","); + if (splitLine.length < 2) { + throw new TestFrameworkException("Invalid Applicable IR Rules format. No comma found: " + splitLine[0]); + } + String testName = splitLine[0]; + IRRuleIds irRulesIds = parseIrRulesIds(splitLine); + applicableIrRules.add(testName, irRulesIds); + } + + /** + * Parse rule indexes from a single line of the Applicable IR Rules in the format: + */ + private IRRuleIds parseIrRulesIds(String[] splitLine) { + List irRuleIds = new ArrayList<>(); + for (int i = 1; i < splitLine.length; i++) { + try { + irRuleIds.add(Integer.parseInt(splitLine[i])); + } catch (NumberFormatException e) { + throw new TestFrameworkException("Invalid Applicable IR Rules format. No number found: " + splitLine[i]); + } + } + return new IRRuleIds(irRuleIds); + } + + @Override + public ApplicableIRRules output() { + return applicableIrRules; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParser.java new file mode 100644 index 00000000000..fbc24eed736 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParser.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.ir_framework.driver.network.testvm.java.multiline; + +import compiler.lib.ir_framework.TestFramework; +import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessage; +import compiler.lib.ir_framework.test.network.MessageTag; + +/** + * Generic multi-line parser that takes a {@link MultiLineParsingStrategy} to decide how to parse a single line of + * a multi line {@link JavaMessage}. Once parsing is done, the strategy is queried for the final parsed output. + */ +public class MultiLineParser { + private enum ParserState { + NOTHING_PARSED, PARSING, FINISHED_PARSING + } + + private ParserState parserState; + private final MultiLineParsingStrategy multiLineParsingStrategy; + + public MultiLineParser(MultiLineParsingStrategy multiLineParsingStrategy) { + this.multiLineParsingStrategy = multiLineParsingStrategy; + this.parserState = ParserState.NOTHING_PARSED; + } + + public void parseLine(String line) { + TestFramework.check(parserState != ParserState.FINISHED_PARSING, + "cannot parse another block"); + parserState = ParserState.PARSING; + multiLineParsingStrategy.parseLine(line); + } + + /** + * Once the {@link MessageTag#END_MARKER} was seen, this method is called to mark the end of this multi-line message. + */ + public void markFinished() { + TestFramework.check(parserState == ParserState.PARSING, + "nothing parsed, cannot have empty block"); + parserState = ParserState.FINISHED_PARSING; + } + + public Output output() { + TestFramework.check(parserState != ParserState.PARSING, "either nothing parsed or finished"); + return multiLineParsingStrategy.output(); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParsingStrategy.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParsingStrategy.java new file mode 100644 index 00000000000..0283c520465 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/MultiLineParsingStrategy.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.ir_framework.driver.network.testvm.java.multiline; + +/** + * Interface to define a strategy to parse a line in a {@link MultiLineParser}. + */ +public interface MultiLineParsingStrategy { + void parseLine(String line); + Output output(); +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/VMInfoStrategy.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/VMInfoStrategy.java new file mode 100644 index 00000000000..7d3072394c0 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/multiline/VMInfoStrategy.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.ir_framework.driver.network.testvm.java.multiline; + +import compiler.lib.ir_framework.driver.network.testvm.java.VMInfo; +import compiler.lib.ir_framework.shared.TestFrameworkException; + +import java.util.HashMap; +import java.util.Map; + +/** + * Dedicated strategy to parse the multi-line VM info message into a new {@link VMInfo} object. + */ +public class VMInfoStrategy implements MultiLineParsingStrategy { + private final Map keyValueMap; + + public VMInfoStrategy() { + this.keyValueMap = new HashMap<>(); + } + + @Override + public void parseLine(String line) { + String[] splitLine = line.split(":", 2); + if (splitLine.length != 2) { + throw new TestFrameworkException("Invalid VmInfo key:value encoding. Found: " + splitLine[0]); + } + String key = splitLine[0]; + String value = splitLine[1]; + keyValueMap.put(key, value); + } + + @Override + public VMInfo output() { + return new VMInfo(keyValueMap); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java index 79f5c7fe18e..a4797a29dc7 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java @@ -47,7 +47,7 @@ import java.util.function.Function; * termination of the Test VM. IR rule indices start at 1. */ public class ApplicableIRRulesPrinter { - public static final int NO_RULES = -1; + public static final String NO_RULES = ""; private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); private static final List> LONG_GETTERS = Arrays.asList( @@ -155,17 +155,15 @@ public class ApplicableIRRulesPrinter { i++; } } - if (irAnnos.length != 0) { - output.append(m.getName()); - if (validRules.isEmpty()) { - output.append("," + NO_RULES); - } else { - for (i = 0; i < validRules.size(); i++) { - output.append(",").append(validRules.get(i)); - } - } - output.append(System.lineSeparator()); + + if (irAnnos.length == 0 || validRules.isEmpty()) { + return; } + output.append(m.getName()); + for (i = 0; i < validRules.size(); i++) { + output.append(",").append(validRules.get(i)); + } + output.append(System.lineSeparator()); } private void printDisableReason(String method, String reason, String[] apply, int ruleIndex, int ruleMax) { @@ -520,9 +518,10 @@ public class ApplicableIRRulesPrinter { } public void emit() { + if (output.isEmpty()) { + output.append(NO_RULES).append(System.lineSeparator()); + } output.append(MessageTag.END_MARKER); TestVmSocket.sendMultiLine(MessageTag.APPLICABLE_IR_RULES, output.toString()); } } - - diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java index b6881fede75..fc8a92cf9a2 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java @@ -329,7 +329,7 @@ public class TestIRMatching { System.out.flush(); String output = baos.toString(); findIrIds(output, "testMatchAllIf50", 1, 22); - findIrIds(output, "testMatchNoneIf50", -1, -1); + assertNoIds(output, "testMatchNoneIf50"); runWithArguments(FlagComparisons.class, "-XX:TLABRefillWasteFraction=49"); System.out.flush(); @@ -431,12 +431,14 @@ public class TestIRMatching { private static void findIrIds(String output, String method, int... numbers) { StringBuilder builder = new StringBuilder(); - builder.append(method); + builder.append(method).append(": "); for (int i = 0; i < numbers.length; i+=2) { int start = numbers[i]; int endIncluded = numbers[i + 1]; for (int j = start; j <= endIncluded; j++) { - builder.append(","); + if (j != numbers[0]) { + builder.append(", "); + } builder.append(j); } } @@ -445,6 +447,13 @@ public class TestIRMatching { System.lineSeparator())); } } + + private static void assertNoIds(String output, String methodName) { + String applicableIRRules = output.split("Applicable IR Rules")[1]; + if (applicableIRRules.contains(methodName)) { + addException(new RuntimeException("Should not find ids for \"" + methodName + "\"" + System.lineSeparator())); + } + } } class AndOr1 { From 4df61ce3fc492c82e6c832236e21b2fed4908939 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 23 Mar 2026 08:03:54 +0000 Subject: [PATCH 068/160] 8380532: Parallel: Rename allocate to cas_allocate in PSYoungGen Reviewed-by: tschatzl, jsikstro --- src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp | 2 +- src/hotspot/share/gc/parallel/psYoungGen.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index f49419595e1..337b2ccdd10 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -272,7 +272,7 @@ HeapWord* ParallelScavengeHeap::mem_allocate(size_t size) { HeapWord* ParallelScavengeHeap::mem_allocate_cas_noexpand(size_t size, bool is_tlab) { // Try young-gen first. - HeapWord* result = young_gen()->allocate(size); + HeapWord* result = young_gen()->cas_allocate(size); if (result != nullptr) { return result; } diff --git a/src/hotspot/share/gc/parallel/psYoungGen.hpp b/src/hotspot/share/gc/parallel/psYoungGen.hpp index 10aa037da98..ed10806ac99 100644 --- a/src/hotspot/share/gc/parallel/psYoungGen.hpp +++ b/src/hotspot/share/gc/parallel/psYoungGen.hpp @@ -128,7 +128,7 @@ class PSYoungGen : public CHeapObj { size_t max_gen_size() const { return _max_gen_size; } // Allocation - HeapWord* allocate(size_t word_size) { + HeapWord* cas_allocate(size_t word_size) { HeapWord* result = eden_space()->cas_allocate(word_size); return result; } From caf37add7ac32e55d0aae161cafa6a379c58c56f Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Mon, 23 Mar 2026 08:11:22 +0000 Subject: [PATCH 069/160] 8374789: C2: refactor GraphKit code that create AddP nodes for raw memory to use helper method Reviewed-by: chagedorn, qamai --- .../share/gc/shared/c2/barrierSetC2.cpp | 8 +-- .../shenandoah/c2/shenandoahBarrierSetC2.cpp | 4 +- .../gc/shenandoah/c2/shenandoahSupport.cpp | 4 +- src/hotspot/share/opto/addnode.cpp | 14 ++-- src/hotspot/share/opto/addnode.hpp | 28 ++++++-- src/hotspot/share/opto/arraycopynode.cpp | 30 ++++---- src/hotspot/share/opto/callnode.cpp | 4 +- src/hotspot/share/opto/castnode.cpp | 4 +- src/hotspot/share/opto/cfgnode.cpp | 2 +- src/hotspot/share/opto/escape.cpp | 2 +- src/hotspot/share/opto/generateOptoStub.cpp | 17 +++-- src/hotspot/share/opto/graphKit.cpp | 18 ++--- src/hotspot/share/opto/graphKit.hpp | 9 ++- src/hotspot/share/opto/idealKit.hpp | 4 +- src/hotspot/share/opto/library_call.cpp | 68 +++++++++---------- src/hotspot/share/opto/loopTransform.cpp | 4 +- src/hotspot/share/opto/loopopts.cpp | 8 +-- src/hotspot/share/opto/macro.cpp | 32 ++++----- src/hotspot/share/opto/macro.hpp | 22 ++++-- src/hotspot/share/opto/macroArrayCopy.cpp | 6 +- src/hotspot/share/opto/memnode.cpp | 13 ++-- src/hotspot/share/opto/parse1.cpp | 22 +++--- src/hotspot/share/opto/parseHelper.cpp | 4 +- src/hotspot/share/opto/subtypenode.cpp | 8 +-- 24 files changed, 182 insertions(+), 153 deletions(-) diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp index ad7fc31dc09..a888ea81707 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -758,8 +758,8 @@ Node* BarrierSetC2::obj_allocate(PhaseMacroExpand* macro, Node* mem, Node* toobi assert(UseTLAB, "Only for TLAB enabled allocations"); Node* thread = macro->transform_later(new ThreadLocalNode()); - Node* tlab_top_adr = macro->basic_plus_adr(macro->top()/*not oop*/, thread, in_bytes(JavaThread::tlab_top_offset())); - Node* tlab_end_adr = macro->basic_plus_adr(macro->top()/*not oop*/, thread, in_bytes(JavaThread::tlab_end_offset())); + Node* tlab_top_adr = macro->off_heap_plus_addr(thread, in_bytes(JavaThread::tlab_top_offset())); + Node* tlab_end_adr = macro->off_heap_plus_addr(thread, in_bytes(JavaThread::tlab_end_offset())); // Load TLAB end. // @@ -778,7 +778,7 @@ Node* BarrierSetC2::obj_allocate(PhaseMacroExpand* macro, Node* mem, Node* toobi macro->transform_later(old_tlab_top); // Add to heap top to get a new TLAB top - Node* new_tlab_top = new AddPNode(macro->top(), old_tlab_top, size_in_bytes); + Node* new_tlab_top = AddPNode::make_off_heap(old_tlab_top, size_in_bytes); macro->transform_later(new_tlab_top); // Check against TLAB end diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index 1947c75314e..f721c3cd001 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Red Hat, Inc. All rights reserved. + * Copyright (c) 2018, 2026, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -918,7 +918,7 @@ void ShenandoahBarrierSetC2::clone_at_expansion(PhaseMacroExpand* phase, ArrayCo Node* thread = phase->transform_later(new ThreadLocalNode()); Node* offset = phase->igvn().MakeConX(in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - Node* gc_state_addr = phase->transform_later(new AddPNode(phase->C->top(), thread, offset)); + Node* gc_state_addr = phase->transform_later(AddPNode::make_off_heap(thread, offset)); uint gc_state_idx = Compile::AliasIdxRaw; const TypePtr* gc_state_adr_type = nullptr; // debug-mode-only argument diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 40fe0c00490..015276feb5c 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Red Hat, Inc. All rights reserved. + * Copyright (c) 2015, 2026, Red Hat, Inc. All rights reserved. * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -871,7 +871,7 @@ void ShenandoahBarrierC2Support::test_gc_state(Node*& ctrl, Node* raw_mem, Node* Node* thread = new ThreadLocalNode(); Node* gc_state_offset = igvn.MakeConX(in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - Node* gc_state_addr = new AddPNode(phase->C->top(), thread, gc_state_offset); + Node* gc_state_addr = AddPNode::make_off_heap(thread, gc_state_offset); Node* gc_state = new LoadBNode(old_ctrl, raw_mem, gc_state_addr, DEBUG_ONLY(phase->C->get_adr_type(Compile::AliasIdxRaw)) NOT_DEBUG(nullptr), TypeInt::BYTE, MemNode::unordered); diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index e04da430ef0..5520e4ae977 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -816,7 +816,7 @@ Node *AddPNode::Ideal(PhaseGVN *phase, bool can_reshape) { offset = phase->MakeConX(t2->get_con() + t12->get_con()); } else { // Else move the constant to the right. ((A+con)+B) into ((A+B)+con) - address = phase->transform(new AddPNode(in(Base),addp->in(Address),in(Offset))); + address = phase->transform(AddPNode::make_with_base(in(Base), addp->in(Address), in(Offset))); offset = addp->in(Offset); } set_req_X(Address, address, phase); @@ -838,11 +838,11 @@ Node *AddPNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Convert: (ptr + (offset+con)) into (ptr+offset)+con. // The idea is to merge array_base+scaled_index groups together, // and only have different constant offsets from the same base. - const Node *add = in(Offset); - if( add->Opcode() == Op_AddX && add->in(1) != add ) { - const Type *t22 = phase->type( add->in(2) ); - if( t22->singleton() && (t22 != Type::TOP) ) { // Right input is an add of a constant? - set_req(Address, phase->transform(new AddPNode(in(Base),in(Address),add->in(1)))); + const Node* add = in(Offset); + if (add->Opcode() == Op_AddX && add->in(1) != add) { + const Type* t22 = phase->type(add->in(2)); + if (t22->singleton() && (t22 != Type::TOP)) { // Right input is an add of a constant? + set_req(Address, phase->transform(AddPNode::make_with_base(in(Base), in(Address), add->in(1)))); set_req_X(Offset, add->in(2), phase); // puts add on igvn worklist if needed return this; // Made progress } diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 6128de00efb..793eff8dd5d 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -221,17 +221,19 @@ public: // So not really an AddNode. Lives here, because people associate it with // an add. class AddPNode : public Node { +private: + AddPNode(Node* base, Node* ptr, Node* off) : Node(nullptr, base, ptr, off) { + init_class_id(Class_AddP); + assert((ptr->bottom_type() == Type::TOP) || + ((base == Compile::current()->top()) == (ptr->bottom_type()->make_ptr()->isa_oopptr() == nullptr)), + "base input only needed for heap addresses"); + } + public: enum { Control, // When is it safe to do this add? Base, // Base oop, for GC purposes Address, // Actually address, derived from base Offset } ; // Offset added to address - AddPNode(Node *base, Node *ptr, Node *off) : Node(nullptr,base,ptr,off) { - init_class_id(Class_AddP); - assert((ptr->bottom_type() == Type::TOP) || - ((base == Compile::current()->top()) == (ptr->bottom_type()->make_ptr()->isa_oopptr() == nullptr)), - "base input only needed for heap addresses"); - } virtual int Opcode() const; virtual Node* Identity(PhaseGVN* phase); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); @@ -243,6 +245,18 @@ public: // second return value: intptr_t& offset); + static AddPNode* make_with_base(Node* base, Node* ptr, Node* offset) { + return new AddPNode(base, ptr, offset); + } + + static AddPNode* make_with_base(Node* base, Node* offset) { + return make_with_base(base, base, offset); + } + + static AddPNode* make_off_heap(Node* ptr, Node* offset) { + return make_with_base(Compile::current()->top(), ptr, offset); + } + // Collect the AddP offset values into the elements array, giving up // if there are more than length. int unpack_offsets(Node* elements[], int length) const; diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp index 3b006ae6b11..82d17440c56 100644 --- a/src/hotspot/share/opto/arraycopynode.cpp +++ b/src/hotspot/share/opto/arraycopynode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -226,8 +226,8 @@ Node* ArrayCopyNode::try_clone_instance(PhaseGVN *phase, bool can_reshape, int c ciField* field = ik->nonstatic_field_at(i); const TypePtr* adr_type = phase->C->alias_type(field)->adr_type(); Node* off = phase->MakeConX(field->offset_in_bytes()); - Node* next_src = phase->transform(new AddPNode(base_src,base_src,off)); - Node* next_dest = phase->transform(new AddPNode(base_dest,base_dest,off)); + Node* next_src = phase->transform(AddPNode::make_with_base(base_src, off)); + Node* next_dest = phase->transform(AddPNode::make_with_base(base_dest, off)); assert(phase->C->get_alias_index(adr_type) == phase->C->get_alias_index(phase->type(next_src)->isa_ptr()), "slice of address and input slice don't match"); assert(phase->C->get_alias_index(adr_type) == phase->C->get_alias_index(phase->type(next_dest)->isa_ptr()), @@ -332,11 +332,11 @@ bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, Node* dest_scale = phase->transform(new LShiftXNode(dest_offset, phase->intcon(shift))); - adr_src = phase->transform(new AddPNode(base_src, base_src, src_scale)); - adr_dest = phase->transform(new AddPNode(base_dest, base_dest, dest_scale)); + adr_src = phase->transform(AddPNode::make_with_base(base_src, src_scale)); + adr_dest = phase->transform(AddPNode::make_with_base(base_dest, dest_scale)); - adr_src = phase->transform(new AddPNode(base_src, adr_src, phase->MakeConX(header))); - adr_dest = phase->transform(new AddPNode(base_dest, adr_dest, phase->MakeConX(header))); + adr_src = phase->transform(AddPNode::make_with_base(base_src, adr_src, phase->MakeConX(header))); + adr_dest = phase->transform(AddPNode::make_with_base(base_dest, adr_dest, phase->MakeConX(header))); copy_type = dest_elem; } else { @@ -355,8 +355,8 @@ bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, return false; } - adr_src = phase->transform(new AddPNode(base_src, base_src, src_offset)); - adr_dest = phase->transform(new AddPNode(base_dest, base_dest, dest_offset)); + adr_src = phase->transform(AddPNode::make_with_base(base_src, src_offset)); + adr_dest = phase->transform(AddPNode::make_with_base(base_dest, dest_offset)); // The address is offsetted to an aligned address where a raw copy would start. // If the clone copy is decomposed into load-stores - the address is adjusted to @@ -366,8 +366,8 @@ bool ArrayCopyNode::prepare_array_copy(PhaseGVN *phase, bool can_reshape, int diff = arrayOopDesc::base_offset_in_bytes(elem) - offset; assert(diff >= 0, "clone should not start after 1st array element"); if (diff > 0) { - adr_src = phase->transform(new AddPNode(base_src, adr_src, phase->MakeConX(diff))); - adr_dest = phase->transform(new AddPNode(base_dest, adr_dest, phase->MakeConX(diff))); + adr_src = phase->transform(AddPNode::make_with_base(base_src, adr_src, phase->MakeConX(diff))); + adr_dest = phase->transform(AddPNode::make_with_base(base_dest, adr_dest, phase->MakeConX(diff))); } copy_type = elem; value_type = ary_src->elem(); @@ -425,12 +425,12 @@ Node* ArrayCopyNode::array_copy_forward(PhaseGVN *phase, store(bs, phase, forward_ctl, mm, adr_dest, atp_dest, v, value_type, copy_type); for (int i = 1; i < count; i++) { Node* off = phase->MakeConX(type2aelembytes(copy_type) * i); - Node* next_src = phase->transform(new AddPNode(base_src,adr_src,off)); + Node* next_src = phase->transform(AddPNode::make_with_base(base_src,adr_src,off)); // We may have narrowed the type of next_src right before calling this method but because this runs with // PhaseIterGVN::_delay_transform true, explicitly update the type of the AddP so it's consistent with its // base and load() picks the right memory slice. phase->set_type(next_src, next_src->Value(phase)); - Node* next_dest = phase->transform(new AddPNode(base_dest,adr_dest,off)); + Node* next_dest = phase->transform(AddPNode::make_with_base(base_dest,adr_dest,off)); // Same as above phase->set_type(next_dest, next_dest->Value(phase)); v = load(bs, phase, forward_ctl, mm, next_src, atp_src, value_type, copy_type); @@ -469,12 +469,12 @@ Node* ArrayCopyNode::array_copy_backward(PhaseGVN *phase, if (count > 0) { for (int i = count-1; i >= 1; i--) { Node* off = phase->MakeConX(type2aelembytes(copy_type) * i); - Node* next_src = phase->transform(new AddPNode(base_src,adr_src,off)); + Node* next_src = phase->transform(AddPNode::make_with_base(base_src,adr_src,off)); // We may have narrowed the type of next_src right before calling this method but because this runs with // PhaseIterGVN::_delay_transform true, explicitly update the type of the AddP so it's consistent with its // base and store() picks the right memory slice. phase->set_type(next_src, next_src->Value(phase)); - Node* next_dest = phase->transform(new AddPNode(base_dest,adr_dest,off)); + Node* next_dest = phase->transform(AddPNode::make_with_base(base_dest,adr_dest,off)); phase->set_type(next_dest, next_dest->Value(phase)); Node* v = load(bs, phase, backward_ctl, mm, next_src, atp_src, value_type, copy_type); store(bs, phase, backward_ctl, mm, next_dest, atp_dest, v, value_type, copy_type); diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index d4c832359d6..e01feb874ef 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1745,7 +1745,7 @@ Node *AllocateNode::make_ideal_mark(PhaseGVN* phase, Node* control, Node* mem) { Node* mark_node = nullptr; if (UseCompactObjectHeaders) { Node* klass_node = in(AllocateNode::KlassNode); - Node* proto_adr = phase->transform(new AddPNode(phase->C->top(), klass_node, phase->MakeConX(in_bytes(Klass::prototype_header_offset())))); + Node* proto_adr = phase->transform(AddPNode::make_off_heap(klass_node, phase->MakeConX(in_bytes(Klass::prototype_header_offset())))); mark_node = LoadNode::make(*phase, control, mem, proto_adr, phase->type(proto_adr)->is_ptr(), TypeX_X, TypeX_X->basic_type(), MemNode::unordered); } else { // For now only enable fast locking for non-array types diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index 4e3750b5ee5..54269a56c19 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -470,9 +470,7 @@ static inline Node* addP_of_X2P(PhaseGVN *phase, if (negate) { dispX = phase->transform(new SubXNode(phase->MakeConX(0), dispX)); } - return new AddPNode(phase->C->top(), - phase->transform(new CastX2PNode(base)), - dispX); + return AddPNode::make_off_heap(phase->transform(new CastX2PNode(base)), dispX); } Node *CastX2PNode::Ideal(PhaseGVN *phase, bool can_reshape) { diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 04061a60bad..a251a253ed1 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -2480,7 +2480,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } phase->is_IterGVN()->register_new_node_with_optimizer(offset); } - return new AddPNode(base, address, offset); + return AddPNode::make_with_base(base, address, offset); } } } diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 5befdd924ff..7041eb0b810 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -780,7 +780,7 @@ Node* ConnectionGraph::split_castpp_load_through_phi(Node* curr_addp, Node* curr base = base->find_out_with(Op_CastPP); } - Node* addr = _igvn->transform(new AddPNode(base, base, curr_addp->in(AddPNode::Offset))); + Node* addr = _igvn->transform(AddPNode::make_with_base(base, curr_addp->in(AddPNode::Offset))); Node* mem = (memory->is_Phi() && (memory->in(0) == region)) ? memory->in(i) : memory; Node* load = curr_load->clone(); load->set_req(0, nullptr); diff --git a/src/hotspot/share/opto/generateOptoStub.cpp b/src/hotspot/share/opto/generateOptoStub.cpp index 77633857cdf..d719c301dc6 100644 --- a/src/hotspot/share/opto/generateOptoStub.cpp +++ b/src/hotspot/share/opto/generateOptoStub.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -88,18 +88,17 @@ void GraphKit::gen_stub(address C_function, const int NoAlias = Compile::AliasIdxBot; - Node* adr_last_Java_pc = basic_plus_adr(top(), - thread, - in_bytes(JavaThread::frame_anchor_offset()) + - in_bytes(JavaFrameAnchor::last_Java_pc_offset())); + Node* adr_last_Java_pc = off_heap_plus_addr(thread, + in_bytes(JavaThread::frame_anchor_offset()) + + in_bytes(JavaFrameAnchor::last_Java_pc_offset())); // Drop in the last_Java_sp. last_Java_fp is not touched. // Always do this after the other "last_Java_frame" fields are set since // as soon as last_Java_sp != nullptr the has_last_Java_frame is true and // users will look at the other fields. // - Node *adr_sp = basic_plus_adr(top(), thread, in_bytes(JavaThread::last_Java_sp_offset())); - Node *last_sp = frameptr(); + Node* adr_sp = off_heap_plus_addr(thread, in_bytes(JavaThread::last_Java_sp_offset())); + Node* last_sp = frameptr(); store_to_memory(control(), adr_sp, last_sp, T_ADDRESS, MemNode::unordered); // Set _thread_in_native @@ -228,7 +227,7 @@ void GraphKit::gen_stub(address C_function, Node* target = map()->in(TypeFunc::Parms); // Runtime call returning oop in TLS? Fetch it out if( pass_tls ) { - Node* adr = basic_plus_adr(top(), thread, in_bytes(JavaThread::vm_result_oop_offset())); + Node* adr = off_heap_plus_addr(thread, in_bytes(JavaThread::vm_result_oop_offset())); Node* vm_result = make_load(nullptr, adr, TypeOopPtr::BOTTOM, T_OBJECT, MemNode::unordered); map()->set_req(TypeFunc::Parms, vm_result); // vm_result passed as result // clear thread-local-storage(tls) @@ -237,7 +236,7 @@ void GraphKit::gen_stub(address C_function, //----------------------------- // check exception - Node* adr = basic_plus_adr(top(), thread, in_bytes(Thread::pending_exception_offset())); + Node* adr = off_heap_plus_addr(thread, in_bytes(Thread::pending_exception_offset())); Node* pending = make_load(nullptr, adr, TypeOopPtr::BOTTOM, T_OBJECT, MemNode::unordered); Node* exit_memory = reset_memory(); diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 31da6fd0e2d..e297292770e 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -508,7 +508,7 @@ void GraphKit::uncommon_trap_if_should_post_on_exceptions(Deoptimization::DeoptR // first must access the should_post_on_exceptions_flag in this thread's JavaThread Node* jthread = _gvn.transform(new ThreadLocalNode()); - Node* adr = basic_plus_adr(top(), jthread, in_bytes(JavaThread::should_post_on_exceptions_flag_offset())); + Node* adr = off_heap_plus_addr(jthread, in_bytes(JavaThread::should_post_on_exceptions_flag_offset())); Node* should_post_flag = make_load(control(), adr, TypeInt::INT, T_INT, MemNode::unordered); // Test the should_post_on_exceptions_flag vs. 0 @@ -1234,7 +1234,7 @@ Node* GraphKit::basic_plus_adr(Node* base, Node* ptr, Node* offset) { "Unexpected zero offset - should have matched MakeConX(0)"); } #endif - return _gvn.transform( new AddPNode(base, ptr, offset) ); + return _gvn.transform(AddPNode::make_with_base(base, ptr, offset)); } Node* GraphKit::ConvI2L(Node* offset) { @@ -2817,9 +2817,9 @@ Node* Phase::gen_subtype_check(Node* subklass, Node* superklass, Node** ctrl, No // will always succeed. We could leave a dependency behind to ensure this. // First load the super-klass's check-offset - Node *p1 = gvn.transform(new AddPNode(C->top(), superklass, gvn.MakeConX(in_bytes(Klass::super_check_offset_offset())))); + Node* p1 = gvn.transform(AddPNode::make_off_heap(superklass, gvn.MakeConX(in_bytes(Klass::super_check_offset_offset())))); Node* m = C->immutable_memory(); - Node *chk_off = gvn.transform(new LoadINode(nullptr, m, p1, gvn.type(p1)->is_ptr(), TypeInt::INT, MemNode::unordered)); + Node* chk_off = gvn.transform(new LoadINode(nullptr, m, p1, gvn.type(p1)->is_ptr(), TypeInt::INT, MemNode::unordered)); int cacheoff_con = in_bytes(Klass::secondary_super_cache_offset()); const TypeInt* chk_off_t = chk_off->Value(&gvn)->isa_int(); int chk_off_con = (chk_off_t != nullptr && chk_off_t->is_con()) ? chk_off_t->get_con() : cacheoff_con; @@ -2835,11 +2835,11 @@ Node* Phase::gen_subtype_check(Node* subklass, Node* superklass, Node** ctrl, No #ifdef _LP64 chk_off_X = gvn.transform(new ConvI2LNode(chk_off_X)); #endif - Node* p2 = gvn.transform(new AddPNode(C->top(), subklass, chk_off_X)); + Node* p2 = gvn.transform(AddPNode::make_off_heap(subklass, chk_off_X)); // For some types like interfaces the following loadKlass is from a 1-word // cache which is mutable so can't use immutable memory. Other // types load from the super-class display table which is immutable. - Node *kmem = C->immutable_memory(); + Node* kmem = C->immutable_memory(); // secondary_super_cache is not immutable but can be treated as such because: // - no ideal node writes to it in a way that could cause an // incorrect/missed optimization of the following Load. @@ -3076,7 +3076,7 @@ bool GraphKit::seems_never_null(Node* obj, ciProfileData* data, bool& speculatin void GraphKit::guard_klass_being_initialized(Node* klass) { int init_state_off = in_bytes(InstanceKlass::init_state_offset()); - Node* adr = basic_plus_adr(top(), klass, init_state_off); + Node* adr = off_heap_plus_addr(klass, init_state_off); Node* init_state = LoadNode::make(_gvn, nullptr, immutable_memory(), adr, adr->bottom_type()->is_ptr(), TypeInt::BYTE, T_BYTE, MemNode::acquire); @@ -3094,7 +3094,7 @@ void GraphKit::guard_klass_being_initialized(Node* klass) { void GraphKit::guard_init_thread(Node* klass) { int init_thread_off = in_bytes(InstanceKlass::init_thread_offset()); - Node* adr = basic_plus_adr(top(), klass, init_thread_off); + Node* adr = off_heap_plus_addr(klass, init_thread_off); Node* init_thread = LoadNode::make(_gvn, nullptr, immutable_memory(), adr, adr->bottom_type()->is_ptr(), TypePtr::NOTNULL, @@ -3672,7 +3672,7 @@ Node* GraphKit::get_layout_helper(Node* klass_node, jint& constant_value) { } } constant_value = Klass::_lh_neutral_value; // put in a known value - Node* lhp = basic_plus_adr(top(), klass_node, in_bytes(Klass::layout_helper_offset())); + Node* lhp = off_heap_plus_addr(klass_node, in_bytes(Klass::layout_helper_offset())); return make_load(nullptr, lhp, TypeInt::INT, T_INT, MemNode::unordered); } diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index dc691173f71..273009ca7ce 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -320,6 +320,13 @@ class GraphKit : public Phase { } Node* basic_plus_adr(Node* base, Node* ptr, Node* offset); + Node* off_heap_plus_addr(Node* ptr, intptr_t offset) { + return basic_plus_adr(top(), ptr, MakeConX(offset)); + } + + Node* off_heap_plus_addr(Node* ptr, Node* offset) { + return basic_plus_adr(top(), ptr, offset); + } // Some convenient shortcuts for common nodes Node* IfTrue(IfNode* iff) { return _gvn.transform(new IfTrueNode(iff)); } @@ -346,7 +353,7 @@ class GraphKit : public Phase { Node* CmpP(Node* l, Node* r) { return _gvn.transform(new CmpPNode(l, r)); } Node* Bool(Node* cmp, BoolTest::mask relop) { return _gvn.transform(new BoolNode(cmp, relop)); } - Node* AddP(Node* b, Node* a, Node* o) { return _gvn.transform(new AddPNode(b, a, o)); } + Node* AddP(Node* b, Node* a, Node* o) { return _gvn.transform(AddPNode::make_with_base(b, a, o)); } // Convert between int and long, and size_t. // (See macros ConvI2X, etc., in type.hpp for ConvI2X, etc.) diff --git a/src/hotspot/share/opto/idealKit.hpp b/src/hotspot/share/opto/idealKit.hpp index 518c3b92136..a2ac9e204ba 100644 --- a/src/hotspot/share/opto/idealKit.hpp +++ b/src/hotspot/share/opto/idealKit.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -200,7 +200,7 @@ class IdealKit: public StackObj { // Raw address should be transformed regardless 'delay_transform' flag // to produce canonical form CastX2P(offset). - Node* AddP(Node *base, Node *ptr, Node *off) { return _gvn.transform(new AddPNode(base, ptr, off)); } + Node* AddP(Node* base, Node* ptr, Node* off) { return _gvn.transform(AddPNode::make_with_base(base, ptr, off)); } Node* CmpP(Node* l, Node* r) { return transform(new CmpPNode(l, r)); } #ifdef _LP64 diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 743612a1051..eed99ceb8bc 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -989,7 +989,7 @@ Node* LibraryCallKit::current_thread_helper(Node*& tls_output, ByteSize handle_o = TypeOopPtr::make_from_klass(thread_klass)->cast_to_ptr_type(TypePtr::NotNull); Node* thread = _gvn.transform(new ThreadLocalNode()); - Node* p = basic_plus_adr(top()/*!oop*/, thread, in_bytes(handle_offset)); + Node* p = off_heap_plus_addr(thread, in_bytes(handle_offset)); tls_output = thread; Node* thread_obj_handle @@ -2161,7 +2161,7 @@ Node* LibraryCallKit::make_unsafe_address(Node*& base, Node* offset, BasicType t Node* uncasted_base = base; int kind = classify_unsafe_addr(uncasted_base, offset, type); if (kind == Type::RawPtr) { - return basic_plus_adr(top(), uncasted_base, offset); + return off_heap_plus_addr(uncasted_base, offset); } else if (kind == Type::AnyPtr) { assert(base == uncasted_base, "unexpected base change"); if (can_cast) { @@ -2181,13 +2181,13 @@ Node* LibraryCallKit::make_unsafe_address(Node*& base, Node* offset, BasicType t base = null_assert(base); Node* raw_base = _gvn.transform(new CastX2PNode(offset)); offset = MakeConX(0); - return basic_plus_adr(top(), raw_base, offset); + return off_heap_plus_addr(raw_base, offset); } } // We don't know if it's an on heap or off heap access. Fall back // to raw memory access. Node* raw = _gvn.transform(new CheckCastPPNode(control(), base, TypeRawPtr::BOTTOM)); - return basic_plus_adr(top(), raw, offset); + return off_heap_plus_addr(raw, offset); } else { assert(base == uncasted_base, "unexpected base change"); // We know it's an on heap access so base can't be null @@ -2962,7 +2962,7 @@ bool LibraryCallKit::inline_unsafe_allocate() { // Note: The argument might still be an illegal value like // Serializable.class or Object[].class. The runtime will handle it. // But we must make an explicit check for initialization. - Node* insp = basic_plus_adr(top(), kls, in_bytes(InstanceKlass::init_state_offset())); + Node* insp = off_heap_plus_addr(kls, in_bytes(InstanceKlass::init_state_offset())); // Use T_BOOLEAN for InstanceKlass::_init_state so the compiler // can generate code to load it as unsigned byte. Node* inst = make_load(nullptr, insp, TypeInt::UBYTE, T_BOOLEAN, MemNode::acquire); @@ -3010,7 +3010,7 @@ bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, co IdealKit ideal(this); Node* thread = ideal.thread(); - Node* jt_addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::is_in_vthread_transition_offset())); + Node* jt_addr = off_heap_plus_addr(thread, in_bytes(JavaThread::is_in_vthread_transition_offset())); Node* vt_addr = basic_plus_adr(vt_oop, java_lang_Thread::is_in_vthread_transition_offset()); access_store_at(nullptr, jt_addr, _gvn.type(jt_addr)->is_ptr(), ideal.ConI(1), TypeInt::BOOL, T_BOOLEAN, IN_NATIVE | MO_UNORDERED); access_store_at(nullptr, vt_addr, _gvn.type(vt_addr)->is_ptr(), ideal.ConI(1), TypeInt::BOOL, T_BOOLEAN, IN_NATIVE | MO_UNORDERED); @@ -3052,7 +3052,7 @@ bool LibraryCallKit::inline_native_vthread_end_transition(address funcAddr, cons ideal.sync_kit(this); } ideal.else_(); { Node* thread = ideal.thread(); - Node* jt_addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::is_in_vthread_transition_offset())); + Node* jt_addr = off_heap_plus_addr(thread, in_bytes(JavaThread::is_in_vthread_transition_offset())); Node* vt_addr = basic_plus_adr(vt_oop, java_lang_Thread::is_in_vthread_transition_offset()); sync_kit(ideal); @@ -3078,7 +3078,7 @@ bool LibraryCallKit::inline_native_notify_jvmti_sync() { // unconditionally update the is_disable_suspend bit in current JavaThread Node* thread = ideal.thread(); Node* arg = argument(0); // argument for notification - Node* addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::is_disable_suspend_offset())); + Node* addr = off_heap_plus_addr(thread, in_bytes(JavaThread::is_disable_suspend_offset())); const TypePtr *addr_type = _gvn.type(addr)->isa_ptr(); sync_kit(ideal); @@ -3185,12 +3185,12 @@ bool LibraryCallKit::inline_native_jvm_commit() { // TLS. Node* tls_ptr = _gvn.transform(new ThreadLocalNode()); // Jfr java buffer. - Node* java_buffer_offset = _gvn.transform(new AddPNode(top(), tls_ptr, MakeConX(in_bytes(JAVA_BUFFER_OFFSET_JFR)))); + Node* java_buffer_offset = _gvn.transform(AddPNode::make_off_heap(tls_ptr, MakeConX(in_bytes(JAVA_BUFFER_OFFSET_JFR)))); Node* java_buffer = _gvn.transform(new LoadPNode(control(), input_memory_state, java_buffer_offset, TypePtr::BOTTOM, TypeRawPtr::NOTNULL, MemNode::unordered)); - Node* java_buffer_pos_offset = _gvn.transform(new AddPNode(top(), java_buffer, MakeConX(in_bytes(JFR_BUFFER_POS_OFFSET)))); + Node* java_buffer_pos_offset = _gvn.transform(AddPNode::make_off_heap(java_buffer, MakeConX(in_bytes(JFR_BUFFER_POS_OFFSET)))); // Load the current value of the notified field in the JfrThreadLocal. - Node* notified_offset = basic_plus_adr(top(), tls_ptr, in_bytes(NOTIFY_OFFSET_JFR)); + Node* notified_offset = off_heap_plus_addr(tls_ptr, in_bytes(NOTIFY_OFFSET_JFR)); Node* notified = make_load(control(), notified_offset, TypeInt::BOOL, T_BOOLEAN, MemNode::unordered); // Test for notification. @@ -3228,7 +3228,7 @@ bool LibraryCallKit::inline_native_jvm_commit() { set_all_memory(commit_memory); // Now load the flags from off the java buffer and decide if the buffer is a lease. If so, it needs to be returned post-commit. - Node* java_buffer_flags_offset = _gvn.transform(new AddPNode(top(), java_buffer, MakeConX(in_bytes(JFR_BUFFER_FLAGS_OFFSET)))); + Node* java_buffer_flags_offset = _gvn.transform(AddPNode::make_off_heap(java_buffer, MakeConX(in_bytes(JFR_BUFFER_FLAGS_OFFSET)))); Node* flags = make_load(control(), java_buffer_flags_offset, TypeInt::UBYTE, T_BYTE, MemNode::unordered); Node* lease_constant = _gvn.intcon(4); @@ -3360,7 +3360,7 @@ bool LibraryCallKit::inline_native_getEventWriter() { Node* tls_ptr = _gvn.transform(new ThreadLocalNode()); // Load the address of java event writer jobject handle from the jfr_thread_local structure. - Node* jobj_ptr = basic_plus_adr(top(), tls_ptr, in_bytes(THREAD_LOCAL_WRITER_OFFSET_JFR)); + Node* jobj_ptr = off_heap_plus_addr(tls_ptr, in_bytes(THREAD_LOCAL_WRITER_OFFSET_JFR)); // Load the eventwriter jobject handle. Node* jobj = make_load(control(), jobj_ptr, TypeRawPtr::BOTTOM, T_ADDRESS, MemNode::unordered); @@ -3537,7 +3537,7 @@ bool LibraryCallKit::inline_native_getEventWriter() { ciInstanceKlass* const instklass_EventWriter = klass_EventWriter->as_instance_klass(); const TypeKlassPtr* const aklass = TypeKlassPtr::make(instklass_EventWriter); const TypeOopPtr* const xtype = aklass->as_instance_type(); - Node* jobj_untagged = _gvn.transform(new AddPNode(top(), jobj, _gvn.MakeConX(-JNIHandles::TypeTag::global))); + Node* jobj_untagged = _gvn.transform(AddPNode::make_off_heap(jobj, _gvn.MakeConX(-JNIHandles::TypeTag::global))); Node* event_writer = access_load(jobj_untagged, xtype, T_OBJECT, IN_NATIVE | C2_CONTROL_DEPENDENT_LOAD); // Load the current thread id from the event writer object. @@ -3652,7 +3652,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { IfNode* iff_thread_not_equal_carrierThread = create_and_map_if(control(), test_thread_not_equal_carrierThread, PROB_FAIR, COUNT_UNKNOWN); - Node* vthread_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_OFFSET_JFR)); + Node* vthread_offset = off_heap_plus_addr(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_OFFSET_JFR)); // False branch, is carrierThread. Node* thread_equal_carrierThread = _gvn.transform(new IfFalseNode(iff_thread_not_equal_carrierThread)); @@ -3677,7 +3677,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { Node* tid = load_field_from_object(thread, "tid", "J"); // Store the vthread tid to the jfr thread local. - Node* thread_id_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_ID_OFFSET_JFR)); + Node* thread_id_offset = off_heap_plus_addr(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_ID_OFFSET_JFR)); Node* tid_memory = store_to_memory(control(), thread_id_offset, tid, T_LONG, MemNode::unordered, true); // Branch is_excluded to conditionalize updating the epoch . @@ -3699,7 +3699,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { Node* epoch = _gvn.transform(new AndINode(epoch_raw, epoch_mask)); // Store the vthread epoch to the jfr thread local. - Node* vthread_epoch_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EPOCH_OFFSET_JFR)); + Node* vthread_epoch_offset = off_heap_plus_addr(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EPOCH_OFFSET_JFR)); Node* included_memory = store_to_memory(control(), vthread_epoch_offset, epoch, T_CHAR, MemNode::unordered, true); RegionNode* excluded_rgn = new RegionNode(PATH_LIMIT); @@ -3722,7 +3722,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { set_all_memory(excluded_mem); // Store the vthread exclusion state to the jfr thread local. - Node* thread_local_excluded_offset = basic_plus_adr(top(), jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EXCLUDED_OFFSET_JFR)); + Node* thread_local_excluded_offset = off_heap_plus_addr(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EXCLUDED_OFFSET_JFR)); store_to_memory(control(), thread_local_excluded_offset, _gvn.transform(exclusion), T_BOOLEAN, MemNode::unordered, true); // Store release @@ -3768,7 +3768,7 @@ bool LibraryCallKit::inline_native_setCurrentThread() { "method changes current Thread but is not annotated ChangesCurrentThread"); Node* arr = argument(1); Node* thread = _gvn.transform(new ThreadLocalNode()); - Node* p = basic_plus_adr(top()/*!oop*/, thread, in_bytes(JavaThread::vthread_offset())); + Node* p = off_heap_plus_addr(thread, in_bytes(JavaThread::vthread_offset())); Node* thread_obj_handle = make_load(nullptr, p, p->bottom_type()->is_ptr(), T_OBJECT, MemNode::unordered); const TypePtr *adr_type = _gvn.type(thread_obj_handle)->isa_ptr(); @@ -3776,7 +3776,7 @@ bool LibraryCallKit::inline_native_setCurrentThread() { // Change the _monitor_owner_id of the JavaThread Node* tid = load_field_from_object(arr, "tid", "J"); - Node* monitor_owner_id_offset = basic_plus_adr(top(), thread, in_bytes(JavaThread::monitor_owner_id_offset())); + Node* monitor_owner_id_offset = off_heap_plus_addr(thread, in_bytes(JavaThread::monitor_owner_id_offset())); store_to_memory(control(), monitor_owner_id_offset, tid, T_LONG, MemNode::unordered, true); JFR_ONLY(extend_setCurrentThread(thread, arr);) @@ -3797,7 +3797,7 @@ const Type* LibraryCallKit::scopedValueCache_type() { Node* LibraryCallKit::scopedValueCache_helper() { Node* thread = _gvn.transform(new ThreadLocalNode()); - Node* p = basic_plus_adr(top()/*!oop*/, thread, in_bytes(JavaThread::scopedValueCache_offset())); + Node* p = off_heap_plus_addr(thread, in_bytes(JavaThread::scopedValueCache_offset())); // We cannot use immutable_memory() because we might flip onto a // different carrier thread, at which point we'll need to use that // carrier thread's cache. @@ -3839,7 +3839,7 @@ bool LibraryCallKit::inline_native_Continuation_pinning(bool unpin) { // TLS Node* tls_ptr = _gvn.transform(new ThreadLocalNode()); - Node* last_continuation_offset = basic_plus_adr(top(), tls_ptr, in_bytes(JavaThread::cont_entry_offset())); + Node* last_continuation_offset = off_heap_plus_addr(tls_ptr, in_bytes(JavaThread::cont_entry_offset())); Node* last_continuation = make_load(control(), last_continuation_offset, last_continuation_offset->get_ptr_type(), T_ADDRESS, MemNode::unordered); // Null check the last continuation object. @@ -3856,7 +3856,7 @@ bool LibraryCallKit::inline_native_Continuation_pinning(bool unpin) { set_control(continuation_is_not_null); // Load the pin count from the last continuation. - Node* pin_count_offset = basic_plus_adr(top(), last_continuation, in_bytes(ContinuationEntry::pin_count_offset())); + Node* pin_count_offset = off_heap_plus_addr(last_continuation, in_bytes(ContinuationEntry::pin_count_offset())); Node* pin_count = make_load(control(), pin_count_offset, TypeInt::INT, T_INT, MemNode::unordered); // The loaded pin count is compared against a context specific rhs for over/underflow detection. @@ -3918,7 +3918,7 @@ bool LibraryCallKit::inline_native_Continuation_pinning(bool unpin) { //---------------------------load_mirror_from_klass---------------------------- // Given a klass oop, load its java mirror (a java.lang.Class oop). Node* LibraryCallKit::load_mirror_from_klass(Node* klass) { - Node* p = basic_plus_adr(top(), klass, in_bytes(Klass::java_mirror_offset())); + Node* p = off_heap_plus_addr(klass, in_bytes(Klass::java_mirror_offset())); Node* load = make_load(nullptr, p, TypeRawPtr::NOTNULL, T_ADDRESS, MemNode::unordered); // mirror = ((OopHandle)mirror)->resolve(); return access_load(load, TypeInstPtr::MIRROR, T_OBJECT, IN_NATIVE); @@ -3958,7 +3958,7 @@ Node* LibraryCallKit::generate_klass_flags_guard(Node* kls, int modifier_mask, i ByteSize offset, const Type* type, BasicType bt) { // Branch around if the given klass has the given modifier bit set. // Like generate_guard, adds a new path onto the region. - Node* modp = basic_plus_adr(top(), kls, in_bytes(offset)); + Node* modp = off_heap_plus_addr(kls, in_bytes(offset)); Node* mods = make_load(nullptr, modp, type, bt, MemNode::unordered); Node* mask = intcon(modifier_mask); Node* bits = intcon(modifier_bits); @@ -4092,7 +4092,7 @@ bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) { phi->add_req(null()); } // If we fall through, it's a plain class. Get its _super. - p = basic_plus_adr(top(), kls, in_bytes(Klass::super_offset())); + p = off_heap_plus_addr(kls, in_bytes(Klass::super_offset())); kls = _gvn.transform(LoadKlassNode::make(_gvn, immutable_memory(), p, TypeRawPtr::BOTTOM, TypeInstKlassPtr::OBJECT_OR_NULL)); null_ctl = top(); kls = null_check_oop(kls, &null_ctl); @@ -4627,10 +4627,10 @@ Node* LibraryCallKit::generate_virtual_guard(Node* obj_klass, assert(vtable_index >= 0 || vtable_index == Method::nonvirtual_vtable_index, "bad index %d", vtable_index); // Get the Method* out of the appropriate vtable entry. - int entry_offset = in_bytes(Klass::vtable_start_offset()) + + int entry_offset = in_bytes(Klass::vtable_start_offset()) + vtable_index*vtableEntry::size_in_bytes() + in_bytes(vtableEntry::method_offset()); - Node* entry_addr = basic_plus_adr(top(), obj_klass, entry_offset); + Node* entry_addr = off_heap_plus_addr(obj_klass, entry_offset); Node* target_call = make_load(nullptr, entry_addr, TypePtr::NOTNULL, T_ADDRESS, MemNode::unordered); // Compare the target method with the expected method (e.g., Object.hashCode). @@ -5105,7 +5105,7 @@ bool LibraryCallKit::inline_unsafe_copyMemory() { Node* dst_addr = make_unsafe_address(dst_base, dst_off); Node* thread = _gvn.transform(new ThreadLocalNode()); - Node* doing_unsafe_access_addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::doing_unsafe_access_offset())); + Node* doing_unsafe_access_addr = off_heap_plus_addr(thread, in_bytes(JavaThread::doing_unsafe_access_offset())); BasicType doing_unsafe_access_bt = T_BYTE; assert((sizeof(bool) * CHAR_BIT) == 8, "not implemented"); @@ -5160,7 +5160,7 @@ bool LibraryCallKit::inline_unsafe_setMemory() { Node* dst_addr = make_unsafe_address(dst_base, dst_off); Node* thread = _gvn.transform(new ThreadLocalNode()); - Node* doing_unsafe_access_addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::doing_unsafe_access_offset())); + Node* doing_unsafe_access_addr = off_heap_plus_addr(thread, in_bytes(JavaThread::doing_unsafe_access_offset())); BasicType doing_unsafe_access_bt = T_BYTE; assert((sizeof(bool) * CHAR_BIT) == 8, "not implemented"); @@ -6696,7 +6696,7 @@ bool LibraryCallKit::inline_updateCRC32() { Node* base = makecon(TypeRawPtr::make(StubRoutines::crc_table_addr())); Node* offset = _gvn.transform(new LShiftINode(result, intcon(0x2))); - Node* adr = basic_plus_adr(top(), base, ConvI2X(offset)); + Node* adr = off_heap_plus_addr(base, ConvI2X(offset)); result = make_load(control(), adr, TypeInt::INT, T_INT, MemNode::unordered); crc = _gvn.transform(new URShiftINode(crc, intcon(8))); @@ -6768,7 +6768,7 @@ bool LibraryCallKit::inline_updateByteBufferCRC32() { offset = ConvI2X(offset); // 'src_start' points to src array + scaled offset - Node* src_start = basic_plus_adr(top(), base, offset); + Node* src_start = off_heap_plus_addr(base, offset); // Call the stub. address stubAddr = StubRoutines::updateBytesCRC32(); @@ -6865,7 +6865,7 @@ bool LibraryCallKit::inline_updateDirectByteBufferCRC32C() { offset = ConvI2X(offset); // 'src_start' points to src array + scaled offset - Node* src_start = basic_plus_adr(top(), base, offset); + Node* src_start = off_heap_plus_addr(base, offset); // static final int[] byteTable in class CRC32C Node* table = get_table_from_crc32c_class(callee()->holder()); @@ -6949,7 +6949,7 @@ bool LibraryCallKit::inline_updateByteBufferAdler32() { offset = ConvI2X(offset); // 'src_start' points to src array + scaled offset - Node* src_start = basic_plus_adr(top(), base, offset); + Node* src_start = off_heap_plus_addr(base, offset); // Call the stub. address stubAddr = StubRoutines::updateBytesAdler32(); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 1801841bad3..80b17efb998 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -3991,7 +3991,7 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) { index = new LShiftXNode(index, shift->in(2)); _igvn.register_new_node_with_optimizer(index); } - Node* from = new AddPNode(base, base, index); + Node* from = AddPNode::make_with_base(base, index); _igvn.register_new_node_with_optimizer(from); // For normal array fills, C2 uses two AddP nodes for array element // addressing. But for array fills with Unsafe call, there's only one @@ -3999,7 +3999,7 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) { assert(offset != nullptr || C->has_unsafe_access(), "Only array fills with unsafe have no extra offset"); if (offset != nullptr) { - from = new AddPNode(base, from, offset); + from = AddPNode::make_with_base(base, from, offset); _igvn.register_new_node_with_optimizer(from); } // Compute the number of elements to copy diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 2a0178530e8..4e447edee8d 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -620,10 +620,10 @@ Node* PhaseIdealLoop::remix_address_expressions(Node* n) { IdealLoopTree* n23_loop = get_loop(n23_ctrl); if (n22loop != n_loop && n22loop->is_member(n_loop) && n23_loop == n_loop) { - Node* add1 = new AddPNode(n->in(1), n->in(2)->in(2), n->in(3)); + Node* add1 = AddPNode::make_with_base(n->in(1), n->in(2)->in(2), n->in(3)); // Stuff new AddP in the loop preheader register_new_node(add1, n_loop->_head->as_Loop()->skip_strip_mined(1)->in(LoopNode::EntryControl)); - Node* add2 = new AddPNode(n->in(1), add1, n->in(2)->in(3)); + Node* add2 = AddPNode::make_with_base(n->in(1), add1, n->in(2)->in(3)); register_new_node(add2, n_ctrl); _igvn.replace_node(n, add2); return add2; @@ -641,10 +641,10 @@ Node* PhaseIdealLoop::remix_address_expressions(Node* n) { Node *tmp = V; V = I; I = tmp; } if (!ctrl_is_member(n_loop, I)) { - Node* add1 = new AddPNode(n->in(1), n->in(2), I); + Node* add1 = AddPNode::make_with_base(n->in(1), n->in(2), I); // Stuff new AddP in the loop preheader register_new_node(add1, n_loop->_head->as_Loop()->skip_strip_mined(1)->in(LoopNode::EntryControl)); - Node* add2 = new AddPNode(n->in(1), add1, V); + Node* add2 = AddPNode::make_with_base(n->in(1), add1, V); register_new_node(add2, n_ctrl); _igvn.replace_node(n, add2); return add2; diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 6995cacd1b0..48277eb46d2 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -274,7 +274,7 @@ Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, if (ac->is_clonebasic()) { assert(ac->in(ArrayCopyNode::Src) != ac->in(ArrayCopyNode::Dest), "clone source equals destination"); Node* base = ac->in(ArrayCopyNode::Src); - Node* adr = _igvn.transform(new AddPNode(base, base, _igvn.MakeConX(offset))); + Node* adr = _igvn.transform(AddPNode::make_with_base(base, _igvn.MakeConX(offset))); const TypePtr* adr_type = _igvn.type(base)->is_ptr()->add_offset(offset); MergeMemNode* mergemen = _igvn.transform(MergeMemNode::make(mem))->as_MergeMem(); BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); @@ -293,7 +293,7 @@ Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, if (src_pos_t->is_con() && dest_pos_t->is_con()) { intptr_t off = ((src_pos_t->get_con() - dest_pos_t->get_con()) << shift) + offset; Node* base = ac->in(ArrayCopyNode::Src); - adr = _igvn.transform(new AddPNode(base, base, _igvn.MakeConX(off))); + adr = _igvn.transform(AddPNode::make_with_base(base, _igvn.MakeConX(off))); adr_type = _igvn.type(base)->is_ptr()->add_offset(off); if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) { // Don't emit a new load from src if src == dst but try to get the value from memory instead @@ -308,7 +308,7 @@ Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* off = _igvn.transform(new AddXNode(_igvn.MakeConX(offset), diff)); Node* base = ac->in(ArrayCopyNode::Src); - adr = _igvn.transform(new AddPNode(base, base, off)); + adr = _igvn.transform(AddPNode::make_with_base(base, off)); adr_type = _igvn.type(base)->is_ptr()->add_offset(Type::OffsetBot); if (ac->in(ArrayCopyNode::Src) == ac->in(ArrayCopyNode::Dest)) { // Non constant offset in the array: we can't statically @@ -1199,7 +1199,7 @@ bool PhaseMacroExpand::eliminate_boxing_node(CallStaticJavaNode *boxing) { Node* PhaseMacroExpand::make_load_raw(Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt) { - Node* adr = basic_plus_adr(top(), base, offset); + Node* adr = off_heap_plus_addr(base, offset); const TypePtr* adr_type = adr->bottom_type()->is_ptr(); Node* value = LoadNode::make(_igvn, ctl, mem, adr, adr_type, value_type, bt, MemNode::unordered); transform_later(value); @@ -1208,7 +1208,7 @@ Node* PhaseMacroExpand::make_load_raw(Node* ctl, Node* mem, Node* base, int offs Node* PhaseMacroExpand::make_store_raw(Node* ctl, Node* mem, Node* base, int offset, Node* value, BasicType bt) { - Node* adr = basic_plus_adr(top(), base, offset); + Node* adr = off_heap_plus_addr(base, offset); mem = StoreNode::make(_igvn, ctl, mem, adr, nullptr, value, bt, MemNode::unordered); transform_later(mem); return mem; @@ -1834,7 +1834,7 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, Node* thread = new ThreadLocalNode(); transform_later(thread); - Node* eden_pf_adr = new AddPNode(top()/*not oop*/, thread, + Node* eden_pf_adr = AddPNode::make_off_heap(thread, _igvn.MakeConX(in_bytes(JavaThread::tlab_pf_top_offset()))); transform_later(eden_pf_adr); @@ -1860,8 +1860,8 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, Node* need_pf_false = new IfFalseNode(need_pf_iff); transform_later(need_pf_false); - Node* new_pf_wmt = new AddPNode(top(), old_pf_wm, - _igvn.MakeConX(AllocatePrefetchDistance)); + Node* new_pf_wmt = AddPNode::make_off_heap(old_pf_wm, + _igvn.MakeConX(AllocatePrefetchDistance)); transform_later(new_pf_wmt); new_pf_wmt->set_req(0, need_pf_true); @@ -1880,8 +1880,8 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, uint distance = 0; for (intx i = 0; i < lines; i++) { - prefetch_adr = new AddPNode(top(), new_pf_wmt, - _igvn.MakeConX(distance)); + prefetch_adr = AddPNode::make_off_heap(new_pf_wmt, + _igvn.MakeConX(distance)); transform_later(prefetch_adr); prefetch = new PrefetchAllocationNode(i_o, prefetch_adr); transform_later(prefetch); @@ -1912,8 +1912,8 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, uint distance = AllocatePrefetchDistance; // Next cache address. - Node* cache_adr = new AddPNode(top(), old_eden_top, - _igvn.MakeConX(step_size + distance)); + Node* cache_adr = AddPNode::make_off_heap(old_eden_top, + _igvn.MakeConX(step_size + distance)); transform_later(cache_adr); cache_adr = new CastP2XNode(needgc_false, cache_adr); transform_later(cache_adr); @@ -1932,8 +1932,8 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, Node* prefetch_adr; distance = step_size; for (intx i = 1; i < lines; i++) { - prefetch_adr = new AddPNode(top(), cache_adr, - _igvn.MakeConX(distance)); + prefetch_adr = AddPNode::make_off_heap(cache_adr, + _igvn.MakeConX(distance)); transform_later(prefetch_adr); prefetch = new PrefetchAllocationNode(contended_phi_rawmem, prefetch_adr); transform_later(prefetch); @@ -1948,8 +1948,8 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, uint step_size = AllocatePrefetchStepSize; uint distance = AllocatePrefetchDistance; for (intx i = 0; i < lines; i++) { - prefetch_adr = new AddPNode(top(), new_eden_top, - _igvn.MakeConX(distance)); + prefetch_adr = AddPNode::make_off_heap(new_eden_top, + _igvn.MakeConX(distance)); transform_later(prefetch_adr); prefetch = new PrefetchAllocationNode(i_o, prefetch_adr); // Do not let it float too high, since if eden_top == eden_end, diff --git a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp index 070bfbe1478..59a455cae6d 100644 --- a/src/hotspot/share/opto/macro.hpp +++ b/src/hotspot/share/opto/macro.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,19 +41,31 @@ private: public: // Helper methods roughly modeled after GraphKit: Node* basic_plus_adr(Node* ptr, int offset, bool raw_base = false) { - return (offset == 0)? ptr: basic_plus_adr(ptr, MakeConX(offset), raw_base); + return basic_plus_adr(ptr, MakeConX(offset), raw_base); } + Node* basic_plus_adr(Node* base, Node* ptr, int offset) { - return (offset == 0)? ptr: basic_plus_adr(base, ptr, MakeConX(offset)); + return basic_plus_adr(base, ptr, MakeConX(offset)); } + Node* basic_plus_adr(Node* ptr, Node* offset, bool raw_base = false) { Node* base = raw_base ? top() : ptr; return basic_plus_adr(base, ptr, offset); } + Node* basic_plus_adr(Node* base, Node* ptr, Node* offset) { - Node* adr = new AddPNode(base, ptr, offset); - return transform_later(adr); + return (offset == MakeConX(0)) ? + ptr : transform_later(AddPNode::make_with_base(base, ptr, offset)); } + + Node* off_heap_plus_addr(Node* ptr, int offset) { + return basic_plus_adr(top(), ptr, MakeConX(offset)); + } + + Node* off_heap_plus_addr(Node* ptr, Node* offset) { + return basic_plus_adr(top(), ptr, offset); + } + Node* transform_later(Node* n) { // equivalent to _gvn.transform in GraphKit, Ideal, etc. _igvn.register_new_node_with_optimizer(n); diff --git a/src/hotspot/share/opto/macroArrayCopy.cpp b/src/hotspot/share/opto/macroArrayCopy.cpp index 344561606b5..139775506db 100644 --- a/src/hotspot/share/opto/macroArrayCopy.cpp +++ b/src/hotspot/share/opto/macroArrayCopy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -640,7 +640,7 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode* // (At this point we can assume disjoint_bases, since types differ.) int ek_offset = in_bytes(ObjArrayKlass::element_klass_offset()); - Node* p1 = basic_plus_adr(top(), dest_klass, ek_offset); + Node* p1 = off_heap_plus_addr(dest_klass, ek_offset); Node* n1 = LoadKlassNode::make(_igvn, C->immutable_memory(), p1, TypeRawPtr::BOTTOM); Node* dest_elem_klass = transform_later(n1); Node* cv = generate_checkcast_arraycopy(&local_ctrl, &local_mem, @@ -1131,7 +1131,7 @@ Node* PhaseMacroExpand::generate_checkcast_arraycopy(Node** ctrl, MergeMemNode** // look in each non-null element's class, at the desired klass's // super_check_offset, for the desired klass. int sco_offset = in_bytes(Klass::super_check_offset_offset()); - Node* p3 = basic_plus_adr(top(), dest_elem_klass, sco_offset); + Node* p3 = off_heap_plus_addr(dest_elem_klass, sco_offset); Node* n3 = new LoadINode(nullptr, *mem /*memory(p3)*/, p3, _igvn.type(p3)->is_ptr(), TypeInt::INT, MemNode::unordered); Node* check_offset = ConvI2X(transform_later(n3)); Node* check_value = dest_elem_klass; diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 85bc41e71b9..3cd553e4bd1 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1846,7 +1846,7 @@ Node* LoadNode::split_through_phi(PhaseGVN* phase, bool ignore_missing_instance_ } if (base_is_phi && (base->in(0) == region)) { Node* base_x = base->in(i); // Clone address for loads from boxed objects. - Node* adr_x = phase->transform(new AddPNode(base_x,base_x,address->in(AddPNode::Offset))); + Node* adr_x = phase->transform(AddPNode::make_with_base(base_x, address->in(AddPNode::Offset))); x->set_req(Address, adr_x); } } @@ -4191,10 +4191,10 @@ Node *ClearArrayNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node *off = phase->MakeConX(BytesPerLong); mem = new StoreLNode(in(0),mem,adr,atp,zero,MemNode::unordered,false); count--; - while( count-- ) { + while (count--) { mem = phase->transform(mem); - adr = phase->transform(new AddPNode(base,adr,off)); - mem = new StoreLNode(in(0),mem,adr,atp,zero,MemNode::unordered,false); + adr = phase->transform(AddPNode::make_with_base(base, adr, off)); + mem = new StoreLNode(in(0), mem, adr, atp, zero, MemNode::unordered, false); } return mem; } @@ -4231,7 +4231,7 @@ Node* ClearArrayNode::make_address(Node* dest, Node* offset, bool raw_base, Phas // May be called as part of the initialization of a just allocated object base = phase->C->top(); } - return phase->transform(new AddPNode(base, dest, offset)); + return phase->transform(AddPNode::make_with_base(base, dest, offset)); } //----------------------------clear_memory------------------------------------- @@ -5042,8 +5042,7 @@ Node* InitializeNode::make_raw_address(intptr_t offset, Node* addr = in(RawAddress); if (offset != 0) { Compile* C = phase->C; - addr = phase->transform( new AddPNode(C->top(), addr, - phase->MakeConX(offset)) ); + addr = phase->transform(AddPNode::make_off_heap(addr, phase->MakeConX(offset))); } return addr; } diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 2f699650037..647e8515b30 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -104,9 +104,9 @@ void Parse::print_statistics() { Node *Parse::fetch_interpreter_state(int index, BasicType bt, Node* local_addrs) { - Node *mem = memory(Compile::AliasIdxRaw); - Node *adr = basic_plus_adr(top(), local_addrs, -index*wordSize); - Node *ctl = control(); + Node* mem = memory(Compile::AliasIdxRaw); + Node* adr = off_heap_plus_addr(local_addrs, -index*wordSize); + Node* ctl = control(); // Very similar to LoadNode::make, except we handle un-aligned longs and // doubles on Sparc. Intel can handle them just fine directly. @@ -120,7 +120,7 @@ Node *Parse::fetch_interpreter_state(int index, case T_DOUBLE: { // Since arguments are in reverse order, the argument address 'adr' // refers to the back half of the long/double. Recompute adr. - adr = basic_plus_adr(top(), local_addrs, -(index+1)*wordSize); + adr = off_heap_plus_addr(local_addrs, -(index+1)*wordSize); if (Matcher::misaligned_doubles_ok) { l = (bt == T_DOUBLE) ? (Node*)new LoadDNode(ctl, mem, adr, TypeRawPtr::BOTTOM, Type::DOUBLE, MemNode::unordered) @@ -219,7 +219,7 @@ void Parse::load_interpreter_state(Node* osr_buf) { // Commute monitors from interpreter frame to compiler frame. assert(jvms()->monitor_depth() == 0, "should be no active locks at beginning of osr"); int mcnt = osr_block->flow()->monitor_count(); - Node *monitors_addr = basic_plus_adr(top(), osr_buf, (max_locals+mcnt*2-1)*wordSize); + Node* monitors_addr = off_heap_plus_addr(osr_buf, (max_locals+mcnt*2-1)*wordSize); for (index = 0; index < mcnt; index++) { // Make a BoxLockNode for the monitor. BoxLockNode* osr_box = new BoxLockNode(next_monitor()); @@ -270,7 +270,7 @@ void Parse::load_interpreter_state(Node* osr_buf) { } // Extract the needed locals from the interpreter frame. - Node *locals_addr = basic_plus_adr(top(), osr_buf, (max_locals-1)*wordSize); + Node* locals_addr = off_heap_plus_addr(osr_buf, (max_locals-1)*wordSize); // find all the locals that the interpreter thinks contain live oops const ResourceBitMap live_oops = method()->live_local_oops_at_bci(osr_bci()); @@ -2127,7 +2127,7 @@ void Parse::call_register_finalizer() { Node* klass_addr = basic_plus_adr( receiver, receiver, oopDesc::klass_offset_in_bytes() ); Node* klass = _gvn.transform(LoadKlassNode::make(_gvn, immutable_memory(), klass_addr, TypeInstPtr::KLASS)); - Node* access_flags_addr = basic_plus_adr(top(), klass, in_bytes(Klass::misc_flags_offset())); + Node* access_flags_addr = off_heap_plus_addr(klass, in_bytes(Klass::misc_flags_offset())); Node* access_flags = make_load(nullptr, access_flags_addr, TypeInt::UBYTE, T_BOOLEAN, MemNode::unordered); Node* mask = _gvn.transform(new AndINode(access_flags, intcon(KlassFlags::_misc_has_finalizer))); @@ -2273,9 +2273,9 @@ void Parse::add_safepoint() { sfpnt->init_req(TypeFunc::FramePtr , top() ); // Create a node for the polling address - Node *polladr; - Node *thread = _gvn.transform(new ThreadLocalNode()); - Node *polling_page_load_addr = _gvn.transform(basic_plus_adr(top(), thread, in_bytes(JavaThread::polling_page_offset()))); + Node* polladr; + Node* thread = _gvn.transform(new ThreadLocalNode()); + Node* polling_page_load_addr = _gvn.transform(off_heap_plus_addr(thread, in_bytes(JavaThread::polling_page_offset()))); polladr = make_load(control(), polling_page_load_addr, TypeRawPtr::BOTTOM, T_ADDRESS, MemNode::unordered); sfpnt->init_req(TypeFunc::Parms+0, _gvn.transform(polladr)); diff --git a/src/hotspot/share/opto/parseHelper.cpp b/src/hotspot/share/opto/parseHelper.cpp index 2e8fca68a8d..232f5d6c89a 100644 --- a/src/hotspot/share/opto/parseHelper.cpp +++ b/src/hotspot/share/opto/parseHelper.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -220,7 +220,7 @@ void Parse::array_store_check() { // Extract the array element class int element_klass_offset = in_bytes(ObjArrayKlass::element_klass_offset()); - Node* p2 = basic_plus_adr(top(), array_klass, element_klass_offset); + Node* p2 = off_heap_plus_addr(array_klass, element_klass_offset); Node* a_e_klass = _gvn.transform(LoadKlassNode::make(_gvn, immutable_memory(), p2, tak)); assert(array_klass->is_Con() == a_e_klass->is_Con() || StressReflectiveCode, "a constant array type must come with a constant element type"); diff --git a/src/hotspot/share/opto/subtypenode.cpp b/src/hotspot/share/opto/subtypenode.cpp index 8e4c7d829a7..69b7058d053 100644 --- a/src/hotspot/share/opto/subtypenode.cpp +++ b/src/hotspot/share/opto/subtypenode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -182,7 +182,7 @@ bool SubTypeCheckNode::verify(PhaseGVN* phase) { return verify_helper(phase, load_klass(phase), cached_t); } case Compile::SSC_full_test: { - Node* p1 = phase->transform(new AddPNode(C->top(), superklass, phase->MakeConX(in_bytes(Klass::super_check_offset_offset())))); + Node* p1 = phase->transform(AddPNode::make_off_heap(superklass, phase->MakeConX(in_bytes(Klass::super_check_offset_offset())))); Node* chk_off = phase->transform(new LoadINode(nullptr, C->immutable_memory(), p1, phase->type(p1)->is_ptr(), TypeInt::INT, MemNode::unordered)); record_for_cleanup(chk_off, phase); @@ -194,7 +194,7 @@ bool SubTypeCheckNode::verify(PhaseGVN* phase) { #ifdef _LP64 chk_off_X = phase->transform(new ConvI2LNode(chk_off_X)); #endif - Node* p2 = phase->transform(new AddPNode(C->top(), subklass, chk_off_X)); + Node* p2 = phase->transform(AddPNode::make_off_heap(subklass, chk_off_X)); Node* nkls = phase->transform(LoadKlassNode::make(*phase, C->immutable_memory(), p2, phase->type(p2)->is_ptr(), TypeInstKlassPtr::OBJECT_OR_NULL)); return verify_helper(phase, nkls, cached_t); @@ -217,7 +217,7 @@ Node* SubTypeCheckNode::load_klass(PhaseGVN* phase) const { const Type* sub_t = phase->type(obj_or_subklass); Node* subklass = nullptr; if (sub_t->isa_oopptr()) { - Node* adr = phase->transform(new AddPNode(obj_or_subklass, obj_or_subklass, phase->MakeConX(oopDesc::klass_offset_in_bytes()))); + Node* adr = phase->transform(AddPNode::make_with_base(obj_or_subklass, phase->MakeConX(oopDesc::klass_offset_in_bytes()))); subklass = phase->transform(LoadKlassNode::make(*phase, phase->C->immutable_memory(), adr, TypeInstPtr::KLASS)); record_for_cleanup(subklass, phase); } else { From 700a3856341cc83e370ca35bd0f703f75c6ce9bf Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 23 Mar 2026 09:38:37 +0000 Subject: [PATCH 070/160] 8080226: G1: Replace collector state booleans with explicit state variable(s) Reviewed-by: iwalulya, ayang --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 10 +-- src/hotspot/share/gc/g1/g1CollectionSet.cpp | 4 +- src/hotspot/share/gc/g1/g1CollectorState.cpp | 29 +++---- src/hotspot/share/gc/g1/g1CollectorState.hpp | 87 +++++++++---------- src/hotspot/share/gc/g1/g1HeapVerifier.cpp | 8 +- src/hotspot/share/gc/g1/g1Policy.cpp | 54 +++++------- src/hotspot/share/gc/g1/g1Policy.hpp | 2 +- src/hotspot/share/gc/g1/g1RootClosures.cpp | 4 +- src/hotspot/share/gc/g1/g1YoungCollector.cpp | 14 +-- .../g1/g1YoungGCAllocationFailureInjector.cpp | 4 +- .../gc/g1/g1YoungGCPostEvacuateTasks.cpp | 2 +- 11 files changed, 100 insertions(+), 118 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 8b41a3b2079..480550d6ed1 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -2481,7 +2481,7 @@ void G1CollectedHeap::trace_heap(GCWhen::Type when, const GCTracer* gc_tracer) { void G1CollectedHeap::gc_prologue(bool full) { // Update common counters. increment_total_collections(full /* full gc */); - if (full || collector_state()->in_concurrent_start_gc()) { + if (full || collector_state()->is_in_concurrent_start_gc()) { increment_old_marking_cycles_started(); } } @@ -2651,7 +2651,7 @@ void G1CollectedHeap::verify_after_young_collection(G1HeapVerifier::G1VerifyType verify_numa_regions("GC End"); _verifier->verify_region_sets_optional(); - if (collector_state()->in_concurrent_start_gc()) { + if (collector_state()->is_in_concurrent_start_gc()) { log_debug(gc, verify)("Marking state"); _verifier->verify_marking_state(); } @@ -2732,7 +2732,7 @@ void G1CollectedHeap::do_collection_pause_at_safepoint(size_t allocation_word_si // Record whether this pause may need to trigger a concurrent operation. Later, // when we signal the G1ConcurrentMarkThread, the collector state has already // been reset for the next pause. - bool should_start_concurrent_mark_operation = collector_state()->in_concurrent_start_gc(); + bool should_start_concurrent_mark_operation = collector_state()->is_in_concurrent_start_gc(); // Perform the collection. G1YoungCollector collector(gc_cause(), allocation_word_size); @@ -2827,7 +2827,7 @@ bool G1STWSubjectToDiscoveryClosure::do_object_b(oop obj) { } void G1CollectedHeap::make_pending_list_reachable() { - if (collector_state()->in_concurrent_start_gc()) { + if (collector_state()->is_in_concurrent_start_gc()) { oop pll_head = Universe::reference_pending_list(); if (pll_head != nullptr) { // Any valid worker id is fine here as we are in the VM thread and single-threaded. @@ -3212,7 +3212,7 @@ void G1CollectedHeap::retire_gc_alloc_region(G1HeapRegion* alloc_region, _survivor.add_used_bytes(allocated_bytes); } - bool const during_im = collector_state()->in_concurrent_start_gc(); + bool const during_im = collector_state()->is_in_concurrent_start_gc(); if (during_im && allocated_bytes > 0) { _cm->add_root_region(alloc_region); } diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index 39acaaab913..978925d88cb 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -325,7 +325,7 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi guarantee(target_pause_time_ms > 0.0, "target_pause_time_ms = %1.6lf should be positive", target_pause_time_ms); - bool in_young_only_phase = _policy->collector_state()->in_young_only_phase(); + bool in_young_only_phase = _policy->collector_state()->is_in_young_only_phase(); size_t pending_cards = _policy->analytics()->predict_pending_cards(in_young_only_phase); log_trace(gc, ergo, cset)("Start choosing CSet. Pending cards: %zu target pause time: %1.2fms", @@ -378,7 +378,7 @@ void G1CollectionSet::finalize_old_part(double time_remaining_ms) { if (!candidates()->is_empty()) { candidates()->verify(); - if (collector_state()->in_mixed_phase()) { + if (collector_state()->is_in_mixed_phase()) { time_remaining_ms = select_candidates_from_marking(time_remaining_ms); } else { log_debug(gc, ergo, cset)("Do not add marking candidates to collection set due to pause type."); diff --git a/src/hotspot/share/gc/g1/g1CollectorState.cpp b/src/hotspot/share/gc/g1/g1CollectorState.cpp index b64e9d45674..d40a445c0ed 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.cpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.cpp @@ -26,24 +26,19 @@ #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1GCPauseType.hpp" +#include "runtime/safepoint.hpp" -G1GCPauseType G1CollectorState::young_gc_pause_type(bool concurrent_operation_is_full_mark) const { - assert(!in_full_gc(), "must be"); - if (in_concurrent_start_gc()) { - assert(!in_young_gc_before_mixed(), "must be"); - return concurrent_operation_is_full_mark ? G1GCPauseType::ConcurrentStartMarkGC : - G1GCPauseType::ConcurrentStartUndoGC; - } else if (in_young_gc_before_mixed()) { - assert(!in_concurrent_start_gc(), "must be"); - return G1GCPauseType::LastYoungGC; - } else if (in_mixed_phase()) { - assert(!in_concurrent_start_gc(), "must be"); - assert(!in_young_gc_before_mixed(), "must be"); - return G1GCPauseType::MixedGC; - } else { - assert(!in_concurrent_start_gc(), "must be"); - assert(!in_young_gc_before_mixed(), "must be"); - return G1GCPauseType::YoungGC; +G1GCPauseType G1CollectorState::gc_pause_type(bool concurrent_operation_is_full_mark) const { + assert(SafepointSynchronize::is_at_safepoint(), "must be"); + switch (_phase) { + case Phase::YoungNormal: return G1GCPauseType::YoungGC; + case Phase::YoungLastYoung: return G1GCPauseType::LastYoungGC; + case Phase::YoungConcurrentStart: + return concurrent_operation_is_full_mark ? G1GCPauseType::ConcurrentStartMarkGC : + G1GCPauseType::ConcurrentStartUndoGC; + case Phase::Mixed: return G1GCPauseType::MixedGC; + case Phase::FullGC: return G1GCPauseType::FullGC; + default: ShouldNotReachHere(); } } diff --git a/src/hotspot/share/gc/g1/g1CollectorState.hpp b/src/hotspot/share/gc/g1/g1CollectorState.hpp index ddf9d7fd181..c2923cb3954 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.hpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.hpp @@ -29,68 +29,61 @@ #include "utilities/globalDefinitions.hpp" // State of the G1 collection. +// +// The rough phasing is Young-Only, Mixed / Space Reclamation and +// Full GC "phase". +// +// We split the Young-only phase into three parts to cover interesting +// sub-phases and avoid separate tracking. class G1CollectorState { - // Indicates whether we are in the phase where we do partial gcs that only contain - // the young generation. Not set while _in_full_gc is set. - bool _in_young_only_phase; + enum class Phase { + // Indicates that the next GC in the Young-Only phase will (likely) be a "Normal" + // young GC. + YoungNormal, + // We are in a concurrent start GC during the Young-Only phase. This is only set + // during that GC because we only decide whether we do this type of GC at the start + // of the pause. + YoungConcurrentStart, + // Indicates that we are about to start or in the last young gc in the Young-Only + // phase before the Mixed phase. This GC is required to keep pause time requirements. + YoungLastYoung, + // Doing extra old generation evacuation. + Mixed, + // The Full GC phase (that coincides with the Full GC pause). + FullGC + } _phase; - // Indicates whether we are in the last young gc before the mixed gc phase. This GC - // is required to keep pause time requirements. - bool _in_young_gc_before_mixed; - - // If _initiate_conc_mark_if_possible is set at the beginning of a - // pause, it is a suggestion that the pause should start a marking - // cycle by doing the concurrent start work. However, it is possible - // that the concurrent marking thread is still finishing up the - // previous marking cycle (e.g., clearing the marking bitmap). - // If that is the case we cannot start a new cycle and - // we'll have to wait for the concurrent marking thread to finish - // what it is doing. In this case we will postpone the marking cycle - // initiation decision for the next pause. When we eventually decide - // to start a cycle, we will set _in_concurrent_start_gc which - // will stay true until the end of the concurrent start pause doing the - // concurrent start work. - volatile bool _in_concurrent_start_gc; - - // At the end of a pause we check the heap occupancy and we decide - // whether we will start a marking cycle during the next pause. If - // we decide that we want to do that, set this parameter. This parameter will - // stay set until the beginning of a subsequent pause (not necessarily - // the next one) when we decide that we will indeed start a marking cycle and - // do the concurrent start phase work. + // _initiate_conc_mark_if_possible indicates that there has been a request to start + // a concurrent cycle but we have not been able to fulfill it because another one + // has been in progress when the request came in. + // + // This flag remembers that there is an unfullfilled request. volatile bool _initiate_conc_mark_if_possible; - // Set during a full gc pause. - bool _in_full_gc; - public: G1CollectorState() : - _in_young_only_phase(true), - _in_young_gc_before_mixed(false), - - _in_concurrent_start_gc(false), - _initiate_conc_mark_if_possible(false), - - _in_full_gc(false) { } + _phase(Phase::YoungNormal), + _initiate_conc_mark_if_possible(false) { } // Phase setters - void set_in_young_only_phase(bool v) { _in_young_only_phase = v; } + void set_in_normal_young_gc() { _phase = Phase::YoungNormal; } + void set_in_space_reclamation_phase() { _phase = Phase::Mixed; } + void set_in_full_gc() { _phase = Phase::FullGC; } // Pause setters - void set_in_young_gc_before_mixed(bool v) { _in_young_gc_before_mixed = v; } - void set_in_concurrent_start_gc(bool v) { _in_concurrent_start_gc = v; } - void set_in_full_gc(bool v) { _in_full_gc = v; } + void set_in_young_gc_before_mixed() { _phase = Phase::YoungLastYoung; } + void set_in_concurrent_start_gc() { _phase = Phase::YoungConcurrentStart; _initiate_conc_mark_if_possible = false; } void set_initiate_conc_mark_if_possible(bool v) { _initiate_conc_mark_if_possible = v; } // Phase getters - bool in_young_only_phase() const { return _in_young_only_phase && !_in_full_gc; } - bool in_mixed_phase() const { return !_in_young_only_phase && !_in_full_gc; } + bool is_in_young_only_phase() const { return _phase == Phase::YoungNormal || _phase == Phase::YoungConcurrentStart || _phase == Phase::YoungLastYoung; } + bool is_in_mixed_phase() const { return _phase == Phase::Mixed; } // Specific pauses - bool in_young_gc_before_mixed() const { return _in_young_gc_before_mixed; } - bool in_full_gc() const { return _in_full_gc; } - bool in_concurrent_start_gc() const { return _in_concurrent_start_gc; } + bool is_in_young_gc_before_mixed() const { return _phase == Phase::YoungLastYoung; } + bool is_in_full_gc() const { return _phase == Phase::FullGC; } + bool is_in_concurrent_start_gc() const { return _phase == Phase::YoungConcurrentStart; } bool initiate_conc_mark_if_possible() const { return _initiate_conc_mark_if_possible; } @@ -100,7 +93,7 @@ public: bool is_in_reset_for_next_cycle() const; // Calculate GC Pause Type from internal state. - G1GCPauseType young_gc_pause_type(bool concurrent_operation_is_full_mark) const; + G1GCPauseType gc_pause_type(bool concurrent_operation_is_full_mark) const; }; #endif // SHARE_GC_G1_G1COLLECTORSTATE_HPP diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index a2a9bc8e857..dd7a8aa117d 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -236,7 +236,7 @@ private: VerifyOption _vo; bool _failures; - bool is_in_full_gc() const { return G1CollectedHeap::heap()->collector_state()->in_full_gc(); } + bool is_in_full_gc() const { return G1CollectedHeap::heap()->collector_state()->is_in_full_gc(); } public: VerifyRegionClosure(VerifyOption vo) @@ -349,7 +349,7 @@ void G1HeapVerifier::verify(VerifyOption vo) { bool failures = rootsCl.failures() || codeRootsCl.failures(); - if (!_g1h->policy()->collector_state()->in_full_gc()) { + if (!_g1h->policy()->collector_state()->is_in_full_gc()) { // If we're verifying during a full GC then the region sets // will have been torn down at the start of the GC. Therefore // verifying the region sets will fail. So we only verify @@ -494,7 +494,7 @@ public: }; void G1HeapVerifier::verify_marking_state() { - assert(G1CollectedHeap::heap()->collector_state()->in_concurrent_start_gc(), "must be"); + assert(G1CollectedHeap::heap()->collector_state()->is_in_concurrent_start_gc(), "must be"); // Verify TAMSes, bitmaps and liveness statistics. // diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 3c8b81bdb8f..1fa5aa877c0 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -177,7 +177,7 @@ uint G1Policy::calculate_desired_eden_length_by_mmu() const { void G1Policy::update_young_length_bounds() { assert(!Universe::is_fully_initialized() || SafepointSynchronize::is_at_safepoint(), "must be"); - bool for_young_only_phase = collector_state()->in_young_only_phase(); + bool for_young_only_phase = collector_state()->is_in_young_only_phase(); update_young_length_bounds(_analytics->predict_pending_cards(for_young_only_phase), _analytics->predict_card_rs_length(for_young_only_phase), _analytics->predict_code_root_rs_length(for_young_only_phase)); @@ -505,7 +505,7 @@ uint G1Policy::calculate_desired_eden_length_before_mixed(double base_time_ms, double G1Policy::predict_survivor_regions_evac_time() const { double survivor_regions_evac_time = predict_young_region_other_time_ms(_g1h->survivor()->length()); for (G1HeapRegion* r : _g1h->survivor()->regions()) { - survivor_regions_evac_time += predict_region_copy_time_ms(r, _g1h->collector_state()->in_young_only_phase()); + survivor_regions_evac_time += predict_region_copy_time_ms(r, _g1h->collector_state()->is_in_young_only_phase()); } return survivor_regions_evac_time; @@ -561,8 +561,7 @@ void G1Policy::revise_young_list_target_length(size_t pending_cards, size_t card void G1Policy::record_full_collection_start() { record_pause_start_time(); // Release the future to-space so that it is available for compaction into. - collector_state()->set_in_young_only_phase(false); - collector_state()->set_in_full_gc(true); + collector_state()->set_in_full_gc(); _collection_set->abandon_all_candidates(); } @@ -571,14 +570,10 @@ void G1Policy::record_full_collection_end(size_t allocation_word_size) { // since last pause. double end_sec = os::elapsedTime(); - collector_state()->set_in_full_gc(false); - // "Nuke" the heuristics that control the young/mixed GC // transitions and make sure we start with young GCs after the Full GC. - collector_state()->set_in_young_only_phase(true); - collector_state()->set_in_young_gc_before_mixed(false); + collector_state()->set_in_normal_young_gc(); collector_state()->set_initiate_conc_mark_if_possible(need_to_start_conc_mark("end of Full GC", allocation_word_size)); - collector_state()->set_in_concurrent_start_gc(false); _eden_surv_rate_group->start_adding_regions(); // also call this on any additional surv rate groups @@ -697,7 +692,7 @@ void G1Policy::record_young_collection_start() { void G1Policy::record_concurrent_mark_init_end() { assert(!collector_state()->initiate_conc_mark_if_possible(), "we should have cleared it by now"); - collector_state()->set_in_concurrent_start_gc(false); + collector_state()->set_in_normal_young_gc(); } void G1Policy::record_concurrent_mark_remark_end() { @@ -735,7 +730,7 @@ double G1Policy::constant_other_time_ms(double pause_time_ms) const { } bool G1Policy::about_to_start_mixed_phase() const { - return collector_state()->is_in_concurrent_cycle() || collector_state()->in_young_gc_before_mixed(); + return collector_state()->is_in_concurrent_cycle() || collector_state()->is_in_young_gc_before_mixed(); } bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_word_size) { @@ -748,7 +743,7 @@ bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_wor bool result = false; if (non_young_occupancy > marking_initiating_used_threshold) { - result = collector_state()->in_young_only_phase(); + result = collector_state()->is_in_young_only_phase(); log_debug(gc, ergo, ihop)("%s non-young occupancy: %zuB allocation request: %zuB threshold: %zuB (%1.2f) source: %s", result ? "Request concurrent cycle initiation (occupancy higher than threshold)" : "Do not request concurrent cycle initiation (still doing mixed collections)", non_young_occupancy, allocation_word_size * HeapWordSize, marking_initiating_used_threshold, (double) marking_initiating_used_threshold / _g1h->capacity() * 100, source); @@ -757,7 +752,7 @@ bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_wor } bool G1Policy::concurrent_operation_is_full_mark(const char* msg, size_t allocation_word_size) { - return collector_state()->in_concurrent_start_gc() && + return collector_state()->is_in_concurrent_start_gc() && ((_g1h->gc_cause() != GCCause::_g1_humongous_allocation) || need_to_start_conc_mark(msg, allocation_word_size)); } @@ -796,7 +791,7 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar double end_time_sec = Ticks::now().seconds(); double pause_time_ms = (end_time_sec - start_time_sec) * 1000.0; - G1GCPauseType this_pause = collector_state()->young_gc_pause_type(concurrent_operation_is_full_mark); + G1GCPauseType this_pause = collector_state()->gc_pause_type(concurrent_operation_is_full_mark); bool is_young_only_pause = G1GCPauseTypeHelper::is_young_only_pause(this_pause); if (G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause)) { @@ -947,14 +942,13 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar // This has been the young GC before we start doing mixed GCs. We already // decided to start mixed GCs much earlier, so there is nothing to do except // advancing the state. - collector_state()->set_in_young_only_phase(false); - collector_state()->set_in_young_gc_before_mixed(false); + collector_state()->set_in_space_reclamation_phase(); } else if (G1GCPauseTypeHelper::is_mixed_pause(this_pause)) { // This is a mixed GC. Here we decide whether to continue doing more // mixed GCs or not. if (!next_gc_should_be_mixed()) { log_debug(gc, ergo)("do not continue mixed GCs (candidate old regions not available)"); - collector_state()->set_in_young_only_phase(true); + collector_state()->set_in_normal_young_gc(); assert(!candidates()->has_more_marking_candidates(), "only end mixed if all candidates from marking were processed"); @@ -1073,7 +1067,7 @@ void G1Policy::record_young_gc_pause_end(bool evacuation_failed) { double G1Policy::predict_base_time_ms(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) const { - bool in_young_only_phase = collector_state()->in_young_only_phase(); + bool in_young_only_phase = collector_state()->is_in_young_only_phase(); // Cards from the refinement table and the cards from the young gen remset are // unique to each other as they are located on the card table. @@ -1097,7 +1091,7 @@ double G1Policy::predict_base_time_ms(size_t pending_cards, } double G1Policy::predict_base_time_ms(size_t pending_cards, size_t card_rs_length) const { - bool for_young_only_phase = collector_state()->in_young_only_phase(); + bool for_young_only_phase = collector_state()->is_in_young_only_phase(); size_t code_root_rs_length = _analytics->predict_code_root_rs_length(for_young_only_phase); return predict_base_time_ms(pending_cards, card_rs_length, code_root_rs_length); } @@ -1137,7 +1131,7 @@ double G1Policy::predict_eden_copy_time_ms(uint count, size_t* bytes_to_copy) co if (bytes_to_copy != nullptr) { *bytes_to_copy = expected_bytes; } - return _analytics->predict_object_copy_time_ms(expected_bytes, collector_state()->in_young_only_phase()); + return _analytics->predict_object_copy_time_ms(expected_bytes, collector_state()->is_in_young_only_phase()); } double G1Policy::predict_region_copy_time_ms(G1HeapRegion* hr, bool for_young_only_phase) const { @@ -1243,8 +1237,7 @@ bool G1Policy::force_concurrent_start_if_outside_cycle(GCCause::Cause gc_cause) } void G1Policy::initiate_conc_mark() { - collector_state()->set_in_concurrent_start_gc(true); - collector_state()->set_initiate_conc_mark_if_possible(false); + collector_state()->set_in_concurrent_start_gc(); } static const char* requester_for_mixed_abort(GCCause::Cause cause) { @@ -1266,7 +1259,7 @@ void G1Policy::decide_on_concurrent_start_pause() { // will set it here if we have to. However, it should be cleared by // the end of the pause (it's only set for the duration of a // concurrent start pause). - assert(!collector_state()->in_concurrent_start_gc(), "pre-condition"); + assert(!collector_state()->is_in_concurrent_start_gc(), "pre-condition"); if (collector_state()->initiate_conc_mark_if_possible()) { // We had noticed on a previous pause that the heap occupancy has @@ -1279,7 +1272,7 @@ void G1Policy::decide_on_concurrent_start_pause() { if ((cause != GCCause::_wb_breakpoint) && ConcurrentGCBreakpoints::is_controlled()) { log_debug(gc, ergo)("Do not initiate concurrent cycle (whitebox controlled)"); - } else if (!about_to_start_mixed_phase() && collector_state()->in_young_only_phase()) { + } else if (!about_to_start_mixed_phase() && collector_state()->is_in_young_only_phase()) { // Initiate a new concurrent start if there is no marking or reclamation going on. initiate_conc_mark(); log_debug(gc, ergo)("Initiate concurrent cycle (concurrent cycle initiation requested)"); @@ -1288,8 +1281,7 @@ void G1Policy::decide_on_concurrent_start_pause() { (cause == GCCause::_wb_breakpoint)) { // Initiate a concurrent start. A concurrent start must be a young only // GC, so the collector state must be updated to reflect this. - collector_state()->set_in_young_only_phase(true); - collector_state()->set_in_young_gc_before_mixed(false); + collector_state()->set_in_normal_young_gc(); // We might have ended up coming here about to start a mixed phase with a collection set // active. The following remark might change the change the "evacuation efficiency" of @@ -1318,10 +1310,10 @@ void G1Policy::decide_on_concurrent_start_pause() { } // Result consistency checks. // We do not allow concurrent start to be piggy-backed on a mixed GC. - assert(!collector_state()->in_concurrent_start_gc() || - collector_state()->in_young_only_phase(), "sanity"); + assert(!collector_state()->is_in_concurrent_start_gc() || + collector_state()->is_in_young_only_phase(), "sanity"); // We also do not allow mixed GCs during marking/rebuilding. - assert(!collector_state()->is_in_mark_or_rebuild() || collector_state()->in_young_only_phase(), "sanity %d %d", collector_state()->is_in_concurrent_cycle(), collector_state()->in_young_only_phase()); + assert(!collector_state()->is_in_mark_or_rebuild() || collector_state()->is_in_young_only_phase(), "sanity %d %d", collector_state()->is_in_concurrent_cycle(), collector_state()->is_in_young_only_phase()); } void G1Policy::record_concurrent_mark_cleanup_end(bool has_rebuilt_remembered_sets) { @@ -1340,7 +1332,9 @@ void G1Policy::record_concurrent_mark_cleanup_end(bool has_rebuilt_remembered_se abort_time_to_mixed_tracking(); log_debug(gc, ergo)("request young-only gcs (candidate old regions not available)"); } - collector_state()->set_in_young_gc_before_mixed(mixed_gc_pending); + if (mixed_gc_pending) { + collector_state()->set_in_young_gc_before_mixed(); + } double end_sec = os::elapsedTime(); double start_sec = cur_pause_start_sec(); diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index ccbde31a465..1209c7c02d5 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -115,7 +115,7 @@ class G1Policy: public CHeapObj { G1ConcurrentStartToMixedTimeTracker _concurrent_start_to_mixed; bool should_update_surv_rate_group_predictors() { - return collector_state()->in_young_only_phase() && !collector_state()->is_in_mark_or_rebuild(); + return collector_state()->is_in_young_only_phase() && !collector_state()->is_in_mark_or_rebuild(); } double pending_cards_processing_time() const; diff --git a/src/hotspot/share/gc/g1/g1RootClosures.cpp b/src/hotspot/share/gc/g1/g1RootClosures.cpp index f03681487cb..2d5150c27aa 100644 --- a/src/hotspot/share/gc/g1/g1RootClosures.cpp +++ b/src/hotspot/share/gc/g1/g1RootClosures.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -72,7 +72,7 @@ G1EvacuationRootClosures* G1EvacuationRootClosures::create_root_closures(G1Colle G1ParScanThreadState* pss, bool process_only_dirty_klasses) { G1EvacuationRootClosures* res = nullptr; - if (g1h->collector_state()->in_concurrent_start_gc()) { + if (g1h->collector_state()->is_in_concurrent_start_gc()) { if (ClassUnloadingWithConcurrentMark) { res = new G1ConcurrentStartMarkClosures(g1h, pss); } else { diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 3fceb42b6d7..a3693456268 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -105,7 +105,7 @@ public: // Take snapshot of current pause type at start as it may be modified during gc. // The strings for all Concurrent Start pauses are the same, so the parameter // does not matter here. - _pause_type(_collector->collector_state()->young_gc_pause_type(false /* concurrent_operation_is_full_mark */)), + _pause_type(_collector->collector_state()->gc_pause_type(false /* concurrent_operation_is_full_mark */)), _pause_cause(cause), // Fake a "no cause" and manually add the correct string in update_young_gc_name() // to make the string look more natural. @@ -160,9 +160,9 @@ class G1YoungGCVerifierMark : public StackObj { static G1HeapVerifier::G1VerifyType young_collection_verify_type() { G1CollectorState* state = G1CollectedHeap::heap()->collector_state(); - if (state->in_concurrent_start_gc()) { + if (state->is_in_concurrent_start_gc()) { return G1HeapVerifier::G1VerifyConcurrentStart; - } else if (state->in_young_only_phase()) { + } else if (state->is_in_young_only_phase()) { return G1HeapVerifier::G1VerifyYoungNormal; } else { return G1HeapVerifier::G1VerifyMixed; @@ -530,7 +530,7 @@ void G1YoungCollector::pre_evacuate_collection_set(G1EvacInfo* evacuation_info) // Needs log buffers flushed. calculate_collection_set(evacuation_info, policy()->max_pause_time_ms()); - if (collector_state()->in_concurrent_start_gc()) { + if (collector_state()->is_in_concurrent_start_gc()) { Ticks start = Ticks::now(); concurrent_mark()->pre_concurrent_start(_gc_cause); phase_times()->record_prepare_concurrent_task_time_ms((Ticks::now() - start).seconds() * 1000.0); @@ -1037,7 +1037,7 @@ void G1YoungCollector::post_evacuate_cleanup_2(G1ParScanThreadStateSet* per_thre } void G1YoungCollector::enqueue_candidates_as_root_regions() { - assert(collector_state()->in_concurrent_start_gc(), "must be"); + assert(collector_state()->is_in_concurrent_start_gc(), "must be"); G1CollectionSetCandidates* candidates = collection_set()->candidates(); candidates->iterate_regions([&] (G1HeapRegion* r) { @@ -1077,7 +1077,7 @@ void G1YoungCollector::post_evacuate_collection_set(G1EvacInfo* evacuation_info, // Regions in the collection set candidates are roots for the marking (they are // not marked through considering they are very likely to be reclaimed soon. // They need to be enqueued explicitly compared to survivor regions. - if (collector_state()->in_concurrent_start_gc()) { + if (collector_state()->is_in_concurrent_start_gc()) { enqueue_candidates_as_root_regions(); } @@ -1180,7 +1180,7 @@ void G1YoungCollector::collect() { // Need to report the collection pause now since record_collection_pause_end() // modifies it to the next state. - jtm.report_pause_type(collector_state()->young_gc_pause_type(_concurrent_operation_is_full_mark)); + jtm.report_pause_type(collector_state()->gc_pause_type(_concurrent_operation_is_full_mark)); policy()->record_young_collection_end(_concurrent_operation_is_full_mark, evacuation_alloc_failed(), _allocation_word_size); } diff --git a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp index 6ba72f6cde0..2291a755cd3 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp @@ -89,8 +89,8 @@ void G1YoungGCAllocationFailureInjector::arm_if_needed() { // Now check if evacuation failure injection should be enabled for the current GC. G1CollectorState* collector_state = g1h->collector_state(); - const bool in_young_only_phase = collector_state->in_young_only_phase(); - const bool in_concurrent_start_gc = collector_state->in_concurrent_start_gc(); + const bool in_young_only_phase = collector_state->is_in_young_only_phase(); + const bool in_concurrent_start_gc = collector_state->is_in_concurrent_start_gc(); const bool in_concurrent_cycle = collector_state->is_in_concurrent_cycle(); _inject_allocation_failure_for_current_gc &= diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index 769cc9a077c..3d49b8f025b 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -501,7 +501,7 @@ class G1PostEvacuateCollectionSetCleanupTask2::ProcessEvacuationFailedRegionsTas // Concurrent mark does not mark through regions that we retain (they are root // regions wrt to marking), so we must clear their mark data (tams, bitmap, ...) // set eagerly or during evacuation failure. - bool clear_mark_data = !g1h->collector_state()->in_concurrent_start_gc() || + bool clear_mark_data = !g1h->collector_state()->is_in_concurrent_start_gc() || g1h->policy()->should_retain_evac_failed_region(r); if (clear_mark_data) { From 174183759e0545daf379ead18d796aa61fe36d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20Sikstr=C3=B6m?= Date: Mon, 23 Mar 2026 10:48:55 +0000 Subject: [PATCH 071/160] 8380281: Remove the client emulation mode Reviewed-by: ayang, kvn --- .../share/compiler/compilerDefinitions.cpp | 61 ------------------- .../share/compiler/compilerDefinitions.hpp | 4 -- .../share/runtime/abstract_vm_version.cpp | 18 +----- src/hotspot/share/runtime/arguments.cpp | 5 +- test/hotspot/jtreg/TEST.ROOT | 1 - .../arraycopy/TestArrayCopyNoInitDeopt.java | 6 +- .../TestDefaultMethodArrayCloneDeoptC2.java | 2 +- .../c2/TestReduceAllocationAndHeapDump.java | 2 +- .../stress/OverloadCompileQueueTest.java | 4 +- .../TestAESIntrinsicsOnSupportedConfig.java | 2 +- ...orphicVirtualCallAfterMorphismChanged.java | 2 +- .../intrinsics/IntrinsicAvailableTest.java | 2 +- .../intrinsics/IntrinsicDisabledTest.java | 3 +- .../bigInteger/MontgomeryMultiplyTest.java | 6 +- .../intrinsics/bmi/verifycode/AndnTestI.java | 2 +- .../intrinsics/bmi/verifycode/AndnTestL.java | 2 +- .../intrinsics/bmi/verifycode/BlsiTestI.java | 2 +- .../intrinsics/bmi/verifycode/BlsiTestL.java | 2 +- .../bmi/verifycode/BlsmskTestI.java | 2 +- .../bmi/verifycode/BlsmskTestL.java | 2 +- .../intrinsics/bmi/verifycode/BlsrTestI.java | 2 +- .../intrinsics/bmi/verifycode/BlsrTestL.java | 2 +- .../bmi/verifycode/BmiIntrinsicBase.java | 2 +- .../bmi/verifycode/BzhiTestI2L.java | 2 +- .../intrinsics/bmi/verifycode/LZcntTestI.java | 2 +- .../intrinsics/bmi/verifycode/LZcntTestL.java | 2 +- .../intrinsics/bmi/verifycode/TZcntTestI.java | 2 +- .../intrinsics/bmi/verifycode/TZcntTestL.java | 2 +- .../klass/CastNullCheckDroppingsTest.java | 6 +- .../mathexact/sanity/IntrinsicBase.java | 2 +- .../TestCountedLoopSafepointBackedge.java | 2 +- .../UseCountedLoopSafepointsTest.java | 3 +- .../compiler/profiling/TestTypeProfiling.java | 6 +- .../rangechecks/TestExplicitRangeChecks.java | 3 +- .../compiler/testlibrary/CompilerUtils.java | 4 +- .../sha/predicate/IntrinsicPredicates.java | 2 +- .../compiler/tiered/NonTieredLevelsTest.java | 4 +- .../types/correctness/CorrectnessTest.java | 6 +- .../compiler/types/correctness/OffTest.java | 2 +- .../uncommontrap/TestUnstableIfTrap.java | 2 +- .../unsafe/UnsafeGetConstantField.java | 6 +- .../unsafe/UnsafeGetStableArrayElement.java | 6 +- .../whitebox/DeoptimizeRelocatedNMethod.java | 1 - .../whitebox/IsMethodCompilableTest.java | 5 +- .../TestGCBasherWithAllocateHeapAt.java | 2 +- .../stress/gcbasher/TestGCBasherWithG1.java | 2 +- .../gcbasher/TestGCBasherWithParallel.java | 2 +- .../gcbasher/TestGCBasherWithSerial.java | 2 +- .../gcbasher/TestGCBasherWithShenandoah.java | 20 +++--- .../gc/stress/gcbasher/TestGCBasherWithZ.java | 4 +- .../ReservedStackTestCompiler.java | 2 +- .../AbstractMethodErrorTest.java | 2 +- .../IncompatibleClassChangeErrorTest.java | 2 +- .../compiler/CompilerDirectivesDCMDTest.java | 2 +- .../NMethodRelocationTest.java | 1 - .../ctw/src/sun/hotspot/tools/ctw/Utils.java | 6 +- .../event/compiler/TestCompilerInlining.java | 4 +- .../runtime/TestThrowableInstrumentation.java | 2 +- test/jdk/jdk/jfr/jvm/TestJFRIntrinsic.java | 4 +- test/jtreg-ext/requires/VMProps.java | 13 ---- ...stMutuallyExclusivePlatformPredicates.java | 2 +- test/lib/jdk/test/lib/Platform.java | 4 -- 62 files changed, 87 insertions(+), 195 deletions(-) diff --git a/src/hotspot/share/compiler/compilerDefinitions.cpp b/src/hotspot/share/compiler/compilerDefinitions.cpp index 5b207334599..0e4e211453b 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.cpp +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp @@ -185,46 +185,6 @@ intx CompilerConfig::scaled_freq_log(intx freq_log, double scale) { } } -void CompilerConfig::set_client_emulation_mode_flags() { - assert(has_c1(), "Must have C1 compiler present"); - CompilationModeFlag::set_quick_only(); - - FLAG_SET_ERGO(ProfileInterpreter, false); -#if INCLUDE_JVMCI - FLAG_SET_ERGO(EnableJVMCI, false); - FLAG_SET_ERGO(UseJVMCICompiler, false); -#endif - if (FLAG_IS_DEFAULT(InitialCodeCacheSize)) { - FLAG_SET_ERGO(InitialCodeCacheSize, 160*K); - } - if (FLAG_IS_DEFAULT(ReservedCodeCacheSize)) { - FLAG_SET_ERGO(ReservedCodeCacheSize, 32*M); - } - if (FLAG_IS_DEFAULT(NonProfiledCodeHeapSize)) { - FLAG_SET_ERGO(NonProfiledCodeHeapSize, 27*M); - } - if (FLAG_IS_DEFAULT(ProfiledCodeHeapSize)) { - FLAG_SET_ERGO(ProfiledCodeHeapSize, 0); - } - if (FLAG_IS_DEFAULT(NonNMethodCodeHeapSize)) { - FLAG_SET_ERGO(NonNMethodCodeHeapSize, 5*M); - } - if (FLAG_IS_DEFAULT(CodeCacheExpansionSize)) { - FLAG_SET_ERGO(CodeCacheExpansionSize, 32*K); - } - if (FLAG_IS_DEFAULT(CICompilerCount)) { - FLAG_SET_ERGO(CICompilerCount, 1); - } -} - -bool CompilerConfig::is_compilation_mode_selected() { - return !FLAG_IS_DEFAULT(TieredCompilation) || - !FLAG_IS_DEFAULT(TieredStopAtLevel) || - !FLAG_IS_DEFAULT(CompilationMode) - JVMCI_ONLY(|| !FLAG_IS_DEFAULT(EnableJVMCI) - || !FLAG_IS_DEFAULT(UseJVMCICompiler)); -} - static bool check_legacy_flags() { JVMFlag* compile_threshold_flag = JVMFlag::flag_from_enum(FLAG_MEMBER_ENUM(CompileThreshold)); if (JVMFlagAccess::check_constraint(compile_threshold_flag, JVMFlagLimit::get_constraint(compile_threshold_flag)->constraint_func(), false) != JVMFlag::SUCCESS) { @@ -540,29 +500,11 @@ bool CompilerConfig::check_args_consistency(bool status) { return status; } -bool CompilerConfig::should_set_client_emulation_mode_flags() { -#if !COMPILER1_OR_COMPILER2 - return false; -#endif - - return has_c1() && - is_compilation_mode_selected() && - !has_c2() && - !is_jvmci_compiler(); -} - void CompilerConfig::ergo_initialize() { #if !COMPILER1_OR_COMPILER2 return; #endif - // This property is also checked when selecting the heap size. Since client - // emulation mode influences Java heap memory usage, part of the logic must - // occur before choosing the heap size. - if (should_set_client_emulation_mode_flags()) { - set_client_emulation_mode_flags(); - } - set_legacy_emulation_flags(); set_compilation_policy_flags(); @@ -581,9 +523,6 @@ void CompilerConfig::ergo_initialize() { } if (ProfileInterpreter && CompilerConfig::is_c1_simple_only()) { - if (!FLAG_IS_DEFAULT(ProfileInterpreter)) { - warning("ProfileInterpreter disabled due to client emulation mode"); - } FLAG_SET_CMDLINE(ProfileInterpreter, false); } diff --git a/src/hotspot/share/compiler/compilerDefinitions.hpp b/src/hotspot/share/compiler/compilerDefinitions.hpp index a9b052ff782..e8ba977f705 100644 --- a/src/hotspot/share/compiler/compilerDefinitions.hpp +++ b/src/hotspot/share/compiler/compilerDefinitions.hpp @@ -151,14 +151,10 @@ public: inline static CompilerType compiler_type(); - static bool should_set_client_emulation_mode_flags(); - private: - static bool is_compilation_mode_selected(); static void set_compilation_policy_flags(); static void set_jvmci_specific_flags(); static void set_legacy_emulation_flags(); - static void set_client_emulation_mode_flags(); }; #endif // SHARE_COMPILER_COMPILERDEFINITIONS_HPP diff --git a/src/hotspot/share/runtime/abstract_vm_version.cpp b/src/hotspot/share/runtime/abstract_vm_version.cpp index 4051ba3f9d6..37c5815f60e 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.cpp +++ b/src/hotspot/share/runtime/abstract_vm_version.cpp @@ -152,28 +152,14 @@ const char* Abstract_VM_Version::vm_info_string() { } case Arguments::_mixed: if (is_vm_statically_linked()) { - if (CompilationModeFlag::quick_only()) { - return CDSConfig::is_using_archive() ? "mixed mode, emulated-client, static, sharing" : "mixed mode, emulated-client, static"; - } else { - return CDSConfig::is_using_archive() ? "mixed mode, static, sharing" : "mixed mode, static"; - } + return CDSConfig::is_using_archive() ? "mixed mode, static, sharing" : "mixed mode, static"; } else { - if (CompilationModeFlag::quick_only()) { - return CDSConfig::is_using_archive() ? "mixed mode, emulated-client, sharing" : "mixed mode, emulated-client"; - } else { - return CDSConfig::is_using_archive() ? "mixed mode, sharing" : "mixed mode"; - } + return CDSConfig::is_using_archive() ? "mixed mode, sharing" : "mixed mode"; } case Arguments::_comp: if (is_vm_statically_linked()) { - if (CompilationModeFlag::quick_only()) { - return CDSConfig::is_using_archive() ? "compiled mode, emulated-client, static, sharing" : "compiled mode, emulated-client, static"; - } return CDSConfig::is_using_archive() ? "compiled mode, static, sharing" : "compiled mode, static"; } else { - if (CompilationModeFlag::quick_only()) { - return CDSConfig::is_using_archive() ? "compiled mode, emulated-client, sharing" : "compiled mode, emulated-client"; - } return CDSConfig::is_using_archive() ? "compiled mode, sharing" : "compiled mode"; } } diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 778e9c1fc6d..b853349242c 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -1513,10 +1513,7 @@ void Arguments::set_heap_size() { !FLAG_IS_DEFAULT(MinRAMPercentage) || !FLAG_IS_DEFAULT(InitialRAMPercentage); - // Limit the available memory if client emulation mode is enabled. - const size_t avail_mem = CompilerConfig::should_set_client_emulation_mode_flags() - ? 1ULL*G - : os::physical_memory(); + const size_t avail_mem = os::physical_memory(); // If the maximum heap size has not been set with -Xmx, then set it as // fraction of the size of physical memory, respecting the maximum and diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT index 2a3d3eebbbd..892529b966f 100644 --- a/test/hotspot/jtreg/TEST.ROOT +++ b/test/hotspot/jtreg/TEST.ROOT @@ -69,7 +69,6 @@ requires.properties= \ vm.gc.Z \ vm.jvmci \ vm.jvmci.enabled \ - vm.emulatedClient \ vm.cpu.features \ vm.pageSize \ vm.debug \ diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestArrayCopyNoInitDeopt.java b/test/hotspot/jtreg/compiler/arraycopy/TestArrayCopyNoInitDeopt.java index 839ff6cf36e..9e920a08b0f 100644 --- a/test/hotspot/jtreg/compiler/arraycopy/TestArrayCopyNoInitDeopt.java +++ b/test/hotspot/jtreg/compiler/arraycopy/TestArrayCopyNoInitDeopt.java @@ -25,7 +25,7 @@ * @test * @bug 8072016 * @summary Infinite deoptimization/recompilation cycles in case of arraycopy with tightly coupled allocation - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management @@ -87,8 +87,8 @@ public class TestArrayCopyNoInitDeopt { } static public void main(String[] args) throws Exception { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } // Only execute if C2 is available if (TIERED_STOP_AT_LEVEL == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) { diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestDefaultMethodArrayCloneDeoptC2.java b/test/hotspot/jtreg/compiler/arraycopy/TestDefaultMethodArrayCloneDeoptC2.java index 7f5a891ce9e..c7554dcffdb 100644 --- a/test/hotspot/jtreg/compiler/arraycopy/TestDefaultMethodArrayCloneDeoptC2.java +++ b/test/hotspot/jtreg/compiler/arraycopy/TestDefaultMethodArrayCloneDeoptC2.java @@ -27,7 +27,7 @@ * @summary C2: Access to [].clone from interfaces fails. * @library /test/lib / * - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xcomp -Xbatch -Xbootclasspath/a:. -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java b/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java index 9319da0e0b9..f65643f8d87 100644 --- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java +++ b/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java @@ -26,7 +26,7 @@ * @bug 8319784 * @summary Check that the JVM is able to dump the heap even when there are ReduceAllocationMerge in the scope. * @library /test/lib / - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @run main/othervm compiler.c2.TestReduceAllocationAndHeapDump */ diff --git a/test/hotspot/jtreg/compiler/codecache/stress/OverloadCompileQueueTest.java b/test/hotspot/jtreg/compiler/codecache/stress/OverloadCompileQueueTest.java index 548a746c3af..7122a81c1e7 100644 --- a/test/hotspot/jtreg/compiler/codecache/stress/OverloadCompileQueueTest.java +++ b/test/hotspot/jtreg/compiler/codecache/stress/OverloadCompileQueueTest.java @@ -112,9 +112,9 @@ public class OverloadCompileQueueTest implements Runnable { AVAILABLE_LEVELS = IntStream .rangeClosed(LEVEL_SIMPLE, TIERED_STOP_AT_LEVEL) .toArray(); - } else if (Platform.isServer() && !Platform.isEmulatedClient()) { + } else if (Platform.isServer()) { AVAILABLE_LEVELS = new int[] { LEVEL_FULL_OPTIMIZATION }; - } else if (Platform.isClient() || Platform.isMinimal() || Platform.isEmulatedClient()) { + } else if (Platform.isClient() || Platform.isMinimal()) { AVAILABLE_LEVELS = new int[] { LEVEL_SIMPLE }; } else { throw new Error("TESTBUG: unknown VM: " + Platform.vmName); diff --git a/test/hotspot/jtreg/compiler/cpuflags/TestAESIntrinsicsOnSupportedConfig.java b/test/hotspot/jtreg/compiler/cpuflags/TestAESIntrinsicsOnSupportedConfig.java index 59c8b2efa11..421307e85ca 100644 --- a/test/hotspot/jtreg/compiler/cpuflags/TestAESIntrinsicsOnSupportedConfig.java +++ b/test/hotspot/jtreg/compiler/cpuflags/TestAESIntrinsicsOnSupportedConfig.java @@ -76,7 +76,7 @@ public class TestAESIntrinsicsOnSupportedConfig extends AESIntrinsicsBase { prepareArguments(prepareBooleanFlag(AESIntrinsicsBase .USE_AES, true))); final String errorMessage = "Case testUseAES failed"; - if (Platform.isServer() && !Platform.isEmulatedClient() && isTieredLevelGreaterThan(3)) { + if (Platform.isServer() && isTieredLevelGreaterThan(3)) { verifyOutput(new String[]{AESIntrinsicsBase.CIPHER_INTRINSIC, AESIntrinsicsBase.AES_INTRINSIC}, null, errorMessage, outputAnalyzer); diff --git a/test/hotspot/jtreg/compiler/inlining/InlineBimorphicVirtualCallAfterMorphismChanged.java b/test/hotspot/jtreg/compiler/inlining/InlineBimorphicVirtualCallAfterMorphismChanged.java index 6f1dadd6b80..bebb3802eb9 100644 --- a/test/hotspot/jtreg/compiler/inlining/InlineBimorphicVirtualCallAfterMorphismChanged.java +++ b/test/hotspot/jtreg/compiler/inlining/InlineBimorphicVirtualCallAfterMorphismChanged.java @@ -29,7 +29,7 @@ * @modules java.base/jdk.internal.misc * @library /test/lib * @requires vm.flagless - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * * @run driver compiler.inlining.InlineBimorphicVirtualCallAfterMorphismChanged */ diff --git a/test/hotspot/jtreg/compiler/intrinsics/IntrinsicAvailableTest.java b/test/hotspot/jtreg/compiler/intrinsics/IntrinsicAvailableTest.java index abf0aa7b885..310fce78f9b 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/IntrinsicAvailableTest.java +++ b/test/hotspot/jtreg/compiler/intrinsics/IntrinsicAvailableTest.java @@ -142,7 +142,7 @@ public class IntrinsicAvailableTest extends CompilerWhiteBoxTest { public void test() throws Exception { Executable intrinsicMethod = testCase.getExecutable(); - if (Platform.isServer() && !Platform.isEmulatedClient() && (TIERED_STOP_AT_LEVEL == COMP_LEVEL_FULL_OPTIMIZATION)) { + if (Platform.isServer() && (TIERED_STOP_AT_LEVEL == COMP_LEVEL_FULL_OPTIMIZATION)) { if (TIERED_COMPILATION) { checkIntrinsicForCompilationLevel(intrinsicMethod, COMP_LEVEL_SIMPLE); } diff --git a/test/hotspot/jtreg/compiler/intrinsics/IntrinsicDisabledTest.java b/test/hotspot/jtreg/compiler/intrinsics/IntrinsicDisabledTest.java index a98bef68c9a..7eb1c855f4b 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/IntrinsicDisabledTest.java +++ b/test/hotspot/jtreg/compiler/intrinsics/IntrinsicDisabledTest.java @@ -217,8 +217,7 @@ public class IntrinsicDisabledTest { } public static void main(String args[]) { - if (Platform.isServer() && !Platform.isEmulatedClient() && - (TIERED_STOP_AT_LEVEL == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) { + if (Platform.isServer() && (TIERED_STOP_AT_LEVEL == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) { if (TIERED_COMPILATION) { test(CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE); } diff --git a/test/hotspot/jtreg/compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java b/test/hotspot/jtreg/compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java index e145a0088c0..0ca419f7dcd 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java @@ -26,7 +26,7 @@ * @test * @bug 8130150 8131779 8139907 * @summary Verify that the Montgomery multiply and square intrinsic works and correctly checks their arguments. - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @modules java.base/jdk.internal.misc:open * @modules java.base/java.math:open * @library /test/lib / @@ -313,8 +313,8 @@ public class MontgomeryMultiplyTest { } public static void main(String args[]) { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } if (wb.isIntrinsicAvailable(getExecutable(true), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) && wb.isIntrinsicAvailable(getExecutable(false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) { diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestI.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestI.java index 4145b0f5641..30ce3b36af6 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestI.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestL.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestL.java index c17e5345d21..1a3e7e1314d 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestL.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/AndnTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestI.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestI.java index 881ed37906e..71d9e52a539 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestI.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestL.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestL.java index 1ea8125b1cd..425f70f1052 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestL.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsiTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java index 38753762f39..3632d9617b8 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java index 9a279083059..8f6958d212f 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestI.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestI.java index 4ea2b3ab841..bd8c26724a1 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestI.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestL.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestL.java index 421e24d07ab..68f82a99bd3 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestL.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BlsrTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java index 0e8c8fe9514..8c6120388a1 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java @@ -83,7 +83,7 @@ public class BmiIntrinsicBase extends CompilerWhiteBoxTest { System.out.println(testCase.name()); - if (TIERED_COMPILATION && TIERED_STOP_AT_LEVEL != CompilerWhiteBoxTest.COMP_LEVEL_MAX || Platform.isEmulatedClient()) { + if (TIERED_COMPILATION && TIERED_STOP_AT_LEVEL != CompilerWhiteBoxTest.COMP_LEVEL_MAX) { System.out.println("TieredStopAtLevel value (" + TIERED_STOP_AT_LEVEL + ") is too low, test SKIPPED"); return; } diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BzhiTestI2L.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BzhiTestI2L.java index 4cf94a0eb8b..872fefd883c 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BzhiTestI2L.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/BzhiTestI2L.java @@ -23,7 +23,7 @@ /* * @test - * @requires vm.simpleArch == "x64" & vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.simpleArch == "x64" & vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestI.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestI.java index c905fca34a2..34f1eb4f3a3 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestI.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestL.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestL.java index cf49937936f..30cadaf200e 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestL.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/LZcntTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestI.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestI.java index 8a8ce4508fa..bc6c1276450 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestI.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestL.java b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestL.java index 926f071074e..b5db7b861c3 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestL.java +++ b/test/hotspot/jtreg/compiler/intrinsics/bmi/verifycode/TZcntTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/intrinsics/klass/CastNullCheckDroppingsTest.java b/test/hotspot/jtreg/compiler/intrinsics/klass/CastNullCheckDroppingsTest.java index 1828e0ba837..31449eefb33 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/klass/CastNullCheckDroppingsTest.java +++ b/test/hotspot/jtreg/compiler/intrinsics/klass/CastNullCheckDroppingsTest.java @@ -26,7 +26,7 @@ * @bug 8054492 * @summary Casting can result in redundant null checks in generated code * @requires vm.hasJFR - * @requires vm.flavor == "server" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & !vm.graal.enabled * @library /test/lib * @modules java.base/jdk.internal.misc * java.management @@ -93,8 +93,8 @@ public class CastNullCheckDroppingsTest { int[] asink; public static void main(String[] args) throws Exception { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } // Make sure background compilation is disabled if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) { diff --git a/test/hotspot/jtreg/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java b/test/hotspot/jtreg/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java index f9b2e156f0c..10ae924c794 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java +++ b/test/hotspot/jtreg/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java @@ -50,7 +50,7 @@ public abstract class IntrinsicBase extends CompilerWhiteBoxTest { int expectedIntrinsicCount = 0; - if (Platform.isServer() && !Platform.isEmulatedClient()) { + if (Platform.isServer()) { if (TIERED_COMPILATION) { int max_level = TIERED_STOP_AT_LEVEL; expectedIntrinsicCount = (max_level == COMP_LEVEL_MAX) ? 1 : 0; diff --git a/test/hotspot/jtreg/compiler/loopopts/TestCountedLoopSafepointBackedge.java b/test/hotspot/jtreg/compiler/loopopts/TestCountedLoopSafepointBackedge.java index e277f15035a..31c32f2cd2a 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestCountedLoopSafepointBackedge.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestCountedLoopSafepointBackedge.java @@ -24,7 +24,7 @@ /** * @test * @bug 8161147 - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Safepoint on backedge breaks UseCountedLoopSafepoints * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:+UseCountedLoopSafepoints TestCountedLoopSafepointBackedge * diff --git a/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java b/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java index 2a9cedfc87e..378c8e6da75 100644 --- a/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java +++ b/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java @@ -28,7 +28,7 @@ * @summary Test that C2 flag UseCountedLoopSafepoints ensures a safepoint is kept in a CountedLoop * @library /test/lib / * @requires vm.compMode != "Xint" & vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) & vm.debug == true - * @requires !vm.emulatedClient & !vm.graal.enabled + * @requires !vm.graal.enabled * @modules java.base/jdk.internal.misc * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox @@ -37,7 +37,6 @@ package compiler.loopopts; -import jdk.test.lib.Platform; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; import java.util.List; diff --git a/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java b/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java index 895589ea03f..9d64a08b691 100644 --- a/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java +++ b/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java @@ -27,7 +27,7 @@ * @summary Parameters type profiling is not performed from aarch64 interpreter * * @requires os.arch != "arm" - * @requires vm.flavor == "server" & vm.compMode == "Xmixed" & !vm.emulatedClient & !vm.graal.enabled + * @requires vm.flavor == "server" & vm.compMode == "Xmixed" & !vm.graal.enabled * * @comment the test can't be run w/ TieredStopAtLevel < 4 * @requires vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4 @@ -94,8 +94,8 @@ public class TestTypeProfiling { } static public void main(String[] args) throws Exception { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } // Only execute if C2 is available if (TIERED_STOP_AT_LEVEL != CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) { diff --git a/test/hotspot/jtreg/compiler/rangechecks/TestExplicitRangeChecks.java b/test/hotspot/jtreg/compiler/rangechecks/TestExplicitRangeChecks.java index d74a10ae02c..c48609647c1 100644 --- a/test/hotspot/jtreg/compiler/rangechecks/TestExplicitRangeChecks.java +++ b/test/hotspot/jtreg/compiler/rangechecks/TestExplicitRangeChecks.java @@ -447,8 +447,7 @@ public class TestExplicitRangeChecks { success = false; } // Only perform these additional checks if C2 is available - if (Platform.isServer() && !Platform.isEmulatedClient() && - TIERED_STOP_AT_LEVEL == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) { + if (Platform.isServer() && TIERED_STOP_AT_LEVEL == CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) { if (deoptimize && WHITE_BOX.isMethodCompiled(m)) { System.out.println(name + " not deoptimized on invalid access"); success = false; diff --git a/test/hotspot/jtreg/compiler/testlibrary/CompilerUtils.java b/test/hotspot/jtreg/compiler/testlibrary/CompilerUtils.java index 93be1971ca3..a8d953c5a30 100644 --- a/test/hotspot/jtreg/compiler/testlibrary/CompilerUtils.java +++ b/test/hotspot/jtreg/compiler/testlibrary/CompilerUtils.java @@ -53,10 +53,10 @@ public class CompilerUtils { "TieredStopAtLevel has value out of int capacity"); return IntStream.rangeClosed(1, maxLevel).toArray(); } else { - if (Platform.isServer() && !Platform.isEmulatedClient()) { + if (Platform.isServer()) { return new int[]{4}; } - if (Platform.isClient() || Platform.isMinimal() || Platform.isEmulatedClient()) { + if (Platform.isClient() || Platform.isMinimal()) { return new int[]{1}; } } diff --git a/test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java b/test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java index b088f58bb5b..52925fcf993 100644 --- a/test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java +++ b/test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java @@ -56,7 +56,7 @@ public class IntrinsicPredicates { "TieredStopAtLevel"); boolean maxLevelIsReachable = (tieredMaxLevel == IntrinsicPredicates.TIERED_MAX_LEVEL); - return Platform.isServer() && !Platform.isEmulatedClient() && (!isTiered || maxLevelIsReachable); + return Platform.isServer() && (!isTiered || maxLevelIsReachable); }; public static final BooleanSupplier MD5_INSTRUCTION_AVAILABLE diff --git a/test/hotspot/jtreg/compiler/tiered/NonTieredLevelsTest.java b/test/hotspot/jtreg/compiler/tiered/NonTieredLevelsTest.java index d23b13a65e4..fd55187a7ef 100644 --- a/test/hotspot/jtreg/compiler/tiered/NonTieredLevelsTest.java +++ b/test/hotspot/jtreg/compiler/tiered/NonTieredLevelsTest.java @@ -47,10 +47,10 @@ public class NonTieredLevelsTest extends CompLevelsTest { private static final int AVAILABLE_COMP_LEVEL; private static final IntPredicate IS_AVAILABLE_COMPLEVEL; static { - if (Platform.isServer() && !Platform.isEmulatedClient()) { + if (Platform.isServer()) { AVAILABLE_COMP_LEVEL = COMP_LEVEL_FULL_OPTIMIZATION; IS_AVAILABLE_COMPLEVEL = x -> x == COMP_LEVEL_FULL_OPTIMIZATION; - } else if (Platform.isClient() || Platform.isMinimal() || Platform.isEmulatedClient()) { + } else if (Platform.isClient() || Platform.isMinimal()) { AVAILABLE_COMP_LEVEL = COMP_LEVEL_SIMPLE; IS_AVAILABLE_COMPLEVEL = x -> x == COMP_LEVEL_SIMPLE; } else { diff --git a/test/hotspot/jtreg/compiler/types/correctness/CorrectnessTest.java b/test/hotspot/jtreg/compiler/types/correctness/CorrectnessTest.java index 895f8674ed5..08cbdb29aea 100644 --- a/test/hotspot/jtreg/compiler/types/correctness/CorrectnessTest.java +++ b/test/hotspot/jtreg/compiler/types/correctness/CorrectnessTest.java @@ -25,7 +25,7 @@ * @test CorrectnessTest * @bug 8038418 * @summary Tests correctness of type usage with type profiling and speculations - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management @@ -87,8 +87,8 @@ public class CorrectnessTest { private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); public static void main(String[] args) { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } Asserts.assertGTE(args.length, 1); ProfilingType profilingType = ProfilingType.valueOf(args[0]); diff --git a/test/hotspot/jtreg/compiler/types/correctness/OffTest.java b/test/hotspot/jtreg/compiler/types/correctness/OffTest.java index 6f2354cf895..338c818fd3a 100644 --- a/test/hotspot/jtreg/compiler/types/correctness/OffTest.java +++ b/test/hotspot/jtreg/compiler/types/correctness/OffTest.java @@ -25,7 +25,7 @@ * @test CorrectnessTest * @key randomness * @bug 8038418 - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @library /test/lib / * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java b/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java index 04293370eb5..d539b56d850 100644 --- a/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java +++ b/test/hotspot/jtreg/compiler/uncommontrap/TestUnstableIfTrap.java @@ -210,7 +210,7 @@ public class TestUnstableIfTrap { boolean isMethodCompiledAtMaxTier = WB.getMethodCompilationLevel(m) == MAX_TIER; - return Platform.isServer() && !Platform.isEmulatedClient() && isMethodCompiled + return Platform.isServer() && isMethodCompiled && (!isTiered || isMethodCompiledAtMaxTier); } diff --git a/test/hotspot/jtreg/compiler/unsafe/UnsafeGetConstantField.java b/test/hotspot/jtreg/compiler/unsafe/UnsafeGetConstantField.java index 8b20cbf8ecd..bab22bb0cd0 100644 --- a/test/hotspot/jtreg/compiler/unsafe/UnsafeGetConstantField.java +++ b/test/hotspot/jtreg/compiler/unsafe/UnsafeGetConstantField.java @@ -27,7 +27,7 @@ * @library /test/lib * @library /testlibrary/asm * - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * * @modules java.base/jdk.internal.vm.annotation * java.base/jdk.internal.misc @@ -97,8 +97,8 @@ public class UnsafeGetConstantField { static final Unsafe U = Unsafe.getUnsafe(); public static void main(String[] args) { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } testUnsafeGetAddress(); testUnsafeGetField(); diff --git a/test/hotspot/jtreg/compiler/unsafe/UnsafeGetStableArrayElement.java b/test/hotspot/jtreg/compiler/unsafe/UnsafeGetStableArrayElement.java index 9d3578bd6a1..a124c58055a 100644 --- a/test/hotspot/jtreg/compiler/unsafe/UnsafeGetStableArrayElement.java +++ b/test/hotspot/jtreg/compiler/unsafe/UnsafeGetStableArrayElement.java @@ -26,7 +26,7 @@ * @summary tests on constant folding of unsafe get operations from stable arrays * @library /test/lib * @build jdk.test.whitebox.WhiteBox - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * * @modules java.base/jdk.internal.vm.annotation * java.base/jdk.internal.misc @@ -343,8 +343,8 @@ public class UnsafeGetStableArrayElement { } public static void main(String[] args) throws Exception { - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } testUnsafeAccess(); System.out.println("TEST PASSED"); diff --git a/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java b/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java index c7cf355259b..672e24ad874 100644 --- a/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java +++ b/test/hotspot/jtreg/compiler/whitebox/DeoptimizeRelocatedNMethod.java @@ -29,7 +29,6 @@ * @modules java.base/jdk.internal.misc java.management * @requires vm.opt.DeoptimizeALot != true * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) - * @requires !vm.emulatedClient * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+SegmentedCodeCache diff --git a/test/hotspot/jtreg/compiler/whitebox/IsMethodCompilableTest.java b/test/hotspot/jtreg/compiler/whitebox/IsMethodCompilableTest.java index 19f577067e7..fff6517fc34 100644 --- a/test/hotspot/jtreg/compiler/whitebox/IsMethodCompilableTest.java +++ b/test/hotspot/jtreg/compiler/whitebox/IsMethodCompilableTest.java @@ -31,7 +31,6 @@ * * @requires vm.opt.DeoptimizeALot != true * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) - * @requires !vm.emulatedClient * * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox @@ -84,8 +83,8 @@ public class IsMethodCompilableTest extends CompilerWhiteBoxTest { protected void test() throws Exception { // Only c2 compilations can be disabled through PerMethodRecompilationCutoff - if (!Platform.isServer() || Platform.isEmulatedClient()) { - throw new Error("TESTBUG: Not server mode"); + if (!Platform.isServer()) { + throw new Error("TESTBUG: Not server VM"); } if (skipXcompOSR()) { diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java index 778d398d9d2..cf837c1c3dd 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java @@ -31,7 +31,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.G1 - * @requires vm.flavor == "server" & !vm.emulatedClient & os.family != "aix" + * @requires vm.flavor == "server" & os.family != "aix" * @summary Stress Java heap allocation with AllocateHeapAt flag using GC basher. * @run main/othervm/timeout=500 -Xlog:gc*=info -Xmx256m -server -XX:+UseG1GC -XX:AllocateHeapAt=. gc.stress.gcbasher.TestGCBasherWithAllocateHeapAt 120000 */ diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java index 4f11ab361fb..a9095d73d9d 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java @@ -31,7 +31,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.G1 - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the G1 GC by trying to make old objects more likely to be garbage than young objects. * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx256m -XX:+UseG1GC gc.stress.gcbasher.TestGCBasherWithG1 120000 */ diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithParallel.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithParallel.java index ee5ab654462..1bb71531abd 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithParallel.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithParallel.java @@ -31,7 +31,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Parallel - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the Parallel GC by trying to make old objects more likely to be garbage than young objects. * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx256m -XX:+UseParallelGC -XX:-UseGCOverheadLimit gc.stress.gcbasher.TestGCBasherWithParallel 120000 */ diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithSerial.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithSerial.java index 25650b6b9d5..ff6ad786302 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithSerial.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithSerial.java @@ -31,7 +31,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Serial - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the Serial GC by trying to make old objects more likely to be garbage than young objects. * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx256m -XX:+UseSerialGC gc.stress.gcbasher.TestGCBasherWithSerial 120000 */ diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java index 0d1a4af9a2a..3bf0e59dce3 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java @@ -32,7 +32,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the Shenandoah GC by trying to make old objects more likely to be garbage than young objects. * * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -51,7 +51,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the Shenandoah GC by trying to make old objects more likely to be garbage than young objects. * * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -74,7 +74,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the Shenandoah GC by trying to make old objects more likely to be garbage than young objects. * * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -92,7 +92,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. * * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -105,7 +105,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. * * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -126,7 +126,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. * * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -152,7 +152,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. * * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -172,7 +172,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. * * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -185,7 +185,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress the Shenandoah GC by trying to make old objects more likely to be garbage than young objects. * * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions @@ -203,7 +203,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. * * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithZ.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithZ.java index d4a95fd9791..2007580f065 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithZ.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithZ.java @@ -31,7 +31,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Z - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @summary Stress ZGC * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx384m -XX:+UseZGC gc.stress.gcbasher.TestGCBasherWithZ 120000 */ @@ -41,7 +41,7 @@ import java.io.IOException; * @key stress * @library / * @requires vm.gc.Z - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false + * @requires vm.flavor == "server" & vm.opt.ClassUnloading != false * @summary Stress ZGC with nmethod barrier forced deoptimization enabled. * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx384m -XX:+UseZGC * -XX:+UnlockDiagnosticVMOptions -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline diff --git a/test/hotspot/jtreg/runtime/ReservedStack/ReservedStackTestCompiler.java b/test/hotspot/jtreg/runtime/ReservedStack/ReservedStackTestCompiler.java index ac9e41b3236..2dc6b7e56a5 100644 --- a/test/hotspot/jtreg/runtime/ReservedStack/ReservedStackTestCompiler.java +++ b/test/hotspot/jtreg/runtime/ReservedStack/ReservedStackTestCompiler.java @@ -25,7 +25,7 @@ * @test ReservedStackTestCompiler * @summary Run ReservedStackTest with dedicated compilers C1 and C2. * - * @requires vm.flavor == "server" & !vm.emulatedClient + * @requires vm.flavor == "server" * @requires vm.opt.DeoptimizeALot != true * @library /test/lib * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/AbstractMethodError/AbstractMethodErrorTest.java b/test/hotspot/jtreg/runtime/exceptionMsgs/AbstractMethodError/AbstractMethodErrorTest.java index a7063ecd2dc..508dcf197e3 100644 --- a/test/hotspot/jtreg/runtime/exceptionMsgs/AbstractMethodError/AbstractMethodErrorTest.java +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/AbstractMethodError/AbstractMethodErrorTest.java @@ -25,7 +25,7 @@ /** * @test * @summary Check that the verbose message of the AME is printed correctly. - * @requires !(os.arch=="arm") & vm.flavor == "server" & !vm.emulatedClient & vm.compMode=="Xmixed" & !vm.graal.enabled & vm.opt.UseJVMCICompiler != true & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel==4) + * @requires !(os.arch=="arm") & vm.flavor == "server" & vm.compMode=="Xmixed" & !vm.graal.enabled & vm.opt.UseJVMCICompiler != true & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel==4) * @requires vm.opt.DeoptimizeALot != true * @library /test/lib / * @build jdk.test.whitebox.WhiteBox diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/IncompatibleClassChangeError/IncompatibleClassChangeErrorTest.java b/test/hotspot/jtreg/runtime/exceptionMsgs/IncompatibleClassChangeError/IncompatibleClassChangeErrorTest.java index 77738191f19..5891ff60c42 100644 --- a/test/hotspot/jtreg/runtime/exceptionMsgs/IncompatibleClassChangeError/IncompatibleClassChangeErrorTest.java +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/IncompatibleClassChangeError/IncompatibleClassChangeErrorTest.java @@ -26,7 +26,7 @@ * @test * @summary Check that the verbose message of ICCE is printed correctly. * The test forces errors in vtable stubs and interpreter. - * @requires !(os.arch=="arm") & vm.flavor == "server" & !vm.emulatedClient & vm.compMode=="Xmixed" & (!vm.graal.enabled | vm.opt.TieredCompilation == true) & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel==4) + * @requires !(os.arch=="arm") & vm.flavor == "server" & vm.compMode=="Xmixed" & (!vm.graal.enabled | vm.opt.TieredCompilation == true) & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel==4) * @library /test/lib / * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox diff --git a/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerDirectivesDCMDTest.java b/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerDirectivesDCMDTest.java index 5e4f73cedd5..690726317b0 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerDirectivesDCMDTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerDirectivesDCMDTest.java @@ -49,7 +49,7 @@ public class CompilerDirectivesDCMDTest { public void run(CommandExecutor executor) { - if (Platform.isServer() && !Platform.isEmulatedClient()) { + if (Platform.isServer()) { filename = System.getProperty("test.src", ".") + File.separator + "control2.txt"; } else { filename = System.getProperty("test.src", ".") + File.separator + "control1.txt"; diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java index 5b4a1c7e663..65a89b152b6 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java @@ -28,7 +28,6 @@ * @requires vm.jvmti & * vm.gc != "Epsilon" & * vm.flavor == "server" & - * !vm.emulatedClient & * (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) & * vm.compMode == "Xmixed" * @library /test/lib /test/hotspot/jtreg diff --git a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/Utils.java b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/Utils.java index ec24d4f186d..20234677929 100644 --- a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/Utils.java +++ b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/Utils.java @@ -86,12 +86,10 @@ public class Utils { INITIAL_COMP_LEVEL = 1; } else { String vmName = System.getProperty("java.vm.name"); - String vmInfo = System.getProperty("java.vm.info"); - boolean isEmulatedClient = (vmInfo != null) && vmInfo.contains("emulated-client"); - if (Utils.endsWithIgnoreCase(vmName, " Server VM") && !isEmulatedClient) { + if (Utils.endsWithIgnoreCase(vmName, " Server VM")) { INITIAL_COMP_LEVEL = 4; } else if (Utils.endsWithIgnoreCase(vmName, " Client VM") - || Utils.endsWithIgnoreCase(vmName, " Minimal VM") || isEmulatedClient) { + || Utils.endsWithIgnoreCase(vmName, " Minimal VM")) { INITIAL_COMP_LEVEL = 1; } else { throw new RuntimeException("Unknown VM: " + vmName); diff --git a/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java b/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java index 9694c054f6a..20e611fc671 100644 --- a/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java +++ b/test/jdk/jdk/jfr/event/compiler/TestCompilerInlining.java @@ -131,10 +131,10 @@ public class TestCompilerInlining { if (WHITE_BOX.getBooleanVMFlag("TieredCompilation")) { return IntStream.rangeClosed(LEVEL_SIMPLE, WHITE_BOX.getIntxVMFlag("TieredStopAtLevel").intValue()).toArray(); } - if (Platform.isServer() && !Platform.isEmulatedClient()) { + if (Platform.isServer()) { return new int[] { LEVEL_FULL_OPTIMIZATION }; } - if (Platform.isClient() || Platform.isEmulatedClient()) { + if (Platform.isClient()) { return new int[] { LEVEL_SIMPLE }; } throw new Error("TESTBUG: unknown VM"); diff --git a/test/jdk/jdk/jfr/event/runtime/TestThrowableInstrumentation.java b/test/jdk/jdk/jfr/event/runtime/TestThrowableInstrumentation.java index e1e135ab0b7..63d5dd16da9 100644 --- a/test/jdk/jdk/jfr/event/runtime/TestThrowableInstrumentation.java +++ b/test/jdk/jdk/jfr/event/runtime/TestThrowableInstrumentation.java @@ -52,7 +52,7 @@ public class TestThrowableInstrumentation { public static void main(String[] args) { // Compile Throwable:: with C1 (if available) if (!WHITE_BOX.enqueueInitializerForCompilation(java.lang.Throwable.class, COMP_LEVEL_SIMPLE)) { - if (!Platform.isServer() || isTieredCompilationEnabled() || Platform.isEmulatedClient()) { + if (!Platform.isServer() || isTieredCompilationEnabled()) { throw new RuntimeException("Unable to compile Throwable:: with C1"); } } diff --git a/test/jdk/jdk/jfr/jvm/TestJFRIntrinsic.java b/test/jdk/jdk/jfr/jvm/TestJFRIntrinsic.java index 75ed0d14ca5..502eefd5375 100644 --- a/test/jdk/jdk/jfr/jvm/TestJFRIntrinsic.java +++ b/test/jdk/jdk/jfr/jvm/TestJFRIntrinsic.java @@ -131,10 +131,10 @@ public class TestJFRIntrinsic { int maxLevel = flagValue.intValue(); return IntStream.rangeClosed(1, maxLevel).toArray(); } else { - if (Platform.isServer() && !Platform.isEmulatedClient()) { + if (Platform.isServer()) { return new int[]{4}; } - if (Platform.isClient() || Platform.isMinimal() || Platform.isEmulatedClient()) { + if (Platform.isClient() || Platform.isMinimal()) { return new int[]{1}; } } diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 70e619f3d3d..2d604a92b75 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -107,7 +107,6 @@ public class VMProps implements Callable> { map.put("vm.debug", this::vmDebug); map.put("vm.jvmci", this::vmJvmci); map.put("vm.jvmci.enabled", this::vmJvmciEnabled); - map.put("vm.emulatedClient", this::vmEmulatedClient); // vm.hasSA is "true" if the VM contains the serviceability agent // and jhsdb. map.put("vm.hasSA", this::vmHasSA); @@ -299,18 +298,6 @@ public class VMProps implements Callable> { return "" + Compiler.isJVMCIEnabled(); } - - /** - * @return true if VM runs in emulated-client mode and false otherwise. - */ - protected String vmEmulatedClient() { - String vmInfo = System.getProperty("java.vm.info"); - if (vmInfo == null) { - return errorWithMessage("Can't get 'java.vm.info' property"); - } - return "" + vmInfo.contains(" emulated-client"); - } - /** * @return supported CPU features */ diff --git a/test/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java b/test/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java index 8047b81c1e6..801874f38c2 100644 --- a/test/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java +++ b/test/lib-test/jdk/test/lib/TestMutuallyExclusivePlatformPredicates.java @@ -50,7 +50,7 @@ public class TestMutuallyExclusivePlatformPredicates { OS("isAix", "isLinux", "isOSX", "isWindows"), VM_TYPE("isClient", "isServer", "isMinimal", "isZero", "isEmbedded"), MODE("isInt", "isMixed", "isComp"), - IGNORED("isEmulatedClient", "isDebugBuild", "isFastDebugBuild", "isMusl", + IGNORED("isDebugBuild", "isFastDebugBuild", "isMusl", "isStatic", "isSlowDebugBuild", "hasSA", "isRoot", "isTieredSupported", "areCustomLoadersSupportedForCDS", "isDefaultCDSArchiveSupported", "isHardenedOSX", "hasOSXPlistEntries", "isOracleLinux7", "isOnWayland"); diff --git a/test/lib/jdk/test/lib/Platform.java b/test/lib/jdk/test/lib/Platform.java index 170e53930d8..892de2338e3 100644 --- a/test/lib/jdk/test/lib/Platform.java +++ b/test/lib/jdk/test/lib/Platform.java @@ -75,10 +75,6 @@ public class Platform { return vmInfo.contains("static"); } - public static boolean isEmulatedClient() { - return vmInfo.contains(" emulated-client"); - } - public static boolean isTieredSupported() { return (compiler != null) && compiler.contains("Tiered Compilers"); } From 9a0fde4a4992bc2a67b66bc86aeceaebe0b77cf4 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 23 Mar 2026 11:04:12 +0000 Subject: [PATCH 072/160] 8367765: G1: Merge G1UpdateRegionLivenessAndSelectForRebuild task and collection set choosing Reviewed-by: ayang, iwalulya --- .../share/gc/g1/g1CollectionSetCandidates.cpp | 10 +- .../share/gc/g1/g1CollectionSetCandidates.hpp | 5 +- .../share/gc/g1/g1CollectionSetChooser.cpp | 296 ------------------ .../share/gc/g1/g1CollectionSetChooser.hpp | 55 ---- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 8 +- .../gc/g1/g1ConcurrentMarkRemarkTasks.cpp | 107 ++++++- .../gc/g1/g1ConcurrentMarkRemarkTasks.hpp | 13 +- src/hotspot/share/gc/g1/g1Policy.cpp | 1 - .../share/gc/g1/g1RemSetTrackingPolicy.cpp | 11 +- 9 files changed, 122 insertions(+), 384 deletions(-) delete mode 100644 src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp delete mode 100644 src/hotspot/share/gc/g1/g1CollectionSetChooser.hpp diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp index d71108d4d0e..debb0bb54b7 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ */ #include "gc/g1/g1CollectionSetCandidates.inline.hpp" -#include "gc/g1/g1CollectionSetChooser.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "utilities/growableArray.hpp" @@ -250,8 +249,9 @@ void G1CollectionSetCandidates::sort_marking_by_efficiency() { _from_marking_groups.verify(); } -void G1CollectionSetCandidates::set_candidates_from_marking(G1HeapRegion** candidates, - uint num_candidates) { +void G1CollectionSetCandidates::set_candidates_from_marking(GrowableArrayCHeap* candidates) { + uint num_candidates = candidates->length(); + if (num_candidates == 0) { log_debug(gc, ergo, cset) ("No regions selected from marking."); return; @@ -273,7 +273,7 @@ void G1CollectionSetCandidates::set_candidates_from_marking(G1HeapRegion** candi current = new G1CSetCandidateGroup(); for (uint i = 0; i < num_candidates; i++) { - G1HeapRegion* r = candidates[i]; + G1HeapRegion* r = candidates->at(i); assert(!contains(r), "must not contain region %u", r->hrm_index()); _contains_map[r->hrm_index()] = CandidateOrigin::Marking; diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp index 7e882de7e5a..8a2235cf89c 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -245,8 +245,7 @@ public: // Merge collection set candidates from marking into the current marking candidates // (which needs to be empty). - void set_candidates_from_marking(G1HeapRegion** candidates, - uint num_candidates); + void set_candidates_from_marking(GrowableArrayCHeap* selected); // The most recent length of the list that had been merged last via // set_candidates_from_marking(). Used for calculating minimum collection set // regions. diff --git a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp b/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp deleted file mode 100644 index e7bab32129e..00000000000 --- a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectionSetCandidates.hpp" -#include "gc/g1/g1CollectionSetChooser.hpp" -#include "gc/g1/g1HeapRegionRemSet.inline.hpp" -#include "gc/shared/space.hpp" -#include "runtime/atomic.hpp" -#include "utilities/quickSort.hpp" - -// Determine collection set candidates (from marking): For all regions determine -// whether they should be a collection set candidate. Calculate their efficiency, -// sort, and put them into the collection set candidates. -// -// Threads calculate the GC efficiency of the regions they get to process, and -// put them into some work area without sorting. At the end that array is sorted and -// moved to the destination. -class G1BuildCandidateRegionsTask : public WorkerTask { - // Work area for building the set of collection set candidates. Contains references - // to heap regions with their GC efficiencies calculated. To reduce contention - // on claiming array elements, worker threads claim parts of this array in chunks; - // Array elements may be null as threads might not get enough regions to fill - // up their chunks completely. - // Final sorting will remove them. - class G1BuildCandidateArray : public StackObj { - uint const _max_size; - uint const _chunk_size; - - G1HeapRegion** _data; - - Atomic _cur_claim_idx; - - static int compare_region_gc_efficiency(G1HeapRegion** rr1, G1HeapRegion** rr2) { - G1HeapRegion* r1 = *rr1; - G1HeapRegion* r2 = *rr2; - // Make sure that null entries are moved to the end. - if (r1 == nullptr) { - if (r2 == nullptr) { - return 0; - } else { - return 1; - } - } else if (r2 == nullptr) { - return -1; - } - - G1Policy* p = G1CollectedHeap::heap()->policy(); - double gc_efficiency1 = p->predict_gc_efficiency(r1); - double gc_efficiency2 = p->predict_gc_efficiency(r2); - - if (gc_efficiency1 > gc_efficiency2) { - return -1; - } else if (gc_efficiency1 < gc_efficiency2) { - return 1; - } else { - return 0; - } - } - - // Calculates the maximum array size that will be used. - static uint required_array_size(uint num_regions, uint chunk_size, uint num_workers) { - uint const max_waste = num_workers * chunk_size; - // The array should be aligned with respect to chunk_size. - uint const aligned_num_regions = ((num_regions + chunk_size - 1) / chunk_size) * chunk_size; - - return aligned_num_regions + max_waste; - } - - public: - G1BuildCandidateArray(uint max_num_regions, uint chunk_size, uint num_workers) : - _max_size(required_array_size(max_num_regions, chunk_size, num_workers)), - _chunk_size(chunk_size), - _data(NEW_C_HEAP_ARRAY(G1HeapRegion*, _max_size, mtGC)), - _cur_claim_idx(0) { - for (uint i = 0; i < _max_size; i++) { - _data[i] = nullptr; - } - } - - ~G1BuildCandidateArray() { - FREE_C_HEAP_ARRAY(G1HeapRegion*, _data); - } - - // Claim a new chunk, returning its bounds [from, to[. - void claim_chunk(uint& from, uint& to) { - uint result = _cur_claim_idx.add_then_fetch(_chunk_size); - assert(_max_size > result - 1, - "Array too small, is %u should be %u with chunk size %u.", - _max_size, result, _chunk_size); - from = result - _chunk_size; - to = result; - } - - // Set element in array. - void set(uint idx, G1HeapRegion* hr) { - assert(idx < _max_size, "Index %u out of bounds %u", idx, _max_size); - assert(_data[idx] == nullptr, "Value must not have been set."); - _data[idx] = hr; - } - - void sort_by_gc_efficiency() { - uint length = _cur_claim_idx.load_relaxed(); - if (length == 0) { - return; - } - for (uint i = length; i < _max_size; i++) { - assert(_data[i] == nullptr, "must be"); - } - qsort(_data, length, sizeof(_data[0]), (_sort_Fn)compare_region_gc_efficiency); - for (uint i = length; i < _max_size; i++) { - assert(_data[i] == nullptr, "must be"); - } - } - - G1HeapRegion** array() const { return _data; } - }; - - // Per-region closure. In addition to determining whether a region should be - // added to the candidates, and calculating those regions' gc efficiencies, also - // gather additional statistics. - class G1BuildCandidateRegionsClosure : public G1HeapRegionClosure { - G1BuildCandidateArray* _array; - - uint _cur_chunk_idx; - uint _cur_chunk_end; - - uint _regions_added; - - void add_region(G1HeapRegion* hr) { - if (_cur_chunk_idx == _cur_chunk_end) { - _array->claim_chunk(_cur_chunk_idx, _cur_chunk_end); - } - assert(_cur_chunk_idx < _cur_chunk_end, "Must be"); - - _array->set(_cur_chunk_idx, hr); - _cur_chunk_idx++; - - _regions_added++; - } - - public: - G1BuildCandidateRegionsClosure(G1BuildCandidateArray* array) : - _array(array), - _cur_chunk_idx(0), - _cur_chunk_end(0), - _regions_added(0) { } - - bool do_heap_region(G1HeapRegion* r) { - // Candidates from marking are always old; also keep regions that are already - // collection set candidates (some retained regions) in that list. - if (!r->is_old() || r->is_collection_set_candidate()) { - // Keep remembered sets and everything for these regions. - return false; - } - - // Can not add a region without a remembered set to the candidates. - if (!r->rem_set()->is_tracked()) { - return false; - } - - // Skip any region that is currently used as an old GC alloc region. We should - // not consider those for collection before we fill them up as the effective - // gain from them is small. I.e. we only actually reclaim from the filled part, - // as the remainder is still eligible for allocation. These objects are also - // likely to have already survived a few collections, so they might be longer - // lived anyway. - // Otherwise the Old region must satisfy the liveness condition. - bool should_add = !G1CollectedHeap::heap()->is_old_gc_alloc_region(r) && - G1CollectionSetChooser::region_occupancy_low_enough_for_evac(r->live_bytes()); - if (should_add) { - add_region(r); - } else { - r->rem_set()->clear(true /* only_cardset */); - } - return false; - } - - uint regions_added() const { return _regions_added; } - }; - - G1CollectedHeap* _g1h; - G1HeapRegionClaimer _hrclaimer; - - Atomic _num_regions_added; - - G1BuildCandidateArray _result; - - void update_totals(uint num_regions) { - if (num_regions > 0) { - _num_regions_added.add_then_fetch(num_regions); - } - } - - // Early prune (remove) regions meeting the G1HeapWastePercent criteria. That - // is, either until only the minimum amount of old collection set regions are - // available (for forward progress in evacuation) or the waste accumulated by the - // removed regions is above the maximum allowed waste. - // Updates number of candidates and reclaimable bytes given. - void prune(G1HeapRegion** data) { - G1Policy* p = G1CollectedHeap::heap()->policy(); - - uint num_candidates = _num_regions_added.load_relaxed(); - - uint min_old_cset_length = p->calc_min_old_cset_length(num_candidates); - uint num_pruned = 0; - size_t wasted_bytes = 0; - - if (min_old_cset_length >= num_candidates) { - // We take all of the candidate regions to provide some forward progress. - return; - } - - size_t allowed_waste = p->allowed_waste_in_collection_set(); - uint max_to_prune = num_candidates - min_old_cset_length; - - while (true) { - G1HeapRegion* r = data[num_candidates - num_pruned - 1]; - size_t const reclaimable = r->reclaimable_bytes(); - if (num_pruned >= max_to_prune || - wasted_bytes + reclaimable > allowed_waste) { - break; - } - r->rem_set()->clear(true /* cardset_only */); - - wasted_bytes += reclaimable; - num_pruned++; - } - - log_debug(gc, ergo, cset)("Pruned %u regions out of %u, leaving %zu bytes waste (allowed %zu)", - num_pruned, - num_candidates, - wasted_bytes, - allowed_waste); - - _num_regions_added.sub_then_fetch(num_pruned, memory_order_relaxed); - } - -public: - G1BuildCandidateRegionsTask(uint max_num_regions, uint chunk_size, uint num_workers) : - WorkerTask("G1 Build Candidate Regions"), - _g1h(G1CollectedHeap::heap()), - _hrclaimer(num_workers), - _num_regions_added(0), - _result(max_num_regions, chunk_size, num_workers) { } - - void work(uint worker_id) { - G1BuildCandidateRegionsClosure cl(&_result); - _g1h->heap_region_par_iterate_from_worker_offset(&cl, &_hrclaimer, worker_id); - update_totals(cl.regions_added()); - } - - void sort_and_prune_into(G1CollectionSetCandidates* candidates) { - _result.sort_by_gc_efficiency(); - prune(_result.array()); - candidates->set_candidates_from_marking(_result.array(), - _num_regions_added.load_relaxed()); - } -}; - -uint G1CollectionSetChooser::calculate_work_chunk_size(uint num_workers, uint num_regions) { - assert(num_workers > 0, "Active gc workers should be greater than 0"); - return MAX2(num_regions / num_workers, 1U); -} - -void G1CollectionSetChooser::build(WorkerThreads* workers, uint max_num_regions, G1CollectionSetCandidates* candidates) { - uint num_workers = workers->active_workers(); - uint chunk_size = calculate_work_chunk_size(num_workers, max_num_regions); - - G1BuildCandidateRegionsTask cl(max_num_regions, chunk_size, num_workers); - workers->run_task(&cl, num_workers); - - cl.sort_and_prune_into(candidates); - candidates->verify(); -} diff --git a/src/hotspot/share/gc/g1/g1CollectionSetChooser.hpp b/src/hotspot/share/gc/g1/g1CollectionSetChooser.hpp deleted file mode 100644 index 4db8ed23c49..00000000000 --- a/src/hotspot/share/gc/g1/g1CollectionSetChooser.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_GC_G1_G1COLLECTIONSETCHOOSER_HPP -#define SHARE_GC_G1_G1COLLECTIONSETCHOOSER_HPP - -#include "gc/g1/g1HeapRegion.hpp" -#include "gc/shared/gc_globals.hpp" -#include "memory/allStatic.hpp" -#include "runtime/globals.hpp" - -class G1CollectionSetCandidates; -class WorkerThreads; - -// Helper class to calculate collection set candidates, and containing some related -// methods. -class G1CollectionSetChooser : public AllStatic { - static uint calculate_work_chunk_size(uint num_workers, uint num_regions); - - static size_t mixed_gc_live_threshold_bytes() { - return G1HeapRegion::GrainBytes * (size_t)G1MixedGCLiveThresholdPercent / 100; - } - -public: - static bool region_occupancy_low_enough_for_evac(size_t live_bytes) { - return live_bytes < mixed_gc_live_threshold_bytes(); - } - - // Build and return set of collection set candidates sorted by decreasing gc - // efficiency. - static void build(WorkerThreads* workers, uint max_num_regions, G1CollectionSetCandidates* candidates); -}; - -#endif // SHARE_GC_G1_G1COLLECTIONSETCHOOSER_HPP diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index ab28feb520f..8fd355615d0 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -30,7 +30,6 @@ #include "gc/g1/g1CardSetMemory.hpp" #include "gc/g1/g1CardTableClaimTable.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectionSetChooser.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentMark.inline.hpp" #include "gc/g1/g1ConcurrentMarkRemarkTasks.hpp" @@ -1328,14 +1327,13 @@ void G1ConcurrentMark::remark() { _g1h->workers()->run_task(&cl, num_workers); log_debug(gc, remset, tracking)("Remembered Set Tracking update regions total %u, selected %u", - _g1h->num_committed_regions(), cl.total_selected_for_rebuild()); + _g1h->num_committed_regions(), cl.total_selected_for_rebuild()); _needs_remembered_set_rebuild = (cl.total_selected_for_rebuild() > 0); if (_needs_remembered_set_rebuild) { - // Prune rebuild candidates based on G1HeapWastePercent. - // Improves rebuild time in addition to remembered set memory usage. - G1CollectionSetChooser::build(_g1h->workers(), _g1h->num_committed_regions(), _g1h->policy()->candidates()); + GrowableArrayCHeap* selected = cl.sort_and_prune_old_selected(); + _g1h->policy()->candidates()->set_candidates_from_marking(selected); } } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp index 4eb11f6d8f6..3eda7200e25 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.cpp @@ -31,28 +31,32 @@ #include "gc/g1/g1RemSetTrackingPolicy.hpp" #include "logging/log.hpp" #include "runtime/mutexLocker.hpp" +#include "utilities/growableArray.hpp" struct G1UpdateRegionLivenessAndSelectForRebuildTask::G1OnRegionClosure : public G1HeapRegionClosure { G1CollectedHeap* _g1h; G1ConcurrentMark* _cm; - // The number of regions actually selected for rebuild. - uint _num_selected_for_rebuild; size_t _freed_bytes; uint _num_old_regions_removed; uint _num_humongous_regions_removed; - G1FreeRegionList* _local_cleanup_list; + + GrowableArrayCHeap _old_selected_for_rebuild; + uint _num_humongous_selected_for_rebuild; + + G1FreeRegionList* _cleanup_list; G1OnRegionClosure(G1CollectedHeap* g1h, G1ConcurrentMark* cm, G1FreeRegionList* local_cleanup_list) : _g1h(g1h), _cm(cm), - _num_selected_for_rebuild(0), _freed_bytes(0), _num_old_regions_removed(0), _num_humongous_regions_removed(0), - _local_cleanup_list(local_cleanup_list) {} + _old_selected_for_rebuild(16), + _num_humongous_selected_for_rebuild(0), + _cleanup_list(local_cleanup_list) {} void reclaim_empty_region_common(G1HeapRegion* hr) { assert(!hr->has_pinned_objects(), "precondition"); @@ -74,7 +78,7 @@ struct G1UpdateRegionLivenessAndSelectForRebuildTask::G1OnRegionClosure : public _num_humongous_regions_removed++; reclaim_empty_region_common(hr); - _g1h->free_humongous_region(hr, _local_cleanup_list); + _g1h->free_humongous_region(hr, _cleanup_list); }; _g1h->humongous_obj_regions_iterate(hr, on_humongous_region); @@ -85,7 +89,7 @@ struct G1UpdateRegionLivenessAndSelectForRebuildTask::G1OnRegionClosure : public _num_old_regions_removed++; reclaim_empty_region_common(hr); - _g1h->free_region(hr, _local_cleanup_list); + _g1h->free_region(hr, _cleanup_list); } bool do_heap_region(G1HeapRegion* hr) override { @@ -98,13 +102,13 @@ struct G1UpdateRegionLivenessAndSelectForRebuildTask::G1OnRegionClosure : public || hr->has_pinned_objects(); if (is_live) { const bool selected_for_rebuild = tracker->update_humongous_before_rebuild(hr); + auto on_humongous_region = [&] (G1HeapRegion* hr) { if (selected_for_rebuild) { - _num_selected_for_rebuild++; + _num_humongous_selected_for_rebuild++; } _cm->update_top_at_rebuild_start(hr); }; - _g1h->humongous_obj_regions_iterate(hr, on_humongous_region); } else { reclaim_empty_humongous_region(hr); @@ -118,7 +122,7 @@ struct G1UpdateRegionLivenessAndSelectForRebuildTask::G1OnRegionClosure : public if (is_live) { const bool selected_for_rebuild = tracker->update_old_before_rebuild(hr); if (selected_for_rebuild) { - _num_selected_for_rebuild++; + _old_selected_for_rebuild.push(hr); } _cm->update_top_at_rebuild_start(hr); } else { @@ -137,7 +141,8 @@ G1UpdateRegionLivenessAndSelectForRebuildTask::G1UpdateRegionLivenessAndSelectFo _g1h(g1h), _cm(cm), _hrclaimer(num_workers), - _total_selected_for_rebuild(0), + _old_selected_for_rebuild(128), + _num_humongous_selected_for_rebuild(0), _cleanup_list("Empty Regions After Mark List") {} G1UpdateRegionLivenessAndSelectForRebuildTask::~G1UpdateRegionLivenessAndSelectForRebuildTask() { @@ -153,8 +158,6 @@ void G1UpdateRegionLivenessAndSelectForRebuildTask::work(uint worker_id) { G1OnRegionClosure on_region_cl(_g1h, _cm, &local_cleanup_list); _g1h->heap_region_par_iterate_from_worker_offset(&on_region_cl, &_hrclaimer, worker_id); - _total_selected_for_rebuild.add_then_fetch(on_region_cl._num_selected_for_rebuild); - // Update the old/humongous region sets _g1h->remove_from_old_gen_sets(on_region_cl._num_old_regions_removed, on_region_cl._num_humongous_regions_removed); @@ -163,6 +166,9 @@ void G1UpdateRegionLivenessAndSelectForRebuildTask::work(uint worker_id) { MutexLocker x(G1RareEvent_lock, Mutex::_no_safepoint_check_flag); _g1h->decrement_summary_bytes(on_region_cl._freed_bytes); + _old_selected_for_rebuild.appendAll(&on_region_cl._old_selected_for_rebuild); + _num_humongous_selected_for_rebuild += on_region_cl._num_humongous_selected_for_rebuild; + _cleanup_list.add_ordered(&local_cleanup_list); assert(local_cleanup_list.is_empty(), "post-condition"); } @@ -172,3 +178,78 @@ uint G1UpdateRegionLivenessAndSelectForRebuildTask::desired_num_workers(uint num const uint num_regions_per_worker = 384; return (num_regions + num_regions_per_worker - 1) / num_regions_per_worker; } + +// Early prune (remove) regions meeting the G1HeapWastePercent criteria. That +// is, either until only the minimum amount of old collection set regions are +// available (for forward progress in evacuation) or the waste accumulated by the +// removed regions is above the maximum allowed waste. +// Updates number of candidates and reclaimable bytes given. +void G1UpdateRegionLivenessAndSelectForRebuildTask::prune(GrowableArrayCHeap* old_regions) { + G1Policy* p = G1CollectedHeap::heap()->policy(); + + uint num_candidates = (uint)old_regions->length(); + + uint min_old_cset_length = p->calc_min_old_cset_length(num_candidates); + uint num_pruned = 0; + size_t wasted_bytes = 0; + + if (min_old_cset_length >= num_candidates) { + // We take all of the candidate regions to provide some forward progress. + return; + } + + size_t allowed_waste = p->allowed_waste_in_collection_set(); + uint max_to_prune = num_candidates - min_old_cset_length; + + while (true) { + G1HeapRegion* r = old_regions->at(num_candidates - num_pruned - 1); + size_t const reclaimable = r->reclaimable_bytes(); + if (num_pruned >= max_to_prune || + wasted_bytes + reclaimable > allowed_waste) { + break; + } + r->rem_set()->clear(true /* cardset_only */); + + wasted_bytes += reclaimable; + num_pruned++; + } + + log_debug(gc, ergo, cset)("Pruned %u regions out of %u, leaving %zu bytes waste (allowed %zu)", + num_pruned, + num_candidates, + wasted_bytes, + allowed_waste); + + old_regions->trunc_to(num_candidates - num_pruned); +} + +static int compare_region_gc_efficiency(G1HeapRegion** rr1, G1HeapRegion** rr2) { + G1HeapRegion* r1 = *rr1; + G1HeapRegion* r2 = *rr2; + + assert(r1 != nullptr, "must be"); + assert(r2 != nullptr, "must be"); + + G1Policy* p = G1CollectedHeap::heap()->policy(); + double gc_efficiency1 = p->predict_gc_efficiency(r1); + double gc_efficiency2 = p->predict_gc_efficiency(r2); + + if (gc_efficiency1 > gc_efficiency2) { + return -1; + } else if (gc_efficiency1 < gc_efficiency2) { + return 1; + } else { + return 0; + } +} + +GrowableArrayCHeap* G1UpdateRegionLivenessAndSelectForRebuildTask::sort_and_prune_old_selected() { + // Nothing to do for the humongous candidates here. Old selected need to be pruned. + + if (_old_selected_for_rebuild.length() != 0) { + _old_selected_for_rebuild.sort(compare_region_gc_efficiency); + prune(&_old_selected_for_rebuild); + } + + return &_old_selected_for_rebuild; +} diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.hpp index a256693ff1d..6905419e2cc 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkRemarkTasks.hpp @@ -29,7 +29,7 @@ #include "gc/g1/g1HeapRegionManager.hpp" #include "gc/g1/g1HeapRegionSet.hpp" #include "gc/shared/workerThread.hpp" -#include "runtime/atomic.hpp" +#include "utilities/growableArray.hpp" class G1CollectedHeap; class G1ConcurrentMark; @@ -42,13 +42,15 @@ class G1UpdateRegionLivenessAndSelectForRebuildTask : public WorkerTask { G1ConcurrentMark* _cm; G1HeapRegionClaimer _hrclaimer; - Atomic _total_selected_for_rebuild; + GrowableArrayCHeap _old_selected_for_rebuild; + uint _num_humongous_selected_for_rebuild; // Reclaimed empty regions G1FreeRegionList _cleanup_list; struct G1OnRegionClosure; + void prune(GrowableArrayCHeap* old_regions); public: G1UpdateRegionLivenessAndSelectForRebuildTask(G1CollectedHeap* g1h, G1ConcurrentMark* cm, @@ -59,9 +61,14 @@ public: void work(uint worker_id) override; uint total_selected_for_rebuild() const { - return _total_selected_for_rebuild.load_relaxed(); + return (uint)_old_selected_for_rebuild.length() + _num_humongous_selected_for_rebuild; } + // Sort selected old regions by efficiency and prune them based on G1HeapWastePercent. + // This pruning improves rebuild time in addition to remembered set memory usage. + // Returns the set of regions selected in efficiency order. + GrowableArrayCHeap* sort_and_prune_old_selected(); + static uint desired_num_workers(uint num_regions); }; diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 1fa5aa877c0..09043c92722 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -28,7 +28,6 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectionSetCandidates.inline.hpp" -#include "gc/g1/g1CollectionSetChooser.hpp" #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" diff --git a/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp b/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp index 0c9973c520d..94f5466b8e0 100644 --- a/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp +++ b/src/hotspot/share/gc/g1/g1RemSetTrackingPolicy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,12 +23,16 @@ */ #include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectionSetChooser.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" #include "gc/g1/g1RemSetTrackingPolicy.hpp" #include "runtime/safepoint.hpp" +static bool region_occupancy_low_enough_for_evac(size_t live_bytes) { + size_t mixed_gc_live_threshold_bytes = G1HeapRegion::GrainBytes * (size_t)G1MixedGCLiveThresholdPercent / 100; + return live_bytes < mixed_gc_live_threshold_bytes; +} + void G1RemSetTrackingPolicy::update_at_allocate(G1HeapRegion* r) { assert(r->is_young() || r->is_humongous() || r->is_old(), "Region %u with unexpected heap region type %s", r->hrm_index(), r->get_type_str()); @@ -75,7 +79,8 @@ bool G1RemSetTrackingPolicy::update_old_before_rebuild(G1HeapRegion* r) { bool selected_for_rebuild = false; - if (G1CollectionSetChooser::region_occupancy_low_enough_for_evac(r->live_bytes()) && + if (region_occupancy_low_enough_for_evac(r->live_bytes()) && + !G1CollectedHeap::heap()->is_old_gc_alloc_region(r) && !r->rem_set()->is_tracked()) { r->rem_set()->set_state_updating(); selected_for_rebuild = true; From c436802bcefcdac53cefe7b7c40d06e56884e132 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Mon, 23 Mar 2026 14:17:11 +0000 Subject: [PATCH 073/160] 8380168: Obsolete unused flag UseNewLongLShift Reviewed-by: kvn, stuefe --- src/hotspot/cpu/x86/globals_x86.hpp | 3 --- src/hotspot/cpu/x86/vm_version_x86.cpp | 4 ---- src/hotspot/share/runtime/arguments.cpp | 2 ++ 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/hotspot/cpu/x86/globals_x86.hpp b/src/hotspot/cpu/x86/globals_x86.hpp index 084793dc262..6de46752790 100644 --- a/src/hotspot/cpu/x86/globals_x86.hpp +++ b/src/hotspot/cpu/x86/globals_x86.hpp @@ -117,9 +117,6 @@ define_pd_global(intx, InitArrayShortSize, 8*BytesPerLong); product(bool, UseIncDec, true, DIAGNOSTIC, \ "Use INC, DEC instructions on x86") \ \ - product(bool, UseNewLongLShift, false, \ - "Use optimized bitwise shift left") \ - \ product(bool, UseAddressNop, false, \ "Use '0F 1F [addr]' NOP instructions on x86 cpus") \ \ diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 747d662f668..4301bd328d6 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1525,10 +1525,6 @@ void VM_Version::get_processor_features() { // Use it on new AMD cpus starting from Opteron. UseAddressNop = true; } - if (supports_sse2() && FLAG_IS_DEFAULT(UseNewLongLShift)) { - // Use it on new AMD cpus starting from Opteron. - UseNewLongLShift = true; - } if (FLAG_IS_DEFAULT(UseXmmLoadAndClearUpper)) { if (supports_sse4a()) { UseXmmLoadAndClearUpper = true; // use movsd only on '10h' Opteron diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index b853349242c..dac34017e45 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -555,6 +555,8 @@ static SpecialFlag const special_jvm_flags[] = { { "NeverActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "AlwaysActAsServerClassMachine", JDK_Version::jdk(26), JDK_Version::jdk(27), JDK_Version::jdk(28) }, { "UseXMMForArrayCopy", JDK_Version::undefined(), JDK_Version::jdk(27), JDK_Version::jdk(28) }, + { "UseNewLongLShift", JDK_Version::undefined(), JDK_Version::jdk(27), JDK_Version::jdk(28) }, + #ifdef ASSERT { "DummyObsoleteTestFlag", JDK_Version::undefined(), JDK_Version::jdk(18), JDK_Version::undefined() }, #endif From 890d62bb8c6ed772a8bdf141856f86992818f264 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 23 Mar 2026 14:40:18 +0000 Subject: [PATCH 074/160] 8380564: Parallel: Rename allocate to cas_allocate_with_expansion in PSOldGen Reviewed-by: tschatzl, jsikstro --- src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp | 2 +- src/hotspot/share/gc/parallel/psOldGen.hpp | 2 +- src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 337b2ccdd10..b77294a2ac1 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -932,7 +932,7 @@ void ParallelScavengeHeap::resize_after_full_gc() { } HeapWord* ParallelScavengeHeap::allocate_loaded_archive_space(size_t size) { - return _old_gen->allocate(size); + return _old_gen->cas_allocate_with_expansion(size); } void ParallelScavengeHeap::complete_loaded_archive_space(MemRegion archive_space) { diff --git a/src/hotspot/share/gc/parallel/psOldGen.hpp b/src/hotspot/share/gc/parallel/psOldGen.hpp index 7e3975036d4..c8e6ada3ebd 100644 --- a/src/hotspot/share/gc/parallel/psOldGen.hpp +++ b/src/hotspot/share/gc/parallel/psOldGen.hpp @@ -110,7 +110,7 @@ class PSOldGen : public CHeapObj { void shrink(size_t bytes); // Used by GC-workers during GC or for CDS at startup. - HeapWord* allocate(size_t word_size) { + HeapWord* cas_allocate_with_expansion(size_t word_size) { HeapWord* res; do { res = cas_allocate_noexpand(word_size); diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp index 9e904e44b22..68370a33a54 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp @@ -204,13 +204,13 @@ inline HeapWord* PSPromotionManager::allocate_in_old_gen(Klass* klass, // Do we allocate directly, or flush and refill? if (obj_size > (OldPLABSize / 2)) { // Allocate this object directly - result = old_gen()->allocate(obj_size); + result = old_gen()->cas_allocate_with_expansion(obj_size); promotion_trace_event(cast_to_oop(result), klass, obj_size, age, true, nullptr); } else { // Flush and fill _old_lab.flush(); - HeapWord* lab_base = old_gen()->allocate(OldPLABSize); + HeapWord* lab_base = old_gen()->cas_allocate_with_expansion(OldPLABSize); if (lab_base != nullptr) { _old_lab.initialize(MemRegion(lab_base, OldPLABSize)); // Try the old lab allocation again. From 00ee63e99e4d81ab522bd3fca5c42a5a3d7a830e Mon Sep 17 00:00:00 2001 From: Ashay Rane <253344819+raneashay@users.noreply.github.com> Date: Mon, 23 Mar 2026 14:58:17 +0000 Subject: [PATCH 075/160] 8380475: C2: Improve AndNode::Identity with KnownBits Reviewed-by: mchevalier, qamai --- src/hotspot/share/opto/mulnode.cpp | 73 +-- .../compiler/c2/irTests/TestShiftAndMask.java | 422 ++++++++++-------- 2 files changed, 256 insertions(+), 239 deletions(-) diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index b0aef9b165e..d7022b5f7ec 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -651,31 +651,19 @@ Node* AndINode::Identity(PhaseGVN* phase) { return in(1); } - Node* in1 = in(1); - uint op = in1->Opcode(); - const TypeInt* t2 = phase->type(in(2))->isa_int(); - if (t2 && t2->is_con()) { - int con = t2->get_con(); - // Masking off high bits which are always zero is useless. - const TypeInt* t1 = phase->type(in(1))->isa_int(); - if (t1 != nullptr && t1->_lo >= 0) { - jint t1_support = right_n_bits(1 + log2i_graceful(t1->_hi)); - if ((t1_support & con) == t1_support) - return in1; - } - // Masking off the high bits of a unsigned-shift-right is not - // needed either. - if (op == Op_URShiftI) { - const TypeInt* t12 = phase->type(in1->in(2))->isa_int(); - if (t12 && t12->is_con()) { // Shift is by a constant - int shift = t12->get_con(); - shift &= BitsPerJavaInteger - 1; // semantics of Java shifts - int mask = max_juint >> shift; - if ((mask & con) == mask) // If AND is useless, skip it - return in1; - } - } + const TypeInt* t1 = phase->type(in(1))->is_int(); + const TypeInt* t2 = phase->type(in(2))->is_int(); + + if ((~t1->_bits._ones & ~t2->_bits._zeros) == 0) { + // All bits that might be 0 in in1 are known to be 0 in in2 + return in(2); } + + if ((~t2->_bits._ones & ~t1->_bits._zeros) == 0) { + // All bits that might be 0 in in2 are known to be 0 in in1 + return in(1); + } + return MulNode::Identity(phase); } @@ -779,32 +767,19 @@ Node* AndLNode::Identity(PhaseGVN* phase) { return in(1); } - Node *usr = in(1); - const TypeLong *t2 = phase->type( in(2) )->isa_long(); - if( t2 && t2->is_con() ) { - jlong con = t2->get_con(); - // Masking off high bits which are always zero is useless. - const TypeLong* t1 = phase->type( in(1) )->isa_long(); - if (t1 != nullptr && t1->_lo >= 0) { - int bit_count = log2i_graceful(t1->_hi) + 1; - jlong t1_support = jlong(max_julong >> (BitsPerJavaLong - bit_count)); - if ((t1_support & con) == t1_support) - return usr; - } - uint lop = usr->Opcode(); - // Masking off the high bits of a unsigned-shift-right is not - // needed either. - if( lop == Op_URShiftL ) { - const TypeInt *t12 = phase->type( usr->in(2) )->isa_int(); - if( t12 && t12->is_con() ) { // Shift is by a constant - int shift = t12->get_con(); - shift &= BitsPerJavaLong - 1; // semantics of Java shifts - jlong mask = max_julong >> shift; - if( (mask&con) == mask ) // If AND is useless, skip it - return usr; - } - } + const TypeLong* t1 = phase->type(in(1))->is_long(); + const TypeLong* t2 = phase->type(in(2))->is_long(); + + if ((~t1->_bits._ones & ~t2->_bits._zeros) == 0) { + // All bits that might be 0 in in1 are known to be 0 in in2 + return in(2); } + + if ((~t2->_bits._ones & ~t1->_bits._zeros) == 0) { + // All bits that might be 0 in in2 are known to be 0 in in1 + return in(1); + } + return MulNode::Identity(phase); } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java b/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java index 1413ee0cafa..8f798a1b7eb 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestShiftAndMask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Red Hat, Inc. All rights reserved. + * Copyright (c) 2021, 2026, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,13 +24,14 @@ package compiler.c2.irTests; import compiler.lib.ir_framework.*; -import jdk.test.lib.Utils; -import java.util.Random; +import static compiler.lib.generators.Generators.G; +import jdk.test.lib.Asserts; + import java.util.Objects; /* * @test - * @bug 8277850 8278949 8285793 8346664 + * @bug 8277850 8278949 8285793 8346664 8380475 * @key randomness * @summary C2: optimize mask checks in counted loops * @library /test/lib / @@ -38,33 +39,31 @@ import java.util.Objects; */ public class TestShiftAndMask { - private static final Random RANDOM = Utils.getRandomInstance(); - public static void main(String[] args) { TestFramework.run(); } // any X << INT_MASK_WIDTH is zero under any INT_MASK - static final int INT_MASK_WIDTH = 1 + RANDOM.nextInt(30); + static final int INT_MASK_WIDTH = G.ints().restricted(1, 30).next(); static final int INT_MAX_MASK = (1 << INT_MASK_WIDTH) - 1; - static final int INT_MASK = 1 + RANDOM.nextInt(INT_MAX_MASK); - static final int INT_MASK2 = 1 + RANDOM.nextInt(INT_MAX_MASK); - static final int INT_ZERO_CONST = RANDOM.nextInt() << INT_MASK_WIDTH; + static final int INT_MASK = G.ints().restricted(1, INT_MAX_MASK).next(); + static final int INT_MASK2 = G.ints().restricted(1, INT_MAX_MASK).next(); + static final int INT_ZERO_CONST = G.ints().next() << INT_MASK_WIDTH; - static final int INT_RANDOM_CONST = RANDOM.nextInt(); - static final int INT_RANDOM_SHIFT = RANDOM.nextInt(); - static final int INT_RANDOM_MASK = RANDOM.nextInt(); + static final int INT_RANDOM_CONST = G.ints().next(); + static final int INT_RANDOM_SHIFT = G.ints().next(); + static final int INT_RANDOM_MASK = G.ints().next(); // any X << LONG_MASK_WIDTH is zero under any LONG_MASK - static final int LONG_MASK_WIDTH = 1 + RANDOM.nextInt(62); + static final int LONG_MASK_WIDTH = G.ints().restricted(1, 62).next(); static final long LONG_MAX_MASK = (1L << LONG_MASK_WIDTH) - 1; - static final long LONG_MASK = 1 + RANDOM.nextLong(LONG_MAX_MASK); - static final long LONG_MASK2 = 1 + RANDOM.nextLong(LONG_MAX_MASK); - static final long LONG_ZERO_CONST = RANDOM.nextLong() << LONG_MASK_WIDTH; + static final long LONG_MASK = G.longs().restricted(1L, LONG_MAX_MASK).next(); + static final long LONG_MASK2 = G.longs().restricted(1L, LONG_MAX_MASK).next(); + static final long LONG_ZERO_CONST = G.longs().next() << LONG_MASK_WIDTH; - static final long LONG_RANDOM_CONST = RANDOM.nextLong(); - static final long LONG_RANDOM_SHIFT = RANDOM.nextLong(); - static final long LONG_RANDOM_MASK = RANDOM.nextLong(); + static final long LONG_RANDOM_CONST = G.longs().next(); + static final long LONG_RANDOM_SHIFT = G.longs().next(); + static final long LONG_RANDOM_MASK = G.longs().next(); @Test public static int intSumAndMask(int i, int j) { @@ -73,12 +72,9 @@ public class TestShiftAndMask { @Run(test = { "intSumAndMask" }) public static void checkIntSumAndMask() { - int j = RANDOM.nextInt(); - int i = RANDOM.nextInt(); - int res = intSumAndMask(i, j); - if (res != ((j + i << INT_RANDOM_SHIFT + INT_RANDOM_CONST) & INT_RANDOM_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int j = G.ints().next(); + int i = G.ints().next(); + Asserts.assertEquals(intSumAndMask(i, j), (j + i << INT_RANDOM_SHIFT + INT_RANDOM_CONST) & INT_RANDOM_MASK); } @Test @@ -90,9 +86,7 @@ public class TestShiftAndMask { @Check(test = "shiftMaskInt") public static void checkShiftMaskInt(int res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0); } @Test @@ -102,12 +96,9 @@ public class TestShiftAndMask { @Run(test = { "longSumAndMask" }) public static void checkLongSumAndMask() { - long j = RANDOM.nextLong(); - long i = RANDOM.nextLong(); - long res = longSumAndMask(i, j); - if (res != ((j + i << LONG_RANDOM_SHIFT + LONG_RANDOM_CONST) & LONG_RANDOM_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + long j = G.longs().next(); + long i = G.longs().next(); + Asserts.assertEquals(longSumAndMask(i, j), (j + i << LONG_RANDOM_SHIFT + LONG_RANDOM_CONST) & LONG_RANDOM_MASK); } @Test @@ -120,9 +111,7 @@ public class TestShiftAndMask { @Check(test = "shiftMaskLong") public static void checkShiftMaskLong(long res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0L); } static volatile int barrier; @@ -143,9 +132,7 @@ public class TestShiftAndMask { @Check(test = "shiftNonConstMaskInt") public static void checkShiftNonConstMaskInt(int res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0); } @Test @@ -164,9 +151,7 @@ public class TestShiftAndMask { @Check(test = "shiftNonConstMaskLong") public static void checkShiftNonConstMaskLong(long res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0L); } @Test @@ -178,12 +163,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskInt") public static void addShiftMaskInt_runner() { - int i = RANDOM.nextInt(); - int j = RANDOM.nextInt(); - int res = addShiftMaskInt(i, j); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.ints().next(); + int j = G.ints().next(); + Asserts.assertEquals(addShiftMaskInt(i, j), j & INT_MASK); } @Test @@ -195,12 +177,9 @@ public class TestShiftAndMask { @Run(test = "addShiftPlusConstMaskInt") public static void addShiftPlusConstMaskInt_runner() { - int i = RANDOM.nextInt(); - int j = RANDOM.nextInt(); - int res = addShiftPlusConstMaskInt(i, j); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.ints().next(); + int j = G.ints().next(); + Asserts.assertEquals(addShiftPlusConstMaskInt(i, j), j & INT_MASK); } @Test @@ -233,16 +212,10 @@ public class TestShiftAndMask { @Run(test = "addSshiftNonConstMaskInt") public static void addSshiftNonConstMaskInt_runner() { - int i = RANDOM.nextInt(); - int j = RANDOM.nextInt(); - int res = addSshiftNonConstMaskInt(i, j, true); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } - res = addSshiftNonConstMaskInt(i, j, false); - if (res != (j & INT_MASK2)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.ints().next(); + int j = G.ints().next(); + Asserts.assertEquals(addSshiftNonConstMaskInt(i, j, true), j & INT_MASK); + Asserts.assertEquals(addSshiftNonConstMaskInt(i, j, false), j & INT_MASK2); } @Test @@ -261,15 +234,9 @@ public class TestShiftAndMask { @Run(test = "addConstNonConstMaskInt") public static void addConstNonConstMaskInt_runner() { - int j = RANDOM.nextInt(); - int res = addConstNonConstMaskInt(j, true); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } - res = addConstNonConstMaskInt(j, false); - if (res != (j & INT_MASK2)) { - throw new RuntimeException("incorrect result: " + res); - } + int j = G.ints().next(); + Asserts.assertEquals(addConstNonConstMaskInt(j, true), j & INT_MASK); + Asserts.assertEquals(addConstNonConstMaskInt(j, false), j & INT_MASK2); } @Test @@ -281,12 +248,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskLong") public static void addShiftMaskLong_runner() { - long i = RANDOM.nextLong(); - long j = RANDOM.nextLong(); - long res = addShiftMaskLong(i, j); - if (res != (j & LONG_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.longs().next(); + long j = G.longs().next(); + Asserts.assertEquals(addShiftMaskLong(i, j), j & LONG_MASK); } @Test @@ -298,12 +262,9 @@ public class TestShiftAndMask { @Run(test = "addShiftPlusConstMaskLong") public static void addShiftPlusConstMaskLong_runner() { - long i = RANDOM.nextLong(); - long j = RANDOM.nextLong(); - long res = addShiftPlusConstMaskLong(i, j); - if (res != (j & LONG_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.longs().next(); + long j = G.longs().next(); + Asserts.assertEquals(addShiftPlusConstMaskLong(i, j), j & LONG_MASK); } @Test @@ -322,16 +283,10 @@ public class TestShiftAndMask { @Run(test = "addSshiftNonConstMaskLong") public static void addSshiftNonConstMaskLong_runner() { - long i = RANDOM.nextLong(); - long j = RANDOM.nextLong(); - long res = addSshiftNonConstMaskLong(i, j, true); - if (res != (j & LONG_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } - res = addSshiftNonConstMaskLong(i, j, false); - if (res != (j & LONG_MASK2)) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.longs().next(); + long j = G.longs().next(); + Asserts.assertEquals(addSshiftNonConstMaskLong(i, j, true), j & LONG_MASK); + Asserts.assertEquals(addSshiftNonConstMaskLong(i, j, false), j & LONG_MASK2); } @Test @@ -350,15 +305,9 @@ public class TestShiftAndMask { @Run(test = "addConstNonConstMaskLong") public static void addConstNonConstMaskLong_runner() { - long j = RANDOM.nextLong(); - long res = addConstNonConstMaskLong(j, true); - if (res != (j & LONG_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } - res = addConstNonConstMaskLong(j, false); - if (res != (j & LONG_MASK2)) { - throw new RuntimeException("incorrect result: " + res); - } + long j = G.longs().next(); + Asserts.assertEquals(addConstNonConstMaskLong(j, true), j & LONG_MASK); + Asserts.assertEquals(addConstNonConstMaskLong(j, false), j & LONG_MASK2); } @Test @@ -370,9 +319,7 @@ public class TestShiftAndMask { @Check(test = "addShiftMaskInt2") public static void checkAddShiftMaskInt2(int res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0); } @Test @@ -384,9 +331,7 @@ public class TestShiftAndMask { @Check(test = "addShiftMaskLong2") public static void checkAddShiftMaskLong2(long res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0L); } // Try to get add inputs swapped compared to other tests @@ -401,12 +346,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskInt3") public static void addShiftMaskInt3_runner() { - int i = RANDOM.nextInt(); - int j = RANDOM.nextInt(); - int res = addShiftMaskInt3(i, j); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.ints().next(); + int j = G.ints().next(); + Asserts.assertEquals(addShiftMaskInt3(i, j), j & INT_MASK); } @Test @@ -420,12 +362,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskLong3") public static void addShiftMaskLong3_runner() { - long i = RANDOM.nextLong(); - float j = RANDOM.nextFloat(); - long res = addShiftMaskLong3(i, j); - if (res != (((long) j) & LONG_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.longs().next(); + float j = G.floats().next(); + Asserts.assertEquals(addShiftMaskLong3(i, j), ((long) j) & LONG_MASK); } @Test @@ -437,9 +376,7 @@ public class TestShiftAndMask { @Check(test = "shiftConvMask") public static void checkShiftConvMask(long res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0L); } @Test @@ -458,9 +395,7 @@ public class TestShiftAndMask { @Check(test = "shiftNotConstConvMask") public static void checkShiftNotConstConvMask(long res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0L); } @Test @@ -472,12 +407,9 @@ public class TestShiftAndMask { @Run(test = "addShiftConvMask") public static void addShiftConvMask_runner() { - int i = RANDOM.nextInt(); - long j = RANDOM.nextLong(); - long res = addShiftConvMask(i, j); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.ints().next(); + long j = G.longs().next(); + Asserts.assertEquals(addShiftConvMask(i, j), j & INT_MASK); } @Test @@ -489,9 +421,7 @@ public class TestShiftAndMask { @Check(test = "addShiftConvMask2") public static void checkAddShiftConvMask2(long res) { - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + Asserts.assertEquals(res, 0L); } @Test @@ -502,11 +432,8 @@ public class TestShiftAndMask { @Run(test = "shiftMaskIntCheckIndex") public static void shiftMaskIntCheckIndex_runner() { - int i = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - int res = shiftMaskIntCheckIndex(i, (i << INT_MASK_WIDTH) + 1); - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + Asserts.assertEquals(shiftMaskIntCheckIndex(i, (i << INT_MASK_WIDTH) + 1), 0); } @Test @@ -517,11 +444,8 @@ public class TestShiftAndMask { @Run(test = "shiftMaskLongCheckIndex") public static void shiftMaskLongCheckIndex_runner() { - long i = RANDOM.nextLong((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH); - long res = shiftMaskLongCheckIndex(i, (i << LONG_MASK_WIDTH) + 1); - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.uniformLongs(0L, ((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH) - 1).next(); + Asserts.assertEquals(shiftMaskLongCheckIndex(i, (i << LONG_MASK_WIDTH) + 1), 0L); } @Test @@ -533,12 +457,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskIntCheckIndex") public static void addShiftMaskIntCheckIndex_runner() { - int i = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - int j = RANDOM.nextInt(); - int res = addShiftMaskIntCheckIndex(i, j, (i << INT_MASK_WIDTH) + 1); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + int j = G.ints().next(); + Asserts.assertEquals(addShiftMaskIntCheckIndex(i, j, (i << INT_MASK_WIDTH) + 1), j & INT_MASK); } @Test @@ -550,12 +471,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskLongCheckIndex") public static void addShiftMaskLongCheckIndex_runner() { - long i = RANDOM.nextLong((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH); - long j = RANDOM.nextLong(); - long res = addShiftMaskLongCheckIndex(i, j, (i << LONG_MASK_WIDTH) + 1); - if (res != (j & LONG_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.uniformLongs(0L, ((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH) - 1).next(); + long j = G.longs().next(); + Asserts.assertEquals(addShiftMaskLongCheckIndex(i, j, (i << LONG_MASK_WIDTH) + 1), j & LONG_MASK); } @Test @@ -567,12 +485,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskIntCheckIndex2") public static void addShiftMaskIntCheckIndex2_runner() { - int i = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - int j = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - int res = addShiftMaskIntCheckIndex2(i, j, (Integer.max(i, j) << INT_MASK_WIDTH) + 1); - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + int j = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + Asserts.assertEquals(addShiftMaskIntCheckIndex2(i, j, (Integer.max(i, j) << INT_MASK_WIDTH) + 1), 0); } @Test @@ -583,12 +498,9 @@ public class TestShiftAndMask { @Run(test = "addShiftMaskLongCheckIndex2") public static void addShiftMaskLongCheckIndex2_runner() { - long i = RANDOM.nextLong((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH); - long j = RANDOM.nextLong((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH); - long res = addShiftMaskLongCheckIndex2(i, j, (Long.max(i, j) << LONG_MASK_WIDTH) + 1); - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + long i = G.uniformLongs(0L, ((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH) - 1).next(); + long j = G.uniformLongs(0L, ((Long.MAX_VALUE - 1) >> LONG_MASK_WIDTH) - 1).next(); + Asserts.assertEquals(addShiftMaskLongCheckIndex2(i, j, (Long.max(i, j) << LONG_MASK_WIDTH) + 1), 0L); } @Test @@ -599,11 +511,8 @@ public class TestShiftAndMask { @Run(test = "shiftConvMaskCheckIndex") public static void shiftConvMaskCheckIndex_runner() { - int i = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - long res = shiftConvMaskCheckIndex(i, (i << INT_MASK_WIDTH) + 1); - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + Asserts.assertEquals(shiftConvMaskCheckIndex(i, (i << INT_MASK_WIDTH) + 1), 0L); } @Test @@ -615,12 +524,9 @@ public class TestShiftAndMask { @Run(test = "addShiftConvMaskCheckIndex") public static void addShiftConvMaskCheckIndex_runner() { - int i = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - long j = RANDOM.nextLong(); - long res = addShiftConvMaskCheckIndex(i, j, (i << INT_MASK_WIDTH) + 1); - if (res != (j & INT_MASK)) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + long j = G.longs().next(); + Asserts.assertEquals(addShiftConvMaskCheckIndex(i, j, (i << INT_MASK_WIDTH) + 1), j & INT_MASK); } @Test @@ -631,11 +537,147 @@ public class TestShiftAndMask { @Run(test = "addShiftConvMaskCheckIndex2") public static void addShiftConvMaskCheckIndex2_runner() { - int i = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - int j = RANDOM.nextInt((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH); - long res = addShiftConvMaskCheckIndex2(i, j, (Integer.max(i, j) << INT_MASK_WIDTH) + 1); - if (res != 0) { - throw new RuntimeException("incorrect result: " + res); - } + int i = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + int j = G.uniformInts(0, ((Integer.MAX_VALUE - 1) >> INT_MASK_WIDTH) - 1).next(); + Asserts.assertEquals(addShiftConvMaskCheckIndex2(i, j, (Integer.max(i, j) << INT_MASK_WIDTH) + 1), 0L); + } + + @Test + @IR(failOn = {IRNode.AND_I}) + @IR(counts = {IRNode.LSHIFT_I, "1"}) + public int shiftLeftWithLowMaskInt(int x) { + return (x << INT_MASK_WIDTH) & (-1 << INT_MASK_WIDTH); // transformed to: return x << INT_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_L}) + @IR(counts = {IRNode.LSHIFT_L, "1"}) + public long shiftLeftWithLowMaskLong(long x) { + return (x << LONG_MASK_WIDTH) & (-1 << LONG_MASK_WIDTH); // transformed to: return x << LONG_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_I}) + @IR(counts = {IRNode.LSHIFT_I, "1"}) + public int shiftLeftWithLowMaskSmallInt(int x) { + return (x << INT_MASK_WIDTH) & (-1 << (INT_MASK_WIDTH - 1)); // transformed to: return x << INT_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_L}) + @IR(counts = {IRNode.LSHIFT_L, "1"}) + public long shiftLeftWithLowMaskSmallLong(long x) { + return (x << LONG_MASK_WIDTH) & (-1 << (LONG_MASK_WIDTH - 1)); // transformed to: return x << LONG_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_I}) + @IR(counts = {IRNode.LSHIFT_I, "1"}) + public static int shiftLeftWithLowMaskIntReversed(int x) { + return (-1 << INT_MASK_WIDTH) & (x << INT_MASK_WIDTH); // transformed to: return x << INT_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_L}) + @IR(counts = {IRNode.LSHIFT_L, "1"}) + public static long shiftLeftWithLowMaskLongReversed(long x) { + return (-1 << LONG_MASK_WIDTH) & (x << LONG_MASK_WIDTH); // transformed to: return x << LONG_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_I}) + @IR(counts = {IRNode.LSHIFT_I, "1"}) + public static int shiftLeftWithLowMaskSmallIntReversed(int x) { + return (-1 << (INT_MASK_WIDTH - 1)) & (x << INT_MASK_WIDTH); // transformed to: return x << INT_MASK_WIDTH; + } + + @Test + @IR(failOn = {IRNode.AND_L}) + @IR(counts = {IRNode.LSHIFT_L, "1"}) + public static long shiftLeftWithLowMaskSmallLongReversed(long x) { + return (-1 << (LONG_MASK_WIDTH - 1)) & (x << LONG_MASK_WIDTH); // transformed to: return x << LONG_MASK_WIDTH; + } + + @Test + @IR(counts = {IRNode.AND_I, "1"}) + public int andMaskNonNegativeInt(int x) { + return (x & 0x7FFF) & 0xFFFF; // transformed to: return x & 0x7FFF; + } + + @Test + @IR(counts = {IRNode.AND_L, "1"}) + public long andMaskNonNegativeLong(long x) { + return (x & 0x7FFFL) & 0xFFFFL; // transformed to: return x & 0x7FFFL; + } + + @Test + @IR(counts = {IRNode.AND_I, "1"}) + public int andMaskNonNegativeIntReversed(int x) { + return 0xFFFF & (x & 0x7FFF); // transformed to: return x & 0x7FFF; + } + + @Test + @IR(counts = {IRNode.AND_L, "1"}) + public long andMaskNonNegativeLongReversed(long x) { + return 0xFFFFL & (x & 0x7FFFL); // transformed to: return x & 0x7FFFL; + } + + @Test + @IR(failOn = {IRNode.AND_I}) + @IR(counts = {IRNode.URSHIFT_I, "1"}) + public int andAfterURShiftInt(int x) { + return (x >>> 8) & 0x00FFFFFF; // transformed to return x >>> 8; + } + + @Test + @IR(failOn = {IRNode.AND_L}) + @IR(counts = {IRNode.URSHIFT_L, "1"}) + public long andAfterURShiftLong(long x) { + return (x >>> 16) & 0x0000FFFFFFFFFFFFL; // transformed to return x >>> 16; + } + + @Test + @IR(failOn = {IRNode.AND_I}) + @IR(counts = {IRNode.URSHIFT_I, "1"}) + public int andAfterURShiftIntReversed(int x) { + return 0x00FFFFFF & (x >>> 8); // transformed to return x >>> 8; + } + + @Test + @IR(failOn = {IRNode.AND_L}) + @IR(counts = {IRNode.URSHIFT_L, "1"}) + public long andAfterURShiftLongReversed(long x) { + return 0x0000FFFFFFFFFFFFL & (x >>> 16); // transformed to return x >>> 16; + } + + @Run(test = {"shiftLeftWithLowMaskInt", "shiftLeftWithLowMaskLong", + "shiftLeftWithLowMaskSmallInt", "shiftLeftWithLowMaskSmallLong", + "shiftLeftWithLowMaskIntReversed", "shiftLeftWithLowMaskLongReversed", + "shiftLeftWithLowMaskSmallIntReversed", "shiftLeftWithLowMaskSmallLongReversed", + "andMaskNonNegativeInt", "andMaskNonNegativeLong", + "andMaskNonNegativeIntReversed", "andMaskNonNegativeLongReversed", + "andAfterURShiftInt", "andAfterURShiftLong", + "andAfterURShiftIntReversed", "andAfterURShiftLongReversed", + }) + public void verifyShiftAndMaskTransforms() { + int xi = G.ints().next(); + long xl = G.longs().next(); + + Asserts.assertEquals(shiftLeftWithLowMaskInt(xi), (xi << INT_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskLong(xl), (xl << LONG_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskSmallInt(xi), (xi << INT_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskSmallLong(xl), (xl << LONG_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskIntReversed(xi), (xi << INT_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskLongReversed(xl), (xl << LONG_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskSmallIntReversed(xi), (xi << INT_MASK_WIDTH)); + Asserts.assertEquals(shiftLeftWithLowMaskSmallLongReversed(xl), (xl << LONG_MASK_WIDTH)); + Asserts.assertEquals(andMaskNonNegativeInt(xi), (xi & 0x7FFF)); + Asserts.assertEquals(andMaskNonNegativeLong(xl), (xl & 0x7FFFL)); + Asserts.assertEquals(andMaskNonNegativeIntReversed(xi), (xi & 0x7FFF)); + Asserts.assertEquals(andMaskNonNegativeLongReversed(xl), (xl & 0x7FFFL)); + Asserts.assertEquals(andAfterURShiftInt(xi), (xi >>> 8)); + Asserts.assertEquals(andAfterURShiftLong(xl), (xl >>> 16)); + Asserts.assertEquals(andAfterURShiftIntReversed(xi), (xi >>> 8)); + Asserts.assertEquals(andAfterURShiftLongReversed(xl), (xl >>> 16)); } } From 2a64074a8bd6024e1e09ae6f413595f3c9dcc86d Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Mon, 23 Mar 2026 14:58:47 +0000 Subject: [PATCH 076/160] 8374706: HttpClient: Ensure that request body publishers support multiple subscriptions Reviewed-by: dfuchs, jpai --- .../ByteBufferUtils.java | 4 +- .../FromPublisherTest.java | 41 ++++-- .../HttpRequestBodyPublishers/NoBodyTest.java | 24 +++- .../OfByteArrayTest.java | 51 ++++--- .../OfByteArraysTest.java | 34 +++-- .../OfFileChannelTest.java} | 31 +++- .../HttpRequestBodyPublishers/OfFileTest.java | 46 ++++-- .../OfInputStreamTest.java | 39 +++-- .../OfStringTest.java | 27 ++-- .../ReplayTestSupport.java | 134 ++++++++++++++++++ 10 files changed, 340 insertions(+), 91 deletions(-) rename test/jdk/java/net/httpclient/{FileChannelPublisherTest.java => HttpRequestBodyPublishers/OfFileChannelTest.java} (96%) create mode 100644 test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ReplayTestSupport.java diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ByteBufferUtils.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ByteBufferUtils.java index 29276dc7b60..0f9d6a373d4 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ByteBufferUtils.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ByteBufferUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,7 +61,7 @@ public final class ByteBufferUtils { private static byte[] bytes(ByteBuffer buffer) { byte[] bytes = new byte[buffer.limit()]; - buffer.get(bytes); + buffer.get(buffer.position(), bytes); return bytes; } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/FromPublisherTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/FromPublisherTest.java index f05eab0a2e5..7d3e78e0b29 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/FromPublisherTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/FromPublisherTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,10 +25,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.nio.ByteBuffer; +import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; +import static java.net.http.HttpRequest.BodyPublishers.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -37,16 +40,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * @test * @bug 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::fromPublisher` behavior - * @build RecordingSubscriber - * @run junit FromPublisherTest + * @build ByteBufferUtils + * RecordingSubscriber + * ReplayTestSupport + * @run junit ${test.main.class} */ -class FromPublisherTest { +class FromPublisherTest extends ReplayTestSupport { @Test void testNullPublisher() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.fromPublisher(null)); - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.fromPublisher(null, 1)); + assertThrows(NullPointerException.class, () -> fromPublisher(null)); + assertThrows(NullPointerException.class, () -> fromPublisher(null, 1)); } @ParameterizedTest @@ -54,7 +59,7 @@ class FromPublisherTest { void testInvalidContentLength(long contentLength) { IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, - () -> HttpRequest.BodyPublishers.fromPublisher(null, contentLength)); + () -> fromPublisher(null, contentLength)); String exceptionMessage = exception.getMessage(); assertTrue( exceptionMessage.contains("non-positive contentLength"), @@ -64,29 +69,26 @@ class FromPublisherTest { @ParameterizedTest @ValueSource(longs = {1, 2, 3, 4}) void testValidContentLength(long contentLength) { - HttpRequest.BodyPublisher publisher = - HttpRequest.BodyPublishers.fromPublisher(HttpRequest.BodyPublishers.noBody(), contentLength); + BodyPublisher publisher = fromPublisher(noBody(), contentLength); assertEquals(contentLength, publisher.contentLength()); } @Test void testNoContentLength() { - HttpRequest.BodyPublisher publisher = - HttpRequest.BodyPublishers.fromPublisher(HttpRequest.BodyPublishers.noBody()); + BodyPublisher publisher = fromPublisher(noBody()); assertEquals(-1, publisher.contentLength()); } @Test void testNullSubscriber() { - HttpRequest.BodyPublisher publisher = - HttpRequest.BodyPublishers.fromPublisher(HttpRequest.BodyPublishers.noBody()); + BodyPublisher publisher = fromPublisher(noBody()); assertThrows(NullPointerException.class, () -> publisher.subscribe(null)); } @Test void testDelegation() throws InterruptedException { BlockingQueue publisherInvocations = new LinkedBlockingQueue<>(); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.fromPublisher(subscriber -> { + BodyPublisher publisher = fromPublisher(subscriber -> { publisherInvocations.add("subscribe"); publisherInvocations.add(subscriber); }); @@ -97,4 +99,13 @@ class FromPublisherTest { assertTrue(subscriber.invocations.isEmpty()); } + @Override + Iterable createReplayTargets() { + byte[] content = ByteBufferUtils.byteArrayOfLength(10); + ByteBuffer expectedBuffer = ByteBuffer.wrap(content); + BodyPublisher delegatePublisher = ofByteArray(content); + BodyPublisher publisher = fromPublisher(delegatePublisher, delegatePublisher.contentLength()); + return List.of(new ReplayTarget(expectedBuffer, publisher)); + } + } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/NoBodyTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/NoBodyTest.java index f58e9505c9a..d45482700d6 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/NoBodyTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/NoBodyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,10 @@ import org.junit.jupiter.api.Test; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; +import java.nio.ByteBuffer; +import java.util.List; import java.util.concurrent.Flow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -32,17 +35,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; * @test * @bug 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::noBody` behavior - * @build RecordingSubscriber - * @run junit NoBodyTest + * @build ByteBufferUtils + * RecordingSubscriber + * ReplayTestSupport + * @run junit ${test.main.class} */ -class NoBodyTest { +class NoBodyTest extends ReplayTestSupport { @Test void test() throws InterruptedException { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.noBody(); + BodyPublisher publisher = BodyPublishers.noBody(); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -54,4 +59,11 @@ class NoBodyTest { } + @Override + Iterable createReplayTargets() { + ByteBuffer expectedBuffer = ByteBuffer.wrap(new byte[0]); + BodyPublisher publisher = BodyPublishers.noBody(); + return List.of(new ReplayTarget(expectedBuffer, publisher)); + } + } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java index 19f7369125d..25233c9dd99 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -39,30 +40,34 @@ import static org.junit.jupiter.api.Assertions.assertThrows; * @test * @bug 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::ofByteArray` behavior - * @build RecordingSubscriber - * @run junit OfByteArrayTest + * + * @build ByteBufferUtils + * RecordingSubscriber + * ReplayTestSupport + * + * @run junit ${test.main.class} * * @comment Using `main/othervm` to initiate tests that depend on a custom-configured JVM - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking "" 0 0 "" - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking a 0 0 "" - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking a 1 0 "" - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking a 0 1 a - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking ab 0 1 a - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking ab 1 1 b - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking ab 0 2 ab - * @run main/othervm -Djdk.httpclient.bufsize=1 OfByteArrayTest testChunking abc 0 3 a:b:c - * @run main/othervm -Djdk.httpclient.bufsize=2 OfByteArrayTest testChunking abc 0 3 ab:c - * @run main/othervm -Djdk.httpclient.bufsize=2 OfByteArrayTest testChunking abcdef 2 4 cd:ef + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking "" 0 0 "" + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking a 0 0 "" + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking a 1 0 "" + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking a 0 1 a + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking ab 0 1 a + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking ab 1 1 b + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking ab 0 2 ab + * @run main/othervm -Djdk.httpclient.bufsize=1 ${test.main.class} testChunking abc 0 3 a:b:c + * @run main/othervm -Djdk.httpclient.bufsize=2 ${test.main.class} testChunking abc 0 3 ab:c + * @run main/othervm -Djdk.httpclient.bufsize=2 ${test.main.class} testChunking abcdef 2 4 cd:ef */ -public class OfByteArrayTest { +public class OfByteArrayTest extends ReplayTestSupport { private static final Charset CHARSET = StandardCharsets.US_ASCII; @Test void testNullContent() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofByteArray(null)); - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofByteArray(null, 1, 2)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofByteArray(null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofByteArray(null, 1, 2)); } @ParameterizedTest @@ -78,7 +83,15 @@ public class OfByteArrayTest { byte[] content = contentText.getBytes(CHARSET); assertThrows( IndexOutOfBoundsException.class, - () -> HttpRequest.BodyPublishers.ofByteArray(content, offset, length)); + () -> BodyPublishers.ofByteArray(content, offset, length)); + } + + @Override + Iterable createReplayTargets() { + byte[] content = "this content needs to be replayed again and again".getBytes(CHARSET); + ByteBuffer expectedBuffer = ByteBuffer.wrap(content); + BodyPublisher publisher = BodyPublishers.ofByteArray(content); + return List.of(new ReplayTarget(expectedBuffer, publisher)); } /** @@ -105,7 +118,7 @@ public class OfByteArrayTest { // Create the publisher byte[] content = contentText.getBytes(CHARSET); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArray(content, offset, length); + BodyPublisher publisher = BodyPublishers.ofByteArray(content, offset, length); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArraysTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArraysTest.java index ab051d2020f..10784f7dbee 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArraysTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArraysTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,8 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Iterator; @@ -46,15 +47,18 @@ import static org.junit.jupiter.api.Assertions.assertThrows; * @test * @bug 8226303 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::ofByteArrays` behavior + * * @build ByteBufferUtils * RecordingSubscriber + * ReplayTestSupport + * * @run junit OfByteArraysTest * * @comment Using `main/othervm` to initiate tests that depend on a custom-configured JVM * @run main/othervm -Xmx64m OfByteArraysTest testOOM */ -public class OfByteArraysTest { +public class OfByteArraysTest extends ReplayTestSupport { @ParameterizedTest @ValueSource(ints = {0, 1, 2, 3}) @@ -65,7 +69,7 @@ public class OfByteArraysTest { .range(0, length) .mapToObj(i -> new byte[]{(byte) i}) .toList(); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(buffers::iterator); + BodyPublisher publisher = BodyPublishers.ofByteArrays(buffers::iterator); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -95,7 +99,7 @@ public class OfByteArraysTest { case 2 -> List.of(buffer2).iterator(); default -> throw new AssertionError(); }; - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(iterable); + BodyPublisher publisher = BodyPublishers.ofByteArrays(iterable); // Subscribe twice (to force two `Iterable::iterator` invocations) RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -112,14 +116,14 @@ public class OfByteArraysTest { @Test void testNullIterable() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofByteArrays(null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofByteArrays(null)); } @Test void testNullIterator() throws InterruptedException { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(() -> null); + BodyPublisher publisher = BodyPublishers.ofByteArrays(() -> null); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -138,7 +142,7 @@ public class OfByteArraysTest { // Create the publisher List iterable = new ArrayList<>(); iterable.add(null); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(iterable); + BodyPublisher publisher = BodyPublishers.ofByteArrays(iterable); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -156,7 +160,7 @@ public class OfByteArraysTest { // Create the publisher RuntimeException exception = new RuntimeException("failure for `testIteratorCreationException`"); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(() -> { + BodyPublisher publisher = BodyPublishers.ofByteArrays(() -> { throw exception; }); @@ -192,7 +196,7 @@ public class OfByteArraysTest { // Create the publisher IteratorThrowingAtEnd iterator = new IteratorThrowingAtEnd(exceptionIndex, hasNextException, nextException); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(() -> iterator); + BodyPublisher publisher = BodyPublishers.ofByteArrays(() -> iterator); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -257,6 +261,14 @@ public class OfByteArraysTest { } + @Override + Iterable createReplayTargets() { + byte[] byteArray = ByteBufferUtils.byteArrayOfLength(9); + ByteBuffer expectedBuffer = ByteBuffer.wrap(byteArray); + BodyPublisher publisher = BodyPublishers.ofByteArrays(List.of(byteArray)); + return List.of(new ReplayTarget(expectedBuffer, -1, publisher, null)); + } + /** * Initiates tests that depend on a custom-configured JVM. */ @@ -273,7 +285,7 @@ public class OfByteArraysTest { // Create the publisher int length = ByteBufferUtils.findLengthExceedingMaxMemory(); Iterable iterable = createIterableOfLength(length); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(iterable); + BodyPublisher publisher = BodyPublishers.ofByteArrays(iterable); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); diff --git a/test/jdk/java/net/httpclient/FileChannelPublisherTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileChannelTest.java similarity index 96% rename from test/jdk/java/net/httpclient/FileChannelPublisherTest.java rename to test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileChannelTest.java index c9c78791da3..47596f40a5f 100644 --- a/test/jdk/java/net/httpclient/FileChannelPublisherTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileChannelTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,9 +26,12 @@ * @summary Verifies `HttpRequest.BodyPublishers::ofFileChannel` * @library /test/lib * /test/jdk/java/net/httpclient/lib - * @build jdk.httpclient.test.lib.common.HttpServerAdapters + * @build ByteBufferUtils + * RecordingSubscriber + * ReplayTestSupport + * jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.test.lib.net.SimpleSSLContext - * @run junit FileChannelPublisherTest + * @run junit ${test.main.class} */ import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestHandler; @@ -52,8 +55,10 @@ import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpClient.Version; import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; +import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.file.Files; @@ -82,9 +87,9 @@ import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -class FileChannelPublisherTest { +class OfFileChannelTest extends ReplayTestSupport { - private static final String CLASS_NAME = FileChannelPublisherTest.class.getSimpleName(); + private static final String CLASS_NAME = OfFileChannelTest.class.getSimpleName(); private static final Logger LOGGER = Utils.getDebugLogger(CLASS_NAME::toString, Utils.DEBUG); @@ -650,6 +655,22 @@ class FileChannelPublisherTest { } + @Override + Iterable createReplayTargets() { + int fileLength = 42; + byte[] fileBytes = generateFileBytes(fileLength); + Path filePath = Path.of("replayTarget.txt"); + try { + Files.write(filePath, fileBytes, StandardOpenOption.CREATE); + FileChannel fileChannel = FileChannel.open(filePath); + BodyPublisher publisher = BodyPublishers.ofFileChannel(fileChannel, 0, fileLength); + ByteBuffer expectedBuffer = ByteBuffer.wrap(fileBytes); + return List.of(new ReplayTarget(expectedBuffer, fileLength, publisher, fileChannel)); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + /** * Performs the initial {@code HEAD} request to the specified server. This * effectively admits a connection to the client's pool, where all protocol diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileTest.java index 94b5a596fc6..7287468d600 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,8 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.io.UncheckedIOException; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; import java.nio.ByteBuffer; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -51,15 +52,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * @test * @bug 8226303 8235459 8358688 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::ofFile` behavior + * * @build ByteBufferUtils * RecordingSubscriber - * @run junit OfFileTest + * ReplayTestSupport + * + * @run junit ${test.main.class} * * @comment Using `main/othervm` to initiate tests that depend on a custom-configured JVM - * @run main/othervm -Xmx64m OfFileTest testOOM + * @run main/othervm -Xmx64m ${test.main.class} testOOM */ -public class OfFileTest { +public class OfFileTest extends ReplayTestSupport { private static final Path DEFAULT_FS_DIR = Path.of(System.getProperty("user.dir", ".")); @@ -89,14 +93,14 @@ public class OfFileTest { @Test void testNullPath() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofFile(null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofFile(null)); } @ParameterizedTest @MethodSource("parentDirs") void testNonExistentPath(Path parentDir) { Path nonExistentPath = createFilePath(parentDir, "testNonExistentPath"); - assertThrows(FileNotFoundException.class, () -> HttpRequest.BodyPublishers.ofFile(nonExistentPath)); + assertThrows(FileNotFoundException.class, () -> BodyPublishers.ofFile(nonExistentPath)); } @ParameterizedTest @@ -106,7 +110,7 @@ public class OfFileTest { // Create the publisher byte[] fileBytes = ByteBufferUtils.byteArrayOfLength(3); Path filePath = createFile(parentDir, "testNonExistentPathAtSubscribe", fileBytes); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(filePath); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); // Delete the file Files.delete(filePath); @@ -131,7 +135,7 @@ public class OfFileTest { void testIrregularFile(Path parentDir) throws Exception { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(parentDir); + BodyPublisher publisher = BodyPublishers.ofFile(parentDir); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -167,7 +171,7 @@ public class OfFileTest { // Create the publisher byte[] fileBytes = ByteBufferUtils.byteArrayOfLength(BIG_FILE_LENGTH); Path filePath = createFile(parentDir, "testFileModificationWhileReading", fileBytes); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(filePath); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -210,7 +214,7 @@ public class OfFileTest { // Create the publisher byte[] fileBytes = ByteBufferUtils.byteArrayOfLength(fileLength); Path filePath = createFile(parentDir, "testFileOfLength", fileBytes); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(filePath); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -222,6 +226,24 @@ public class OfFileTest { } + @Override + Iterable createReplayTargets() { + byte[] fileBytes = ByteBufferUtils.byteArrayOfLength(3); + ByteBuffer expectedBuffer = ByteBuffer.wrap(fileBytes); + return parentDirs() + .stream() + .map(parentDir -> { + try { + Path filePath = createFile(parentDir, "replayTest", fileBytes); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); + return new ReplayTarget(expectedBuffer, publisher); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + }) + .toList(); + } + /** * Initiates tests that depend on a custom-configured JVM. */ @@ -248,7 +270,7 @@ public class OfFileTest { // Create the publisher int fileLength = ByteBufferUtils.findLengthExceedingMaxMemory(); Path filePath = createFileOfLength(parentDir, "testOOM", fileLength); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(filePath); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfInputStreamTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfInputStreamTest.java index 7688c1674ee..06ed13333ae 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfInputStreamTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfInputStreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,9 @@ import org.junit.jupiter.params.provider.ValueSource; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; +import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.Flow; import java.util.function.Supplier; @@ -41,19 +43,22 @@ import static org.junit.jupiter.api.Assertions.assertThrows; * @test * @bug 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::ofInputStream` behavior + * * @build ByteBufferUtils * RecordingSubscriber - * @run junit OfInputStreamTest + * ReplayTestSupport + * + * @run junit ${test.main.class} * * @comment Using `main/othervm` to initiate tests that depend on a custom-configured JVM - * @run main/othervm -Xmx64m OfInputStreamTest testOOM + * @run main/othervm -Xmx64m ${test.main.class} testOOM */ -public class OfInputStreamTest { +public class OfInputStreamTest extends ReplayTestSupport { @Test void testNullInputStreamSupplier() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofInputStream(null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofInputStream(null)); } @Test @@ -61,7 +66,7 @@ public class OfInputStreamTest { // Create the publisher RuntimeException exception = new RuntimeException(); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(() -> { throw exception; }); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> { throw exception; }); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -80,7 +85,7 @@ public class OfInputStreamTest { void testNullInputStream() throws InterruptedException { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(() -> null); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> null); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -107,7 +112,7 @@ public class OfInputStreamTest { case 2 -> new ByteArrayInputStream(buffer2); default -> throw new AssertionError(); }; - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(inputStreamSupplier); + BodyPublisher publisher = BodyPublishers.ofInputStream(inputStreamSupplier); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -129,7 +134,7 @@ public class OfInputStreamTest { // Create the publisher byte[] content = ByteBufferUtils.byteArrayOfLength(length); InputStream inputStream = new ByteArrayInputStream(content); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(() -> inputStream); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> inputStream); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -148,7 +153,7 @@ public class OfInputStreamTest { // Create the publisher RuntimeException exception = new RuntimeException("failure for `read`"); InputStream inputStream = new InputStreamThrowingOnCompletion(exceptionIndex, exception); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(() -> inputStream); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> inputStream); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -185,6 +190,14 @@ public class OfInputStreamTest { } + @Override + Iterable createReplayTargets() { + byte[] content = ByteBufferUtils.byteArrayOfLength(10); + ByteBuffer expectedBuffer = ByteBuffer.wrap(content); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> new ByteArrayInputStream(content)); + return List.of(new ReplayTarget(expectedBuffer, -1, publisher, null)); + } + /** * Initiates tests that depend on a custom-configured JVM. */ @@ -200,8 +213,8 @@ public class OfInputStreamTest { // Create the publisher using an `InputStream` that emits content exceeding the maximum memory int length = ByteBufferUtils.findLengthExceedingMaxMemory(); - HttpRequest.BodyPublisher publisher = - HttpRequest.BodyPublishers.ofInputStream(() -> new InputStream() { + BodyPublisher publisher = + BodyPublishers.ofInputStream(() -> new InputStream() { private int position; diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfStringTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfStringTest.java index 143b5ce17da..2e1fcb11f99 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfStringTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfStringTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,10 +26,12 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.concurrent.Flow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -41,10 +43,11 @@ import static org.junit.jupiter.api.Assertions.assertThrows; * @summary Verify all specified `HttpRequest.BodyPublishers::ofString` behavior * @build ByteBufferUtils * RecordingSubscriber - * @run junit OfStringTest + * ReplayTestSupport + * @run junit ${test.main.class} */ -class OfStringTest { +class OfStringTest extends ReplayTestSupport { private static final Charset CHARSET = StandardCharsets.US_ASCII; @@ -58,7 +61,7 @@ class OfStringTest { contentChars[i] = (char) ('a' + i); } String content = new String(contentChars); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofString(content, CHARSET); + BodyPublisher publisher = BodyPublishers.ofString(content, CHARSET); // Subscribe assertEquals(length, publisher.contentLength()); @@ -87,7 +90,7 @@ class OfStringTest { void testCharset(String content, Charset charset) throws InterruptedException { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofString(content, charset); + BodyPublisher publisher = BodyPublishers.ofString(content, charset); // Subscribe ByteBuffer expectedBuffer = charset.encode(content); @@ -108,12 +111,20 @@ class OfStringTest { @Test void testNullContent() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofString(null, CHARSET)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofString(null, CHARSET)); } @Test void testNullCharset() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofString("foo", null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofString("foo", null)); + } + + @Override + Iterable createReplayTargets() { + String content = "this content needs to be replayed again and again"; + ByteBuffer expectedBuffer = CHARSET.encode(content); + BodyPublisher publisher = BodyPublishers.ofString(content, CHARSET); + return List.of(new ReplayTarget(expectedBuffer, publisher)); } } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ReplayTestSupport.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ReplayTestSupport.java new file mode 100644 index 00000000000..788b59e96b7 --- /dev/null +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ReplayTestSupport.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.net.http.HttpRequest.BodyPublisher; +import java.nio.ByteBuffer; +import java.util.concurrent.Flow; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Tests for verifying that a request body publisher supports multiple subscriptions, aka. replayable. + */ +public abstract class ReplayTestSupport { + + @ParameterizedTest + @ValueSource(strings = { + // 2 subscriptions + "subscribe-cancel-subscribe-cancel", + "subscribe-cancel-subscribe-request", + "subscribe-request-subscribe-cancel", + "subscribe-request-subscribe-request", + // 3 subscriptions + "subscribe-cancel-subscribe-cancel-subscribe-request", + "subscribe-cancel-subscribe-request-subscribe-cancel", + "subscribe-cancel-subscribe-request-subscribe-request", + "subscribe-request-subscribe-cancel-subscribe-cancel", + "subscribe-request-subscribe-cancel-subscribe-request", + "subscribe-request-subscribe-request-subscribe-cancel", + "subscribe-request-subscribe-request-subscribe-request", + }) + void testReplay(String opSequence) throws Exception { + for (ReplayTarget replayTarget : createReplayTargets()) { + try (replayTarget) { + System.err.printf("Executing test for replay target: %s%n", replayTarget); + testReplay(opSequence, replayTarget); + } + } + } + + private static void testReplay(String opSequence, ReplayTarget replayTarget) throws InterruptedException { + + // Create the publisher + ByteBuffer expectedBuffer = replayTarget.expectedBuffer; + BodyPublisher publisher = replayTarget.publisher; + assertEquals(replayTarget.expectedContentLength, publisher.contentLength()); + + // Execute the specified operations + RecordingSubscriber subscriber = null; + Flow.Subscription subscription = null; + String[] ops = opSequence.split("-"); + for (int opIndex = 0; opIndex < ops.length; opIndex++) { + String op = ops[opIndex]; + System.err.printf("Executing operation at index %s: %s%n", opIndex, op); + switch (op) { + + case "subscribe": { + subscriber = new RecordingSubscriber(); + publisher.subscribe(subscriber); + assertEquals("onSubscribe", subscriber.invocations.take()); + subscription = (Flow.Subscription) subscriber.invocations.take(); + break; + } + + case "request": { + assert subscription != null; + subscription.request(Long.MAX_VALUE); + if (expectedBuffer.hasRemaining()) { + assertEquals("onNext", subscriber.invocations.take()); + ByteBuffer actualBuffer = (ByteBuffer) subscriber.invocations.take(); + ByteBufferUtils.assertEquals(expectedBuffer, actualBuffer, null); + } + assertEquals("onComplete", subscriber.invocations.take()); + break; + } + + case "cancel": { + assert subscription != null; + subscription.cancel(); + break; + } + + default: throw new IllegalArgumentException("Unknown operation: " + op); + + } + } + + } + + abstract Iterable createReplayTargets(); + + public record ReplayTarget( + ByteBuffer expectedBuffer, + int expectedContentLength, + BodyPublisher publisher, + AutoCloseable resource) + implements AutoCloseable { + + public ReplayTarget(ByteBuffer expectedBuffer, BodyPublisher publisher) { + this(expectedBuffer, expectedBuffer.limit(), publisher, null); + } + + @Override + public void close() throws Exception { + if (resource != null) { + resource.close(); + } + } + + } + +} From 60a141c3b5c0dcbf1cccfc55dc5b6a457b6a9a12 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 23 Mar 2026 15:42:33 +0000 Subject: [PATCH 077/160] 8380441: G1: Move G1GCPauseType to G1CollectorState Reviewed-by: iwalulya, ayang --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 1 - src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 1 - .../share/gc/g1/g1CollectionSetCandidates.cpp | 2 +- src/hotspot/share/gc/g1/g1CollectorState.cpp | 15 ++-- src/hotspot/share/gc/g1/g1CollectorState.hpp | 63 ++++++++++++- src/hotspot/share/gc/g1/g1GCPauseType.hpp | 89 ------------------- src/hotspot/share/gc/g1/g1Policy.cpp | 48 +++++----- src/hotspot/share/gc/g1/g1Policy.hpp | 8 +- src/hotspot/share/gc/g1/g1Trace.cpp | 13 ++- src/hotspot/share/gc/g1/g1Trace.hpp | 10 +-- src/hotspot/share/gc/g1/g1YoungCollector.cpp | 6 +- 11 files changed, 111 insertions(+), 145 deletions(-) delete mode 100644 src/hotspot/share/gc/g1/g1GCPauseType.hpp diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 480550d6ed1..be9ecf19123 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -42,7 +42,6 @@ #include "gc/g1/g1FullCollector.hpp" #include "gc/g1/g1GCCounters.hpp" #include "gc/g1/g1GCParPhaseTimesTracker.hpp" -#include "gc/g1/g1GCPauseType.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionPrinter.hpp" diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 7a4cde9001e..b5cb9167d92 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -34,7 +34,6 @@ #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1EdenRegions.hpp" #include "gc/g1/g1EvacStats.hpp" -#include "gc/g1/g1GCPauseType.hpp" #include "gc/g1/g1HeapRegionAttr.hpp" #include "gc/g1/g1HeapRegionManager.hpp" #include "gc/g1/g1HeapRegionSet.hpp" diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp index debb0bb54b7..2113db1163b 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp @@ -264,7 +264,7 @@ void G1CollectionSetCandidates::set_candidates_from_marking(GrowableArrayCHeapcalc_min_old_cset_length(num_candidates); G1CSetCandidateGroup::reset_next_group_id(); diff --git a/src/hotspot/share/gc/g1/g1CollectorState.cpp b/src/hotspot/share/gc/g1/g1CollectorState.cpp index d40a445c0ed..66292642603 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.cpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.cpp @@ -25,19 +25,18 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" -#include "gc/g1/g1GCPauseType.hpp" #include "runtime/safepoint.hpp" -G1GCPauseType G1CollectorState::gc_pause_type(bool concurrent_operation_is_full_mark) const { +G1CollectorState::Pause G1CollectorState::gc_pause_type(bool concurrent_operation_is_full_mark) const { assert(SafepointSynchronize::is_at_safepoint(), "must be"); switch (_phase) { - case Phase::YoungNormal: return G1GCPauseType::YoungGC; - case Phase::YoungLastYoung: return G1GCPauseType::LastYoungGC; + case Phase::YoungNormal: return Pause::Normal; + case Phase::YoungLastYoung: return Pause::LastYoung; case Phase::YoungConcurrentStart: - return concurrent_operation_is_full_mark ? G1GCPauseType::ConcurrentStartMarkGC : - G1GCPauseType::ConcurrentStartUndoGC; - case Phase::Mixed: return G1GCPauseType::MixedGC; - case Phase::FullGC: return G1GCPauseType::FullGC; + return concurrent_operation_is_full_mark ? Pause::ConcurrentStartFull : + Pause::ConcurrentStartUndo; + case Phase::Mixed: return Pause::Mixed; + case Phase::FullGC: return Pause::Full; default: ShouldNotReachHere(); } } diff --git a/src/hotspot/share/gc/g1/g1CollectorState.hpp b/src/hotspot/share/gc/g1/g1CollectorState.hpp index c2923cb3954..fc59df7349d 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.hpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.hpp @@ -25,7 +25,8 @@ #ifndef SHARE_GC_G1_G1COLLECTORSTATE_HPP #define SHARE_GC_G1_G1COLLECTORSTATE_HPP -#include "gc/g1/g1GCPauseType.hpp" +#include "utilities/debug.hpp" +#include "utilities/enumIterator.hpp" #include "utilities/globalDefinitions.hpp" // State of the G1 collection. @@ -92,8 +93,66 @@ public: bool is_in_mark_or_rebuild() const; bool is_in_reset_for_next_cycle() const; + enum class Pause : uint { + Normal, + LastYoung, + ConcurrentStartFull, + ConcurrentStartUndo, + Cleanup, + Remark, + Mixed, + Full + }; + // Calculate GC Pause Type from internal state. - G1GCPauseType gc_pause_type(bool concurrent_operation_is_full_mark) const; + Pause gc_pause_type(bool concurrent_operation_is_full_mark) const; + + static const char* to_string(Pause type) { + static const char* pause_strings[] = { "Normal", + "Prepare Mixed", + "Concurrent Start", // Do not distinguish between the different + "Concurrent Start", // Concurrent Start pauses. + "Cleanup", + "Remark", + "Mixed", + "Full" }; + return pause_strings[static_cast(type)]; + } + + static void assert_is_young_pause(Pause type) { + assert(type != Pause::Full, "must be"); + assert(type != Pause::Remark, "must be"); + assert(type != Pause::Cleanup, "must be"); + } + + static bool is_young_only_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::ConcurrentStartUndo || + type == Pause::ConcurrentStartFull || + type == Pause::LastYoung || + type == Pause::Normal; + } + + static bool is_mixed_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::Mixed; + } + + static bool is_last_young_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::LastYoung; + } + + static bool is_concurrent_start_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::ConcurrentStartFull || type == Pause::ConcurrentStartUndo; + } + + static bool is_concurrent_cycle_pause(Pause type) { + return type == Pause::Cleanup || type == Pause::Remark; + } }; +ENUMERATOR_RANGE(G1CollectorState::Pause, G1CollectorState::Pause::Normal, G1CollectorState::Pause::Full) + #endif // SHARE_GC_G1_G1COLLECTORSTATE_HPP diff --git a/src/hotspot/share/gc/g1/g1GCPauseType.hpp b/src/hotspot/share/gc/g1/g1GCPauseType.hpp deleted file mode 100644 index 254edb28fea..00000000000 --- a/src/hotspot/share/gc/g1/g1GCPauseType.hpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_GC_G1_G1GCPAUSETYPES_HPP -#define SHARE_GC_G1_G1GCPAUSETYPES_HPP - -#include "utilities/debug.hpp" -#include "utilities/enumIterator.hpp" - -enum class G1GCPauseType : uint { - YoungGC, - LastYoungGC, - ConcurrentStartMarkGC, - ConcurrentStartUndoGC, - Cleanup, - Remark, - MixedGC, - FullGC -}; - -ENUMERATOR_RANGE(G1GCPauseType, G1GCPauseType::YoungGC, G1GCPauseType::FullGC) - -class G1GCPauseTypeHelper { -public: - - static void assert_is_young_pause(G1GCPauseType type) { - assert(type != G1GCPauseType::FullGC, "must be"); - assert(type != G1GCPauseType::Remark, "must be"); - assert(type != G1GCPauseType::Cleanup, "must be"); - } - - static bool is_young_only_pause(G1GCPauseType type) { - assert_is_young_pause(type); - return type == G1GCPauseType::ConcurrentStartUndoGC || - type == G1GCPauseType::ConcurrentStartMarkGC || - type == G1GCPauseType::LastYoungGC || - type == G1GCPauseType::YoungGC; - } - - static bool is_mixed_pause(G1GCPauseType type) { - assert_is_young_pause(type); - return type == G1GCPauseType::MixedGC; - } - - static bool is_last_young_pause(G1GCPauseType type) { - assert_is_young_pause(type); - return type == G1GCPauseType::LastYoungGC; - } - - static bool is_concurrent_start_pause(G1GCPauseType type) { - assert_is_young_pause(type); - return type == G1GCPauseType::ConcurrentStartMarkGC || type == G1GCPauseType::ConcurrentStartUndoGC; - } - - static const char* to_string(G1GCPauseType type) { - static const char* pause_strings[] = { "Normal", - "Prepare Mixed", - "Concurrent Start", // Do not distinguish between the different - "Concurrent Start", // Concurrent Start pauses. - "Cleanup", - "Remark", - "Mixed", - "Full" }; - return pause_strings[static_cast(type)]; - } -}; - -#endif // SHARE_GC_G1_G1GCPAUSETYPES_HPP diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 09043c92722..dce83a3f084 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -584,7 +584,7 @@ void G1Policy::record_full_collection_end(size_t allocation_word_size) { _old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * G1HeapRegion::GrainBytes); double start_time_sec = cur_pause_start_sec(); - record_pause(G1GCPauseType::FullGC, start_time_sec, end_sec); + record_pause(Pause::Full, start_time_sec, end_sec); } static void log_refinement_stats(const G1ConcurrentRefineStats& stats) { @@ -699,7 +699,7 @@ void G1Policy::record_concurrent_mark_remark_end() { double start_time_sec = cur_pause_start_sec(); double elapsed_time_ms = (end_time_sec - start_time_sec) * 1000.0; _analytics->report_concurrent_mark_remark_times_ms(elapsed_time_ms); - record_pause(G1GCPauseType::Remark, start_time_sec, end_time_sec); + record_pause(Pause::Remark, start_time_sec, end_time_sec); } G1CollectionSetCandidates* G1Policy::candidates() const { @@ -790,10 +790,10 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar double end_time_sec = Ticks::now().seconds(); double pause_time_ms = (end_time_sec - start_time_sec) * 1000.0; - G1GCPauseType this_pause = collector_state()->gc_pause_type(concurrent_operation_is_full_mark); - bool is_young_only_pause = G1GCPauseTypeHelper::is_young_only_pause(this_pause); + Pause this_pause = collector_state()->gc_pause_type(concurrent_operation_is_full_mark); + bool is_young_only_pause = G1CollectorState::is_young_only_pause(this_pause); - if (G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause)) { + if (G1CollectorState::is_concurrent_start_pause(this_pause)) { record_concurrent_mark_init_end(); } else { maybe_start_marking(allocation_word_size); @@ -935,14 +935,14 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar record_pause(this_pause, start_time_sec, end_time_sec); - if (G1GCPauseTypeHelper::is_last_young_pause(this_pause)) { - assert(!G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause), + if (G1CollectorState::is_last_young_pause(this_pause)) { + assert(!G1CollectorState::is_concurrent_start_pause(this_pause), "The young GC before mixed is not allowed to be concurrent start GC"); // This has been the young GC before we start doing mixed GCs. We already // decided to start mixed GCs much earlier, so there is nothing to do except // advancing the state. collector_state()->set_in_space_reclamation_phase(); - } else if (G1GCPauseTypeHelper::is_mixed_pause(this_pause)) { + } else if (G1CollectorState::is_mixed_pause(this_pause)) { // This is a mixed GC. Here we decide whether to continue doing more // mixed GCs or not. if (!next_gc_should_be_mixed()) { @@ -960,7 +960,7 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar _eden_surv_rate_group->start_adding_regions(); - assert(!(G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause) && collector_state()->is_in_concurrent_cycle()), + assert(!(G1CollectorState::is_concurrent_start_pause(this_pause) && collector_state()->is_in_concurrent_cycle()), "If the last pause has been concurrent start, we should not have been in the marking cycle"); _free_regions_at_end_of_collection = _g1h->num_free_regions(); @@ -972,7 +972,7 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar _old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * G1HeapRegion::GrainBytes); if (update_ihop_prediction(app_time_ms / 1000.0, - G1GCPauseTypeHelper::is_young_only_pause(this_pause))) { + G1CollectorState::is_young_only_pause(this_pause))) { _ihop_control->report_statistics(_g1h->gc_tracer_stw(), _g1h->non_young_occupancy_after_allocation(allocation_word_size)); } } else { @@ -1340,7 +1340,7 @@ void G1Policy::record_concurrent_mark_cleanup_end(bool has_rebuilt_remembered_se double elapsed_time_ms = (end_sec - start_sec) * 1000.0; _analytics->report_concurrent_mark_cleanup_times_ms(elapsed_time_ms); - record_pause(G1GCPauseType::Cleanup, start_sec, end_sec); + record_pause(Pause::Cleanup, start_sec, end_sec); } void G1Policy::abandon_collection_set_candidates() { @@ -1356,25 +1356,25 @@ void G1Policy::maybe_start_marking(size_t allocation_word_size) { } } -void G1Policy::update_gc_pause_time_ratios(G1GCPauseType gc_type, double start_time_sec, double end_time_sec) { +void G1Policy::update_gc_pause_time_ratios(Pause gc_type, double start_time_sec, double end_time_sec) { double pause_time_sec = end_time_sec - start_time_sec; double pause_time_ms = pause_time_sec * 1000.0; _analytics->update_gc_time_ratios(end_time_sec, pause_time_ms); - if (gc_type == G1GCPauseType::Cleanup || gc_type == G1GCPauseType::Remark) { + if (G1CollectorState::is_concurrent_cycle_pause(gc_type)) { _analytics->append_prev_collection_pause_end_ms(pause_time_ms); } else { _analytics->set_prev_collection_pause_end_ms(end_time_sec * 1000.0); } } -void G1Policy::record_pause(G1GCPauseType gc_type, +void G1Policy::record_pause(Pause gc_type, double start, double end) { // Manage the MMU tracker. For some reason it ignores Full GCs. - if (gc_type != G1GCPauseType::FullGC) { + if (gc_type != Pause::Full) { _mmu_tracker->add_pause(start, end); } @@ -1386,21 +1386,21 @@ void G1Policy::record_pause(G1GCPauseType gc_type, _analytics->set_gc_cpu_time_at_pause_end_ms(elapsed_gc_cpu_time); } -void G1Policy::update_time_to_mixed_tracking(G1GCPauseType gc_type, +void G1Policy::update_time_to_mixed_tracking(Pause gc_type, double start, double end) { // Manage the mutator time tracking from concurrent start to first mixed gc. switch (gc_type) { - case G1GCPauseType::FullGC: + case Pause::Full: abort_time_to_mixed_tracking(); break; - case G1GCPauseType::Cleanup: - case G1GCPauseType::Remark: - case G1GCPauseType::YoungGC: - case G1GCPauseType::LastYoungGC: + case Pause::Cleanup: + case Pause::Remark: + case Pause::Normal: + case Pause::LastYoung: _concurrent_start_to_mixed.add_pause(end - start); break; - case G1GCPauseType::ConcurrentStartMarkGC: + case Pause::ConcurrentStartFull: // Do not track time-to-mixed time for periodic collections as they are likely // to be not representative to regular operation as the mutators are idle at // that time. Also only track full concurrent mark cycles. @@ -1408,12 +1408,12 @@ void G1Policy::update_time_to_mixed_tracking(G1GCPauseType gc_type, _concurrent_start_to_mixed.record_concurrent_start_end(end); } break; - case G1GCPauseType::ConcurrentStartUndoGC: + case Pause::ConcurrentStartUndo: assert(_g1h->gc_cause() == GCCause::_g1_humongous_allocation, "GC cause must be humongous allocation but is %d", _g1h->gc_cause()); break; - case G1GCPauseType::MixedGC: + case Pause::Mixed: _concurrent_start_to_mixed.record_mixed_gc_start(start); break; default: diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 1209c7c02d5..585e26441d5 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -56,7 +56,7 @@ class GCPolicyCounters; class STWGCTimer; class G1Policy: public CHeapObj { - private: + using Pause = G1CollectorState::Pause; static G1IHOPControl* create_ihop_control(const G1OldGenAllocationTracker* old_gen_alloc_tracker, const G1Predictions* predictor); @@ -268,13 +268,13 @@ private: // Sets up marking if proper conditions are met. void maybe_start_marking(size_t allocation_word_size); // Manage time-to-mixed tracking. - void update_time_to_mixed_tracking(G1GCPauseType gc_type, double start, double end); + void update_time_to_mixed_tracking(Pause gc_type, double start, double end); // Record the given STW pause with the given start and end times (in s). - void record_pause(G1GCPauseType gc_type, + void record_pause(Pause gc_type, double start, double end); - void update_gc_pause_time_ratios(G1GCPauseType gc_type, double start_sec, double end_sec); + void update_gc_pause_time_ratios(Pause gc_type, double start_sec, double end_sec); // Indicate that we aborted marking before doing any mixed GCs. void abort_time_to_mixed_tracking(); diff --git a/src/hotspot/share/gc/g1/g1Trace.cpp b/src/hotspot/share/gc/g1/g1Trace.cpp index ed6a91f41ed..242a97ca4e3 100644 --- a/src/hotspot/share/gc/g1/g1Trace.cpp +++ b/src/hotspot/share/gc/g1/g1Trace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ */ #include "gc/g1/g1EvacInfo.hpp" -#include "gc/g1/g1GCPauseType.hpp" #include "gc/g1/g1HeapRegionTraceType.hpp" #include "gc/g1/g1Trace.hpp" #include "gc/shared/gcHeapSummary.hpp" @@ -48,12 +47,12 @@ public: class G1YCTypeConstant : public JfrSerializer { public: void serialize(JfrCheckpointWriter& writer) { - constexpr EnumRange types{}; + constexpr EnumRange types{}; static const u4 nof_entries = static_cast(types.size()); writer.write_count(nof_entries); for (auto index : types) { writer.write_key(static_cast(index)); - writer.write(G1GCPauseTypeHelper::to_string(index)); + writer.write(G1CollectorState::to_string(index)); } } }; @@ -72,8 +71,8 @@ void G1NewTracer::initialize() { JFR_ONLY(register_jfr_type_constants();) } -void G1NewTracer::report_young_gc_pause(G1GCPauseType pause) { - G1GCPauseTypeHelper::assert_is_young_pause(pause); +void G1NewTracer::report_young_gc_pause(G1CollectorState::Pause pause) { + G1CollectorState::assert_is_young_pause(pause); _pause = pause; } @@ -128,7 +127,7 @@ void G1NewTracer::report_adaptive_ihop_statistics(size_t threshold, void G1NewTracer::send_g1_young_gc_event() { // Check that the pause type has been updated to something valid for this event. - G1GCPauseTypeHelper::assert_is_young_pause(_pause); + G1CollectorState::assert_is_young_pause(_pause); EventG1GarbageCollection e(UNTIMED); if (e.should_commit()) { diff --git a/src/hotspot/share/gc/g1/g1Trace.hpp b/src/hotspot/share/gc/g1/g1Trace.hpp index a2e11ed4496..bfcc275d2ca 100644 --- a/src/hotspot/share/gc/g1/g1Trace.hpp +++ b/src/hotspot/share/gc/g1/g1Trace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ #ifndef SHARE_GC_G1_G1TRACE_HPP #define SHARE_GC_G1_G1TRACE_HPP -#include "gc/g1/g1GCPauseType.hpp" +#include "gc/g1/g1CollectorState.hpp" #include "gc/shared/gcTrace.hpp" class G1EvacInfo; @@ -33,17 +33,17 @@ class G1HeapSummary; class G1EvacSummary; class G1NewTracer : public YoungGCTracer, public CHeapObj { - G1GCPauseType _pause; + G1CollectorState::Pause _pause; public: G1NewTracer() : YoungGCTracer(G1New), - _pause(G1GCPauseType::FullGC) // Initialize to something invalid. For this event, which + _pause(G1CollectorState::Pause::Full) // Initialize to something invalid. For this event, which // is about young collections, FullGC is not a valid value. { } void initialize(); - void report_young_gc_pause(G1GCPauseType pause); + void report_young_gc_pause(G1CollectorState::Pause pause); void report_gc_end_impl(const Ticks& timestamp, TimePartitions* time_partitions); void report_evacuation_info(G1EvacInfo* info); void report_evacuation_failed(EvacuationFailedInfo& ef_info); diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index a3693456268..04ccac5ff05 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -70,7 +70,7 @@ class G1YoungGCTraceTime { G1YoungCollector* _collector; - G1GCPauseType _pause_type; + G1CollectorState::Pause _pause_type; GCCause::Cause _pause_cause; static const uint MaxYoungGCNameLength = 128; @@ -93,7 +93,7 @@ class G1YoungGCTraceTime { os::snprintf_checked(_young_gc_name_data, MaxYoungGCNameLength, "Pause Young (%s) (%s)%s", - G1GCPauseTypeHelper::to_string(_pause_type), + G1CollectorState::to_string(_pause_type), GCCause::to_string(_pause_cause), evacuation_failed_string); return _young_gc_name_data; @@ -142,7 +142,7 @@ public: G1YoungGCJFRTracerMark(STWGCTimer* gc_timer_stw, G1NewTracer* gc_tracer_stw, GCCause::Cause cause) : G1JFRTracerMark(gc_timer_stw, gc_tracer_stw), _evacuation_info() { } - void report_pause_type(G1GCPauseType type) { + void report_pause_type(G1CollectorState::Pause type) { tracer()->report_young_gc_pause(type); } From 5c2ef74931c05cac1aa589a8e1f9c242361dc247 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Mon, 23 Mar 2026 15:58:21 +0000 Subject: [PATCH 078/160] 8380316: Test runtime/os/AvailableProcessors.java fails Invalid argument Reviewed-by: dholmes, ayang --- .../jtreg/runtime/os/AvailableProcessors.java | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/runtime/os/AvailableProcessors.java b/test/hotspot/jtreg/runtime/os/AvailableProcessors.java index 18201d99127..51d6742b832 100644 --- a/test/hotspot/jtreg/runtime/os/AvailableProcessors.java +++ b/test/hotspot/jtreg/runtime/os/AvailableProcessors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,9 @@ import jtreg.SkippedException; import java.util.ArrayList; import java.io.File; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; public class AvailableProcessors { @@ -73,13 +76,17 @@ public class AvailableProcessors { "AvailableProcessors"); int[] expected = new int[] { 1, available/2, available-1, available }; + int cpuId = getFirstAllowedCpu(); + if (cpuId == -1) { + throw new SkippedException("Could not determine allowed CPU cores"); + } for (int i : expected) { System.out.println("Testing for " + i + " processors ..."); - int max = i - 1; + int max = i - 1 + cpuId; ArrayList cmdline = new ArrayList<>(master.command()); // prepend taskset command - cmdline.add(0, "0-" + max); + cmdline.add(0, cpuId + "-" + max); cmdline.add(0, "-c"); cmdline.add(0, taskset); // append expected processor count @@ -104,4 +111,40 @@ public class AvailableProcessors { else System.out.println(SUCCESS_STRING + available); } + + /** + * Retrieves the first available CPU core ID allowed for the current process on Linux. + * + * @return The first CPU ID in Cpus_allowed_list, or -1 if unavailable. + */ + static int getFirstAllowedCpu() { + final String statusFile = "/proc/self/status"; + final String targetKey = "Cpus_allowed_list:"; + + try (BufferedReader br = new BufferedReader(new FileReader(statusFile))) { + String line; + while ((line = br.readLine()) != null) { + // Look for the line starting with "Cpus_allowed_list:" + if (line.startsWith(targetKey)) { + // Extract the value part, e.g., "0-15,32-47" or "80,82,84" + String listValue = line.substring(targetKey.length()).trim(); + if (listValue.isEmpty()) return -1; + + // Get the first segment before any comma (e.g., "0-15" from "0-15,32") + String firstSegment = listValue.split(",")[0]; + + // If it is a range (e.g., "80-159"), take the start number + if (firstSegment.contains("-")) { + return Integer.parseInt(firstSegment.split("-")[0]); + } else { + // If it is a single ID (e.g., "1"), parse it directly + return Integer.parseInt(firstSegment); + } + } + } + } catch (IOException | NumberFormatException | ArrayIndexOutOfBoundsException e) { + throw new RuntimeException("Failed to read or parse " + statusFile, e); + } + return -1; + } } From 0798dd149799049ebeb17cc7ed4e57fdebab8fda Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Mon, 23 Mar 2026 16:17:35 +0000 Subject: [PATCH 079/160] 8379551: sun/text/IntHashtable/Bug4705389.java timed out Reviewed-by: liach, jpai --- test/jdk/sun/text/IntHashtable/Bug4705389.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/jdk/sun/text/IntHashtable/Bug4705389.java b/test/jdk/sun/text/IntHashtable/Bug4705389.java index bb688439706..c74dd03b532 100644 --- a/test/jdk/sun/text/IntHashtable/Bug4705389.java +++ b/test/jdk/sun/text/IntHashtable/Bug4705389.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,6 @@ * @bug 4705389 * @summary Make sure to find removed slots, which test case will be timed out without the fix. * @modules java.base/sun.text - * @run main/timeout=10 Bug4705389 */ import sun.text.IntHashtable; From c6a20173a37ec56f3264d5a312543bdfc40ce3d2 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Mon, 23 Mar 2026 17:15:07 +0000 Subject: [PATCH 080/160] 8378615: FFM Bound up call stub keeps JNI Global Ref to bound parameter Reviewed-by: jdv, psadhukhan --- .../share/classes/sun/font/HBShaper.java | 45 +++++++------------ 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/src/java.desktop/share/classes/sun/font/HBShaper.java b/src/java.desktop/share/classes/sun/font/HBShaper.java index 7d3f58fb88f..3a532072004 100644 --- a/src/java.desktop/share/classes/sun/font/HBShaper.java +++ b/src/java.desktop/share/classes/sun/font/HBShaper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -138,6 +138,7 @@ public class HBShaper { private static final MemorySegment get_h_advance_stub; private static final MemorySegment get_v_advance_stub; private static final MemorySegment get_contour_pt_stub; + private static final MemorySegment get_table_data_fn_stub; private static final MemorySegment store_layout_results_stub; @@ -209,6 +210,12 @@ public class HBShaper { jdk_hb_shape_handle = tmp4; Arena garena = Arena.global(); // creating stubs that exist until VM exit. + + get_table_data_fn_stub = getUpcallStub(garena, + "getFontTableData", // method name + JAVA_INT, // return type + JAVA_INT, ADDRESS); // arg types + FunctionDescriptor get_var_glyph_fd = getFunctionDescriptor(JAVA_INT, // return type ADDRESS, ADDRESS, JAVA_INT, JAVA_INT, ADDRESS, ADDRESS); // arg types MethodHandle get_var_glyph_mh = @@ -303,15 +310,9 @@ public class HBShaper { clusterHandle = getVarHandle(GlyphInfoLayout, "cluster"); } - - /* - * This is expensive but it is done just once per font. - * The unbound stub could be cached but the savings would - * be very low in the only case it is used. - */ @SuppressWarnings("restricted") - private static MemorySegment getBoundUpcallStub - (Arena arena, Class clazz, Object bindArg, String mName, + private static MemorySegment getUpcallStub + (Arena arena, String mName, MemoryLayout retType, MemoryLayout... argTypes) { try { @@ -320,10 +321,8 @@ public class HBShaper { FunctionDescriptor.ofVoid(argTypes) : FunctionDescriptor.of(retType, argTypes); MethodType mType = nativeDescriptor.toMethodType(); - mType = mType.insertParameterTypes(0, clazz); MethodHandle mh = MH_LOOKUP.findStatic(HBShaper.class, mName, mType); - MethodHandle bound_handle = mh.bindTo(bindArg); - return LINKER.upcallStub(bound_handle, nativeDescriptor, arena); + return LINKER.upcallStub(mh, nativeDescriptor, arena); } catch (IllegalAccessException | NoSuchMethodException e) { return null; } @@ -480,15 +479,16 @@ public class HBShaper { }); } - private static int getFontTableData(Font2D font2D, - int tag, - MemorySegment data_ptr_out) { + private static int getFontTableData(int tag, MemorySegment data_ptr_out) { /* * On return, the data_out_ptr will point to memory allocated by native malloc, * so it will be freed by the caller using native free - when it is * done with it. */ + + Font2D font2D = scopedVars.get().font(); + @SuppressWarnings("restricted") MemorySegment data_ptr = data_ptr_out.reinterpret(ADDRESS.byteSize()); if (tag == 0) { @@ -539,10 +539,6 @@ public class HBShaper { private static class FaceRef implements DisposerRecord { private Font2D font2D; private MemorySegment face; - // get_table_data_fn uses an Arena managed by GC, - // so we need to keep a reference to it here until - // this FaceRef is collected. - private MemorySegment get_table_data_fn; private FaceRef(Font2D font) { this.font2D = font; @@ -561,16 +557,7 @@ public class HBShaper { private void createFace() { try { - get_table_data_fn = getBoundUpcallStub(Arena.ofAuto(), - Font2D.class, - font2D, // bind arg - "getFontTableData", // method name - JAVA_INT, // return type - JAVA_INT, ADDRESS); // arg types - if (get_table_data_fn == null) { - return; - } - face = (MemorySegment)create_face_handle.invokeExact(get_table_data_fn); + face = (MemorySegment)create_face_handle.invokeExact(get_table_data_fn_stub); } catch (Throwable t) { } } From 2095051a10f01ffac4fedcd2e3febd7d63f65f40 Mon Sep 17 00:00:00 2001 From: Frederic Parain Date: Mon, 23 Mar 2026 17:46:44 +0000 Subject: [PATCH 081/160] 8380219: Interpreter debugging would be easier if InterpreterCodelets could print their assembly code Reviewed-by: dholmes, ayang --- src/hotspot/share/compiler/disassembler.hpp | 2 +- src/hotspot/share/interpreter/interpreter.cpp | 10 +++++++--- src/hotspot/share/interpreter/interpreter.hpp | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/compiler/disassembler.hpp b/src/hotspot/share/compiler/disassembler.hpp index db7066c9023..b9de9c3d27d 100644 --- a/src/hotspot/share/compiler/disassembler.hpp +++ b/src/hotspot/share/compiler/disassembler.hpp @@ -112,7 +112,7 @@ class Disassembler : public AbstractDisassembler { // interpreter code, by riding on the customary __ macro in the interpreter generator. // See templateTable_x86.cpp for an example. template inline static T* hook(const char* file, int line, T* masm) { - if (PrintInterpreter) { + if (PrintInterpreter NOT_PRODUCT(|| true)) { _hook(file, line, masm); } return masm; diff --git a/src/hotspot/share/interpreter/interpreter.cpp b/src/hotspot/share/interpreter/interpreter.cpp index 2cc163186e8..1f327152e0c 100644 --- a/src/hotspot/share/interpreter/interpreter.cpp +++ b/src/hotspot/share/interpreter/interpreter.cpp @@ -61,10 +61,10 @@ void InterpreterCodelet::initialize(const char* description, Bytecodes::Code byt void InterpreterCodelet::verify() {} -void InterpreterCodelet::print_on(outputStream* st) const { +void InterpreterCodelet::print_on(outputStream* st, bool print_code) const { ttyLocker ttyl; - if (AbstractInterpreter::should_print_instructions()) { + if (print_code) { st->cr(); st->print_cr("----------------------------------------------------------------------"); } @@ -74,12 +74,16 @@ void InterpreterCodelet::print_on(outputStream* st) const { st->print_cr("[" INTPTR_FORMAT ", " INTPTR_FORMAT "] %d bytes", p2i(code_begin()), p2i(code_end()), code_size()); - if (AbstractInterpreter::should_print_instructions()) { + if (print_code) { st->cr(); Disassembler::decode(code_begin(), code_end(), st NOT_PRODUCT(COMMA &_asm_remarks)); } } +void InterpreterCodelet::print_on(outputStream* st) const { + print_on(st, AbstractInterpreter::should_print_instructions()); +} + void InterpreterCodelet::print() const { print_on(tty); } CodeletMark::CodeletMark(InterpreterMacroAssembler*& masm, diff --git a/src/hotspot/share/interpreter/interpreter.hpp b/src/hotspot/share/interpreter/interpreter.hpp index f7d42fcb4da..fb368638332 100644 --- a/src/hotspot/share/interpreter/interpreter.hpp +++ b/src/hotspot/share/interpreter/interpreter.hpp @@ -67,6 +67,7 @@ class InterpreterCodelet: public Stub { // Debugging void verify(); + void print_on(outputStream* st, bool print_code) const; void print_on(outputStream* st) const; void print() const; From f1169bfcf1e5c916c12e33539a2ba8624eca1ead Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Mon, 23 Mar 2026 18:09:23 +0000 Subject: [PATCH 082/160] 8378195: guarantee(static_cast(_oops_size) == align_up(code_buffer->total_oop_size(), oopSize)) failed: failed: 12272 != 77808 Co-authored-by: Doug Simon Reviewed-by: dnsimon, iveresov, vlivanov --- src/hotspot/share/code/nmethod.cpp | 9 ++------- src/hotspot/share/code/nmethod.hpp | 3 +-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 4c2f9157b99..5a6ed8ab3ed 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -1306,9 +1306,7 @@ nmethod::nmethod( _deopt_handler_entry_offset = 0; _unwind_handler_offset = 0; - CHECKED_CAST(_oops_size, uint16_t, align_up(code_buffer->total_oop_size(), oopSize)); - uint16_t metadata_size; - CHECKED_CAST(metadata_size, uint16_t, align_up(code_buffer->total_metadata_size(), wordSize)); + int metadata_size = align_up(code_buffer->total_metadata_size(), wordSize); JVMCI_ONLY( _metadata_size = metadata_size; ) assert(_mutable_data_size == _relocation_size + metadata_size, "wrong mutable data size: %d != %d + %d", @@ -1446,7 +1444,6 @@ nmethod::nmethod(const nmethod &nm) : CodeBlob(nm._name, nm._kind, nm._size, nm. _deopt_handler_entry_offset = nm._deopt_handler_entry_offset; _unwind_handler_offset = nm._unwind_handler_offset; _num_stack_arg_slots = nm._num_stack_arg_slots; - _oops_size = nm._oops_size; #if INCLUDE_JVMCI _metadata_size = nm._metadata_size; #endif @@ -1749,9 +1746,7 @@ nmethod::nmethod( _unwind_handler_offset = -1; } - CHECKED_CAST(_oops_size, uint16_t, align_up(code_buffer->total_oop_size(), oopSize)); - uint16_t metadata_size; - CHECKED_CAST(metadata_size, uint16_t, align_up(code_buffer->total_metadata_size(), wordSize)); + int metadata_size = align_up(code_buffer->total_metadata_size(), wordSize); JVMCI_ONLY( _metadata_size = metadata_size; ) int jvmci_data_size = 0 JVMCI_ONLY( + align_up(compiler->is_jvmci() ? jvmci_data->size() : 0, oopSize)); assert(_mutable_data_size == _relocation_size + metadata_size + jvmci_data_size, diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 2391bc6d830..092da181f12 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -235,11 +235,10 @@ class nmethod : public CodeBlob { // Number of arguments passed on the stack uint16_t _num_stack_arg_slots; - uint16_t _oops_size; #if INCLUDE_JVMCI // _metadata_size is not specific to JVMCI. In the non-JVMCI case, it can be derived as: // _metadata_size = mutable_data_size - relocation_size - uint16_t _metadata_size; + int _metadata_size; #endif // Offset in immutable data section From d85fbd38cd53f7a8f0a40668c6b0c33e1b17641e Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Mon, 23 Mar 2026 21:06:03 +0000 Subject: [PATCH 083/160] 8370138: C2: clean up Matcher::use_asm_for_ldiv_by_con can Reviewed-by: qamai, snatarajan --- src/hotspot/cpu/aarch64/aarch64.ad | 4 ---- src/hotspot/cpu/arm/arm.ad | 6 +----- src/hotspot/cpu/ppc/ppc.ad | 4 ---- src/hotspot/cpu/riscv/riscv.ad | 4 ---- src/hotspot/cpu/s390/s390.ad | 4 ---- src/hotspot/cpu/x86/x86.ad | 7 ------- src/hotspot/share/opto/divnode.cpp | 3 +-- src/hotspot/share/opto/matcher.hpp | 4 ---- 8 files changed, 2 insertions(+), 34 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 3989c5a17f0..b79030f07e7 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -2524,10 +2524,6 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.size() : FLOATPRESSURE; } -bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { - return false; -} - const RegMask& Matcher::divI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 7d0d31c1f79..60a0ef307b5 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // // This code is free software; you can redistribute it and/or modify it @@ -1112,10 +1112,6 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 30 : FLOATPRESSURE; } -bool Matcher::use_asm_for_ldiv_by_con( jlong divisor ) { - return false; -} - // Register for DIVI projection of divmodI const RegMask& Matcher::divI_proj_mask() { ShouldNotReachHere(); diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 3b25604471f..f3d33b4305d 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -2457,10 +2457,6 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 28 : FLOATPRESSURE; } -bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { - return false; -} - // Register for DIVI projection of divmodI. const RegMask& Matcher::divI_proj_mask() { ShouldNotReachHere(); diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index e140052d168..54c0d9c0955 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -2111,10 +2111,6 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.size() : FLOATPRESSURE; } -bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { - return false; -} - const RegMask& Matcher::divI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index b9982c795cd..2208a197ac9 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -1929,10 +1929,6 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 15 : FLOATPRESSURE; } -bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { - return false; -} - // Register for DIVI projection of divmodI const RegMask& Matcher::divI_proj_mask() { return _Z_RARG4_INT_REG_mask; diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 8b90655c53c..2fd4e5516fc 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -2763,13 +2763,6 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? default_float_pressure_threshold : FLOATPRESSURE; } -bool Matcher::use_asm_for_ldiv_by_con( jlong divisor ) { - // In 64 bit mode a code which use multiply when - // devisor is constant is faster than hardware - // DIV instruction (it uses MulHiL). - return false; -} - // Register for DIVI projection of divmodI const RegMask& Matcher::divI_proj_mask() { return INT_RAX_REG_mask(); diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index c38e784a7af..b398ec27b80 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -419,8 +419,7 @@ static Node *transform_long_divide( PhaseGVN *phase, Node *dividend, jlong divis if (!d_pos) { q = new SubLNode(phase->longcon(0), phase->transform(q)); } - } else if ( !Matcher::use_asm_for_ldiv_by_con(d) ) { // Use hardware DIV instruction when - // it is faster than code generated below. + } else { // Attempt the jlong constant divide -> multiply transform found in // "Division by Invariant Integers using Multiplication" // by Granlund and Montgomery diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index 69e5ab354f1..4de41d6f2ef 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -429,10 +429,6 @@ public: // Register for MODL projection of divmodL static const RegMask& modL_proj_mask(); - // Use hardware DIV instruction when it is faster than - // a code which use multiply for division by constant. - static bool use_asm_for_ldiv_by_con( jlong divisor ); - // Java-Interpreter calling convention // (what you use when calling between compiled-Java and Interpreted-Java From e0fe86b53bdd0b208b10cc8a3e8ddc09910dfd75 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Tue, 24 Mar 2026 01:17:38 +0000 Subject: [PATCH 084/160] 8371924: --mac-app-store should be accepted option for app image signing Reviewed-by: almatvee --- .../jdk/jpackage/internal/MacFromOptions.java | 22 ++++----- .../internal/MacRuntimeValidator.java | 34 ++++++++++---- .../resources/MacResources.properties | 1 + .../jpackage/internal/cli/StandardOption.java | 2 +- .../jdk/jpackage/test/JPackageCommand.java | 14 +++++- .../jdk/jpackage/test/LauncherVerifier.java | 14 +++++- .../cli/OptionsValidationFailTest.excludes | 1 + .../jpackage/internal/cli/jpackage-options.md | 2 +- .../jpackage/macosx/SigningAppImageTest.java | 1 - .../macosx/SigningAppImageTwoStepsTest.java | 34 +++++++++++++- test/jdk/tools/jpackage/share/ErrorTest.java | 45 +++++++++++++++++-- 11 files changed, 138 insertions(+), 32 deletions(-) diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java index 20dcbcf5742..2c247ed3989 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java @@ -27,8 +27,6 @@ package jdk.jpackage.internal; import static jdk.jpackage.internal.FromOptions.buildApplicationBuilder; import static jdk.jpackage.internal.FromOptions.createPackageBuilder; import static jdk.jpackage.internal.MacPackagingPipeline.APPLICATION_LAYOUT; -import static jdk.jpackage.internal.MacRuntimeValidator.validateRuntimeHasJliLib; -import static jdk.jpackage.internal.MacRuntimeValidator.validateRuntimeHasNoBinDir; import static jdk.jpackage.internal.OptionUtils.isBundlingOperation; import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_MAC_PKG; import static jdk.jpackage.internal.cli.StandardOption.APPCLASS; @@ -203,12 +201,14 @@ final class MacFromOptions { final var predefinedRuntimeLayout = PREDEFINED_RUNTIME_IMAGE.findIn(options) .map(MacPackage::guessRuntimeLayout); - predefinedRuntimeLayout.ifPresent(layout -> { - validateRuntimeHasJliLib(layout); - if (MAC_APP_STORE.containsIn(options)) { - validateRuntimeHasNoBinDir(layout); - } - }); + predefinedRuntimeLayout.ifPresent(MacRuntimeValidator::validateRuntimeHasJliLib); + + if (MAC_APP_STORE.containsIn(options)) { + PREDEFINED_APP_IMAGE.findIn(options) + .map(APPLICATION_LAYOUT::resolveAt) + .ifPresent(MacRuntimeValidator::validateRuntimeHasNoBinDir); + predefinedRuntimeLayout.ifPresent(MacRuntimeValidator::validateRuntimeHasNoBinDir); + } final var launcherFromOptions = new LauncherFromOptions().faMapper(MacFromOptions::createMacFa); @@ -269,11 +269,13 @@ final class MacFromOptions { final boolean sign = MAC_SIGN.getFrom(options); final boolean appStore; - if (PREDEFINED_APP_IMAGE.containsIn(options)) { + if (MAC_APP_STORE.containsIn(options)) { + appStore = MAC_APP_STORE.getFrom(options); + } else if (PREDEFINED_APP_IMAGE.containsIn(options)) { final var appImageFileOptions = appBuilder.externalApplication().orElseThrow().extra(); appStore = MAC_APP_STORE.getFrom(appImageFileOptions); } else { - appStore = MAC_APP_STORE.getFrom(options); + appStore = false; } appBuilder.appStore(appStore); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacRuntimeValidator.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacRuntimeValidator.java index cfab12cbcba..340078849c7 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacRuntimeValidator.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacRuntimeValidator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,10 @@ import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.function.Predicate; +import jdk.jpackage.internal.model.AppImageLayout; +import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.RuntimeLayout; final class MacRuntimeValidator { @@ -45,17 +49,29 @@ final class MacRuntimeValidator { throw new UncheckedIOException(ex); } - throw I18N.buildConfigException("error.invalid-runtime-image-missing-file", + throw new JPackageException(I18N.format("error.invalid-runtime-image-missing-file", runtimeLayout.rootDirectory(), - runtimeLayout.unresolve().runtimeDirectory().resolve("lib/**").resolve(jliName)).create(); + runtimeLayout.unresolve().runtimeDirectory().resolve("lib/**").resolve(jliName))); } - static void validateRuntimeHasNoBinDir(RuntimeLayout runtimeLayout) { - if (Files.isDirectory(runtimeLayout.runtimeDirectory().resolve("bin"))) { - throw I18N.buildConfigException() - .message("error.invalid-runtime-image-bin-dir", runtimeLayout.rootDirectory()) - .advice("error.invalid-runtime-image-bin-dir.advice", "--mac-app-store") - .create(); + static void validateRuntimeHasNoBinDir(AppImageLayout appImageLayout) { + if (Files.isDirectory(appImageLayout.runtimeDirectory().resolve("bin"))) { + switch (appImageLayout) { + case RuntimeLayout runtimeLayout -> { + throw new ConfigException( + I18N.format("error.invalid-runtime-image-bin-dir", runtimeLayout.rootDirectory()), + I18N.format("error.invalid-runtime-image-bin-dir.advice", "--mac-app-store")); + } + case ApplicationLayout appLayout -> { + throw new JPackageException(I18N.format("error.invalid-app-image-runtime-image-bin-dir", + appLayout.rootDirectory().relativize(appLayout.runtimeDirectory()), + appLayout.rootDirectory())); + } + default -> { + throw new IllegalArgumentException(); + } + } + } } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties index 90c4162b80e..95fff00152b 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties @@ -29,6 +29,7 @@ error.cert.not.found=No certificate found matching [{0}] using keychain [{1}] error.multiple.certs.found=Multiple certificates matching name [{0}] found in keychain [{1}] error.app-image.mac-sign.required=--mac-sign option is required with predefined application image and with type [app-image] error.invalid-runtime-image-missing-file=Runtime image "{0}" is missing "{1}" file +error.invalid-app-image-runtime-image-bin-dir=Runtime directory {0} in the predefined application image [{1}] should not contain "bin" folder error.invalid-runtime-image-bin-dir=Runtime image "{0}" should not contain "bin" folder error.invalid-runtime-image-bin-dir.advice=Use --strip-native-commands jlink option when generating runtime image used with {0} option error.invalid-app-image-plist-file=Invalid "{0}" file in the predefined application image diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java index c2338b87fad..438a5ac3e6f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java @@ -342,7 +342,7 @@ public final class StandardOption { public static final OptionValue MAC_SIGN = booleanOption("mac-sign").scope(MAC_SIGNING).addAliases("s").create(); - public static final OptionValue MAC_APP_STORE = booleanOption("mac-app-store").create(); + public static final OptionValue MAC_APP_STORE = booleanOption("mac-app-store").scope(MAC_SIGNING).create(); public static final OptionValue MAC_APP_CATEGORY = stringOption("mac-app-category").create(); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index 6b1f67d50f4..4fad120d0f6 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -392,7 +392,6 @@ public class JPackageCommand extends CommandArguments { } public JPackageCommand setFakeRuntime() { - verifyMutable(); addPrerequisiteAction(cmd -> { cmd.setArgumentValue("--runtime-image", createInputRuntimeImage(RuntimeImageType.RUNTIME_TYPE_FAKE)); }); @@ -400,12 +399,22 @@ public class JPackageCommand extends CommandArguments { return this; } + public JPackageCommand usePredefinedAppImage(JPackageCommand appImageCmd) { + appImageCmd.verifyIsOfType(PackageType.IMAGE); + verifyIsOfType(PackageType.IMAGE); + appImageCmd.getVerifyActionsWithRole(ActionRole.LAUNCHER_VERIFIER).forEach(verifier -> { + addVerifyAction(verifier, ActionRole.LAUNCHER_VERIFIER); + }); + return usePredefinedAppImage(appImageCmd.outputBundle()); + } + public JPackageCommand usePredefinedAppImage(Path predefinedAppImagePath) { return setArgumentValue("--app-image", Objects.requireNonNull(predefinedAppImagePath)) .removeArgumentWithValue("--input"); } JPackageCommand addPrerequisiteAction(ThrowingConsumer action) { + verifyMutable(); prerequisiteActions.add(action); return this; } @@ -421,6 +430,7 @@ public class JPackageCommand extends CommandArguments { } JPackageCommand addVerifyAction(ThrowingConsumer action, ActionRole actionRole) { + verifyMutable(); verifyActions.add(action, actionRole); return this; } @@ -2033,7 +2043,7 @@ public class JPackageCommand extends CommandArguments { // `--runtime-image` parameter set. public static final Path DEFAULT_RUNTIME_IMAGE = Optional.ofNullable(TKit.getConfigProperty("runtime-image")).map(Path::of).orElse(null); - public final static String DEFAULT_VERSION = "1.0"; + public static final String DEFAULT_VERSION = "1.0"; // [HH:mm:ss.SSS] private static final Pattern TIMESTAMP_REGEXP = Pattern.compile( diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java index f9fcfb905af..7657517bad5 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java @@ -372,14 +372,21 @@ public final class LauncherVerifier { TKit.assertTrue(entitlements.isPresent(), String.format("Check [%s] launcher is signed with entitlements", name)); + String expectedEntitlementsOrigin; + var customFile = Optional.ofNullable(cmd.getArgumentValue("--mac-entitlements")).map(Path::of); - if (customFile.isEmpty()) { + if (customFile.isPresent()) { + expectedEntitlementsOrigin = String.format("custom entitlements from [%s] file", customFile.get()); + } else { // Try from the resource dir. var resourceDirFile = Optional.ofNullable(cmd.getArgumentValue("--resource-dir")).map(Path::of).map(resourceDir -> { return resourceDir.resolve(cmd.name() + ".entitlements"); }).filter(Files::exists); if (resourceDirFile.isPresent()) { customFile = resourceDirFile; + expectedEntitlementsOrigin = "custom entitlements from the resource directory"; + } else { + expectedEntitlementsOrigin = null; } } @@ -388,11 +395,14 @@ public final class LauncherVerifier { expected = new PListReader(Files.readAllBytes(customFile.orElseThrow())).toMap(true); } else if (cmd.hasArgument("--mac-app-store")) { expected = DefaultEntitlements.APP_STORE; + expectedEntitlementsOrigin = "App Store entitlements"; } else { + expectedEntitlementsOrigin = "default entitlements"; expected = DefaultEntitlements.STANDARD; } - TKit.assertEquals(expected, entitlements.orElseThrow().toMap(true), String.format("Check [%s] launcher is signed with expected entitlements", name)); + TKit.assertEquals(expected, entitlements.orElseThrow().toMap(true), + String.format("Check [%s] launcher is signed with %s", name, expectedEntitlementsOrigin)); } private void executeLauncher(JPackageCommand cmd) throws IOException { diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes index 32968db0606..19478aaa4a7 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes @@ -40,6 +40,7 @@ ErrorTest.test(WIN_MSI; app-desc=Hello; args-add=[--app-version, 1234]; errors=[ ErrorTest.test(WIN_MSI; app-desc=Hello; args-add=[--app-version, 256.1]; errors=[message.error-header+[error.msi-product-version-major-out-of-range], message.advice-header+[error.version-string-wrong-format.advice]]) ErrorTest.test(WIN_MSI; app-desc=Hello; args-add=[--launcher-as-service]; errors=[message.error-header+[error.missing-service-installer], message.advice-header+[error.missing-service-installer.advice]]) ErrorTest.test(args-add=[@foo]; errors=[message.error-header+[ERR_CannotParseOptions, foo]]) +ErrorTest.testMacSignAppStoreInvalidRuntime ErrorTest.testMacSignWithoutIdentity(IMAGE; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) ErrorTest.testMacSignWithoutIdentity(IMAGE; args-add=[--app-image, @@APP_IMAGE_WITH_SHORT_NAME@@, --mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) ErrorTest.testMacSignWithoutIdentity(MAC_DMG; app-desc=Hello; args-add=[--mac-sign, --mac-signing-keychain, @@EMPTY_KEYCHAIN@@]; errors=[message.error-header+[error.cert.not.found, CODE_SIGN, EMPTY_KEYCHAIN]]) diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/jpackage-options.md b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/jpackage-options.md index 64d3c6c075b..59ae0d176c1 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/jpackage-options.md +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/jpackage-options.md @@ -29,7 +29,7 @@ | --linux-shortcut | linux-deb, linux-rpm | x | x | x | USE_LAST | | --mac-app-category | mac-bundle | x | x | | USE_LAST | | --mac-app-image-sign-identity | mac | x | x | | USE_LAST | -| --mac-app-store | mac-bundle | x | x | | USE_LAST | +| --mac-app-store | mac | x | x | | USE_LAST | | --mac-dmg-content | mac-dmg | x | x | | CONCATENATE | | --mac-entitlements | mac | x | x | | USE_LAST | | --mac-installer-sign-identity | mac-pkg | x | x | | USE_LAST | diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java index 3a257a425b6..fc146270ab8 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java @@ -64,7 +64,6 @@ public class SigningAppImageTest { var testAL = new AdditionalLauncher("testAL"); testAL.applyTo(cmd); - cmd.executeAndAssertHelloAppImageCreated(); MacSign.withKeychain(keychain -> { sign.addTo(cmd); diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java index a6d94b59bd9..20af9572044 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java @@ -26,6 +26,7 @@ import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.jpackage.test.AdditionalLauncher; @@ -68,6 +69,22 @@ public class SigningAppImageTwoStepsTest { spec.test(); } + @Test + public static void testAppStore() { + + var sign = new SignKeyOptionWithKeychain( + SignKeyOption.Type.SIGN_KEY_USER_SHORT_NAME, + SigningBase.StandardCertificateRequest.CODESIGN, + SigningBase.StandardKeychain.MAIN.keychain()); + + var spec = new TestSpec(Optional.empty(), sign); + + spec.signAppImage(spec.createAppImage(), Optional.of(cmd -> { + cmd.addArgument("--mac-app-store"); + })); + } + + public record TestSpec(Optional signAppImage, SignKeyOptionWithKeychain sign) { public TestSpec { @@ -133,7 +150,7 @@ public class SigningAppImageTwoStepsTest { private SignKeyOptionWithKeychain sign; } - void test() { + JPackageCommand createAppImage() { var appImageCmd = JPackageCommand.helloAppImage() .setFakeRuntime() .setArgumentValue("--dest", TKit.createTempDirectory("appimage")); @@ -150,16 +167,29 @@ public class SigningAppImageTwoStepsTest { }, signOption.keychain()); }, appImageCmd::execute); + return appImageCmd; + } + + void signAppImage(JPackageCommand appImageCmd, Optional> mutator) { + Objects.requireNonNull(appImageCmd); + Objects.requireNonNull(mutator); + MacSign.withKeychain(keychain -> { var cmd = new JPackageCommand() .setPackageType(PackageType.IMAGE) - .addArguments("--app-image", appImageCmd.outputBundle()) + .usePredefinedAppImage(appImageCmd) .mutate(sign::addTo); + mutator.ifPresent(cmd::mutate); + cmd.executeAndAssertHelloAppImageCreated(); MacSignVerify.verifyAppImageSigned(cmd, sign.certRequest()); }, sign.keychain()); } + + void test() { + signAppImage(createAppImage(), Optional.empty()); + } } public static Collection test() { diff --git a/test/jdk/tools/jpackage/share/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java index 2797115b202..fbaec8283e8 100644 --- a/test/jdk/tools/jpackage/share/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -26,7 +26,6 @@ import static java.util.stream.Collectors.toMap; import static jdk.internal.util.OperatingSystem.LINUX; import static jdk.internal.util.OperatingSystem.MACOS; import static jdk.internal.util.OperatingSystem.WINDOWS; -import static jdk.jpackage.internal.util.PListWriter.writeDict; import static jdk.jpackage.internal.util.PListWriter.writePList; import static jdk.jpackage.internal.util.XmlUtils.createXml; import static jdk.jpackage.internal.util.XmlUtils.toXmlConsumer; @@ -35,6 +34,7 @@ import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import static jdk.jpackage.test.JPackageCommand.makeAdvice; import static jdk.jpackage.test.JPackageCommand.makeError; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -56,6 +56,7 @@ import jdk.jpackage.internal.util.TokenReplace; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.ApplicationLayout; import jdk.jpackage.test.CannedArgument; import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.JPackageCommand; @@ -699,11 +700,48 @@ public final class ErrorTest { )); } + @Test(ifOS = MACOS) + public static void testMacSignAppStoreInvalidRuntime() throws IOException { + + // Create app image with the runtime directory content that will fail the subsequent signing jpackage command. + var appImageCmd = JPackageCommand.helloAppImage().setFakeRuntime(); + appImageCmd.executeAndAssertImageCreated(); + Files.createDirectory(appImageCmd.appLayout().runtimeHomeDirectory().resolve("bin")); + + final var keychain = SignEnvMock.SingleCertificateKeychain.FOO.keychain(); + + var spec = testSpec() + .noAppDesc() + .addArgs("--mac-app-store", "--mac-sign", "--app-image", appImageCmd.outputBundle().toString()) + .error("error.invalid-app-image-runtime-image-bin-dir", + ApplicationLayout.macAppImage().runtimeHomeDirectory(), appImageCmd.outputBundle()) + .create(); + + TKit.withNewState(() -> { + var script = Script.build() + // Disable the mutation making mocks "run once". + .commandMockBuilderMutator(null) + // Replace "/usr/bin/security" with the mock bound to the keychain mock. + .map(MacSignMockUtils.securityMock(SignEnvMock.VALUE)) + // Don't mock other external commands. + .use(VerbatimCommandMock.INSTANCE) + .createLoop(); + + // Create jpackage tool provider using the /usr/bin/security mock. + var jpackage = JPackageMockUtils.createJPackageToolProvider(OperatingSystem.MACOS, script); + + // Override the default jpackage tool provider with the one using the /usr/bin/security mock. + JPackageCommand.useToolProviderByDefault(jpackage); + + spec.test(); + }); + } + @Test(ifOS = MACOS) @ParameterSupplier @ParameterSupplier("testMacPkgSignWithoutIdentity") public static void testMacSignWithoutIdentity(TestSpec spec) { - // The test called JPackage Command.useToolProviderBy Default(), + // The test calls JPackageCommand.useToolProviderByDefault(), // which alters global variables in the test library, // so run the test case with a new global state to isolate the alteration of the globals. TKit.withNewState(() -> { @@ -998,8 +1036,7 @@ public final class ErrorTest { // Test a few app-image options that should not be used when signing external app image testCases.addAll(Stream.of( new ArgumentGroup("--app-version", "2.0"), - new ArgumentGroup("--name", "foo"), - new ArgumentGroup("--mac-app-store") + new ArgumentGroup("--name", "foo") ).flatMap(argGroup -> { var withoutSign = testSpec() .noAppDesc() From e6c870ec43fc0b34e7afe7970054c658b7486e3e Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Tue, 24 Mar 2026 01:46:08 +0000 Subject: [PATCH 085/160] 8378731: Move AOT-inited classes to initialized state in early VM bootstrap Reviewed-by: kvn, liach --- src/hotspot/share/cds/aotClassInitializer.cpp | 5 +- .../share/cds/aotLinkedClassBulkLoader.cpp | 104 +++++++++++++++--- .../share/cds/aotLinkedClassBulkLoader.hpp | 7 +- src/hotspot/share/oops/instanceKlass.cpp | 35 +++++- src/hotspot/share/oops/instanceKlass.hpp | 4 +- .../cds/appcds/aotCache/EarlyClassInit.java | 61 ++++++++++ 6 files changed, 190 insertions(+), 26 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/EarlyClassInit.java diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp index 06fc3af6f30..41fdeb537cc 100644 --- a/src/hotspot/share/cds/aotClassInitializer.cpp +++ b/src/hotspot/share/cds/aotClassInitializer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -234,7 +234,8 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { } void AOTClassInitializer::call_runtime_setup(JavaThread* current, InstanceKlass* ik) { - assert(ik->has_aot_initialized_mirror(), "sanity"); + precond(ik->has_aot_initialized_mirror()); + precond(!AOTLinkedClassBulkLoader::is_initializing_classes_early()); if (ik->is_runtime_setup_required()) { if (log_is_enabled(Info, aot, init)) { ResourceMark rm; diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp index 3653f9d518c..6a60177fc40 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -116,11 +116,24 @@ void AOTLinkedClassBulkLoader::preload_classes_in_table(Array* c } } +#ifdef ASSERT +// true iff we are inside AOTLinkedClassBulkLoader::link_classes(), when +// we are moving classes into the fully_initialized state before the +// JVM is able to execute any bytecodes. +static bool _is_initializing_classes_early = false; +bool AOTLinkedClassBulkLoader::is_initializing_classes_early() { + return _is_initializing_classes_early; +} +#endif + // Some cached heap objects may hold references to methods in aot-linked // classes (via MemberName). We need to make sure all classes are // linked before executing any bytecode. void AOTLinkedClassBulkLoader::link_classes(JavaThread* current) { + DEBUG_ONLY(_is_initializing_classes_early = true); link_classes_impl(current); + DEBUG_ONLY(_is_initializing_classes_early = false); + if (current->has_pending_exception()) { exit_on_exception(current); } @@ -135,6 +148,13 @@ void AOTLinkedClassBulkLoader::link_classes_impl(TRAPS) { link_classes_in_table(table->boot2(), CHECK); link_classes_in_table(table->platform(), CHECK); link_classes_in_table(table->app(), CHECK); + + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot1(), /*early_only=*/true, CHECK); + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot2(), /*early_only=*/true, CHECK); + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->platform(), /*early_only=*/true, CHECK); + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->app(), /*early_only=*/true, CHECK); + + log_info(aot, init)("------ finished early class init"); } void AOTLinkedClassBulkLoader::link_classes_in_table(Array* classes, TRAPS) { @@ -216,7 +236,7 @@ void AOTLinkedClassBulkLoader::validate_module(Klass* k, const char* category_na #endif void AOTLinkedClassBulkLoader::init_javabase_classes(JavaThread* current) { - init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot1(), current); + init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot1(), /*early_only=*/false, current); if (current->has_pending_exception()) { exit_on_exception(current); } @@ -246,9 +266,9 @@ void AOTLinkedClassBulkLoader::init_non_javabase_classes_impl(TRAPS) { assert(h_system_loader() != nullptr, "must be"); AOTLinkedClassTable* table = AOTLinkedClassTable::get(); - init_classes_for_loader(Handle(), table->boot2(), CHECK); - init_classes_for_loader(h_platform_loader, table->platform(), CHECK); - init_classes_for_loader(h_system_loader, table->app(), CHECK); + init_classes_for_loader(Handle(), table->boot2(), /*early_only=*/false, CHECK); + init_classes_for_loader(h_platform_loader, table->platform(), /*early_only=*/false, CHECK); + init_classes_for_loader(h_system_loader, table->app(), /*early_only=*/false, CHECK); if (Universe::is_fully_initialized() && VerifyDuringStartup) { // Make sure we're still in a clean state. @@ -324,22 +344,80 @@ void AOTLinkedClassBulkLoader::initiate_loading(JavaThread* current, const char* } } -// Some AOT-linked classes for must be initialized early. This includes -// - classes that were AOT-initialized by AOTClassInitializer -// - the classes of all objects that are reachable from the archived mirrors of -// the AOT-linked classes for . -void AOTLinkedClassBulkLoader::init_classes_for_loader(Handle class_loader, Array* classes, TRAPS) { +// Can we move ik into fully_initialized state before the JVM is able to execute +// bytecodes? +static bool is_early_init_possible(InstanceKlass* ik) { + if (ik->is_runtime_setup_required()) { + // Bytecodes need to be executed in order to initialize this class. + if (log_is_enabled(Debug, aot, init)) { + ResourceMark rm; + log_debug(aot, init)("No early init %s: needs runtimeSetup()", + ik->external_name()); + } + return false; + } + + if (ik->super() != nullptr && !ik->super()->is_initialized()) { + // is_runtime_setup_required() == true for a super type + if (log_is_enabled(Debug, aot, init)) { + ResourceMark rm; + log_debug(aot, init)("No early init %s: super type %s not initialized", + ik->external_name(), ik->super()->external_name()); + } + return false; + } + + Array* interfaces = ik->local_interfaces(); + int num_interfaces = interfaces->length(); + for (int i = 0; i < num_interfaces; i++) { + InstanceKlass* intf = interfaces->at(i); + if (!intf->is_initialized() && intf->interface_needs_clinit_execution_as_super(/*also_check_supers*/false)) { + // is_runtime_setup_required() == true for this interface + if (log_is_enabled(Debug, aot, init)) { + ResourceMark rm; + log_debug(aot, init)("No early init %s: interface type %s not initialized", + ik->external_name(), intf->external_name()); + } + return false; + } + } + + return true; +} + +// Normally, classes are initialized on demand. However, some AOT-linked classes +// for the class_loader must be proactively intialized, including: +// - Classes that have an AOT-initialized mirror (they were AOT-initialized by +// AOTClassInitializer during the assembly phase). +// - The classes of all objects that are reachable from the archived mirrors of +// the AOT-linked classes for the class_loader. These are recorded in the special +// subgraph. +// +// (early_only == true) means that this function is called before the JVM +// is capable of executing Java bytecodes. +void AOTLinkedClassBulkLoader::init_classes_for_loader(Handle class_loader, Array* classes, + bool early_only, TRAPS) { if (classes != nullptr) { for (int i = 0; i < classes->length(); i++) { InstanceKlass* ik = classes->at(i); assert(ik->class_loader_data() != nullptr, "must be"); - if (ik->has_aot_initialized_mirror()) { - ik->initialize_with_aot_initialized_mirror(CHECK); + + bool do_init = ik->has_aot_initialized_mirror(); + if (do_init && early_only && !is_early_init_possible(ik)) { + // ik will be proactively initialized later when init_classes_for_loader() + // is called again with (early_only == false). + do_init = false; + } + + if (do_init) { + ik->initialize_with_aot_initialized_mirror(early_only, CHECK); } } } - HeapShared::init_classes_for_special_subgraph(class_loader, CHECK); + if (!early_only) { + HeapShared::init_classes_for_special_subgraph(class_loader, CHECK); + } } void AOTLinkedClassBulkLoader::replay_training_at_init(Array* classes, TRAPS) { diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp index 31fdac386fe..24ff61cea1e 100644 --- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp +++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,7 +56,7 @@ class AOTLinkedClassBulkLoader : AllStatic { static void link_classes_impl(TRAPS); static void link_classes_in_table(Array* classes, TRAPS); static void init_non_javabase_classes_impl(TRAPS); - static void init_classes_for_loader(Handle class_loader, Array* classes, TRAPS); + static void init_classes_for_loader(Handle class_loader, Array* classes, bool early_only, TRAPS); static void replay_training_at_init(Array* classes, TRAPS) NOT_CDS_RETURN; #ifdef ASSERT @@ -73,8 +73,9 @@ public: static void init_javabase_classes(JavaThread* current) NOT_CDS_RETURN; static void init_non_javabase_classes(JavaThread* current) NOT_CDS_RETURN; static void exit_on_exception(JavaThread* current); - static void replay_training_at_init_for_preloaded_classes(TRAPS) NOT_CDS_RETURN; + + static bool is_initializing_classes_early() NOT_DEBUG({return false;}); }; #endif // SHARE_CDS_AOTLINKEDCLASSBULKLOADER_HPP diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 1963327fc78..919afbf3abd 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -23,6 +23,7 @@ */ #include "cds/aotClassInitializer.hpp" +#include "cds/aotLinkedClassBulkLoader.hpp" #include "cds/aotMetaspace.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" @@ -150,6 +151,7 @@ #endif // ndef DTRACE_ENABLED bool InstanceKlass::_finalization_enabled = true; +static int call_class_initializer_counter = 0; // for debugging static inline bool is_class_loader(const Symbol* class_name, const ClassFileParser& parser) { @@ -884,7 +886,9 @@ void InstanceKlass::assert_no_clinit_will_run_for_aot_initialized_class() const #endif #if INCLUDE_CDS -void InstanceKlass::initialize_with_aot_initialized_mirror(TRAPS) { +// early_init -- we are moving this class into the fully_initialized state before the +// JVM is able to execute any bytecodes. See AOTLinkedClassBulkLoader::is_initializing_classes_early(). +void InstanceKlass::initialize_with_aot_initialized_mirror(bool early_init, TRAPS) { assert(has_aot_initialized_mirror(), "must be"); assert(CDSConfig::is_loading_heap(), "must be"); assert(CDSConfig::is_using_aot_linked_classes(), "must be"); @@ -894,15 +898,36 @@ void InstanceKlass::initialize_with_aot_initialized_mirror(TRAPS) { return; } + if (log_is_enabled(Info, aot, init)) { + ResourceMark rm; + log_info(aot, init)("%s (aot-inited%s)", external_name(), early_init ? ", early" : ""); + } + if (is_runtime_setup_required()) { + assert(!early_init, "must not call"); // Need to take the slow path, which will call the runtimeSetup() function instead // of initialize(CHECK); return; } - if (log_is_enabled(Info, aot, init)) { - ResourceMark rm; - log_info(aot, init)("%s (aot-inited)", external_name()); + + LogTarget(Info, class, init) lt; + if (lt.is_enabled()) { + ResourceMark rm(THREAD); + LogStream ls(lt); + ls.print("%d Initializing ", call_class_initializer_counter++); + name()->print_value_on(&ls); + ls.print_cr("(aot-inited) (" PTR_FORMAT ") by thread \"%s\"", + p2i(this), THREAD->name()); + } + + if (early_init) { + precond(AOTLinkedClassBulkLoader::is_initializing_classes_early()); + precond(is_linked()); + precond(init_thread() == nullptr); + set_init_state(fully_initialized); + fence_and_clear_init_lock(); + return; } link_class(CHECK); @@ -1699,8 +1724,6 @@ ArrayKlass* InstanceKlass::array_klass_or_null() { return array_klass_or_null(1); } -static int call_class_initializer_counter = 0; // for debugging - Method* InstanceKlass::class_initializer() const { Method* clinit = find_method( vmSymbols::class_initializer_name(), vmSymbols::void_method_signature()); diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index e370a3b7a7c..dd563ad3492 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -556,7 +556,7 @@ public: // initialization (virtuals from Klass) bool should_be_initialized() const override; // means that initialize should be called - void initialize_with_aot_initialized_mirror(TRAPS); + void initialize_with_aot_initialized_mirror(bool early_init, TRAPS); void assert_no_clinit_will_run_for_aot_initialized_class() const NOT_DEBUG_RETURN; void initialize(TRAPS) override; void initialize_preemptable(TRAPS) override; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/EarlyClassInit.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/EarlyClassInit.java new file mode 100644 index 00000000000..f78c3f83861 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/EarlyClassInit.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 Early init of classes in the AOT cache + * @bug 8378731 + * @requires vm.cds.supports.aot.class.linking + * @library /test/lib + * @build EarlyClassInit + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar EarlyClassInitApp + * @run driver EarlyClassInit + */ + +import jdk.test.lib.cds.SimpleCDSAppTester; +import jdk.test.lib.process.OutputAnalyzer; + +public class EarlyClassInit { + public static void main(String... args) throws Exception { + SimpleCDSAppTester.of("EarlyClassInit") + .addVmArgs("-Xlog:aot+init=debug") + .classpath("app.jar") + .appCommandLine("EarlyClassInitApp") + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldContain("java.lang.Object (aot-inited, early)") + .shouldContain("No early init java.lang.ClassLoader: needs runtimeSetup()") + .shouldContain("No early init java.security.SecureClassLoader: super type java.lang.ClassLoader not initialized") + .shouldContain("Calling java.lang.ClassLoader::runtimeSetup()") + .shouldContain("java.security.SecureClassLoader (aot-inited)"); + out.shouldContain("HelloWorld"); + }) + .runAOTWorkflow(); + } +} + +class EarlyClassInitApp { + public static void main(String[] args) { + System.out.println("HelloWorld"); + } +} From 105d526fa7dbf371115d9e2e4b906a8c15afd0bf Mon Sep 17 00:00:00 2001 From: Roger Calnan Date: Tue, 24 Mar 2026 02:09:18 +0000 Subject: [PATCH 086/160] 8377921: Add anchors to the options in the Core Libs tool man pages Reviewed-by: jpai, iris --- src/jdk.jartool/share/man/jar.md | 42 ++++++++++++++++---------------- src/jdk.jlink/share/man/jlink.md | 30 +++++++++++------------ 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/jdk.jartool/share/man/jar.md b/src/jdk.jartool/share/man/jar.md index d944afcfb7f..658fa0cb4fa 100644 --- a/src/jdk.jartool/share/man/jar.md +++ b/src/jdk.jartool/share/man/jar.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -84,29 +84,29 @@ appropriate operation arguments described in this section. You can mix an operation argument with other one-letter options. Generally the operation argument is the first argument specified on the command line. -`-c` or `--create` +[`-c`]{#option--create} or `--create` : Creates the archive. -`-i` *FILE* or `--generate-index=`*FILE* +[`-i`]{#option--generate-index} *FILE* or `--generate-index=`*FILE* : Generates index information for the specified JAR file. This option is deprecated and may be removed in a future release. -`-t` or `--list` +[`-t`]{#option--list} or `--list` : Lists the table of contents for the archive. -`-u` or `--update` +[`-u`]{#option--update} or `--update` : Updates an existing JAR file. -`-x` or `--extract` +[`-x`]{#option--extract} or `--extract` : Extracts the named (or all) files from the archive. If a file with the same name appears more than once in the archive, each copy will be extracted, with later copies overwriting (replacing) earlier copies unless -k is specified. -`-d` or `--describe-module` +[`-d`]{#option--describe-module} or `--describe-module` : Prints the module descriptor or automatic module name. -`--validate` +[`--validate`]{#option--validate} : Validate the contents of the JAR file. See `Integrity of a JAR File` section below for more details. @@ -115,7 +115,7 @@ argument is the first argument specified on the command line. You can use the following options to customize the actions of any operation mode included in the `jar` command. -`-C` *DIR* +[`-C`]{#option-C} *DIR* : When used with the create operation mode, changes the specified directory and includes the *files* specified at the end of the command line. @@ -126,10 +126,10 @@ mode included in the `jar` command. where the JAR file will be extracted. Unlike with the create operation mode, this option can be specified only once with the extract operation mode. -`-f` *FILE* or `--file=`*FILE* +[`-f`]{#option--file} *FILE* or `--file=`*FILE* : Specifies the archive file name. -`--release` *VERSION* +[`--release`]{#option--release} *VERSION* : Creates a multirelease JAR file. Places all files specified after the option into a versioned directory of the JAR file named `META-INF/versions/`*VERSION*`/`, where *VERSION* must be must be a @@ -149,26 +149,26 @@ mode included in the `jar` command. You can use the following options to customize the actions of the create and the update main operation modes: -`-e` *CLASSNAME* or `--main-class=`*CLASSNAME* +[`-e`]{#option--main-class} *CLASSNAME* or `--main-class=`*CLASSNAME* : Specifies the application entry point for standalone applications bundled into a modular or executable modular JAR file. -`-m` *FILE* or `--manifest=`*FILE* +[`-m`]{#option--manifest} *FILE* or `--manifest=`*FILE* : Includes the manifest information from the given manifest file. -`-M` or `--no-manifest` +[`-M`]{#option--no-manifest} or `--no-manifest` : Doesn't create a manifest file for the entries. -`--module-version=`*VERSION* +[`--module-version=`]{#option--module-version}*VERSION* : Specifies the module version, when creating or updating a modular JAR file, or updating a non-modular JAR file. -`--hash-modules=`*PATTERN* +[`--hash-modules=`]{#option--hash-modules}*PATTERN* : Computes and records the hashes of modules matched by the given pattern and that depend upon directly or indirectly on a modular JAR file being created or a non-modular JAR file being updated. -`-p` or `--module-path` +[`-p`]{#option--module-path} or `--module-path` : Specifies the location of module dependence for generating the hash. `@`*file* @@ -181,20 +181,20 @@ You can use the following options to customize the actions of the create (`-c` or `--create`) the update (`-u` or `--update` ) and the generate-index (`-i` or `--generate-index=`*FILE*) main operation modes: -`-0` or `--no-compress` +[`-0`]{#option--no-compress} or `--no-compress` : Stores without using ZIP compression. -`--date=`*TIMESTAMP* +[`--date=`]{#option--date}*TIMESTAMP* : The timestamp in ISO-8601 extended offset date-time with optional time-zone format, to use for the timestamp of the entries, e.g. "2022-02-12T12:30:00-05:00". ## Operation Modifiers Valid Only in Extract Mode -`--dir` *DIR* +[`--dir`]{#option--dir} *DIR* : Directory into which the JAR file will be extracted. -`-k` or `--keep-old-files` +[`-k`]{#option--keep-old-files} or `--keep-old-files` : Do not overwrite existing files. If a Jar file entry with the same name exists in the target directory, the existing file will not be overwritten. diff --git a/src/jdk.jlink/share/man/jlink.md b/src/jdk.jlink/share/man/jlink.md index 5c77202434c..b95424fdde9 100644 --- a/src/jdk.jlink/share/man/jlink.md +++ b/src/jdk.jlink/share/man/jlink.md @@ -57,14 +57,14 @@ Developers are responsible for updating their custom runtime images. ## jlink Options -`--add-modules` *mod*\[`,`*mod*...\] +[`--add-modules`]{#option--add-modules} *mod*\[`,`*mod*...\] : Adds the named modules, *mod*, to the default set of root modules. The default set of root modules is empty. -`--bind-services` +[`--bind-services`]{#option--bind-services} : Link service provider modules and their dependencies. -`-c zip-{0-9}` or `--compress=zip-{0-9}` +[`-c zip-{0-9}`]{#option--compress} or `--compress=zip-{0-9}` : Enable compression of resources. The accepted values are: zip-{0-9}, where zip-0 provides no compression, and zip-9 provides the best compression. Default is zip-6. @@ -75,37 +75,37 @@ Developers are responsible for updating their custom runtime images. - `1`: Constant string sharing - `2`: ZIP. Use zip-6 instead. -`--disable-plugin` *pluginname* +[`--disable-plugin`]{#option--disable-plugin} *pluginname* : Disables the specified plug-in. See [jlink Plug-ins] for the list of supported plug-ins. -`--endian` {`little`\|`big`} +[`--endian`]{#option--endian} {`little`\|`big`} : Specifies the byte order of the generated image. The default value is the format of your system's architecture. `-h` or `--help` : Prints the help message. -`--ignore-signing-information` +[`--ignore-signing-information`]{#option--ignore-signing-information} : Suppresses a fatal error when signed modular JARs are linked in the runtime image. The signature-related files of the signed modular JARs aren't copied to the runtime image. -`--launcher` *command*`=`*module* or `--launcher` *command*`=`*module*`/`*main* +[`--launcher`]{#option--launcher} *command*`=`*module* or `--launcher` *command*`=`*module*`/`*main* : Specifies the launcher command name for the module or the command name for the module and main class (the module and the main class names are separated by a slash (`/`)). -`--limit-modules` *mod*\[`,`*mod*...\] +[`--limit-modules`]{#option--limit-modules} *mod*\[`,`*mod*...\] : Limits the universe of observable modules to those in the transitive closure of the named modules, `mod`, plus the main module, if any, plus any further modules specified in the `--add-modules` option. -`--list-plugins` +[`--list-plugins`]{#option--list-plugins} : Lists available plug-ins, which you can access through command-line options; see [jlink Plug-ins]. -`-p` or `--module-path` *modulepath* +[`-p`]{#option-module-path} or `--module-path` *modulepath* : Specifies the module path. If this option is not specified, then the default module path is @@ -114,19 +114,19 @@ Developers are responsible for updating their custom runtime images. `java.base` module cannot be resolved from it, then the `jlink` command appends `$JAVA_HOME/jmods` to the module path. -`--no-header-files` +[`--no-header-files`]{#option--no-header-files} : Excludes header files. -`--no-man-pages` +[`--no-man-pages`]{#option--no-man-pages} : Excludes man pages. -`--output` *path* +[`--output`]{#option--output} *path* : Specifies the location of the generated runtime image. -`--save-opts` *filename* +[`--save-opts`]{#option--save-opts} *filename* : Saves `jlink` options in the specified file. -`--suggest-providers` \[*name*`,` ...\] +[`--suggest-providers`]{#option--suggest-providers} \[*name*`,` ...\] : Suggest providers that implement the given service types from the module path. From cc29010ae29c65964b44e9f472ad0c1d9f848f0a Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Tue, 24 Mar 2026 03:18:25 +0000 Subject: [PATCH 087/160] 8380664: Remove stub entries used in x86-32 Reviewed-by: kvn --- src/hotspot/share/runtime/stubDeclarations.hpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/hotspot/share/runtime/stubDeclarations.hpp b/src/hotspot/share/runtime/stubDeclarations.hpp index c478eda3e7c..7dc0f2d2bed 100644 --- a/src/hotspot/share/runtime/stubDeclarations.hpp +++ b/src/hotspot/share/runtime/stubDeclarations.hpp @@ -667,16 +667,6 @@ do_entry(initial, dcbrt, dcbrt, dcbrt) \ do_stub(initial, fmod) \ do_entry(initial, fmod, fmod, fmod) \ - /* following generic entries should really be x86_32 only */ \ - do_stub(initial, dlibm_sin_cos_huge) \ - do_entry(initial, dlibm_sin_cos_huge, dlibm_sin_cos_huge, \ - dlibm_sin_cos_huge) \ - do_stub(initial, dlibm_reduce_pi04l) \ - do_entry(initial, dlibm_reduce_pi04l, dlibm_reduce_pi04l, \ - dlibm_reduce_pi04l) \ - do_stub(initial, dlibm_tan_cot_huge) \ - do_entry(initial, dlibm_tan_cot_huge, dlibm_tan_cot_huge, \ - dlibm_tan_cot_huge) \ /* merge in stubs and entries declared in arch header */ \ STUBGEN_INITIAL_BLOBS_ARCH_DO(do_stub, do_arch_blob, \ do_arch_entry, do_arch_entry_init) \ From fd2ef1b870ca840e31436a60e726b197254f623f Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Tue, 24 Mar 2026 10:44:03 +0000 Subject: [PATCH 088/160] 8380665: (dc) java/nio/channels/DatagramChannel/SendReceiveMaxSize.java could also test the loopback interface Reviewed-by: alanb, jpai --- .../DatagramChannel/SendReceiveMaxSize.java | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java b/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java index 8d74fd8a387..a2f5844ce25 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java +++ b/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,7 +22,7 @@ */ /* - * @test + * @test id=default * @bug 8239355 8242885 8240901 * @key randomness * @summary Check that it is possible to send and receive datagrams of @@ -30,14 +30,41 @@ * @library /test/lib * @build jdk.test.lib.net.IPSupport * @run testng/othervm SendReceiveMaxSize + */ +/* + * @test id=preferIPv4Stack + * @key randomness + * @summary Check that it is possible to send and receive datagrams of + * maximum size on macOS, using an IPv4 only socket. + * @library /test/lib + * @build jdk.test.lib.net.IPSupport * @run testng/othervm -Djava.net.preferIPv4Stack=true SendReceiveMaxSize */ +/* + * @test id=preferIPv6Loopback + * @key randomness + * @summary Check that it is possible to send and receive datagrams of + * maximum size on macOS, using a dual socket and the loopback + * interface. + * @library /test/lib + * @build jdk.test.lib.net.IPSupport + * @run testng/othervm -Dtest.preferLoopback=true SendReceiveMaxSize + */ +/* + * @test id=preferIPv4Loopback + * @key randomness + * @summary Check that it is possible to send and receive datagrams of + * maximum size on macOS, using an IPv4 only socket and the + * loopback interface + * @library /test/lib + * @build jdk.test.lib.net.IPSupport + * @run testng/othervm -Dtest.preferLoopback=true -Djava.net.preferIPv4Stack=true SendReceiveMaxSize + */ import jdk.test.lib.RandomFactory; import jdk.test.lib.NetworkConfiguration; import jdk.test.lib.Platform; import jdk.test.lib.net.IPSupport; -import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -68,6 +95,7 @@ import static org.testng.Assert.assertTrue; public class SendReceiveMaxSize { private final static Class IOE = IOException.class; private final static Random random = RandomFactory.getRandom(); + private final static boolean PREFER_LOOPBACK = Boolean.getBoolean("test.preferLoopback"); public interface DatagramChannelSupplier { DatagramChannel open() throws IOException; @@ -83,11 +111,14 @@ public class SendReceiveMaxSize { public Object[][] invariants() throws IOException { var testcases = new ArrayList(); var nc = NetworkConfiguration.probe(); + var ipv4Loopback = (Inet4Address) InetAddress.getByName("127.0.0.1"); + var ipv6Loopback = (Inet6Address) InetAddress.getByName("::1"); if (hasIPv4()) { - InetAddress IPv4Addr = nc.ip4Addresses() + InetAddress IPv4Addr = PREFER_LOOPBACK ? ipv4Loopback + : nc.ip4Addresses() .filter(Predicate.not(InetAddress::isLoopbackAddress)) .findFirst() - .orElse((Inet4Address) InetAddress.getByName("127.0.0.1")); + .orElse(ipv4Loopback); testcases.add(new Object[]{ supplier(() -> DatagramChannel.open()), IPSupport.getMaxUDPSendBufSizeIPv4(), @@ -100,10 +131,11 @@ public class SendReceiveMaxSize { }); } if (!preferIPv4Stack() && hasIPv6()) { - InetAddress IPv6Addr = nc.ip6Addresses() + InetAddress IPv6Addr = PREFER_LOOPBACK ? ipv6Loopback + : nc.ip6Addresses() .filter(Predicate.not(InetAddress::isLoopbackAddress)) .findFirst() - .orElse((Inet6Address) InetAddress.getByName("::1")); + .orElse(ipv6Loopback); testcases.add(new Object[]{ supplier(() -> DatagramChannel.open()), IPSupport.getMaxUDPSendBufSizeIPv6(), From 90eebaa344d3db6dd92ed6f0531a7dd30c311d67 Mon Sep 17 00:00:00 2001 From: Kevin Walls Date: Tue, 24 Mar 2026 10:52:07 +0000 Subject: [PATCH 089/160] 8380516: HotSpot has two different ways of making Windows strtok_s available as strtok_r Reviewed-by: kbarrett, dlong --- src/hotspot/share/utilities/globalDefinitions_visCPP.hpp | 4 +++- src/hotspot/share/utilities/stringUtils.hpp | 8 ++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp index dfd6f2f1880..f106d325c68 100644 --- a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp +++ b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp @@ -69,7 +69,9 @@ inline int strncasecmp(const char *s1, const char *s2, size_t n) { // *not* the same as the C99 Annex K strtok_s. VS provides that function // under the name strtok_s_l. Make strtok_r a synonym so we can use that name // in shared code. -const auto strtok_r = strtok_s; +inline char* strtok_r(char* str, const char* delim, char** saveptr) { + return strtok_s(str, delim, saveptr); +} // VS doesn't provide POSIX macros S_ISFIFO or S_IFIFO. It doesn't even // provide _S_ISFIFO, per its usual naming convention for POSIX stuff. But it diff --git a/src/hotspot/share/utilities/stringUtils.hpp b/src/hotspot/share/utilities/stringUtils.hpp index c3d21233808..66c8d30c7c0 100644 --- a/src/hotspot/share/utilities/stringUtils.hpp +++ b/src/hotspot/share/utilities/stringUtils.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,11 +26,7 @@ #define SHARE_UTILITIES_STRINGUTILS_HPP #include "memory/allStatic.hpp" - -#ifdef _WINDOWS - // strtok_s is the Windows thread-safe equivalent of POSIX strtok_r -# define strtok_r strtok_s -#endif +#include "utilities/globalDefinitions.hpp" class StringUtils : AllStatic { public: From d0d85cd6b5e8272066b424f4d5f4c84c923a4274 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 24 Mar 2026 15:36:15 +0000 Subject: [PATCH 090/160] 8380526: G1: Remove "last young" use for the Prepare Mixed GC Reviewed-by: ayang, iwalulya --- src/hotspot/share/gc/g1/g1CollectorState.cpp | 2 +- src/hotspot/share/gc/g1/g1CollectorState.hpp | 22 ++++++++++---------- src/hotspot/share/gc/g1/g1Policy.cpp | 8 +++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectorState.cpp b/src/hotspot/share/gc/g1/g1CollectorState.cpp index 66292642603..2b550f36904 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.cpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.cpp @@ -31,7 +31,7 @@ G1CollectorState::Pause G1CollectorState::gc_pause_type(bool concurrent_operatio assert(SafepointSynchronize::is_at_safepoint(), "must be"); switch (_phase) { case Phase::YoungNormal: return Pause::Normal; - case Phase::YoungLastYoung: return Pause::LastYoung; + case Phase::YoungPrepareMixed: return Pause::PrepareMixed; case Phase::YoungConcurrentStart: return concurrent_operation_is_full_mark ? Pause::ConcurrentStartFull : Pause::ConcurrentStartUndo; diff --git a/src/hotspot/share/gc/g1/g1CollectorState.hpp b/src/hotspot/share/gc/g1/g1CollectorState.hpp index fc59df7349d..64c848959ae 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.hpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.hpp @@ -45,9 +45,9 @@ class G1CollectorState { // during that GC because we only decide whether we do this type of GC at the start // of the pause. YoungConcurrentStart, - // Indicates that we are about to start or in the last young gc in the Young-Only + // Indicates that we are about to start or in the prepare mixed gc in the Young-Only // phase before the Mixed phase. This GC is required to keep pause time requirements. - YoungLastYoung, + YoungPrepareMixed, // Doing extra old generation evacuation. Mixed, // The Full GC phase (that coincides with the Full GC pause). @@ -72,19 +72,19 @@ public: void set_in_full_gc() { _phase = Phase::FullGC; } // Pause setters - void set_in_young_gc_before_mixed() { _phase = Phase::YoungLastYoung; } void set_in_concurrent_start_gc() { _phase = Phase::YoungConcurrentStart; _initiate_conc_mark_if_possible = false; } + void set_in_prepare_mixed_gc() { _phase = Phase::YoungPrepareMixed; } void set_initiate_conc_mark_if_possible(bool v) { _initiate_conc_mark_if_possible = v; } // Phase getters - bool is_in_young_only_phase() const { return _phase == Phase::YoungNormal || _phase == Phase::YoungConcurrentStart || _phase == Phase::YoungLastYoung; } + bool is_in_young_only_phase() const { return _phase == Phase::YoungNormal || _phase == Phase::YoungConcurrentStart || _phase == Phase::YoungPrepareMixed; } bool is_in_mixed_phase() const { return _phase == Phase::Mixed; } // Specific pauses - bool is_in_young_gc_before_mixed() const { return _phase == Phase::YoungLastYoung; } - bool is_in_full_gc() const { return _phase == Phase::FullGC; } bool is_in_concurrent_start_gc() const { return _phase == Phase::YoungConcurrentStart; } + bool is_in_prepare_mixed_gc() const { return _phase == Phase::YoungPrepareMixed; } + bool is_in_full_gc() const { return _phase == Phase::FullGC; } bool initiate_conc_mark_if_possible() const { return _initiate_conc_mark_if_possible; } @@ -95,9 +95,9 @@ public: enum class Pause : uint { Normal, - LastYoung, ConcurrentStartFull, ConcurrentStartUndo, + PrepareMixed, Cleanup, Remark, Mixed, @@ -109,9 +109,9 @@ public: static const char* to_string(Pause type) { static const char* pause_strings[] = { "Normal", - "Prepare Mixed", "Concurrent Start", // Do not distinguish between the different "Concurrent Start", // Concurrent Start pauses. + "Prepare Mixed", "Cleanup", "Remark", "Mixed", @@ -129,7 +129,7 @@ public: assert_is_young_pause(type); return type == Pause::ConcurrentStartUndo || type == Pause::ConcurrentStartFull || - type == Pause::LastYoung || + type == Pause::PrepareMixed || type == Pause::Normal; } @@ -138,9 +138,9 @@ public: return type == Pause::Mixed; } - static bool is_last_young_pause(Pause type) { + static bool is_prepare_mixed_pause(Pause type) { assert_is_young_pause(type); - return type == Pause::LastYoung; + return type == Pause::PrepareMixed; } static bool is_concurrent_start_pause(Pause type) { diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index dce83a3f084..05caff1257a 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -729,7 +729,7 @@ double G1Policy::constant_other_time_ms(double pause_time_ms) const { } bool G1Policy::about_to_start_mixed_phase() const { - return collector_state()->is_in_concurrent_cycle() || collector_state()->is_in_young_gc_before_mixed(); + return collector_state()->is_in_concurrent_cycle() || collector_state()->is_in_prepare_mixed_gc(); } bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_word_size) { @@ -935,7 +935,7 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar record_pause(this_pause, start_time_sec, end_time_sec); - if (G1CollectorState::is_last_young_pause(this_pause)) { + if (G1CollectorState::is_prepare_mixed_pause(this_pause)) { assert(!G1CollectorState::is_concurrent_start_pause(this_pause), "The young GC before mixed is not allowed to be concurrent start GC"); // This has been the young GC before we start doing mixed GCs. We already @@ -1332,7 +1332,7 @@ void G1Policy::record_concurrent_mark_cleanup_end(bool has_rebuilt_remembered_se log_debug(gc, ergo)("request young-only gcs (candidate old regions not available)"); } if (mixed_gc_pending) { - collector_state()->set_in_young_gc_before_mixed(); + collector_state()->set_in_prepare_mixed_gc(); } double end_sec = os::elapsedTime(); @@ -1397,7 +1397,7 @@ void G1Policy::update_time_to_mixed_tracking(Pause gc_type, case Pause::Cleanup: case Pause::Remark: case Pause::Normal: - case Pause::LastYoung: + case Pause::PrepareMixed: _concurrent_start_to_mixed.add_pause(end - start); break; case Pause::ConcurrentStartFull: From 9658c19afdf401bfda7ac2d7e79e37bfb78c1330 Mon Sep 17 00:00:00 2001 From: Kangcheng Xu Date: Tue, 24 Mar 2026 16:16:51 +0000 Subject: [PATCH 091/160] 8378713: C2: performance regression due to missing constant folding for Math.pow() Reviewed-by: roland, mchevalier --- src/hotspot/share/opto/callnode.cpp | 174 ++++++++++++++ src/hotspot/share/opto/callnode.hpp | 17 ++ src/hotspot/share/opto/classes.hpp | 1 + src/hotspot/share/opto/library_call.cpp | 62 +---- src/hotspot/share/opto/macro.cpp | 18 +- .../intrinsics/math/PowDNodeTests.java | 218 ++++++++++++++++++ .../compiler/lib/ir_framework/IRNode.java | 6 + 7 files changed, 431 insertions(+), 65 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/intrinsics/math/PowDNodeTests.java diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index e01feb874ef..c7c0810ae85 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -43,6 +43,7 @@ #include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" #include "utilities/powerOfTwo.hpp" // Portions of code courtesy of Clifford Click @@ -1371,6 +1372,25 @@ TupleNode* CallLeafPureNode::make_tuple_of_input_state_and_top_return_values(con return tuple; } +CallLeafPureNode* CallLeafPureNode::inline_call_leaf_pure_node(Node* control) const { + Node* top = Compile::current()->top(); + if (control == nullptr) { + control = in(TypeFunc::Control); + } + + CallLeafPureNode* call = new CallLeafPureNode(tf(), entry_point(), _name); + call->init_req(TypeFunc::Control, control); + call->init_req(TypeFunc::I_O, top); + call->init_req(TypeFunc::Memory, top); + call->init_req(TypeFunc::ReturnAdr, top); + call->init_req(TypeFunc::FramePtr, top); + for (unsigned int i = 0; i < tf()->domain()->cnt() - TypeFunc::Parms; i++) { + call->init_req(TypeFunc::Parms + i, in(TypeFunc::Parms + i)); + } + + return call; +} + Node* CallLeafPureNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (is_dead()) { return nullptr; @@ -2437,3 +2457,157 @@ bool CallNode::may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeO return true; } + +PowDNode::PowDNode(Compile* C, Node* base, Node* exp) + : CallLeafPureNode( + OptoRuntime::Math_DD_D_Type(), + StubRoutines::dpow() != nullptr ? StubRoutines::dpow() : CAST_FROM_FN_PTR(address, SharedRuntime::dpow), + "pow") { + add_flag(Flag_is_macro); + C->add_macro_node(this); + + init_req(TypeFunc::Parms + 0, base); + init_req(TypeFunc::Parms + 1, C->top()); // double slot padding + init_req(TypeFunc::Parms + 2, exp); + init_req(TypeFunc::Parms + 3, C->top()); // double slot padding +} + +const Type* PowDNode::Value(PhaseGVN* phase) const { + const Type* t_base = phase->type(base()); + const Type* t_exp = phase->type(exp()); + + if (t_base == Type::TOP || t_exp == Type::TOP) { + return Type::TOP; + } + + const TypeD* base_con = t_base->isa_double_constant(); + const TypeD* exp_con = t_exp->isa_double_constant(); + const TypeD* result_t = nullptr; + + // constant folding: both inputs are constants + if (base_con != nullptr && exp_con != nullptr) { + result_t = TypeD::make(SharedRuntime::dpow(base_con->getd(), exp_con->getd())); + } + + // Special cases when only the exponent is known: + if (exp_con != nullptr) { + double e = exp_con->getd(); + + // If the second argument is positive or negative zero, then the result is 1.0. + // i.e., pow(x, +/-0.0D) => 1.0 + if (e == 0.0) { // true for both -0.0 and +0.0 + result_t = TypeD::ONE; + } + + // If the second argument is NaN, then the result is NaN. + // i.e., pow(x, NaN) => NaN + if (g_isnan(e)) { + result_t = TypeD::make(NAN); + } + } + + if (result_t != nullptr) { + // We can't simply return a TypeD here, it must be a tuple type to be compatible with call nodes. + const Type** fields = TypeTuple::fields(2); + fields[TypeFunc::Parms + 0] = result_t; + fields[TypeFunc::Parms + 1] = Type::HALF; + return TypeTuple::make(TypeFunc::Parms + 2, fields); + } + + return tf()->range(); +} + +Node* PowDNode::Ideal(PhaseGVN* phase, bool can_reshape) { + if (!can_reshape) { + return nullptr; // wait for igvn + } + + PhaseIterGVN* igvn = phase->is_IterGVN(); + Node* base = this->base(); + Node* exp = this->exp(); + + const Type* t_exp = phase->type(exp); + const TypeD* exp_con = t_exp->isa_double_constant(); + + // Special cases when only the exponent is known: + if (exp_con != nullptr) { + double e = exp_con->getd(); + + // If the second argument is 1.0, then the result is the same as the first argument. + // i.e., pow(x, 1.0) => x + if (e == 1.0) { + return make_tuple_of_input_state_and_result(igvn, base); + } + + // If the second argument is 2.0, then strength reduce to multiplications. + // i.e., pow(x, 2.0) => x * x + if (e == 2.0) { + Node* mul = igvn->transform(new MulDNode(base, base)); + return make_tuple_of_input_state_and_result(igvn, mul); + } + + // If the second argument is 0.5, the strength reduce to square roots. + // i.e., pow(x, 0.5) => sqrt(x) iff x > 0 + if (e == 0.5 && Matcher::match_rule_supported(Op_SqrtD)) { + Node* ctrl = in(TypeFunc::Control); + Node* zero = igvn->zerocon(T_DOUBLE); + + // According to the API specs, pow(-0.0, 0.5) = 0.0 and sqrt(-0.0) = -0.0. + // So pow(-0.0, 0.5) shouldn't be replaced with sqrt(-0.0). + // -0.0/+0.0 are both excluded since floating-point comparison doesn't distinguish -0.0 from +0.0. + Node* cmp = igvn->register_new_node_with_optimizer(new CmpDNode(base, zero)); + Node* test = igvn->register_new_node_with_optimizer(new BoolNode(cmp, BoolTest::le)); + + IfNode* iff = new IfNode(ctrl, test, PROB_UNLIKELY_MAG(3), COUNT_UNKNOWN); + igvn->register_new_node_with_optimizer(iff); + Node* if_slow = igvn->register_new_node_with_optimizer(new IfTrueNode(iff)); // x <= 0 + Node* if_fast = igvn->register_new_node_with_optimizer(new IfFalseNode(iff)); // x > 0 + + // slow path: call pow(x, 0.5) + Node* call = igvn->register_new_node_with_optimizer(inline_call_leaf_pure_node(if_slow)); + Node* call_ctrl = igvn->register_new_node_with_optimizer(new ProjNode(call, TypeFunc::Control)); + Node* call_result = igvn->register_new_node_with_optimizer(new ProjNode(call, TypeFunc::Parms + 0)); + + // fast path: sqrt(x) + Node* sqrt = igvn->register_new_node_with_optimizer(new SqrtDNode(igvn->C, if_fast, base)); + + // merge paths + RegionNode* region = new RegionNode(3); + igvn->register_new_node_with_optimizer(region); + region->init_req(1, call_ctrl); // slow path + region->init_req(2, if_fast); // fast path + + PhiNode* phi = new PhiNode(region, Type::DOUBLE); + igvn->register_new_node_with_optimizer(phi); + phi->init_req(1, call_result); // slow: pow() result + phi->init_req(2, sqrt); // fast: sqrt() result + + igvn->C->set_has_split_ifs(true); // Has chance for split-if optimization + + return make_tuple_of_input_state_and_result(igvn, phi, region); + } + } + + return CallLeafPureNode::Ideal(phase, can_reshape); +} + +// We can't simply have Ideal() returning a Con or MulNode since the users are still expecting a Call node, but we could +// produce a tuple that follows the same pattern so users can still get control, io, memory, etc.. +TupleNode* PowDNode::make_tuple_of_input_state_and_result(PhaseIterGVN* phase, Node* result, Node* control) { + if (control == nullptr) { + control = in(TypeFunc::Control); + } + + Compile* C = phase->C; + C->remove_macro_node(this); + TupleNode* tuple = TupleNode::make( + tf()->range(), + control, + in(TypeFunc::I_O), + in(TypeFunc::Memory), + in(TypeFunc::FramePtr), + in(TypeFunc::ReturnAdr), + result, + C->top()); + return tuple; +} diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index 95d1fc27d45..a5131676347 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -948,6 +948,8 @@ public: } int Opcode() const override; Node* Ideal(PhaseGVN* phase, bool can_reshape) override; + + CallLeafPureNode* inline_call_leaf_pure_node(Node* control = nullptr) const; }; //------------------------------CallLeafNoFPNode------------------------------- @@ -1299,4 +1301,19 @@ public: JVMState* dbg_jvms() const { return nullptr; } #endif }; + +//------------------------------PowDNode-------------------------------------- +class PowDNode : public CallLeafPureNode { + TupleNode* make_tuple_of_input_state_and_result(PhaseIterGVN* phase, Node* result, Node* control = nullptr); + +public: + PowDNode(Compile* C, Node* base, Node* exp); + int Opcode() const override; + const Type* Value(PhaseGVN* phase) const override; + Node* Ideal(PhaseGVN* phase, bool can_reshape) override; + + Node* base() const { return in(TypeFunc::Parms + 0); } + Node* exp() const { return in(TypeFunc::Parms + 2); } +}; + #endif // SHARE_OPTO_CALLNODE_HPP diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 719b90ad6dd..d9290492337 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -286,6 +286,7 @@ macro(OpaqueZeroTripGuard) macro(OpaqueConstantBool) macro(OpaqueInitializedAssertionPredicate) macro(OpaqueTemplateAssertionPredicate) +macro(PowD) macro(ProfileBoolean) macro(OrI) macro(OrL) diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index eed99ceb8bc..e0f95377cde 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -1819,61 +1819,17 @@ bool LibraryCallKit::runtime_math(const TypeFunc* call_type, address funcAddr, c //------------------------------inline_math_pow----------------------------- bool LibraryCallKit::inline_math_pow() { + Node* base = argument(0); Node* exp = argument(2); - const TypeD* d = _gvn.type(exp)->isa_double_constant(); - if (d != nullptr) { - if (d->getd() == 2.0) { - // Special case: pow(x, 2.0) => x * x - Node* base = argument(0); - set_result(_gvn.transform(new MulDNode(base, base))); - return true; - } else if (d->getd() == 0.5 && Matcher::match_rule_supported(Op_SqrtD)) { - // Special case: pow(x, 0.5) => sqrt(x) - Node* base = argument(0); - Node* zero = _gvn.zerocon(T_DOUBLE); - RegionNode* region = new RegionNode(3); - Node* phi = new PhiNode(region, Type::DOUBLE); - - Node* cmp = _gvn.transform(new CmpDNode(base, zero)); - // According to the API specs, pow(-0.0, 0.5) = 0.0 and sqrt(-0.0) = -0.0. - // So pow(-0.0, 0.5) shouldn't be replaced with sqrt(-0.0). - // -0.0/+0.0 are both excluded since floating-point comparison doesn't distinguish -0.0 from +0.0. - Node* test = _gvn.transform(new BoolNode(cmp, BoolTest::le)); - - Node* if_pow = generate_slow_guard(test, nullptr); - Node* value_sqrt = _gvn.transform(new SqrtDNode(C, control(), base)); - phi->init_req(1, value_sqrt); - region->init_req(1, control()); - - if (if_pow != nullptr) { - set_control(if_pow); - address target = StubRoutines::dpow() != nullptr ? StubRoutines::dpow() : - CAST_FROM_FN_PTR(address, SharedRuntime::dpow); - const TypePtr* no_memory_effects = nullptr; - Node* trig = make_runtime_call(RC_LEAF, OptoRuntime::Math_DD_D_Type(), target, "POW", - no_memory_effects, base, top(), exp, top()); - Node* value_pow = _gvn.transform(new ProjNode(trig, TypeFunc::Parms+0)); -#ifdef ASSERT - Node* value_top = _gvn.transform(new ProjNode(trig, TypeFunc::Parms+1)); - assert(value_top == top(), "second value must be top"); -#endif - phi->init_req(2, value_pow); - region->init_req(2, _gvn.transform(new ProjNode(trig, TypeFunc::Control))); - } - - C->set_has_split_ifs(true); // Has chance for split-if optimization - set_control(_gvn.transform(region)); - record_for_igvn(region); - set_result(_gvn.transform(phi)); - - return true; - } - } - - return StubRoutines::dpow() != nullptr ? - runtime_math(OptoRuntime::Math_DD_D_Type(), StubRoutines::dpow(), "dpow") : - runtime_math(OptoRuntime::Math_DD_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dpow), "POW"); + CallNode* pow = new PowDNode(C, base, exp); + set_predefined_input_for_runtime_call(pow); + pow = _gvn.transform(pow)->as_CallLeafPure(); + set_predefined_output_for_runtime_call(pow); + Node* result = _gvn.transform(new ProjNode(pow, TypeFunc::Parms + 0)); + record_for_igvn(pow); + set_result(result); + return true; } //------------------------------inline_math_native----------------------------- diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 48277eb46d2..c78f6533840 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2500,6 +2500,7 @@ void PhaseMacroExpand::eliminate_macro_nodes() { assert(n->Opcode() == Op_LoopLimit || n->Opcode() == Op_ModD || n->Opcode() == Op_ModF || + n->Opcode() == Op_PowD || n->is_OpaqueConstantBool() || n->is_OpaqueInitializedAssertionPredicate() || n->Opcode() == Op_MaxL || @@ -2656,18 +2657,11 @@ bool PhaseMacroExpand::expand_macro_nodes() { default: switch (n->Opcode()) { case Op_ModD: - case Op_ModF: { - CallNode* mod_macro = n->as_Call(); - CallNode* call = new CallLeafPureNode(mod_macro->tf(), mod_macro->entry_point(), mod_macro->_name); - call->init_req(TypeFunc::Control, mod_macro->in(TypeFunc::Control)); - call->init_req(TypeFunc::I_O, C->top()); - call->init_req(TypeFunc::Memory, C->top()); - call->init_req(TypeFunc::ReturnAdr, C->top()); - call->init_req(TypeFunc::FramePtr, C->top()); - for (unsigned int i = 0; i < mod_macro->tf()->domain()->cnt() - TypeFunc::Parms; i++) { - call->init_req(TypeFunc::Parms + i, mod_macro->in(TypeFunc::Parms + i)); - } - _igvn.replace_node(mod_macro, call); + case Op_ModF: + case Op_PowD: { + CallLeafPureNode* call_macro = n->as_CallLeafPure(); + CallLeafPureNode* call = call_macro->inline_call_leaf_pure_node(); + _igvn.replace_node(call_macro, call); transform_later(call); break; } diff --git a/test/hotspot/jtreg/compiler/intrinsics/math/PowDNodeTests.java b/test/hotspot/jtreg/compiler/intrinsics/math/PowDNodeTests.java new file mode 100644 index 00000000000..e28cc5ab346 --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/math/PowDNodeTests.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2026, IBM and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package compiler.intrinsics.math; + +import jdk.test.lib.Asserts; + +import compiler.lib.ir_framework.*; +import compiler.lib.generators.*; +import static compiler.lib.generators.Generators.*; + +import java.util.Random; + +/* + * @test + * @bug 8378713 + * @key randomness + * @summary Math.pow(base, exp) should constant propagate + * @library /test/lib / + * @run driver ${test.main.class} + */ +public class PowDNodeTests { + public static final Generator UNIFORMS = G.uniformDoubles(); // [0, 1) + + public static final double B = UNIFORMS.next() * 1000.0d; + public static final double E = UNIFORMS.next() * 1000.0d + 3.0d; // e >= 3 to avoid strength reduction code + + public static void main(String[] args) { + TestFramework.run(); + + testCorrectness(); + } + + // Test 1: pow(2.0, 10.0) -> 1024.0 + @Test + @IR(failOn = {IRNode.POW_D}) + public static double constantLiteralFolding() { + return Math.pow(2.0, 10.0); // should fold to 1024.0 + } + + // Test 2: pow(final B, final E) -> B^E + @Test + @IR(failOn = {IRNode.POW_D}) + public static double constantStaticFolding() { + return Math.pow(B, E); // should fold to B^E + } + + // Test 3: pow(b, 0.0) -> 1.0 + @Test + @IR(failOn = {IRNode.POW_D}) + @Arguments(values = {Argument.RANDOM_EACH}) + public static double expZero(double b) { + return Math.pow(b, 0.0); + } + + // Test 4: pow(b, 1.0) -> b (identity) + @Test + @IR(failOn = {IRNode.POW_D}) + @Arguments(values = {Argument.RANDOM_EACH}) + public static double expOne(double b) { + return Math.pow(b, 1.0); + } + + // Test 5: pow(b, NaN) -> NaN + @Test + @IR(failOn = {IRNode.POW_D}) + @Arguments(values = {Argument.RANDOM_EACH}) + public static double expNaN(double b) { + return Math.pow(b, Double.NaN); + } + + // Test 6: pow(b, 2.0) -> b * b + // More tests in TestPow2Opt.java + @Test + @IR(failOn = {IRNode.POW_D}) + @IR(counts = {IRNode.MUL_D, "1"}) + @Arguments(values = {Argument.RANDOM_EACH}) + public static double expTwo(double b) { + return Math.pow(b, 2.0); + } + + // Test 7: pow(b, 0.5) -> b <= 0.0 ? pow(b, 0.5) : sqrt(b) + // More tests in TestPow0Dot5Opt.java + @Test + @IR(counts = {IRNode.IF, "1"}) + @IR(counts = {IRNode.SQRT_D, "1"}) + @IR(counts = {".*CallLeaf.*pow.*", "1"}, phase = CompilePhase.BEFORE_MATCHING) + @Arguments(values = {Argument.RANDOM_EACH}) + public static double expDot5(double b) { + return Math.pow(b, 0.5); // expand to: if (b > 0) { sqrt(b) } else { call(b) } + } + + // Test 8: non-constant exponent stays as call + @Test + @IR(counts = {IRNode.POW_D, "1"}) + @Arguments(values = {Argument.RANDOM_EACH, Argument.RANDOM_EACH}) + public static double nonConstant(double b, double e) { + return Math.pow(b, e); + } + + // Test 9: late constant discovery on base (after loop opts) + @Test + @IR(counts = {IRNode.POW_D, "1"}, phase = CompilePhase.AFTER_PARSING) + @IR(failOn = {IRNode.POW_D}) + public static double lateBaseConstant() { + double base = 0; + for (int i = 0; i < 4; i++) { + if ((i % 2) == 0) { + base = B; + } + } + // After loop opts, base == B (constant), so pow(B, E) folds + return Math.pow(base, E); + } + + // Test 10: late constant discovery on exp (after loop opts) + @Test + @IR(counts = {IRNode.POW_D, "1"}, phase = CompilePhase.AFTER_PARSING) + @IR(failOn = {IRNode.POW_D}) + public static double lateExpConstant() { + double exp = 0; + for (int i = 0; i < 4; i++) { + if ((i % 2) == 0) { + exp = E; + } + } + // After loop opts, exp == E (constant), so pow(B, E) folds + return Math.pow(B, exp); + } + + // Test 11: late constant discoveries on both base and exp (after loop opts) + @Test + @IR(counts = {IRNode.POW_D, "1"}, phase = CompilePhase.AFTER_PARSING) + @IR(failOn = {IRNode.POW_D}) + public static double lateBothConstant() { + double base = 0, exp = 0; + for (int i = 0; i < 4; i++) { + if ((i % 2) == 0) { + base = B; + exp = E; + } + } + // After loop opts, base = B, exp == E, so pow(B, E) folds + return Math.pow(base, exp); + } + + private static void assertEQWithinOneUlp(double expected, double observed) { + if (Double.isNaN(expected) && Double.isNaN(observed)) return; + + // Math.pow() requires result must be within 1 ulp of the respective magnitude + double ulp = Math.max(Math.ulp(expected), Math.ulp(observed)); + if (Math.abs(expected - observed) > ulp) { + throw new AssertionError(String.format( + "expect = %x, observed = %x, ulp = %x", + Double.doubleToRawLongBits(expected), Double.doubleToRawLongBits(observed), Double.doubleToRawLongBits(ulp) + )); + } + } + + private static void testCorrectness() { + // No need to warm up for intrinsics + Asserts.assertEQ(1024.0d, constantLiteralFolding()); + + double BE = StrictMath.pow(B, E); + assertEQWithinOneUlp(BE, constantStaticFolding()); + assertEQWithinOneUlp(BE, lateBaseConstant()); + assertEQWithinOneUlp(BE, lateExpConstant()); + assertEQWithinOneUlp(BE, lateBothConstant()); + + Generator anyBits = G.anyBitsDouble(); + Generator largeDoubles = G.uniformDoubles(Long.MAX_VALUE, Double.MAX_VALUE); + Generator doubles = G.doubles(); + double[] values = { + Double.MIN_VALUE, Double.MIN_NORMAL, -42.0d, -1.0d, -0.0d, +0.0d, 0.5d, 1.0d, 2.0d, 123d, Double.MAX_VALUE, + Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN, + UNIFORMS.next(), UNIFORMS.next(), + largeDoubles.next(), -largeDoubles.next(), // some sufficiently large magnitudes + anyBits.next(), anyBits.next(), // any bits with potentially more NaN representation + doubles.next(), doubles.next() // a healthy sprinkle of whatever else is possible + }; + + for (double b : values) { + // Strength reduced, so we know the bits matches exactly + Asserts.assertEQ(1.0d, expZero(b)); + Asserts.assertEQ(b, expOne(b)); + Asserts.assertEQ(b * b, expTwo(b)); + + assertEQWithinOneUlp(Double.NaN, expNaN(b)); + + // Runtime calls, so make sure the result is within 1 ulp + assertEQWithinOneUlp(StrictMath.pow(b, 0.5d), expDot5(b)); + + for (double e : values) { + assertEQWithinOneUlp(StrictMath.pow(b, e), nonConstant(b, e)); + } + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index ebc95527344..8885d1283df 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -3134,6 +3134,12 @@ public class IRNode { macroNodes(MOD_D, regex); } + public static final String POW_D = PREFIX + "POW_D" + POSTFIX; + static { + String regex = START + "PowD" + MID + END; + macroNodes(POW_D, regex); + } + public static final String BLACKHOLE = PREFIX + "BLACKHOLE" + POSTFIX; static { fromBeforeRemoveUselessToFinalCode(BLACKHOLE, "Blackhole"); From 7b7f40b143ff84b7d4580930ec11926b833af031 Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Tue, 24 Mar 2026 17:12:50 +0000 Subject: [PATCH 092/160] 8380561: Refactor java/nio/channels/DatagramChannel TestNG tests to use JUnit Reviewed-by: alanb, bpb --- .../DatagramChannel/AdaptorConnect.java | 31 +++--- .../DatagramChannel/AdaptorGetters.java | 105 +++++++++--------- .../DatagramChannel/AfterDisconnect.java | 40 +++---- .../DatagramChannel/ConnectExceptions.java | 93 +++++++++------- .../DatagramChannel/ConnectPortZero.java | 59 +++++----- .../DatagramChannel/ConnectedSend.java | 34 +++--- .../nio/channels/DatagramChannel/SRTest.java | 32 ++++-- .../DatagramChannel/SendExceptions.java | 99 ++++++++++------- .../DatagramChannel/SendPortZero.java | 59 +++++----- .../DatagramChannel/SendReceiveMaxSize.java | 64 ++++++----- 10 files changed, 343 insertions(+), 273 deletions(-) diff --git a/test/jdk/java/nio/channels/DatagramChannel/AdaptorConnect.java b/test/jdk/java/nio/channels/DatagramChannel/AdaptorConnect.java index a609089e459..bf6b9023eb4 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/AdaptorConnect.java +++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorConnect.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /* @test * @bug 8232673 * @summary Test DatagramChannel socket adaptor connect method with illegal args - * @run testng AdaptorConnect + * @run junit AdaptorConnect */ import java.net.DatagramSocket; @@ -34,10 +34,9 @@ import java.net.SocketException; import java.nio.channels.DatagramChannel; import static java.net.InetAddress.getLoopbackAddress; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; -@Test public class AdaptorConnect { /** @@ -59,6 +58,7 @@ public class AdaptorConnect { /** * Test connect method with an open socket. */ + @Test public void testOpenSocket() throws Exception { try (DatagramChannel dc = DatagramChannel.open()) { DatagramSocket s = dc.socket(); @@ -66,42 +66,43 @@ public class AdaptorConnect { testConnectWithIllegalArguments(s); // should not be bound or connected - assertTrue(s.getLocalSocketAddress() == null); - assertTrue(s.getRemoteSocketAddress() == null); + assertNull(s.getLocalSocketAddress()); + assertNull(s.getRemoteSocketAddress()); // connect(SocketAddress) var remote1 = new InetSocketAddress(getLoopbackAddress(), 7001); s.connect(remote1); - assertEquals(s.getRemoteSocketAddress(), remote1); + assertEquals(remote1, s.getRemoteSocketAddress()); testConnectWithIllegalArguments(s); - assertEquals(s.getRemoteSocketAddress(), remote1); + assertEquals(remote1, s.getRemoteSocketAddress()); // connect(SocketAddress) var remote2 = new InetSocketAddress(getLoopbackAddress(), 7002); s.connect(remote2); - assertEquals(s.getRemoteSocketAddress(), remote2); + assertEquals(remote2, s.getRemoteSocketAddress()); testConnectWithIllegalArguments(s); - assertEquals(s.getRemoteSocketAddress(), remote2); + assertEquals(remote2, s.getRemoteSocketAddress()); // connect(InetAddress, int) var remote3 = new InetSocketAddress(getLoopbackAddress(), 7003); s.connect(remote3.getAddress(), remote3.getPort()); - assertEquals(s.getRemoteSocketAddress(), remote3); + assertEquals(remote3, s.getRemoteSocketAddress()); testConnectWithIllegalArguments(s); - assertEquals(s.getRemoteSocketAddress(), remote3); + assertEquals(remote3, s.getRemoteSocketAddress()); // connect(InetAddress, int) var remote4 = new InetSocketAddress(getLoopbackAddress(), 7004); s.connect(remote4.getAddress(), remote4.getPort()); - assertEquals(s.getRemoteSocketAddress(), remote4); + assertEquals(remote4, s.getRemoteSocketAddress()); testConnectWithIllegalArguments(s); - assertEquals(s.getRemoteSocketAddress(), remote4); + assertEquals(remote4, s.getRemoteSocketAddress()); } } /** * Test connect method with a closed socket. */ + @Test public void testClosedSocket() throws Exception { DatagramChannel dc = DatagramChannel.open(); DatagramSocket s = dc.socket(); diff --git a/test/jdk/java/nio/channels/DatagramChannel/AdaptorGetters.java b/test/jdk/java/nio/channels/DatagramChannel/AdaptorGetters.java index 9e12f46be04..69f5a76c12d 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/AdaptorGetters.java +++ b/test/jdk/java/nio/channels/DatagramChannel/AdaptorGetters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /* @test * @bug 8232673 * @summary Test the DatagramChannel socket adaptor getter methods - * @run testng AdaptorGetters + * @run junit AdaptorGetters */ import java.net.DatagramSocket; @@ -32,15 +32,15 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.channels.DatagramChannel; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; -@Test public class AdaptorGetters { /** * Test getters on unbound socket, before and after it is closed. */ + @Test public void testUnboundSocket() throws Exception { DatagramChannel dc = DatagramChannel.open(); DatagramSocket s = dc.socket(); @@ -53,12 +53,12 @@ public class AdaptorGetters { // local address assertTrue(s.getLocalAddress().isAnyLocalAddress()); - assertTrue(s.getLocalPort() == 0); - assertTrue(s.getLocalSocketAddress() == null); + assertEquals(0, s.getLocalPort()); + assertNull(s.getLocalSocketAddress()); // remote address - assertTrue(s.getInetAddress() == null); - assertTrue(s.getPort() == -1); + assertNull(s.getInetAddress()); + assertEquals(-1, s.getPort()); } finally { dc.close(); @@ -70,19 +70,20 @@ public class AdaptorGetters { assertTrue(s.isClosed()); // local address - assertTrue(s.getLocalAddress() == null); - assertTrue(s.getLocalPort() == -1); - assertTrue(s.getLocalSocketAddress() == null); + assertNull(s.getLocalAddress()); + assertEquals(-1, s.getLocalPort()); + assertNull(s.getLocalSocketAddress()); // remote address - assertTrue(s.getInetAddress() == null); - assertTrue(s.getPort() == -1); - assertTrue((s.getRemoteSocketAddress() == null)); + assertNull(s.getInetAddress()); + assertEquals(-1, s.getPort()); + assertNull(s.getRemoteSocketAddress()); } /** * Test getters on bound socket, before and after it is closed. */ + @Test public void testBoundSocket() throws Exception { DatagramChannel dc = DatagramChannel.open(); DatagramSocket s = dc.socket(); @@ -96,14 +97,14 @@ public class AdaptorGetters { assertFalse(s.isClosed()); // local address - assertEquals(s.getLocalAddress(), localAddress.getAddress()); - assertTrue(s.getLocalPort() == localAddress.getPort()); - assertEquals(s.getLocalSocketAddress(), localAddress); + assertEquals(localAddress.getAddress(), s.getLocalAddress()); + assertEquals(localAddress.getPort(), s.getLocalPort()); + assertEquals(localAddress, s.getLocalSocketAddress()); // remote address - assertTrue(s.getInetAddress() == null); - assertTrue(s.getPort() == -1); - assertTrue((s.getRemoteSocketAddress() == null)); + assertNull(s.getInetAddress()); + assertEquals(-1, s.getPort()); + assertNull(s.getRemoteSocketAddress()); } finally { dc.close(); @@ -115,19 +116,20 @@ public class AdaptorGetters { assertTrue(s.isClosed()); // local address - assertTrue(s.getLocalAddress() == null); - assertTrue(s.getLocalPort() == -1); - assertTrue(s.getLocalSocketAddress() == null); + assertNull(s.getLocalAddress()); + assertEquals(-1, s.getLocalPort()); + assertNull(s.getLocalSocketAddress()); // remote address - assertTrue(s.getInetAddress() == null); - assertTrue(s.getPort() == -1); - assertTrue((s.getRemoteSocketAddress() == null)); + assertNull(s.getInetAddress()); + assertEquals(-1, s.getPort()); + assertNull(s.getRemoteSocketAddress()); } /** * Test getters on connected socket, before and after it is closed. */ + @Test public void testConnectedSocket() throws Exception { var loopback = InetAddress.getLoopbackAddress(); var remoteAddress = new InetSocketAddress(loopback, 7777); @@ -143,14 +145,14 @@ public class AdaptorGetters { assertFalse(s.isClosed()); // local address - assertEquals(s.getLocalAddress(), localAddress.getAddress()); - assertTrue(s.getLocalPort() == localAddress.getPort()); - assertEquals(s.getLocalSocketAddress(), localAddress); + assertEquals(localAddress.getAddress(), s.getLocalAddress()); + assertEquals(localAddress.getPort(), s.getLocalPort()); + assertEquals(localAddress, s.getLocalSocketAddress()); // remote address - assertEquals(s.getInetAddress(), remoteAddress.getAddress()); - assertTrue(s.getPort() == remoteAddress.getPort()); - assertEquals(s.getRemoteSocketAddress(), remoteAddress); + assertEquals(remoteAddress.getAddress(), s.getInetAddress()); + assertEquals(remoteAddress.getPort(), s.getPort()); + assertEquals(remoteAddress, s.getRemoteSocketAddress()); } finally { dc.close(); @@ -162,19 +164,20 @@ public class AdaptorGetters { assertTrue(s.isClosed()); // local address - assertTrue(s.getLocalAddress() == null); - assertTrue(s.getLocalPort() == -1); - assertTrue(s.getLocalSocketAddress() == null); + assertNull(s.getLocalAddress()); + assertEquals(-1, s.getLocalPort()); + assertNull(s.getLocalSocketAddress()); // remote address - assertEquals(s.getInetAddress(), remoteAddress.getAddress()); - assertTrue(s.getPort() == remoteAddress.getPort()); - assertEquals(s.getRemoteSocketAddress(), remoteAddress); + assertEquals(remoteAddress.getAddress(), s.getInetAddress()); + assertEquals(remoteAddress.getPort(), s.getPort()); + assertEquals(remoteAddress, s.getRemoteSocketAddress()); } /** * Test getters on disconnected socket, before and after it is closed. */ + @Test public void testDisconnectedSocket() throws Exception { DatagramChannel dc = DatagramChannel.open(); DatagramSocket s = dc.socket(); @@ -191,14 +194,14 @@ public class AdaptorGetters { assertFalse(s.isClosed()); // local address - assertEquals(s.getLocalAddress(), localAddress.getAddress()); - assertTrue(s.getLocalPort() == localAddress.getPort()); - assertEquals(s.getLocalSocketAddress(), localAddress); + assertEquals(localAddress.getAddress(), s.getLocalAddress()); + assertEquals(localAddress.getPort(), s.getLocalPort()); + assertEquals(localAddress, s.getLocalSocketAddress()); // remote address - assertTrue(s.getInetAddress() == null); - assertTrue(s.getPort() == -1); - assertTrue((s.getRemoteSocketAddress() == null)); + assertNull(s.getInetAddress()); + assertEquals(-1, s.getPort()); + assertNull(s.getRemoteSocketAddress()); } finally { @@ -211,13 +214,13 @@ public class AdaptorGetters { assertTrue(s.isClosed()); // local address - assertTrue(s.getLocalAddress() == null); - assertTrue(s.getLocalPort() == -1); - assertTrue(s.getLocalSocketAddress() == null); + assertNull(s.getLocalAddress()); + assertEquals(-1, s.getLocalPort()); + assertNull(s.getLocalSocketAddress()); // remote address - assertTrue(s.getInetAddress() == null); - assertTrue(s.getPort() == -1); - assertTrue((s.getRemoteSocketAddress() == null)); + assertNull(s.getInetAddress()); + assertEquals(-1, s.getPort()); + assertNull(s.getRemoteSocketAddress()); } } diff --git a/test/jdk/java/nio/channels/DatagramChannel/AfterDisconnect.java b/test/jdk/java/nio/channels/DatagramChannel/AfterDisconnect.java index de59984dae1..44a34ea3e55 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/AfterDisconnect.java +++ b/test/jdk/java/nio/channels/DatagramChannel/AfterDisconnect.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,9 +25,9 @@ * @bug 8231880 8231258 * @library /test/lib * @summary Test DatagramChannel bound to specific address/ephemeral port after disconnect - * @run testng/othervm AfterDisconnect - * @run testng/othervm -Djava.net.preferIPv4Stack=true AfterDisconnect - * @run testng/othervm -Djava.net.preferIPv6Addresses=true AfterDisconnect + * @run junit/othervm AfterDisconnect + * @run junit/othervm -Djava.net.preferIPv4Stack=true AfterDisconnect + * @run junit/othervm -Djava.net.preferIPv6Addresses=true AfterDisconnect */ import java.io.IOException; @@ -49,15 +49,17 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Predicate; -import org.testng.annotations.Test; -import static org.testng.Assert.*; - import jdk.test.lib.net.IPSupport; +import org.junit.jupiter.api.Test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.*; + public class AfterDisconnect { interface RetryableTest { - public void runTest() throws T; + void runTest() throws T; } // retry the given lambda (RetryableTest) if an exception @@ -81,7 +83,7 @@ public class AfterDisconnect { * When calling {@link DatagramChannel#disconnect()} a {@link BindException} * may occur. In which case we want to retry the test. */ - class BindExceptionOnDisconnect extends BindException { + static class BindExceptionOnDisconnect extends BindException { BindExceptionOnDisconnect(BindException x) { super(x.getMessage()); initCause(x); @@ -149,8 +151,8 @@ public class AfterDisconnect { dc.connect(remote); assertTrue(dc.isConnected()); - assertEquals(dc.getLocalAddress(), local); - assertEquals(dc.getRemoteAddress(), remote); + assertEquals(local, dc.getLocalAddress()); + assertEquals(remote, dc.getRemoteAddress()); try { dc.disconnect(); @@ -158,8 +160,8 @@ public class AfterDisconnect { throw new BindExceptionOnDisconnect(x); } assertFalse(dc.isConnected()); - assertEquals(dc.getLocalAddress(), local); - assertTrue(dc.getRemoteAddress() == null); + assertEquals(local, dc.getLocalAddress()); + assertNull(dc.getRemoteAddress()); } } @@ -192,7 +194,7 @@ public class AfterDisconnect { /** * Returns a map of the given channel's socket options and values. */ - private Map, Object> options(DatagramChannel dc) throws IOException { + private Map, Object> options(DatagramChannel dc) { Map, Object> map = new HashMap<>(); for (SocketOption option : dc.supportedOptions()) { try { @@ -229,15 +231,15 @@ public class AfterDisconnect { // check blocking mode with non-blocking receive ByteBuffer bb = ByteBuffer.allocate(100); SocketAddress sender = dc.receive(bb); - assertTrue(sender == null); + assertNull(sender); // send datagram and ensure that channel is selected - dc.send(ByteBuffer.wrap("Hello".getBytes("UTF-8")), dc.getLocalAddress()); + dc.send(ByteBuffer.wrap("Hello".getBytes(UTF_8)), dc.getLocalAddress()); assertFalse(key.isReadable()); while (sel.select() == 0); assertTrue(key.isReadable()); sender = dc.receive(bb); - assertEquals(sender, dc.getLocalAddress()); + assertEquals(dc.getLocalAddress(), sender); // cancel key, flush from Selector, and restore blocking mode key.cancel(); @@ -273,10 +275,10 @@ public class AfterDisconnect { assertTrue(key.isValid()); // send datagram to multicast group, should be received - dc.send(ByteBuffer.wrap("Hello".getBytes("UTF-8")), dc.getLocalAddress()); + dc.send(ByteBuffer.wrap("Hello".getBytes(UTF_8)), dc.getLocalAddress()); ByteBuffer bb = ByteBuffer.allocate(100); SocketAddress sender = dc.receive(bb); - assertEquals(sender, dc.getLocalAddress()); + assertEquals(dc.getLocalAddress(), sender); // drop membership key.drop(); diff --git a/test/jdk/java/nio/channels/DatagramChannel/ConnectExceptions.java b/test/jdk/java/nio/channels/DatagramChannel/ConnectExceptions.java index a913f1f42e9..a89c4798c08 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/ConnectExceptions.java +++ b/test/jdk/java/nio/channels/DatagramChannel/ConnectExceptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,26 +25,32 @@ * @bug 8198753 * @summary Test DatagramChannel connect exceptions * @library .. - * @run testng ConnectExceptions + * @run junit ConnectExceptions */ -import java.io.*; -import java.net.*; -import java.nio.*; -import java.nio.channels.*; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.channels.AlreadyConnectedException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.UnresolvedAddressException; +import java.nio.channels.UnsupportedAddressTypeException; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; public class ConnectExceptions { - static DatagramChannel sndChannel; - static DatagramChannel rcvChannel; - static InetSocketAddress sender; - static InetSocketAddress receiver; + private DatagramChannel sndChannel; + private DatagramChannel rcvChannel; + private InetSocketAddress sender; + private InetSocketAddress receiver; - @BeforeTest - public static void setup() throws Exception { + @BeforeEach + public void setup() throws Exception { sndChannel = DatagramChannel.open(); sndChannel.bind(null); InetAddress address = InetAddress.getLocalHost(); @@ -60,29 +66,40 @@ public class ConnectExceptions { rcvChannel.socket().getLocalPort()); } - @Test(expectedExceptions = UnsupportedAddressTypeException.class) - public static void unsupportedAddressTypeException() throws Exception { - rcvChannel.connect(sender); - sndChannel.connect(new SocketAddress() {}); - } - - @Test(expectedExceptions = UnresolvedAddressException.class) - public static void unresolvedAddressException() throws Exception { - String host = TestUtil.UNRESOLVABLE_HOST; - InetSocketAddress unresolvable = new InetSocketAddress (host, 37); - sndChannel.connect(unresolvable); - } - - @Test(expectedExceptions = AlreadyConnectedException.class) - public static void alreadyConnectedException() throws Exception { - sndChannel.connect(receiver); - InetSocketAddress random = new InetSocketAddress(0); - sndChannel.connect(random); - } - - @AfterTest - public static void cleanup() throws Exception { + @AfterEach + public void cleanup() throws Exception { rcvChannel.close(); sndChannel.close(); } + + @Test + public void unsupportedAddressTypeException() throws IOException { + rcvChannel.connect(sender); + assertThrows(UnsupportedAddressTypeException.class, () -> { + sndChannel.connect(new SocketAddress() {}); + }); + } + + @Test + public void unresolvedAddressException() throws IOException { + String host = TestUtil.UNRESOLVABLE_HOST; + InetSocketAddress unresolvable = new InetSocketAddress (host, 37); + assertThrows(UnresolvedAddressException.class, () -> { + sndChannel.connect(unresolvable); + }); + sndChannel.connect(receiver); + assertThrows(UnresolvedAddressException.class, () -> { + sndChannel.connect(unresolvable); + }); + } + + @Test + public void alreadyConnectedException() throws IOException { + sndChannel.connect(receiver); + assertThrows(AlreadyConnectedException.class, () -> { + InetSocketAddress random = new InetSocketAddress(0); + sndChannel.connect(random); + }); + } + } diff --git a/test/jdk/java/nio/channels/DatagramChannel/ConnectPortZero.java b/test/jdk/java/nio/channels/DatagramChannel/ConnectPortZero.java index 4aa28e5226e..3ddad9ef526 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/ConnectPortZero.java +++ b/test/jdk/java/nio/channels/DatagramChannel/ConnectPortZero.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,10 +21,6 @@ * questions. */ -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.net.InetAddress; @@ -38,7 +34,15 @@ import static jdk.test.lib.net.IPSupport.hasIPv6; import static jdk.test.lib.net.IPSupport.hasIPv4; import static java.net.StandardProtocolFamily.INET; import static java.net.StandardProtocolFamily.INET6; -import static org.testng.Assert.assertThrows; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test @@ -46,51 +50,54 @@ import static org.testng.Assert.assertThrows; * @library /test/lib * @build jdk.test.lib.net.IPSupport * @summary Check that DatagramChannel throws expected Exception when connecting to port 0 - * @run testng ConnectPortZero - * @run testng/othervm -Djava.net.preferIPv4Stack=true ConnectPortZero + * @run junit ConnectPortZero + * @run junit/othervm -Djava.net.preferIPv4Stack=true ConnectPortZero */ public class ConnectPortZero { - private InetSocketAddress loopbackZeroAddr, wildcardZeroAddr; - private DatagramChannel datagramChannel, datagramChannelIPv4, datagramChannelIPv6; - private List channels; + private static InetSocketAddress loopbackZeroAddr, wildcardZeroAddr; + private static DatagramChannel datagramChannel, datagramChannelIPv4, datagramChannelIPv6; + private static List channels; private static final Class SE = SocketException.class; - @BeforeTest - public void setUp() throws IOException { + @BeforeAll + public static void setUp() throws IOException { wildcardZeroAddr = new InetSocketAddress(0); loopbackZeroAddr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); channels = new ArrayList<>(); datagramChannel = DatagramChannel.open(); - channels.add(new Object[]{datagramChannel}); + channels.add(datagramChannel); if (hasIPv4()) { datagramChannelIPv4 = DatagramChannel.open(INET); - channels.add(new Object[]{datagramChannelIPv4}); + channels.add(datagramChannelIPv4); } if (hasIPv6()) { datagramChannelIPv6 = DatagramChannel.open(INET6); - channels.add(new Object[]{datagramChannelIPv6}); + channels.add(datagramChannelIPv6); } } - @DataProvider(name = "data") - public Object[][] variants() { - return channels.toArray(Object[][]::new); + @AfterAll + public static void tearDown() throws IOException { + for(DatagramChannel ch : channels) { + ch.close(); + } } - @Test(dataProvider = "data") + public static List channels() { + return channels; + } + + @ParameterizedTest + @MethodSource("channels") public void testChannelConnect(DatagramChannel dc) { + assertTrue(dc.isOpen()); + assertFalse(dc.isConnected()); assertThrows(SE, () -> dc.connect(loopbackZeroAddr)); assertThrows(SE, () -> dc.connect(wildcardZeroAddr)); } - @AfterTest - public void tearDown() throws IOException { - for(Object[] ch : channels) { - ((DatagramChannel)ch[0]).close(); - } - } } diff --git a/test/jdk/java/nio/channels/DatagramChannel/ConnectedSend.java b/test/jdk/java/nio/channels/DatagramChannel/ConnectedSend.java index d761f8db3f0..78e5a37294f 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/ConnectedSend.java +++ b/test/jdk/java/nio/channels/DatagramChannel/ConnectedSend.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,27 @@ * @bug 4849277 7183800 * @summary Test DatagramChannel send while connected * @library .. - * @run testng ConnectedSend + * @run junit ConnectedSend * @author Mike McCloskey */ -import java.io.*; -import java.net.*; -import java.nio.*; -import java.nio.channels.*; -import java.nio.charset.*; -import org.testng.annotations.Test; -import static org.testng.Assert.*; + +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.DatagramChannel; +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; public class ConnectedSend { // Check if DatagramChannel.send while connected can include // address without throwing @Test - public static void sendToConnectedAddress() throws Exception { + public void sendToConnectedAddress() throws Exception { DatagramChannel sndChannel = DatagramChannel.open(); sndChannel.bind(null); InetAddress address = InetAddress.getLocalHost(); @@ -68,7 +72,7 @@ public class ConnectedSend { bb.clear(); rcvChannel.receive(bb); bb.flip(); - CharBuffer cb = Charset.forName("US-ASCII").newDecoder().decode(bb); + CharBuffer cb = StandardCharsets.US_ASCII.newDecoder().decode(bb); assertTrue(cb.toString().startsWith("h"), "Unexpected message content"); rcvChannel.close(); @@ -79,7 +83,7 @@ public class ConnectedSend { // that has not been initialized with an address; the legacy // datagram socket will send in this case @Test - public static void sendAddressedPacket() throws Exception { + public void sendAddressedPacket() throws Exception { DatagramChannel sndChannel = DatagramChannel.open(); sndChannel.bind(null); InetAddress address = InetAddress.getLocalHost(); @@ -99,19 +103,19 @@ public class ConnectedSend { rcvChannel.connect(sender); sndChannel.connect(receiver); - byte b[] = "hello".getBytes("UTF-8"); + byte[] b = "hello".getBytes(StandardCharsets.UTF_8); DatagramPacket pkt = new DatagramPacket(b, b.length); sndChannel.socket().send(pkt); ByteBuffer bb = ByteBuffer.allocate(256); rcvChannel.receive(bb); bb.flip(); - CharBuffer cb = Charset.forName("US-ASCII").newDecoder().decode(bb); + CharBuffer cb = StandardCharsets.US_ASCII.newDecoder().decode(bb); assertTrue(cb.toString().startsWith("h"), "Unexpected message content"); // Check that the pkt got set with the target address; // This is legacy behavior - assertEquals(pkt.getSocketAddress(), receiver, + assertEquals(receiver, pkt.getSocketAddress(), "Unexpected address set on packet"); rcvChannel.close(); diff --git a/test/jdk/java/nio/channels/DatagramChannel/SRTest.java b/test/jdk/java/nio/channels/DatagramChannel/SRTest.java index 9a7309cc46e..5ad66e6cdff 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/SRTest.java +++ b/test/jdk/java/nio/channels/DatagramChannel/SRTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,13 +23,19 @@ /* @test * @summary Test DatagramChannel's send and receive methods - * @run testng/othervm/timeout=20 SRTest + * @run junit/othervm SRTest */ -import java.io.*; -import java.net.*; -import java.nio.*; -import java.nio.channels.*; +import java.io.IOException; +import java.io.PrintStream; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.DatagramChannel; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutorService; @@ -37,22 +43,24 @@ import java.util.concurrent.Executors; import java.util.stream.Stream; import static java.nio.charset.StandardCharsets.US_ASCII; -import org.testng.annotations.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class SRTest { - ExecutorService executorService; + static ExecutorService executorService; static PrintStream log = System.err; static final String DATA_STRING = "hello"; - @BeforeClass - public void beforeClass() { + @BeforeAll + public static void beforeClass() { executorService = Executors.newCachedThreadPool(); } - @AfterClass - public void afterClass() { + @AfterAll + public static void afterClass() { executorService.shutdown(); } diff --git a/test/jdk/java/nio/channels/DatagramChannel/SendExceptions.java b/test/jdk/java/nio/channels/DatagramChannel/SendExceptions.java index 343ab1b8232..94508f9b6e2 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/SendExceptions.java +++ b/test/jdk/java/nio/channels/DatagramChannel/SendExceptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,27 +25,34 @@ * @bug 4675045 8198753 * @summary Test DatagramChannel send exceptions * @library .. - * @run testng SendExceptions + * @run junit SendExceptions */ -import java.io.*; -import java.net.*; -import java.nio.*; -import java.nio.channels.*; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.AlreadyConnectedException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.UnresolvedAddressException; +import java.nio.channels.UnsupportedAddressTypeException; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; public class SendExceptions { - static DatagramChannel sndChannel; - static DatagramChannel rcvChannel; - static InetSocketAddress sender; - static InetSocketAddress receiver; - static ByteBuffer buf = ByteBuffer.allocate(17); + private DatagramChannel sndChannel; + private DatagramChannel rcvChannel; + private InetSocketAddress sender; + private InetSocketAddress receiver; + static final ByteBuffer buf = ByteBuffer.allocate(17); - @BeforeTest - public static void setup() throws Exception { + @BeforeEach + public void setup() throws Exception { + buf.rewind(); sndChannel = DatagramChannel.open(); sndChannel.bind(null); InetAddress address = InetAddress.getLocalHost(); @@ -61,29 +68,43 @@ public class SendExceptions { rcvChannel.socket().getLocalPort()); } - @Test(expectedExceptions = UnsupportedAddressTypeException.class) - public static void unsupportedAddressTypeException() throws Exception { - rcvChannel.connect(sender); - sndChannel.send(buf, new SocketAddress() {}); - } - - @Test(expectedExceptions = UnresolvedAddressException.class) - public static void unresolvedAddressException() throws Exception { - String host = TestUtil.UNRESOLVABLE_HOST; - InetSocketAddress unresolvable = new InetSocketAddress (host, 37); - sndChannel.send(buf, unresolvable); - } - - @Test(expectedExceptions = AlreadyConnectedException.class) - public static void alreadyConnectedException() throws Exception { - sndChannel.connect(receiver); - InetSocketAddress random = new InetSocketAddress(0); - sndChannel.send(buf, random); - } - - @AfterTest - public static void cleanup() throws Exception { + @AfterEach + public void cleanup() throws Exception { rcvChannel.close(); sndChannel.close(); } + + @Test + public void unsupportedAddressTypeException() throws IOException { + assertThrows(UnsupportedAddressTypeException.class, () -> { + sndChannel.send(buf, new SocketAddress() {}); + }); + rcvChannel.connect(sender); + assertThrows(UnsupportedAddressTypeException.class, () -> { + sndChannel.send(buf, new SocketAddress() {}); + }); + sndChannel.connect(receiver); + assertThrows(UnsupportedAddressTypeException.class, () -> { + sndChannel.send(buf, new SocketAddress() {}); + }); + } + + @Test + public void unresolvedAddressException() { + String host = TestUtil.UNRESOLVABLE_HOST; + InetSocketAddress unresolvable = new InetSocketAddress (host, 37); + assertThrows(UnresolvedAddressException.class, () -> { + sndChannel.send(buf, unresolvable); + }); + } + + @Test + public void alreadyConnectedException() throws IOException { + sndChannel.connect(receiver); + InetSocketAddress random = new InetSocketAddress(0); + assertThrows(AlreadyConnectedException.class, () -> { + sndChannel.send(buf, random); + }); + } + } diff --git a/test/jdk/java/nio/channels/DatagramChannel/SendPortZero.java b/test/jdk/java/nio/channels/DatagramChannel/SendPortZero.java index 6a8b9f24d41..9733dad83f8 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/SendPortZero.java +++ b/test/jdk/java/nio/channels/DatagramChannel/SendPortZero.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,11 +21,6 @@ * questions. */ -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -39,7 +34,13 @@ import static java.net.StandardProtocolFamily.INET; import static java.net.StandardProtocolFamily.INET6; import static jdk.test.lib.net.IPSupport.hasIPv4; import static jdk.test.lib.net.IPSupport.hasIPv6; -import static org.testng.Assert.assertThrows; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test @@ -47,20 +48,20 @@ import static org.testng.Assert.assertThrows; * @library /test/lib * @build jdk.test.lib.net.IPSupport * @summary Check that DatagramChannel throws expected Exception when sending to port 0 - * @run testng/othervm SendPortZero - * @run testng/othervm -Djava.net.preferIPv4Stack=true SendPortZero + * @run junit/othervm SendPortZero + * @run junit/othervm -Djava.net.preferIPv4Stack=true SendPortZero */ public class SendPortZero { - private ByteBuffer buf; - private List channels; - private InetSocketAddress loopbackZeroAddr, wildcardZeroAddr; - private DatagramChannel datagramChannel, datagramChannelIPv4, datagramChannelIPv6; + private static ByteBuffer buf; + private static List channels; + private static InetSocketAddress loopbackZeroAddr, wildcardZeroAddr; + private static DatagramChannel datagramChannel, datagramChannelIPv4, datagramChannelIPv6; private static final Class SE = SocketException.class; - @BeforeTest - public void setUp() throws IOException { + @BeforeAll + public static void setUp() throws IOException { buf = ByteBuffer.wrap("test".getBytes()); wildcardZeroAddr = new InetSocketAddress(0); @@ -69,32 +70,34 @@ public class SendPortZero { channels = new ArrayList<>(); datagramChannel = DatagramChannel.open(); - channels.add(new Object[]{datagramChannel}); + channels.add(datagramChannel); if (hasIPv4()) { datagramChannelIPv4 = DatagramChannel.open(INET); - channels.add(new Object[]{datagramChannelIPv4}); + channels.add(datagramChannelIPv4); } if (hasIPv6()) { datagramChannelIPv6 = DatagramChannel.open(INET6); - channels.add(new Object[]{datagramChannelIPv6}); + channels.add(datagramChannelIPv6); } } - @DataProvider(name = "data") - public Object[][] variants() { - return channels.toArray(Object[][]::new); + @AfterAll + public static void tearDown() throws IOException { + for(DatagramChannel ch : channels) { + ch.close(); + } } - @Test(dataProvider = "data") + public static List channels() { + return channels; + } + + @ParameterizedTest + @MethodSource("channels") public void testChannelSend(DatagramChannel dc) { + assertTrue(dc.isOpen()); assertThrows(SE, () -> dc.send(buf, loopbackZeroAddr)); assertThrows(SE, () -> dc.send(buf, wildcardZeroAddr)); } - @AfterTest - public void tearDown() throws IOException { - for(Object[] ch : channels) { - ((DatagramChannel)ch[0]).close(); - } - } } diff --git a/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java b/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java index a2f5844ce25..cb6e66dd5de 100644 --- a/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java +++ b/test/jdk/java/nio/channels/DatagramChannel/SendReceiveMaxSize.java @@ -29,7 +29,7 @@ * maximum size on macOS. * @library /test/lib * @build jdk.test.lib.net.IPSupport - * @run testng/othervm SendReceiveMaxSize + * @run junit/othervm SendReceiveMaxSize */ /* * @test id=preferIPv4Stack @@ -38,7 +38,7 @@ * maximum size on macOS, using an IPv4 only socket. * @library /test/lib * @build jdk.test.lib.net.IPSupport - * @run testng/othervm -Djava.net.preferIPv4Stack=true SendReceiveMaxSize + * @run junit/othervm -Djava.net.preferIPv4Stack=true SendReceiveMaxSize */ /* * @test id=preferIPv6Loopback @@ -48,7 +48,7 @@ * interface. * @library /test/lib * @build jdk.test.lib.net.IPSupport - * @run testng/othervm -Dtest.preferLoopback=true SendReceiveMaxSize + * @run junit/othervm -Dtest.preferLoopback=true SendReceiveMaxSize */ /* * @test id=preferIPv4Loopback @@ -58,16 +58,13 @@ * loopback interface * @library /test/lib * @build jdk.test.lib.net.IPSupport - * @run testng/othervm -Dtest.preferLoopback=true -Djava.net.preferIPv4Stack=true SendReceiveMaxSize + * @run junit/othervm -Dtest.preferLoopback=true -Djava.net.preferIPv4Stack=true SendReceiveMaxSize */ import jdk.test.lib.RandomFactory; import jdk.test.lib.NetworkConfiguration; import jdk.test.lib.Platform; import jdk.test.lib.net.IPSupport; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; import java.net.Inet4Address; @@ -78,6 +75,7 @@ import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import java.util.ArrayList; +import java.util.List; import java.util.Random; import java.util.function.Predicate; @@ -88,9 +86,14 @@ import static java.net.StandardSocketOptions.SO_RCVBUF; import static jdk.test.lib.net.IPSupport.hasIPv4; import static jdk.test.lib.net.IPSupport.hasIPv6; import static jdk.test.lib.net.IPSupport.preferIPv4Stack; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class SendReceiveMaxSize { private final static Class IOE = IOException.class; @@ -102,14 +105,13 @@ public class SendReceiveMaxSize { } static DatagramChannelSupplier supplier(DatagramChannelSupplier supplier) { return supplier; } - @BeforeTest - public void setUp() { + @BeforeAll + public static void setUp() { IPSupport.throwSkippedExceptionIfNonOperational(); } - @DataProvider - public Object[][] invariants() throws IOException { - var testcases = new ArrayList(); + public static List testCases() throws IOException { + var testcases = new ArrayList(); var nc = NetworkConfiguration.probe(); var ipv4Loopback = (Inet4Address) InetAddress.getByName("127.0.0.1"); var ipv6Loopback = (Inet6Address) InetAddress.getByName("::1"); @@ -119,16 +121,16 @@ public class SendReceiveMaxSize { .filter(Predicate.not(InetAddress::isLoopbackAddress)) .findFirst() .orElse(ipv4Loopback); - testcases.add(new Object[]{ + testcases.add(Arguments.of( supplier(() -> DatagramChannel.open()), IPSupport.getMaxUDPSendBufSizeIPv4(), IPv4Addr - }); - testcases.add(new Object[]{ + )); + testcases.add(Arguments.of( supplier(() -> DatagramChannel.open(INET)), IPSupport.getMaxUDPSendBufSizeIPv4(), IPv4Addr - }); + )); } if (!preferIPv4Stack() && hasIPv6()) { InetAddress IPv6Addr = PREFER_LOOPBACK ? ipv6Loopback @@ -136,21 +138,22 @@ public class SendReceiveMaxSize { .filter(Predicate.not(InetAddress::isLoopbackAddress)) .findFirst() .orElse(ipv6Loopback); - testcases.add(new Object[]{ + testcases.add(Arguments.of( supplier(() -> DatagramChannel.open()), IPSupport.getMaxUDPSendBufSizeIPv6(), IPv6Addr - }); - testcases.add(new Object[]{ + )); + testcases.add(Arguments.of( supplier(() -> DatagramChannel.open(INET6)), IPSupport.getMaxUDPSendBufSizeIPv6(), IPv6Addr - }); + )); } - return testcases.toArray(Object[][]::new); + return testcases; } - @Test(dataProvider = "invariants") + @ParameterizedTest + @MethodSource("testCases") public void testGetOption(DatagramChannelSupplier supplier, int capacity, InetAddress host) throws IOException { if (Platform.isOSX()) { @@ -160,7 +163,8 @@ public class SendReceiveMaxSize { } } - @Test(dataProvider = "invariants") + @ParameterizedTest + @MethodSource("testCases") public void testSendReceiveMaxSize(DatagramChannelSupplier supplier, int capacity, InetAddress host) throws IOException { try (var receiver = DatagramChannel.open()) { @@ -205,8 +209,8 @@ public class SendReceiveMaxSize { // check that data has been fragmented and re-assembled correctly at receiver System.out.println("sendBuf: " + sendBuf); System.out.println("receiveBuf: " + receiveBuf); - assertEquals(sendBuf, receiveBuf); - assertEquals(sendBuf.compareTo(receiveBuf), 0); + assertEquals(receiveBuf, sendBuf); + assertEquals(0, sendBuf.compareTo(receiveBuf)); testData = new byte[capacity - 1]; random.nextBytes(testData); @@ -232,8 +236,8 @@ public class SendReceiveMaxSize { // check that data has been fragmented and re-assembled correctly at receiver System.out.println("sendBuf: " + sendBuf); System.out.println("receiveBuf: " + receiveBuf); - assertEquals(sendBuf, receiveBuf); - assertEquals(sendBuf.compareTo(receiveBuf), 0); + assertEquals(receiveBuf, sendBuf); + assertEquals(0, sendBuf.compareTo(receiveBuf)); var failSendBuf = ByteBuffer.allocate(capacity + 1); assertThrows(IOE, () -> sender.send(failSendBuf, addr)); From 4a9903bae4691947391942064624c44f5d26cab5 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Tue, 24 Mar 2026 18:06:28 +0000 Subject: [PATCH 093/160] 8380431: Shenandoah: Concurrent modification of stack-chunk objects during evacuation Reviewed-by: rkennke --- .../share/gc/shenandoah/shenandoahFullGC.cpp | 5 ++++- .../shenandoah/shenandoahGenerationalHeap.cpp | 17 +++++++++-------- .../share/gc/shenandoah/shenandoahHeap.cpp | 13 +++++++++++-- .../gc/shenandoah/shenandoahMark.inline.hpp | 7 +++---- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index 750f7e9122d..21b1fd9e0a8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -878,8 +878,11 @@ public: Copy::aligned_conjoint_words(compact_from, compact_to, size); oop new_obj = cast_to_oop(compact_to); - ContinuationGCSupport::relativize_stack_chunk(new_obj); + // Restore the mark word before relativizing the stack chunk. The copy's + // mark word contains the full GC forwarding encoding, which would cause + // is_stackChunk() to read garbage (especially with compact headers). new_obj->init_mark(); + ContinuationGCSupport::relativize_stack_chunk(new_obj); } } }; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index 27e374a0f85..d5cfa4b7fb9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -346,18 +346,19 @@ oop ShenandoahGenerationalHeap::try_evacuate_object(oop p, Thread* thread, uint increase_object_age(copy_val, from_region_age + 1); } + // Relativize stack chunks before publishing the copy. After the forwarding CAS, + // mutators can see the copy and thaw it via the fast path if flags == 0. We must + // relativize derived pointers and set gc_mode before that happens. Skip if the + // copy's mark word is already a forwarding pointer (another thread won the race + // and overwrote the original's header before we copied it). + if (!ShenandoahForwarding::is_forwarded(copy_val)) { + ContinuationGCSupport::relativize_stack_chunk(copy_val); + } + // Try to install the new forwarding pointer. oop result = ShenandoahForwarding::try_update_forwardee(p, copy_val); if (result == copy_val) { // Successfully evacuated. Our copy is now the public one! - - // This is necessary for virtual thread support. This uses the mark word without - // considering that it may now be a forwarding pointer (and could therefore crash). - // Secondarily, we do not want to spend cycles relativizing stack chunks for oops - // that lost the evacuation race (and will therefore not become visible). It is - // safe to do this on the public copy (this is also done during concurrent mark). - ContinuationGCSupport::relativize_stack_chunk(copy_val); - if (ShenandoahEvacTracking) { // Record that the evacuation succeeded evac_tracker()->end_evacuation(thread, size * HeapWordSize, FROM_GENERATION, TO_GENERATION); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 817f5e8dc47..5bf76505506 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1353,12 +1353,21 @@ oop ShenandoahHeap::try_evacuate_object(oop p, Thread* thread, ShenandoahHeapReg // Copy the object: Copy::aligned_disjoint_words(cast_from_oop(p), copy, size); - // Try to install the new forwarding pointer. oop copy_val = cast_to_oop(copy); + + // Relativize stack chunks before publishing the copy. After the forwarding CAS, + // mutators can see the copy and thaw it via the fast path if flags == 0. We must + // relativize derived pointers and set gc_mode before that happens. Skip if the + // copy's mark word is already a forwarding pointer (another thread won the race + // and overwrote the original's header before we copied it). + if (!ShenandoahForwarding::is_forwarded(copy_val)) { + ContinuationGCSupport::relativize_stack_chunk(copy_val); + } + + // Try to install the new forwarding pointer. oop result = ShenandoahForwarding::try_update_forwardee(p, copy_val); if (result == copy_val) { // Successfully evacuated. Our copy is now the public one! - ContinuationGCSupport::relativize_stack_chunk(copy_val); shenandoah_assert_correct(nullptr, copy_val); if (ShenandoahEvacTracking) { evac_tracker()->end_evacuation(thread, size * HeapWordSize, from_region->affiliation(), target_gen); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index ba24e890769..d4699a61166 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -77,10 +77,9 @@ void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveD if (task->is_not_chunked()) { if (obj->is_instance()) { // Case 1: Normal oop, process as usual. - if (ContinuationGCSupport::relativize_stack_chunk(obj)) { - // Loom doesn't support mixing of weak marking and strong marking of - // stack chunks. - cl->set_weak(false); + if (obj->is_stackChunk()) { + // Loom doesn't support mixing of weak marking and strong marking of stack chunks. + cl->set_weak(false); } obj->oop_iterate(cl); From 6b156ab45b763c7e79e4df15a8cab61949623eea Mon Sep 17 00:00:00 2001 From: Matias Saavedra Silva Date: Tue, 24 Mar 2026 18:23:53 +0000 Subject: [PATCH 094/160] 8357037: Verifier rejects method ending in switch instruction Reviewed-by: dlong, dholmes, coleenp --- src/hotspot/share/interpreter/bytecodes.cpp | 2 +- .../verifier/MethodEndsWithLookupSwitch.jasm | 54 ++++++++++++++++++ .../verifier/MethodEndsWithSwitch.java | 42 ++++++++++++++ .../verifier/MethodEndsWithTableSwitch.jasm | 57 +++++++++++++++++++ 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/runtime/verifier/MethodEndsWithLookupSwitch.jasm create mode 100644 test/hotspot/jtreg/runtime/verifier/MethodEndsWithSwitch.java create mode 100644 test/hotspot/jtreg/runtime/verifier/MethodEndsWithTableSwitch.jasm diff --git a/src/hotspot/share/interpreter/bytecodes.cpp b/src/hotspot/share/interpreter/bytecodes.cpp index 1526b3c330e..a7914b6b93a 100644 --- a/src/hotspot/share/interpreter/bytecodes.cpp +++ b/src/hotspot/share/interpreter/bytecodes.cpp @@ -402,7 +402,7 @@ int Bytecodes::special_length_at(Bytecodes::Code code, address bcp, address end) case _fast_binaryswitch: // fall through case _fast_linearswitch: { address aligned_bcp = align_up(bcp + 1, jintSize); - if (end != nullptr && aligned_bcp + 2*jintSize >= end) { + if (end != nullptr && aligned_bcp + 2*jintSize > end) { return -1; // don't read past end of code buffer } // Promote calculation to 64 bits to do range checks, used by the verifier. diff --git a/test/hotspot/jtreg/runtime/verifier/MethodEndsWithLookupSwitch.jasm b/test/hotspot/jtreg/runtime/verifier/MethodEndsWithLookupSwitch.jasm new file mode 100644 index 00000000000..c6043674da9 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/MethodEndsWithLookupSwitch.jasm @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 MethodEndsWithLookupSwitch version 51:0 { + +/* + * See if verifier allows a method to end with a switch instruction. + */ +private Method "foo":"()V" + stack 1 locals 1 +{ + goto L1; +L3: + stack_frame_type same; + return; +L1: + stack_frame_type same; + iconst_0; + lookupswitch { + default: L3 + }; +} + +public Method "":"()V" + stack 2 locals 1 +{ + aload_0; + dup; + invokespecial Method java/lang/Object."":"()V"; + invokespecial Method "foo":"()V"; + return; +} + +} // end Class MethodEndsWithLookupSwitch diff --git a/test/hotspot/jtreg/runtime/verifier/MethodEndsWithSwitch.java b/test/hotspot/jtreg/runtime/verifier/MethodEndsWithSwitch.java new file mode 100644 index 00000000000..9fca02eb5f0 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/MethodEndsWithSwitch.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 Verify that method can end with a switch instruction. + * @compile MethodEndsWithLookupSwitch.jasm MethodEndsWithTableSwitch.jasm + * @run main/othervm MethodEndsWithSwitch + */ + +public class MethodEndsWithSwitch { + + static void test(String testName) throws Exception { + System.out.println("Testing: " + testName); + Class c = Class.forName(testName); + } + + public static void main(String[] args) throws Exception { + test("MethodEndsWithLookupSwitch"); + test("MethodEndsWithTableSwitch"); + } +} diff --git a/test/hotspot/jtreg/runtime/verifier/MethodEndsWithTableSwitch.jasm b/test/hotspot/jtreg/runtime/verifier/MethodEndsWithTableSwitch.jasm new file mode 100644 index 00000000000..97505f48643 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/MethodEndsWithTableSwitch.jasm @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 MethodEndsWithTableSwitch version 51:0 { + +/* + * See if verifier allows a method to end with a switch instruction. + */ +private Method "foo":"()V" + stack 1 locals 1 +{ + goto L1; +L3: + stack_frame_type same; + return; +L1: + stack_frame_type same; + iconst_0; + tableswitch { + 0: L3; + 1: L3; + 2: L3; + default: L3 + }; +} + +public Method "":"()V" + stack 2 locals 1 +{ + aload_0; + dup; + invokespecial Method java/lang/Object."":"()V"; + invokespecial Method "foo":"()V"; + return; +} + +} // end Class MethodEndsWithTableSwitch From 2afd7b8b1459951fd476f0e9b0b8e1845c97890d Mon Sep 17 00:00:00 2001 From: Brent Christian Date: Tue, 24 Mar 2026 18:53:53 +0000 Subject: [PATCH 095/160] 8379672: jdk/internal/misc/VM/RuntimeArguments.java test still fails in Virtual threads mode after JDK-8373718 Reviewed-by: jpai --- test/jdk/jdk/internal/misc/VM/RuntimeArguments.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/jdk/jdk/internal/misc/VM/RuntimeArguments.java b/test/jdk/jdk/internal/misc/VM/RuntimeArguments.java index b86593d84ba..47df5da36f9 100644 --- a/test/jdk/jdk/internal/misc/VM/RuntimeArguments.java +++ b/test/jdk/jdk/internal/misc/VM/RuntimeArguments.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /** * @test * @requires vm.flagless - * @requires test.thread.factory == null + * @requires test.thread.factory == "" * @library /test/lib * @modules java.base/jdk.internal.misc * jdk.zipfs From 18fdbd2404b3b6b23ba8457e4c42674ffc3fffa4 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Tue, 24 Mar 2026 20:32:31 +0000 Subject: [PATCH 096/160] 8314258: Add integer_cast for checking conversions don't change the value Reviewed-by: azafari, stefank, lkorinth --- src/hotspot/share/utilities/integerCast.hpp | 157 ++++++++++ .../gtest/utilities/test_integerCast.cpp | 287 ++++++++++++++++++ 2 files changed, 444 insertions(+) create mode 100644 src/hotspot/share/utilities/integerCast.hpp create mode 100644 test/hotspot/gtest/utilities/test_integerCast.cpp diff --git a/src/hotspot/share/utilities/integerCast.hpp b/src/hotspot/share/utilities/integerCast.hpp new file mode 100644 index 00000000000..0715cab18d5 --- /dev/null +++ b/src/hotspot/share/utilities/integerCast.hpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_UTILITIES_INTEGERCAST_HPP +#define SHARE_UTILITIES_INTEGERCAST_HPP + +#include "cppstdlib/limits.hpp" +#include "cppstdlib/type_traits.hpp" +#include "metaprogramming/enableIf.hpp" +#include "utilities/debug.hpp" +#include "utilities/macros.hpp" + +#include + +// Tests whether all values for the From type are within the range of values +// for the To Type. From and To must be integral types. This is used by +// integer_cast to test for tautological conversions. +template), + ENABLE_IF(std::is_integral_v)> +constexpr bool is_always_integer_convertible() { + if constexpr (std::is_signed_v == std::is_signed_v) { + // signed => signed or unsigned => unsigned. + return sizeof(To) >= sizeof(From); + } else if constexpr (std::is_signed_v) { + // signed => unsigned is never tautological, because of negative values. + return false; + } else { + // unsigned => signed. + return sizeof(To) > sizeof(From); + } +} + +// Tests whether the value of from is within the range of values for the To +// type. To and From must be integral types. This is used by integer_cast +// to test whether the conversion should be performed. +template), + ENABLE_IF(std::is_integral_v)> +constexpr bool is_integer_convertible(From from) { + if constexpr (is_always_integer_convertible()) { + // This clause simplifies direct calls and the implementation below. It + // isn't needed by integer_cast, where a tautological call is discarded. + return true; + } else if constexpr (std::is_unsigned_v) { + // unsigned => signed or unsigned => unsigned. + // Convert To::max to corresponding unsigned for compare. + using U = std::make_unsigned_t; + return from <= static_cast(std::numeric_limits::max()); + } else if constexpr (std::is_signed_v) { + // signed => signed. + return ((std::numeric_limits::min() <= from) && + (from <= std::numeric_limits::max())); + } else { + // signed => unsigned. Convert from to corresponding unsigned for compare. + using U = std::make_unsigned_t; + return (0 <= from) && (static_cast(from) <= std::numeric_limits::max()); + } +} + +// Convert the from value to the To type, after a debug-only check that the +// value of from is within the range of values for the To type. To and From +// must be integral types. +// +// permit_tautology determines the behavior when a conversion will always +// succeed because the range of values for the From type is enclosed by the +// range of values for the To type (is_always_integer_convertible() +// is true). If true, the conversion will be performed as requested. If +// false, a compile-time error is produced. The default is false for 64bit +// platforms, true for 32bit platforms. See integer_cast_permit_tautology as +// the preferred way to override the default and always provide a true value. +// +// Unnecessary integer_casts make code harder to understand. Hence the +// compile-time failure for tautological conversions, to alert that a code +// change is making a integer_cast unnecessary. This can be suppressed on a +// per-call basis, because there are cases where a conversion might only +// sometimes be tautological. For example, the types involved may vary by +// platform. Another case is if the operation is in a template with dependent +// types, with the operation only being tautological for some instantiations. +// Suppressing the tautology check is an alternative to possibly complex +// metaprogramming to only perform the integer_cast when necessary. +// +// Despite that, for 32bit platforms the default is to not reject unnecessary +// integer_casts. This is because 64bit platforms are the primary target, and +// are likely to require conversions in some places. However, some of those +// conversions will be tautological on 32bit platforms, such as size_t => uint. +template), + ENABLE_IF(std::is_integral_v)> +constexpr To integer_cast(From from) { + if constexpr (is_always_integer_convertible()) { + static_assert(permit_tautology, "tautological integer_cast"); + } else { +#ifdef ASSERT + if (!is_integer_convertible(from)) { + if constexpr (std::is_signed_v) { + fatal("integer_cast failed: %jd", static_cast(from)); + } else { + fatal("integer_cast failed: %ju", static_cast(from)); + } + } +#endif // ASSERT + } + return static_cast(from); +} + +// Equivalent to "integer_cast(from)", disabling the compile-time +// check for tautological casts. Using this function is prefered to direct +// use of the permit_tautology template parameter for integer_cast, unless the +// choice is computed. +template), + ENABLE_IF(std::is_integral_v)> +constexpr To integer_cast_permit_tautology(From from) { + return integer_cast(from); +} + +// Convert an enumerator to an integral value via static_cast, after a +// debug-only check that the value is within the range for the destination +// type. This is mostly for compatibility with old code. Class scoped enums +// were used to work around ancient compilers that didn't implement class +// scoped static integral constants properly, and HotSpot code still has many +// examples of this. For others it might be sufficient to provide an explicit +// underlying type and either permit implicit conversions or use +// PrimitiveConversion::cast. +template), + ENABLE_IF(std::is_enum_v)> +constexpr To integer_cast(From from) { + using U = std::underlying_type_t; + return integer_cast(static_cast(from)); +} + +#endif // SHARE_UTILITIES_INTEGERCAST_HPP diff --git a/test/hotspot/gtest/utilities/test_integerCast.cpp b/test/hotspot/gtest/utilities/test_integerCast.cpp new file mode 100644 index 00000000000..87d35384c2c --- /dev/null +++ b/test/hotspot/gtest/utilities/test_integerCast.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "cppstdlib/limits.hpp" +#include "cppstdlib/type_traits.hpp" +#include "utilities/integerCast.hpp" +#include "utilities/globalDefinitions.hpp" + +#include "unittest.hpp" + +// Enable gcc warnings to verify we don't get any of these. +// Eventually we plan to have these globally enabled, but not there yet. +#ifdef __GNUC__ +#pragma GCC diagnostic warning "-Wconversion" +#pragma GCC diagnostic warning "-Wsign-conversion" +#endif + +// Tautology tests for signed -> signed types. +static_assert(is_always_integer_convertible()); +static_assert(!is_always_integer_convertible()); +static_assert(is_always_integer_convertible()); +static_assert(is_always_integer_convertible()); + +// Tautology tests for unsigned -> unsigned types. +static_assert(is_always_integer_convertible()); +static_assert(!is_always_integer_convertible()); +static_assert(is_always_integer_convertible()); +static_assert(is_always_integer_convertible()); + +// Tautology tests for signed -> unsigned types. +static_assert(!is_always_integer_convertible()); +static_assert(!is_always_integer_convertible()); +static_assert(!is_always_integer_convertible()); +static_assert(!is_always_integer_convertible()); + +// Tautology tests for unsigned -> signed types. +static_assert(!is_always_integer_convertible()); +static_assert(!is_always_integer_convertible()); +static_assert(is_always_integer_convertible()); +static_assert(!is_always_integer_convertible()); + +template +struct TestIntegerCastValues { + static TestIntegerCastValues values; + + T minus_one = static_cast(-1); + T zero = static_cast(0); + T one = static_cast(1); + T min = std::numeric_limits::min(); + T max = std::numeric_limits::max(); +}; + +template +TestIntegerCastValues TestIntegerCastValues::values{}; + +template +struct TestIntegerCastPairedValues { + static TestIntegerCastPairedValues values; + + From min = static_cast(std::numeric_limits::min()); + From max = static_cast(std::numeric_limits::max()); +}; + +template +TestIntegerCastPairedValues +TestIntegerCastPairedValues::values{}; + +////////////////////////////////////////////////////////////////////////////// +// Integer casts between integral types of different sizes. +// Test narrowing to verify checking. +// Test widening to verify no compiler warnings for tautological comparisons. + +template +struct TestIntegerCastIntegerValues { + static TestIntegerCastIntegerValues values; + + TestIntegerCastValues to; + TestIntegerCastValues from; + TestIntegerCastPairedValues to_as_from; +}; + +template +TestIntegerCastIntegerValues +TestIntegerCastIntegerValues::values{}; + +template +static void good_integer_conversion(From from) { + ASSERT_TRUE(is_integer_convertible(from)); + EXPECT_EQ(static_cast(from), integer_cast(from)); +} + +template +static void bad_integer_conversion(From from) { + EXPECT_FALSE(is_integer_convertible(from)); +} + +// signed -> signed is tautological unless From is wider than To. + +TEST(TestIntegerCast, wide_signed_to_narrow_signed_integers) { + using To = int32_t; + using From = int64_t; + using Values = TestIntegerCastIntegerValues; + const Values& values = Values::values; + + good_integer_conversion(values.from.minus_one); + good_integer_conversion(values.from.zero); + good_integer_conversion(values.from.one); + bad_integer_conversion(values.from.min); + bad_integer_conversion(values.from.max); + good_integer_conversion(values.to_as_from.min); + good_integer_conversion(values.to_as_from.max); + bad_integer_conversion(values.to_as_from.min - 1); + bad_integer_conversion(values.to_as_from.max + 1); +} + +// unsigned -> unsigned is tautological unless From is wider than To. + +TEST(TestIntegerCast, wide_unsigned_to_narrow_unsigned_integers) { + using To = uint32_t; + using From = uint64_t; + using Values = TestIntegerCastIntegerValues; + const Values& values = Values::values; + + bad_integer_conversion(values.from.minus_one); + good_integer_conversion(values.from.zero); + good_integer_conversion(values.from.one); + good_integer_conversion(values.from.min); + bad_integer_conversion(values.from.max); + good_integer_conversion(values.to_as_from.min); + good_integer_conversion(values.to_as_from.min); + bad_integer_conversion(values.to_as_from.min - 1); + bad_integer_conversion(values.to_as_from.max + 1); +} + +TEST(TestIntegerCast, unsigned_to_signed_same_size_integers) { + using To = int32_t; + using From = uint32_t; + using Values = TestIntegerCastIntegerValues; + const Values& values = Values::values; + + good_integer_conversion(values.from.zero); + good_integer_conversion(values.from.one); + good_integer_conversion(values.from.min); + bad_integer_conversion(values.from.max); + bad_integer_conversion(values.to_as_from.min); + good_integer_conversion(values.to_as_from.max); + bad_integer_conversion(values.to_as_from.max + 1); +} + +// Narrow unsigned to wide signed is tautological. + +TEST(TestIntegerCast, wide_unsigned_to_narrow_signed_integers) { + using To = int32_t; + using From = uint64_t; + using Values = TestIntegerCastIntegerValues; + const Values& values = Values::values; + + bad_integer_conversion(values.from.minus_one); + good_integer_conversion(values.from.zero); + good_integer_conversion(values.from.one); + good_integer_conversion(values.from.min); + bad_integer_conversion(values.from.max); + bad_integer_conversion(values.to_as_from.min); + good_integer_conversion(values.to_as_from.max); + bad_integer_conversion(values.to_as_from.min - 1); + bad_integer_conversion(values.to_as_from.max + 1); +} + +TEST(TestIntegerCast, signed_to_unsigned_same_size_integers) { + using To = uint32_t; + using From = int32_t; + using Values = TestIntegerCastIntegerValues; + const Values& values = Values::values; + + bad_integer_conversion(values.from.minus_one); + good_integer_conversion(values.from.zero); + good_integer_conversion(values.from.one); + bad_integer_conversion(values.from.min); + good_integer_conversion(values.from.max); + good_integer_conversion(values.to_as_from.min); + bad_integer_conversion(values.to_as_from.max); +} + +TEST(TestIntegerCast, narrow_signed_to_wide_unsigned_integers) { + using To = uint64_t; + using From = int32_t; + using Values = TestIntegerCastIntegerValues; + const Values& values = Values::values; + + bad_integer_conversion(values.from.minus_one); + good_integer_conversion(values.from.zero); + good_integer_conversion(values.from.one); + bad_integer_conversion(values.from.min); + good_integer_conversion(values.from.max); + good_integer_conversion(values.to_as_from.min); + bad_integer_conversion(values.to_as_from.max); +} + +TEST(TestIntegerCast, wide_signed_to_narrow_unsigned_integers) { + using To = uint32_t; + using From = int64_t; + using Values = TestIntegerCastIntegerValues; + const Values& values = Values::values; + + bad_integer_conversion(values.from.minus_one); + good_integer_conversion(values.from.zero); + good_integer_conversion(values.from.one); + bad_integer_conversion(values.from.min); + bad_integer_conversion(values.from.max); + good_integer_conversion(values.to_as_from.min); + good_integer_conversion(values.to_as_from.max); +} + +TEST(TestIntegerCast, permit_tautology) { + using From = uint32_t; + using To = int64_t; + using Values = TestIntegerCastIntegerValues; + const Values& values = Values::values; + + static_assert(is_always_integer_convertible()); + EXPECT_EQ(static_cast(values.from.min), + (integer_cast(values.from.min))); + EXPECT_EQ(static_cast(values.from.min), + (integer_cast_permit_tautology(values.from.min))); + EXPECT_EQ(static_cast(values.from.max), + (integer_cast(values.from.max))); + EXPECT_EQ(static_cast(values.from.max), + integer_cast_permit_tautology(values.from.max)); +} + +TEST(TestIntegerCast, check_constexpr) { + using From = int64_t; + using To = int32_t; + constexpr From value = std::numeric_limits::max(); + constexpr To converted = integer_cast(value); + EXPECT_EQ(static_cast(value), converted); +} + +#ifdef ASSERT + +TEST_VM_ASSERT(TestIntegerCast, cast_failure_signed_range) { + using From = int64_t; + using To = int32_t; + using Values = TestIntegerCastIntegerValues; + const Values& values = Values::values; + + From value = values.from.max; + To expected = static_cast(value); // Narrowing conversion. + EXPECT_FALSE(is_integer_convertible(value)); + // Should assert. If it doesn't, then shuld be equal, so fail. + EXPECT_NE(static_cast(value), integer_cast(value)); +} + +TEST_VM_ASSERT(TestIntegerCast, cast_failure_unsigned_range) { + using From = uint64_t; + using To = uint32_t; + using Values = TestIntegerCastIntegerValues; + const Values& values = Values::values; + + From value = values.from.max; + To expected = static_cast(value); // Narrowing conversion. + EXPECT_FALSE(is_integer_convertible(value)); + // Should assert. If it doesn't, then should be equal, so fail. + EXPECT_NE(static_cast(value), integer_cast(value)); +} + +#endif // ASSERT From 4b38e7bcd3989a5e33d2b38e3c7904a5d3be7e45 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Tue, 24 Mar 2026 21:35:17 +0000 Subject: [PATCH 097/160] 8346133: Refactor java.time.ZoneOffset caching Reviewed-by: jlu, liach, rriggs --- .../share/classes/java/time/ZoneOffset.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/java.base/share/classes/java/time/ZoneOffset.java b/src/java.base/share/classes/java/time/ZoneOffset.java index 2a45e7cbf82..3bcb75db3e4 100644 --- a/src/java.base/share/classes/java/time/ZoneOffset.java +++ b/src/java.base/share/classes/java/time/ZoneOffset.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -87,9 +87,9 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.function.Supplier; import jdk.internal.util.DecimalDigits; -import jdk.internal.vm.annotation.Stable; /** * A time-zone offset from Greenwich/UTC, such as {@code +02:00}. @@ -178,8 +178,13 @@ public final class ZoneOffset /** * The zone rules for an offset will always return this offset. Cache it for efficiency. */ - @Stable - private transient ZoneRules rules; + private final transient LazyConstant rules = + LazyConstant.of(new Supplier() { + @Override + public ZoneRules get() { + return ZoneRules.of(ZoneOffset.this); + } + }); //----------------------------------------------------------------------- /** @@ -521,11 +526,7 @@ public final class ZoneOffset */ @Override public ZoneRules getRules() { - ZoneRules rules = this.rules; - if (rules == null) { - rules = this.rules = ZoneRules.of(this); - } - return rules; + return rules.get(); } @Override From 4e7901576851ebe22b40349bf0e155b39ca6c34a Mon Sep 17 00:00:00 2001 From: William Kemper Date: Tue, 24 Mar 2026 23:52:27 +0000 Subject: [PATCH 098/160] 8380846: GenShen: Remove the experimental option to disable adaptive tenuring Reviewed-by: kdnilsen --- .../gc/shenandoah/shenandoahAgeCensus.cpp | 28 ++++--------------- .../gc/shenandoah/shenandoahAgeCensus.hpp | 1 - .../gc/shenandoah/shenandoahGeneration.cpp | 2 +- .../gc/shenandoah/shenandoahMark.inline.hpp | 12 ++++---- .../gc/shenandoah/shenandoah_globals.hpp | 6 +--- 5 files changed, 12 insertions(+), 37 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp index 86ff6f22c72..71fd6e37614 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp @@ -57,21 +57,13 @@ ShenandoahAgeCensus::ShenandoahAgeCensus(uint max_workers) // Sentinel value _tenuring_threshold[i] = MAX_COHORTS; } - if (ShenandoahGenerationalAdaptiveTenuring) { - _local_age_tables = NEW_C_HEAP_ARRAY(AgeTable*, _max_workers, mtGC); - CENSUS_NOISE(_local_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, max_workers, mtGC);) - for (uint i = 0; i < _max_workers; i++) { - _local_age_tables[i] = new AgeTable(false); - CENSUS_NOISE(_local_noise[i].clear();) - } - } else { - _local_age_tables = nullptr; + _local_age_tables = NEW_C_HEAP_ARRAY(AgeTable*, _max_workers, mtGC); + CENSUS_NOISE(_local_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, max_workers, mtGC);) + for (uint i = 0; i < _max_workers; i++) { + _local_age_tables[i] = new AgeTable(false); + CENSUS_NOISE(_local_noise[i].clear();) } _epoch = MAX_SNAPSHOTS - 1; // see prepare_for_census_update() - - if (!ShenandoahGenerationalAdaptiveTenuring) { - _tenuring_threshold[_epoch] = InitialTenuringThreshold; - } } ShenandoahAgeCensus::~ShenandoahAgeCensus() { @@ -154,7 +146,6 @@ void ShenandoahAgeCensus::prepare_for_census_update() { // and compute the new tenuring threshold. void ShenandoahAgeCensus::update_census(size_t age0_pop) { prepare_for_census_update(); - assert(ShenandoahGenerationalAdaptiveTenuring, "Only update census when adaptive tenuring is enabled"); assert(_global_age_tables[_epoch]->is_clear(), "Dirty decks"); CENSUS_NOISE(assert(_global_noise[_epoch].is_clear(), "Dirty decks");) @@ -195,10 +186,6 @@ void ShenandoahAgeCensus::reset_global() { // Reset the local age tables, clearing any partial census. void ShenandoahAgeCensus::reset_local() { - if (!ShenandoahGenerationalAdaptiveTenuring) { - assert(_local_age_tables == nullptr, "Error"); - return; - } for (uint i = 0; i < _max_workers; i++) { _local_age_tables[i]->clear(); CENSUS_NOISE(_local_noise[i].clear();) @@ -221,10 +208,6 @@ bool ShenandoahAgeCensus::is_clear_global() { // Is local census information clear? bool ShenandoahAgeCensus::is_clear_local() { - if (!ShenandoahGenerationalAdaptiveTenuring) { - assert(_local_age_tables == nullptr, "Error"); - return true; - } for (uint i = 0; i < _max_workers; i++) { bool clear = _local_age_tables[i]->is_clear(); CENSUS_NOISE(clear |= _local_noise[i].is_clear();) @@ -258,7 +241,6 @@ void ShenandoahAgeCensus::update_total() { #endif // !PRODUCT void ShenandoahAgeCensus::update_tenuring_threshold() { - assert(ShenandoahGenerationalAdaptiveTenuring, "Only update when adaptive tenuring is enabled"); uint tt = compute_tenuring_threshold(); assert(tt <= MAX_COHORTS, "Out of bounds"); _tenuring_threshold[_epoch] = tt; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp index 39ea4ee9002..5ccd0b21398 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp @@ -173,7 +173,6 @@ class ShenandoahAgeCensus: public CHeapObj { ~ShenandoahAgeCensus(); // Return the local age table (population vector) for worker_id. - // Only used in the case of ShenandoahGenerationalAdaptiveTenuring AgeTable* get_local_age_table(uint worker_id) const { return _local_age_tables[worker_id]; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index 750389ae6c3..f3c05049079 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -272,7 +272,7 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { } // Tally the census counts and compute the adaptive tenuring threshold - if (is_generational && ShenandoahGenerationalAdaptiveTenuring) { + if (is_generational) { // Objects above TAMS weren't included in the age census. Since they were all // allocated in this cycle they belong in the age 0 cohort. We walk over all // young regions and sum the volume of objects between TAMS and top. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index d4699a61166..0d42b95164b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -117,13 +117,11 @@ inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop ob // Age census for objects in the young generation if (GENERATION == YOUNG || (GENERATION == GLOBAL && region->is_young())) { assert(heap->mode()->is_generational(), "Only if generational"); - if (ShenandoahGenerationalAdaptiveTenuring) { - assert(region->is_young(), "Only for young objects"); - uint age = ShenandoahHeap::get_object_age(obj); - ShenandoahAgeCensus* const census = ShenandoahGenerationalHeap::heap()->age_census(); - CENSUS_NOISE(census->add(age, region->age(), region->youth(), size, worker_id);) - NO_CENSUS_NOISE(census->add(age, region->age(), size, worker_id);) - } + assert(region->is_young(), "Only for young objects"); + const uint age = ShenandoahHeap::get_object_age(obj); + ShenandoahAgeCensus* const census = ShenandoahGenerationalHeap::heap()->age_census(); + CENSUS_NOISE(census->add(age, region->age(), region->youth(), size, worker_id);) + NO_CENSUS_NOISE(census->add(age, region->age(), size, worker_id);) } if (!region->is_humongous_start()) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index d3e9a1f9fae..bbfa19934d9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -152,9 +152,6 @@ "evvort even if the usage of old generation is below " \ "ShenandoahIgnoreOldGrowthBelowPercentage.") \ \ - product(bool, ShenandoahGenerationalAdaptiveTenuring, true, EXPERIMENTAL, \ - "(Generational mode only) Dynamically adapt tenuring age.") \ - \ product(bool, ShenandoahGenerationalCensusIgnoreOlderCohorts, true, \ EXPERIMENTAL,\ "(Generational mode only) Ignore mortality rates older than the " \ @@ -179,8 +176,7 @@ "(Generational mode only) Cohort mortality rates below this " \ "value will be treated as indicative of longevity, leading to " \ "tenuring. A lower value delays tenuring, a higher value hastens "\ - "it. Used only when ShenandoahGenerationalhenAdaptiveTenuring is "\ - "enabled.") \ + "it.") \ range(0.001,0.999) \ \ product(size_t, ShenandoahGenerationalTenuringCohortPopulationThreshold, \ From e8ce930e28c26b12fb0eafab201ccedaa2650e5d Mon Sep 17 00:00:00 2001 From: William Kemper Date: Wed, 25 Mar 2026 00:23:41 +0000 Subject: [PATCH 099/160] 8379020: GenShen: Promote all objects when whitebox full GC is requested Reviewed-by: ysr, kdnilsen --- .../gc/shenandoah/shenandoahAgeCensus.hpp | 28 +++++++++++++++++++ .../shenandoahGenerationalControlThread.cpp | 7 +++++ 2 files changed, 35 insertions(+) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp index 5ccd0b21398..9c5baaedcd6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp @@ -97,6 +97,8 @@ struct ShenandoahNoiseStats { // once the per-worker data is consolidated into the appropriate population vector // per minor collection. The _local_age_table is thus C x N, for N GC workers. class ShenandoahAgeCensus: public CHeapObj { + friend class ShenandoahTenuringOverride; + AgeTable** _global_age_tables; // Global age tables used for adapting tenuring threshold, one per snapshot AgeTable** _local_age_tables; // Local scratch age tables to track object ages, one per worker @@ -148,6 +150,10 @@ class ShenandoahAgeCensus: public CHeapObj { return _tenuring_threshold[prev]; } + // Override the tenuring threshold for the current epoch. This is used to + // cause everything to be promoted for a whitebox full gc request. + void set_tenuring_threshold(uint threshold) { _tenuring_threshold[_epoch] = threshold; } + #ifndef PRODUCT // Return the sum of size of objects of all ages recorded in the // census at snapshot indexed by snap. @@ -232,4 +238,26 @@ class ShenandoahAgeCensus: public CHeapObj { void print(); }; +// RAII object that temporarily overrides the tenuring threshold for the +// duration of a scope, restoring the original value on destruction. +// Used to force promotion of all young objects during whitebox full GCs. +class ShenandoahTenuringOverride : public StackObj { + ShenandoahAgeCensus* _census; + uint _saved_threshold; + bool _active; +public: + ShenandoahTenuringOverride(bool active, ShenandoahAgeCensus* census) : + _census(census), _saved_threshold(0), _active(active) { + if (_active) { + _saved_threshold = _census->tenuring_threshold(); + _census->set_tenuring_threshold(0); + } + } + ~ShenandoahTenuringOverride() { + if (_active) { + _census->set_tenuring_threshold(_saved_threshold); + } + } +}; + #endif // SHARE_GC_SHENANDOAH_SHENANDOAHAGECENSUS_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 1edff443ded..67cc4d2f703 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -24,6 +24,7 @@ * */ +#include "gc/shenandoah/shenandoahAgeCensus.hpp" #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahConcurrentGC.hpp" @@ -271,6 +272,12 @@ void ShenandoahGenerationalControlThread::run_gc_cycle(const ShenandoahGCRequest // Cannot uncommit bitmap slices during concurrent reset ShenandoahNoUncommitMark forbid_region_uncommit(_heap); + // When a whitebox full GC is requested, set the tenuring threshold to zero + // so that all young objects are promoted to old. This ensures that tests + // using WB.fullGC() to promote objects to old gen will not loop forever. + ShenandoahTenuringOverride tenuring_override(request.cause == GCCause::_wb_full_gc, + _heap->age_census()); + _heap->print_before_gc(); switch (gc_mode()) { case concurrent_normal: { From 56885958c2207eabd24c079a8f4a69e2aad066a5 Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Wed, 25 Mar 2026 00:40:37 +0000 Subject: [PATCH 100/160] 8380408: GenShen: Fix regression with vmTestbase/gc/memory/Churn/Churn2/TestDescription.java Reviewed-by: wkemper --- .../heuristics/shenandoahGenerationalHeuristics.cpp | 4 ++++ .../heuristics/shenandoahGenerationalHeuristics.hpp | 2 +- .../gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp | 6 ++++-- src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp | 6 ++++-- src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp | 6 ------ src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp | 3 +-- .../share/gc/shenandoah/shenandoahYoungGeneration.cpp | 8 ++++++++ .../share/gc/shenandoah/shenandoahYoungGeneration.hpp | 2 ++ 8 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp index 08d628e67ff..d5622ed5d79 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -498,7 +498,11 @@ void ShenandoahGenerationalHeuristics::adjust_evacuation_budgets(ShenandoahHeap* size_t young_evacuated = collection_set->get_live_bytes_in_untenurable_regions(); size_t young_evacuated_reserve_used = (size_t) (ShenandoahEvacWaste * double(young_evacuated)); + // In top_off_collection_set(), we shrunk planned future reserve by _add_regions_to_old * region_size_bytes, but we + // didn't shrink available. The current reserve is not affected by the planned future reserve. Current available is + // larger than planned available by the planned adjustment amount. size_t total_young_available = young_generation->available_with_reserve() - _add_regions_to_old * region_size_bytes; + assert(young_evacuated_reserve_used <= total_young_available, "Cannot evacuate (%zu) more than is available in young (%zu)", young_evacuated_reserve_used, total_young_available); young_generation->set_evacuation_reserve(young_evacuated_reserve_used); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp index c418e8c24ec..d6551cffb73 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp @@ -76,7 +76,7 @@ private: // Filter and sort remaining regions before adding to collection set. void filter_regions(ShenandoahCollectionSet* collection_set); - // Adjust evacuation budgets after choosing collection set. The argument regions_to_xfer + // Adjust evacuation budgets after choosing collection set. On entry, the instance variable _regions_to_xfer // represents regions to be transferred to old based on decisions made in top_off_collection_set() void adjust_evacuation_budgets(ShenandoahHeap* const heap, ShenandoahCollectionSet* const collection_set); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index 4f71562f4f6..33c91e27be5 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -369,7 +369,6 @@ bool ShenandoahOldHeuristics::top_off_collection_set(ShenandoahCollectionSet* co assert(consumed_from_young_cset <= max_young_cset, "sanity"); assert(max_young_cset <= young_unaffiliated_regions * region_size_bytes, "sanity"); - size_t regions_for_old_expansion; if (consumed_from_young_cset < max_young_cset) { size_t excess_young_reserves = max_young_cset - consumed_from_young_cset; @@ -392,7 +391,10 @@ bool ShenandoahOldHeuristics::top_off_collection_set(ShenandoahCollectionSet* co _unspent_unfragmented_old_budget += supplement_without_waste; _old_generation->augment_evacuation_reserve(budget_supplement); young_generation->set_evacuation_reserve(max_young_cset - budget_supplement); - + assert(young_generation->get_evacuation_reserve() >= + collection_set->get_live_bytes_in_untenurable_regions() * ShenandoahEvacWaste, + "adjusted evac reserve (%zu) must be large enough for planned evacuation (%zu)", + young_generation->get_evacuation_reserve(), collection_set->get_live_bytes_in_untenurable_regions()); return add_old_regions_to_cset(collection_set); } else { add_regions_to_old = 0; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index 2df06432bd2..eeff0fde87c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -857,14 +857,16 @@ public: ShenandoahRebuildLocker locker(rebuild_lock()); return _partitions.available_in_locked_for_rebuild(ShenandoahFreeSetPartitionId::Mutator); } - inline size_t available_holding_lock() const - { return _partitions.available_in(ShenandoahFreeSetPartitionId::Mutator); } // Use this version of available() if the heap lock is held. inline size_t available_locked() const { return _partitions.available_in(ShenandoahFreeSetPartitionId::Mutator); } + inline size_t collector_available_locked() const { + return _partitions.available_in(ShenandoahFreeSetPartitionId::Collector); + } + inline size_t total_humongous_waste() const { return _total_humongous_waste; } inline size_t humongous_waste_in_mutator() const { return _partitions.humongous_waste(ShenandoahFreeSetPartitionId::Mutator); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index f3c05049079..3d592e9f9be 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -423,12 +423,6 @@ size_t ShenandoahGeneration::available() const { return result; } -// For ShenandoahYoungGeneration, Include the young available that may have been reserved for the Collector. -size_t ShenandoahGeneration::available_with_reserve() const { - size_t result = available(max_capacity()); - return result; -} - size_t ShenandoahGeneration::soft_mutator_available() const { size_t result = available(ShenandoahHeap::heap()->soft_max_capacity() * (100.0 - ShenandoahEvacReserve) / 100); return result; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp index 1a549be8988..6f32d101152 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp @@ -63,7 +63,7 @@ private: // Return available assuming that we can allocate no more than capacity bytes within this generation. size_t available(size_t capacity) const; - public: +public: ShenandoahGeneration(ShenandoahGenerationType type, uint max_workers); ~ShenandoahGeneration(); @@ -96,7 +96,6 @@ private: virtual size_t max_capacity() const override = 0; size_t available() const override; - size_t available_with_reserve() const; // Returns the memory available based on the _soft_ max heap capacity (soft_max_heap - used). // The soft max heap size may be adjusted lower than the max heap size to cause the trigger diff --git a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp index f00ce16136f..7a76bc50078 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp @@ -131,6 +131,14 @@ size_t ShenandoahYoungGeneration::free_unaffiliated_regions() const { return _free_set->young_unaffiliated_regions(); } +size_t ShenandoahYoungGeneration::available_with_reserve() const { + shenandoah_assert_heaplocked(); + ShenandoahFreeSet* free_set = ShenandoahHeap::heap()->free_set(); + size_t mutator_available = free_set->available_locked(); + size_t collector_available = free_set->collector_available_locked(); + return mutator_available + collector_available; +} + size_t ShenandoahYoungGeneration::available() const { // The collector reserve may eat into what the mutator is allowed to use. Make sure we are looking // at what is available to the mutator when reporting how much memory is available. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp index 930c5ff1747..c3b6944ec80 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp @@ -82,6 +82,8 @@ public: size_t get_affiliated_region_count() const override; size_t max_capacity() const override; + // Return sum of bytes available to mutator and to Collector, assuming heap lock is held. + size_t available_with_reserve() const; size_t available() const override; size_t soft_mutator_available() const override; From 4408e1c9279184fa0558e41d77f5683f61e5b314 Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Wed, 25 Mar 2026 02:16:59 +0000 Subject: [PATCH 101/160] 8380580: Enable TestCombineAddPWithConstantOffsets.java IR tests for RISC-V Reviewed-by: fyang --- .../c2/irTests/igvn/TestCombineAddPWithConstantOffsets.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/igvn/TestCombineAddPWithConstantOffsets.java b/test/hotspot/jtreg/compiler/c2/irTests/igvn/TestCombineAddPWithConstantOffsets.java index 69963316fa9..cb7a63f5fe6 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/igvn/TestCombineAddPWithConstantOffsets.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/igvn/TestCombineAddPWithConstantOffsets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ import compiler.lib.ir_framework.*; /* * @test * @bug 8343067 - * @requires os.simpleArch == "x64" | os.simpleArch == "aarch64" + * @requires os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64" * @requires vm.compiler2.enabled * @summary Test that chains of AddP nodes with constant offsets are idealized * when their offset input changes. @@ -43,6 +43,7 @@ public class TestCombineAddPWithConstantOffsets { @Test @IR(applyIfPlatform = {"x64", "true"}, failOn = {IRNode.ADD_P_OF, ".*"}) @IR(applyIfPlatform = {"aarch64", "true"}, failOn = {IRNode.ADD_P_OF, "reg_imm"}) + @IR(applyIfPlatform = {"riscv64", "true"}, failOn = {IRNode.ADD_P_OF, "reg_imm"}) static void testCombineAddPWithConstantOffsets(int[] arr) { for (long i = 6; i < 14; i++) { arr[(int)i] = 1; From 274f8e601c60fe48c6dc0fe113c44dc253cd757b Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Wed, 25 Mar 2026 02:44:14 +0000 Subject: [PATCH 102/160] 8371182: [macos] Improve error messages for "invalid mac bundle identifier" Reviewed-by: almatvee --- .../internal/MacApplicationBuilder.java | 45 ++-- .../resources/MacResources.properties | 5 +- .../jpackage/internal/cli/StandardOption.java | 7 + .../internal/cli/StandardValidator.java | 6 +- .../resources/MainResources.properties | 2 + .../jpackage/test/CannedFormattedString.java | 20 +- .../jdk/jpackage/test/JPackageCommand.java | 47 ++++ .../test/JPackageOutputValidator.java | 5 + .../internal/MacApplicationBuilderTest.java | 96 ++++++++ .../tools/jpackage/junit/macosx/junit.java | 8 + .../cli/OptionsValidationFailTest.excludes | 1 - .../internal/cli/StandardOptionTest.java | 36 +++ .../jpackage/macosx/MacPropertiesTest.java | 223 ++++++++++++++++-- .../tools/jpackage/share/AppVersionTest.java | 41 ++-- test/jdk/tools/jpackage/share/ErrorTest.java | 4 +- 15 files changed, 465 insertions(+), 81 deletions(-) create mode 100644 test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacApplicationBuilderTest.java diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java index cdccd488ed6..d6b816ca75e 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java @@ -24,6 +24,8 @@ */ package jdk.jpackage.internal; +import static jdk.jpackage.internal.cli.StandardValidator.IS_VALID_MAC_BUNDLE_IDENTIFIER; + import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; @@ -37,6 +39,7 @@ import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.AppImageSigningConfig; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLaunchers; +import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.ExternalApplication; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.Launcher; @@ -138,20 +141,6 @@ final class MacApplicationBuilder { return MacApplication.create(ApplicationBuilder.overrideAppImageLayout(app, appImageLayout), mixin); } - static boolean isValidBundleIdentifier(String id) { - for (int i = 0; i < id.length(); i++) { - char a = id.charAt(i); - // We check for ASCII codes first which we accept. If check fails, - // check if it is acceptable extended ASCII or unicode character. - if ((a >= 'A' && a <= 'Z') || (a >= 'a' && a <= 'z') - || (a >= '0' && a <= '9') || (a == '-' || a == '.')) { - continue; - } - return false; - } - return true; - } - private static void validateAppVersion(Application app) { try { CFBundleVersion.of(app.version()); @@ -246,8 +235,8 @@ final class MacApplicationBuilder { } private String validatedBundleIdentifier(Application app) { - final var value = Optional.ofNullable(bundleIdentifier).orElseGet(() -> { - return app.mainLauncher() + return Optional.ofNullable(bundleIdentifier).orElseGet(() -> { + var derivedValue = app.mainLauncher() .flatMap(Launcher::startupInfo) .map(li -> { final var packageName = li.packageName(); @@ -258,15 +247,23 @@ final class MacApplicationBuilder { } }) .orElseGet(app::name); + + if (!IS_VALID_MAC_BUNDLE_IDENTIFIER.test(derivedValue)) { + // Derived bundle identifier is invalid. Try to adjust it by dropping all invalid characters. + derivedValue = derivedValue.codePoints() + .mapToObj(Character::toString) + .filter(IS_VALID_MAC_BUNDLE_IDENTIFIER) + .collect(Collectors.joining("")); + if (!IS_VALID_MAC_BUNDLE_IDENTIFIER.test(derivedValue)) { + throw new ConfigException( + I18N.format("error.invalid-derived-bundle-identifier"), + I18N.format("error.invalid-derived-bundle-identifier.advice")); + } + } + + Log.verbose(I18N.format("message.derived-bundle-identifier", derivedValue)); + return derivedValue; }); - - if (!isValidBundleIdentifier(value)) { - throw I18N.buildConfigException("message.invalid-identifier", value) - .advice("message.invalid-identifier.advice") - .create(); - } - - return value; } private String validatedCategory() { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties index 95fff00152b..2ed1f740805 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties @@ -33,6 +33,8 @@ error.invalid-app-image-runtime-image-bin-dir=Runtime directory {0} in the prede error.invalid-runtime-image-bin-dir=Runtime image "{0}" should not contain "bin" folder error.invalid-runtime-image-bin-dir.advice=Use --strip-native-commands jlink option when generating runtime image used with {0} option error.invalid-app-image-plist-file=Invalid "{0}" file in the predefined application image +error.invalid-derived-bundle-identifier=Can't derive a valid bundle identifier from the input data +error.invalid-derived-bundle-identifier.advice=Specify bundle identifier with --mac-package-identifier option resource.app-info-plist=Application Info.plist resource.app-runtime-info-plist=Embedded Java Runtime Info.plist @@ -57,8 +59,7 @@ message.icon-not-icns= The specified icon "{0}" is not an ICNS file and will not message.version-string-too-many-components='app-version' may have between 1 and 3 numbers: 1, 1.2, 1.2.3. message.version-string-first-number-not-zero=The first number in an app-version cannot be zero or negative. message.keychain.error=Unable to get keychain list. -message.invalid-identifier=Invalid mac bundle identifier [{0}]. -message.invalid-identifier.advice=specify identifier with "--mac-package-identifier". +message.derived-bundle-identifier=Derived bundle identifier: {0} message.preparing-dmg-setup=Preparing dmg setup: {0}. message.preparing-scripts=Preparing package scripts. message.preparing-distribution-dist=Preparing distribution.dist: {0}. diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java index 438a5ac3e6f..f193cff0dc0 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardOption.java @@ -58,9 +58,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.cli.OptionValueExceptionFactory.StandardArgumentsMapper; import jdk.jpackage.internal.model.AppImageBundleType; import jdk.jpackage.internal.model.BundleType; import jdk.jpackage.internal.model.BundlingOperationDescriptor; +import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; @@ -352,6 +354,11 @@ public final class StandardOption { public static final OptionValue MAC_BUNDLE_IDENTIFIER = stringOption("mac-package-identifier") .valuePattern("package identifier") + .validator(StandardValidator.IS_VALID_MAC_BUNDLE_IDENTIFIER) + .validatorExceptionFactory(OptionValueExceptionFactory.build((message, cause) -> { + return new ConfigException(message, I18N.format("error.parameter-not-mac-bundle-identifier.advice"), cause); + }).formatArgumentsTransformer(StandardArgumentsMapper.VALUE_AND_NAME).create()) + .validatorExceptionFormatString("error.parameter-not-mac-bundle-identifier") .create(); public static final OptionValue MAC_BUNDLE_SIGNING_PREFIX = stringOption("mac-package-signing-prefix").scope(MAC_SIGNING).create(); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValidator.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValidator.java index cfa97439592..ee9f39ee9cd 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValidator.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValidator.java @@ -36,11 +36,12 @@ import java.nio.file.Path; import java.util.Objects; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.regex.Pattern; import jdk.jpackage.internal.cli.Validator.ValidatingConsumerException; import jdk.jpackage.internal.util.FileUtils; import jdk.jpackage.internal.util.MacBundle; -final public class StandardValidator { +public final class StandardValidator { private StandardValidator() { } @@ -143,6 +144,9 @@ final public class StandardValidator { return MacBundle.fromPath(path).isPresent(); }; + // https://developer.apple.com/documentation/BundleResources/Information-Property-List/CFBundleIdentifier + public static final Predicate IS_VALID_MAC_BUNDLE_IDENTIFIER = Pattern.compile("[\\p{Alnum}-\\.]+").asMatchPredicate(); + public static final class DirectoryListingIOException extends RuntimeException { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties index 02784a52acc..9dfd13d60bb 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/MainResources.properties @@ -88,6 +88,8 @@ error.parameter-not-empty-directory=The value "{0}" provided for parameter {1} i error.parameter-not-url=The value "{0}" provided for parameter {1} is not a valid URL error.parameter-not-launcher-shortcut-dir=The value "{0}" provided for parameter {1} is not a valid shortcut startup directory error.parameter-not-mac-bundle=The value "{0}" provided for parameter {1} is not a valid macOS bundle +error.parameter-not-mac-bundle-identifier=The value "{0}" provided for parameter {1} is not a valid macOS bundle identifier. +error.parameter-not-mac-bundle-identifier.advice=Bundle identifier must be a non-empty string containing only alphanumeric characters (A-Z, a-z, and 0-9), hyphens (-), and periods (.) error.path-parameter-ioexception=I/O error accessing path value "{0}" of parameter {1} error.parameter-add-launcher-malformed=The value "{0}" provided for parameter {1} does not match the pattern = error.parameter-add-launcher-not-file=The value of path to a property file "{0}" provided for additional launcher "{1}" is not a valid file path diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java index d5248ee2b17..cc31c416d44 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ import java.util.List; import java.util.Objects; import java.util.function.BiFunction; import java.util.function.UnaryOperator; +import java.util.regex.Pattern; import java.util.stream.Stream; public record CannedFormattedString(BiFunction formatter, String key, List args) implements CannedArgument { @@ -67,6 +68,23 @@ public record CannedFormattedString(BiFunction formatt } } + public interface Spec { + + String format(); + List modelArgs(); + + default CannedFormattedString asCannedFormattedString(Object ... args) { + if (args.length != modelArgs().size()) { + throw new IllegalArgumentException(); + } + return JPackageStringBundle.MAIN.cannedFormattedString(format(), args); + } + + default Pattern asPattern() { + return JPackageStringBundle.MAIN.cannedFormattedStringAsPattern(format(), modelArgs().toArray()); + } + } + private record AddPrefixFormatter(BiFunction formatter) implements BiFunction { AddPrefixFormatter { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index 4fad120d0f6..b173d345596 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -1078,6 +1078,53 @@ public class JPackageCommand extends CommandArguments { return this; } + public JPackageCommand validateOutput( + Class messageGroup, + Consumer validatorMutator, + List expectedMessages) { + + Objects.requireNonNull(validatorMutator); + + if (!messageGroup.isEnum()) { + throw new IllegalArgumentException(); + } + + var messageSpecs = messageGroup.getEnumConstants(); + + var expectMessageFormats = expectedMessages.stream().map(CannedFormattedString::key).toList(); + + var groupMessageFormats = Stream.of(messageSpecs) + .map(CannedFormattedString.Spec::format) + .collect(Collectors.toMap(x -> x, x -> x)) + .keySet(); + + if (!groupMessageFormats.containsAll(expectMessageFormats)) { + // Expected format strings should be a subset of the group format strings. + throw new IllegalArgumentException(); + } + + if (!expectedMessages.isEmpty()) { + new JPackageOutputValidator().expectMatchingStrings(expectedMessages).mutate(validatorMutator).applyTo(this); + } + + Stream.of(messageSpecs).filter(spec -> { + return !expectMessageFormats.contains(spec.format()); + }).map(CannedFormattedString.Spec::asPattern).map(pattern -> { + return TKit.assertTextStream(pattern).negate(); + }).forEach(validator -> { + new JPackageOutputValidator().add(validator).stdoutAndStderr().applyTo(this); + }); + + return this; + } + + public JPackageCommand validateOutput( + Class messageGroup, + Consumer validatorMutator, + CannedFormattedString... expected) { + return validateOutput(messageGroup, validatorMutator, List.of(expected)); + } + public boolean isWithToolProvider() { return toolProviderSource.toolProvider().isPresent(); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageOutputValidator.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageOutputValidator.java index ed14f40f65c..8817ca0b730 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageOutputValidator.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageOutputValidator.java @@ -140,6 +140,11 @@ public final class JPackageOutputValidator { return this; } + public JPackageOutputValidator mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + public void applyTo(JPackageCommand cmd) { toResultConsumer(cmd).ifPresent(cmd::validateResult); } diff --git a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacApplicationBuilderTest.java b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacApplicationBuilderTest.java new file mode 100644 index 00000000000..559e8d53827 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacApplicationBuilderTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal; + +import static jdk.jpackage.internal.MacPackagingPipeline.APPLICATION_LAYOUT; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import jdk.jpackage.internal.model.ApplicationLaunchers; +import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.Launcher; +import jdk.jpackage.internal.model.LauncherStartupInfo; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class MacApplicationBuilderTest { + + @ParameterizedTest + @CsvSource({ + "NAME,!fo#o,foo", + "NAME,bar,bar", + "MAIN_LAUNCHER_CLASSNAME,foo.b$ar.Hello,foo.bar", + "MAIN_LAUNCHER_CLASSNAME,Hello$2,Hello2", + "NAME,!#,", + }) + void testDerivedBundleIdentifier(ApplicationBuilderProperty type, String value, String expectedBundleIdentifier) { + + var builder = buildApplication(); + var macBuilder = new MacApplicationBuilder(builder); + + type.applyTo(value, builder, macBuilder); + + if (expectedBundleIdentifier != null) { + assertEquals(expectedBundleIdentifier, macBuilder.create().bundleIdentifier()); + } else { + var ex = assertThrowsExactly(ConfigException.class, macBuilder::create); + assertEquals(I18N.format("error.invalid-derived-bundle-identifier"), ex.getMessage()); + assertEquals(I18N.format("error.invalid-derived-bundle-identifier.advice"), ex.getAdvice()); + } + } + + private static ApplicationBuilder buildApplication() { + return new ApplicationBuilder().appImageLayout(APPLICATION_LAYOUT); + } + + enum ApplicationBuilderProperty { + NAME { + void applyTo(String value, ApplicationBuilder builder, MacApplicationBuilder macBuilder) { + builder.name(Objects.requireNonNull(value)); + } + }, + MAIN_LAUNCHER_CLASSNAME { + void applyTo(String value, ApplicationBuilder builder, MacApplicationBuilder macBuilder) { + var startupInfo = new LauncherStartupInfo.Stub(Objects.requireNonNull(value), List.of(), List.of(), List.of()); + var launcher = new Launcher.Stub( + startupInfo.simpleClassName(), + Optional.of(startupInfo), + List.of(), + false, + "", + Optional.empty(), + null, + Map.of()); + builder.launchers(new ApplicationLaunchers(launcher)); + } + } + ; + + abstract void applyTo(String value, ApplicationBuilder builder, MacApplicationBuilder macBuilder); + } +} diff --git a/test/jdk/tools/jpackage/junit/macosx/junit.java b/test/jdk/tools/jpackage/junit/macosx/junit.java index 4ab05daf1ae..55d309a5d7c 100644 --- a/test/jdk/tools/jpackage/junit/macosx/junit.java +++ b/test/jdk/tools/jpackage/junit/macosx/junit.java @@ -63,3 +63,11 @@ * jdk/jpackage/internal/ActiveKeychainListTest.java * @run junit jdk.jpackage/jdk.jpackage.internal.ActiveKeychainListTest */ + +/* @test + * @summary Test MacApplicationBuilderTest + * @requires (os.family == "mac") + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/MacApplicationBuilderTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.MacApplicationBuilderTest + */ diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes index 19478aaa4a7..a7a65402b9c 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.excludes @@ -18,7 +18,6 @@ ErrorTest.test(IMAGE; args-add=[--module, com.foo.bar, --runtime-image, @@JAVA_H ErrorTest.test(IMAGE; args-add=[--module, java.base, --runtime-image, @@JAVA_HOME@@]; errors=[message.error-header+[ERR_NoMainClass]]) ErrorTest.test(LINUX_DEB; app-desc=Hello; args-add=[--linux-package-name, #]; errors=[message.error-header+[error.deb-invalid-value-for-package-name, #], message.advice-header+[error.deb-invalid-value-for-package-name.advice]]) ErrorTest.test(LINUX_RPM; app-desc=Hello; args-add=[--linux-package-name, #]; errors=[message.error-header+[error.rpm-invalid-value-for-package-name, #], message.advice-header+[error.rpm-invalid-value-for-package-name.advice]]) -ErrorTest.test(MAC_PKG; app-desc=Hello; args-add=[--mac-package-identifier, #1]; errors=[message.error-header+[message.invalid-identifier, #1], message.advice-header+[message.invalid-identifier.advice]]) ErrorTest.test(NATIVE; app-desc=Hello; args-add=[--mac-app-store, --runtime-image, @@JAVA_HOME@@]; errors=[message.error-header+[error.invalid-runtime-image-bin-dir, @@JAVA_HOME@@], message.advice-header+[error.invalid-runtime-image-bin-dir.advice, --mac-app-store]]) ErrorTest.test(NATIVE; app-desc=Hello; args-add=[--runtime-image, @@EMPTY_DIR@@]; errors=[message.error-header+[error.invalid-runtime-image-missing-file, @@EMPTY_DIR@@, lib/**/libjli.dylib]]) ErrorTest.test(NATIVE; app-desc=Hello; args-add=[--runtime-image, @@INVALID_MAC_RUNTIME_BUNDLE@@]; errors=[message.error-header+[error.invalid-runtime-image-missing-file, @@INVALID_MAC_RUNTIME_BUNDLE@@, Contents/Home/lib/**/libjli.dylib]]) diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardOptionTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardOptionTest.java index 58a78edb627..4d47bced8f1 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardOptionTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardOptionTest.java @@ -58,6 +58,7 @@ import jdk.jpackage.internal.cli.StandardOption.LauncherProperty; import jdk.jpackage.internal.model.AppImageBundleType; import jdk.jpackage.internal.model.BundleType; import jdk.jpackage.internal.model.JPackageException; +import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; import jdk.jpackage.internal.util.RootedPath; @@ -71,6 +72,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; @@ -258,6 +260,40 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { }).toList(), result.errors()); } + @ParameterizedTest + @ValueSource(strings = { + ".", + "a-b.c", + "A", + "com.acme.Foo" + }) + void test_MAC_BUNDLE_IDENTIFIER_valid(String id) { + + var spec = StandardOption.MAC_BUNDLE_IDENTIFIER.getSpec(); + + var result = spec.convert(spec.name(), StringToken.of(id)).orElseThrow(); + + assertEquals(result, id); + } + + @ParameterizedTest + @ValueSource(strings = { + "", + ",", + "Hello!" + }) + void test_MAC_BUNDLE_IDENTIFIER_invalid(String id) { + + var spec = StandardOption.MAC_BUNDLE_IDENTIFIER.getSpec(); + + var result = spec.convert(spec.name(), StringToken.of(id)); + + var ex = assertThrows(ConfigException.class, result::orElseThrow); + + assertEquals(I18N.format("error.parameter-not-mac-bundle-identifier", id, spec.name().formatForCommandLine()), ex.getMessage()); + assertEquals(I18N.format("error.parameter-not-mac-bundle-identifier.advice"), ex.getAdvice()); + } + @ParameterizedTest @EnumSource(OptionMutatorTest.TestType.class) public void test_pathOptionMutator(OptionMutatorTest.TestType type) { diff --git a/test/jdk/tools/jpackage/macosx/MacPropertiesTest.java b/test/jdk/tools/jpackage/macosx/MacPropertiesTest.java index 67a5cf6b609..f8638735a6a 100644 --- a/test/jdk/tools/jpackage/macosx/MacPropertiesTest.java +++ b/test/jdk/tools/jpackage/macosx/MacPropertiesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,11 +21,20 @@ * questions. */ -import jdk.jpackage.test.TKit; -import jdk.jpackage.test.MacHelper; -import jdk.jpackage.test.JPackageCommand; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.CannedFormattedString; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageOutputValidator; +import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.TKit; /** @@ -43,30 +52,198 @@ import jdk.jpackage.test.Annotations.Parameter; * --jpt-run=MacPropertiesTest */ public class MacPropertiesTest { - @Test - @Parameter("MacPackageNameTest") - public void testPackageName(String packageName) { - testParameterInAppImage("--mac-package-name", "CFBundleName", - packageName); - } @Test - @Parameter("Foo") - public void testPackageIdetifier(String packageId) { - testParameterInAppImage("--mac-package-identifier", "CFBundleIdentifier", - packageId); + @ParameterSupplier + public void test(TestSpec spec) { + spec.run(); } - private static void testParameterInAppImage(String jpackageParameterName, - String plistKeyName, String value) { - JPackageCommand cmd = JPackageCommand.helloAppImage() - .addArguments(jpackageParameterName, value); + public static Collection test() { - cmd.executeAndAssertHelloAppImageCreated(); + var testCases = new ArrayList(); - var plist = MacHelper.readPListFromAppImage(cmd.outputBundle()); + testCases.addAll(List.of( + TestSpec.build("CFBundleName").addArgs("--mac-package-name", "MacPackageNameTest").expect("MacPackageNameTest"), + TestSpec.build("CFBundleIdentifier").addArgs("--mac-package-identifier", "Foo").expect("Foo"), + // Should derive from the input data. + TestSpec.build("CFBundleIdentifier").appDesc("com.acme.Hello").expect("com.acme").expect(BundleIdentifierMessage.VALUE.asCannedFormattedString("com.acme")) + )); - TKit.assertEquals(value, plist.queryValue(plistKeyName), String.format( - "Check value of %s plist key", plistKeyName)); + return testCases.stream().map(TestSpec.Builder::create).map(v -> { + return new Object[] {v}; + }).toList(); + } + + enum BundleIdentifierMessage implements CannedFormattedString.Spec { + VALUE("message.derived-bundle-identifier", "bundle-id"), + ; + + BundleIdentifierMessage(String key, Object ... args) { + this.key = Objects.requireNonNull(key); + this.args = List.of(args); + } + + @Override + public String format() { + return key; + } + + @Override + public List modelArgs() { + return args; + } + + private final String key; + private final List args; + } + + record TestSpec( + Optional appDesc, + List addArgs, + List delArgs, + List expectedTraceMessages, + String expectedInfoPlistKeyValue, + String infoPlistKey, + Optional> traceMessagesClass) { + + TestSpec { + Objects.requireNonNull(addArgs); + Objects.requireNonNull(delArgs); + Objects.requireNonNull(expectedTraceMessages); + Objects.requireNonNull(expectedInfoPlistKeyValue); + Objects.requireNonNull(infoPlistKey); + Objects.requireNonNull(traceMessagesClass); + } + + void run() { + var cmd = appDesc.map(JPackageCommand::helloAppImage).orElseGet(JPackageCommand::helloAppImage) + .setFakeRuntime().addArguments(addArgs); + + delArgs.forEach(cmd::removeArgumentWithValue); + + Consumer validatorMutator = validator -> { + validator.matchTimestamps().stripTimestamps(); + }; + + traceMessagesClass.ifPresentOrElse(v -> { + cmd.validateOutput(v, validatorMutator, expectedTraceMessages); + }, () -> { + new JPackageOutputValidator() + .mutate(validatorMutator) + .expectMatchingStrings(expectedTraceMessages) + .applyTo(cmd); + }); + + cmd.executeAndAssertHelloAppImageCreated(); + + var plist = MacHelper.readPListFromAppImage(cmd.outputBundle()); + + TKit.assertEquals( + expectedInfoPlistKeyValue, + plist.queryValue(infoPlistKey), + String.format("Check value of %s plist key", infoPlistKey)); + } + + @Override + public String toString() { + var tokens = new ArrayList(); + + tokens.add(String.format("%s=>%s", infoPlistKey, expectedInfoPlistKeyValue)); + + appDesc.ifPresent(v -> { + tokens.add("app-desc=" + v); + }); + + if (!addArgs.isEmpty()) { + tokens.add("args-add=" + addArgs); + } + + if (!delArgs.isEmpty()) { + tokens.add("args-del=" + delArgs); + } + + if (!expectedTraceMessages.isEmpty()) { + tokens.add("expect=" + expectedTraceMessages); + } + + return tokens.stream().collect(Collectors.joining("; ")); + } + + static Builder build() { + return new Builder(); + } + + static Builder build(String infoPlistKey) { + return build().infoPlistKey(Objects.requireNonNull(infoPlistKey)); + } + + static final class Builder { + + TestSpec create() { + + Class traceMessagesClass = switch (Objects.requireNonNull(infoPlistKey)) { + case "CFBundleIdentifier" -> BundleIdentifierMessage.class; + case "CFBundleName" -> null; + default -> { + throw new IllegalStateException(); + } + }; + + return new TestSpec( + Optional.ofNullable(appDesc), + addArgs, + delArgs, + expectedTraceMessages, + expectedInfoPlistKeyValue, + infoPlistKey, + Optional.ofNullable(traceMessagesClass)); + } + + Builder appDesc(String v) { + appDesc = v; + return this; + } + + Builder addArgs(List v) { + addArgs.addAll(v); + return this; + } + + Builder addArgs(String... args) { + return addArgs(List.of(args)); + } + + Builder delArgs(List v) { + delArgs.addAll(v); + return this; + } + + Builder delArgs(String... args) { + return delArgs(List.of(args)); + } + + Builder expect(CannedFormattedString traceMessage) { + expectedTraceMessages.add(traceMessage); + return this; + } + + Builder expect(String v) { + expectedInfoPlistKeyValue = v; + return this; + } + + Builder infoPlistKey(String v) { + infoPlistKey = v; + return this; + } + + private String appDesc; + private final List addArgs = new ArrayList<>(); + private final List delArgs = new ArrayList<>(); + private final List expectedTraceMessages = new ArrayList<>(); + private String expectedInfoPlistKeyValue; + private String infoPlistKey; + } } } diff --git a/test/jdk/tools/jpackage/share/AppVersionTest.java b/test/jdk/tools/jpackage/share/AppVersionTest.java index 7ba1870def1..598ee1551b3 100644 --- a/test/jdk/tools/jpackage/share/AppVersionTest.java +++ b/test/jdk/tools/jpackage/share/AppVersionTest.java @@ -46,7 +46,6 @@ import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.RuntimeReleaseFile; @@ -58,8 +57,6 @@ import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.ConfigurationTarget; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageCommand.StandardAssert; -import jdk.jpackage.test.JPackageOutputValidator; -import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.JavaAppDesc; import jdk.jpackage.test.MacHelper; import jdk.jpackage.test.PackageTest; @@ -270,7 +267,7 @@ public final class AppVersionTest { }).toList(); } - enum Message { + enum Message implements CannedFormattedString.Spec { VERSION_FROM_MODULE("message.module-version", "version", "module"), VERSION_FROM_RELEASE_FILE("message.release-version", "version"), VERSION_NORMALIZED("message.version-normalized", "version", "version"), @@ -278,24 +275,21 @@ public final class AppVersionTest { Message(String key, Object ... args) { this.key = Objects.requireNonNull(key); - this.args = args; + this.args = List.of(args); } - CannedFormattedString cannedFormattedString(Object ... args) { - return JPackageStringBundle.MAIN.cannedFormattedString(key, args); - } - - TKit.TextStreamVerifier negateFind() { - var pattern = JPackageStringBundle.MAIN.cannedFormattedStringAsPattern(key, args); - return TKit.assertTextStream(pattern).negate(); - } - - String key() { + @Override + public String format() { return key; } + @Override + public List modelArgs() { + return args; + } + private final String key; - private final Object[] args; + private final List args; } sealed interface VersionSource { @@ -389,16 +383,9 @@ public final class AppVersionTest { } void applyTo(JPackageCommand cmd) { - Objects.requireNonNull(cmd); - new JPackageOutputValidator().expectMatchingStrings(messages).matchTimestamps().stripTimestamps().applyTo(cmd); - cmd.version(version); - - var expectMessageKeys = messages.stream().map(CannedFormattedString::key).toList(); - Stream.of(Message.values()).filter(message -> { - return !expectMessageKeys.contains(message.key()); - }).map(Message::negateFind).forEach(validator -> { - new JPackageOutputValidator().add(validator).stdoutAndStderr().applyTo(cmd); - }); + cmd.version(version).validateOutput(Message.class, validator -> { + validator.matchTimestamps().stripTimestamps(); + }, messages); } @Override @@ -436,7 +423,7 @@ public final class AppVersionTest { } Builder message(Message message, Object ... args) { - return messages(message.cannedFormattedString(args)); + return messages(message.asCannedFormattedString(args)); } private String version; diff --git a/test/jdk/tools/jpackage/share/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java index fbaec8283e8..ed5865024fa 100644 --- a/test/jdk/tools/jpackage/share/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -1015,8 +1015,8 @@ public final class ErrorTest { testSpec().noAppDesc().addArgs("--app-image", Token.APP_IMAGE.token()) .error("error.app-image.mac-sign.required"), testSpec().type(PackageType.MAC_PKG).addArgs("--mac-package-identifier", "#1") - .error("message.invalid-identifier", "#1") - .advice("message.invalid-identifier.advice"), + .error("error.parameter-not-mac-bundle-identifier", "#1", "--mac-package-identifier") + .advice("error.parameter-not-mac-bundle-identifier.advice"), // Bundle for mac app store should not have runtime commands testSpec().nativeType().addArgs("--mac-app-store", "--jlink-options", "--bind-services") .error("ERR_MissingJLinkOptMacAppStore", "--strip-native-commands"), From ec8bcf72c82b4c7a6a3e6098d4f48a0fe6522e78 Mon Sep 17 00:00:00 2001 From: Vladimir Petko Date: Wed, 25 Mar 2026 04:58:48 +0000 Subject: [PATCH 103/160] 8352567: [s390x] ProblemList JFR tests requiring JFR stubs Reviewed-by: amitkumar, aph --- test/hotspot/jtreg/ProblemList.txt | 2 ++ test/jdk/ProblemList.txt | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index dec9682f0cf..3b871d9f4b6 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -78,6 +78,7 @@ compiler/floatingpoint/TestSubnormalDouble.java 8317810 generic-i586 compiler/codecache/CodeCacheFullCountTest.java 8332954 generic-all compiler/interpreter/Test6833129.java 8335266 generic-i586 +compiler/intrinsics/TestReturnOopSetForJFRWriteCheckpoint.java 8286300 linux-s390x compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 @@ -113,6 +114,7 @@ runtime/StackGuardPages/TestStackGuardPagesNative.java 8303612 linux-all runtime/ErrorHandling/MachCodeFramesInErrorFile.java 8313315 linux-ppc64le runtime/NMT/VirtualAllocCommitMerge.java 8309698 linux-s390x +applications/ctw/modules/jdk_jfr.java 8286300 linux-s390x applications/jcstress/copy.java 8229852 linux-all containers/docker/TestJFREvents.java 8327723 linux-x64 diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 38ffe2ae963..f1bd91d86aa 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -713,6 +713,24 @@ jdk/incubator/vector/LoadJsvmlTest.java 8305390 windows- # jdk_jfr +jdk/jfr/api/consumer/TestRecordingFileWrite.java 8286300 linux-s390x +jdk/jfr/api/consumer/streaming/TestCrossProcessStreaming.java 8286300 linux-s390x +jdk/jfr/api/consumer/streaming/TestFilledChunks.java 8286300 linux-s390x +jdk/jfr/api/consumer/streaming/TestRemovedChunks.java 8286300 linux-s390x +jdk/jfr/api/recording/misc/TestGetStreamWithFailure.java 8286300 linux-s390x +jdk/jfr/api/settings/TestSettingControl.java 8286300 linux-s390x +jdk/jfr/event/runtime/TestBackToBackSensitive.java 8286300 linux-s390x +jdk/jfr/event/runtime/TestSyncOnValueBasedClassEvent.java 8286300 linux-s390x +jdk/jfr/event/tracing/TestMultipleThreads.java 8286300 linux-s390x +jdk/jfr/event/tracing/TestTracedString.java 8286300 linux-s390x +jdk/jfr/javaagent/TestLoadedAgent.java 8286300 linux-s390x +jdk/jfr/javaagent/TestPremainAgent.java 8286300 linux-s390x +jdk/jfr/jmx/streaming/TestClose.java 8286300 linux-s390x +jdk/jfr/jmx/streaming/TestMaxSize.java 8286300 linux-s390x +jdk/jfr/jvm/TestChunkIntegrity.java 8286300 linux-s390x +jdk/jfr/jvm/TestJFRIntrinsic.java 8286300 linux-s390x +jdk/jfr/tool/TestDisassemble.java 8286300 linux-s390x +jdk/jfr/tool/TestScrub.java 8286300 linux-s390x jdk/jfr/event/compiler/TestCodeSweeper.java 8338127 generic-all jdk/jfr/event/oldobject/TestShenandoah.java 8342951 generic-all jdk/jfr/event/runtime/TestResidentSetSizeEvent.java 8309846 aix-ppc64 From 51ea257460cc999468c7799afb76fb8cebaff80c Mon Sep 17 00:00:00 2001 From: Fredrik Bredberg Date: Wed, 25 Mar 2026 06:53:14 +0000 Subject: [PATCH 104/160] 8379782: Implement Object Monitor Table enabled by default Reviewed-by: stefank, coleenp, aartemov --- src/hotspot/share/runtime/globals.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 17e10e2f87c..60feddde09b 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -1929,7 +1929,7 @@ const int ObjectAlignmentInBytes = 8; "Mark all threads after a safepoint, and clear on a modify " \ "fence. Add cleanliness checks.") \ \ - product(bool, UseObjectMonitorTable, false, DIAGNOSTIC, \ + product(bool, UseObjectMonitorTable, true, DIAGNOSTIC, \ "Use a table to record inflated monitors rather than the first " \ "word of the object.") \ \ From 0c60c9ca1477a114a9c7095226d3a1e5d31600fb Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bempel Date: Wed, 25 Mar 2026 06:56:41 +0000 Subject: [PATCH 105/160] 8376185: NoSuchFieldError thrown after a record with type annotation retransformed Reviewed-by: sspitsyn, dholmes --- .../share/prims/jvmtiRedefineClasses.cpp | 4 +- .../RetransformRecordTypeAnn/MyRecord.java | 47 ++ .../TestRetransformRecord.java | 94 ++++ .../altered/MyRecord.jcod | 421 ++++++++++++++++++ test/lib/RedefineClassHelper.java | 2 +- 5 files changed, 566 insertions(+), 2 deletions(-) create mode 100644 test/jdk/java/lang/instrument/RetransformRecordTypeAnn/MyRecord.java create mode 100644 test/jdk/java/lang/instrument/RetransformRecordTypeAnn/TestRetransformRecord.java create mode 100644 test/jdk/java/lang/instrument/RetransformRecordTypeAnn/altered/MyRecord.jcod diff --git a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp index 13b239b4df0..c594cfc6816 100644 --- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp +++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp @@ -1481,6 +1481,8 @@ jvmtiError VM_RedefineClasses::load_new_class_versions() { } else { return JVMTI_ERROR_INTERNAL; } + } else if (res != JVMTI_ERROR_NONE) { + return res; } #ifdef ASSERT @@ -2045,7 +2047,7 @@ bool VM_RedefineClasses::rewrite_cp_refs_in_record_attribute(InstanceKlass* scra AnnotationArray* type_annotations = component->type_annotations(); if (type_annotations != nullptr && type_annotations->length() != 0) { int byte_i = 0; // byte index into annotations - if (!rewrite_cp_refs_in_annotations_typeArray(type_annotations, byte_i)) { + if (!rewrite_cp_refs_in_type_annotations_typeArray(type_annotations, byte_i, "record_info")) { log_debug(redefine, class, annotation)("bad record_component_type_annotations at %d", i); // propagate failure back to caller return false; diff --git a/test/jdk/java/lang/instrument/RetransformRecordTypeAnn/MyRecord.java b/test/jdk/java/lang/instrument/RetransformRecordTypeAnn/MyRecord.java new file mode 100644 index 00000000000..e0f4ecc50f5 --- /dev/null +++ b/test/jdk/java/lang/instrument/RetransformRecordTypeAnn/MyRecord.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026, Datadog, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@MyTypeAnnotation +public record MyRecord(@MyTypeUseAnnotation String filter) { + public static MyRecord parse(String param) { + if (param == null) { + throw new IllegalArgumentException("Filter cannot be null"); + } + return new MyRecord(param); + } +} + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@interface MyTypeAnnotation { +} + +@Target({ElementType.TYPE_USE}) +@Retention(RetentionPolicy.RUNTIME) +@interface MyTypeUseAnnotation { +} diff --git a/test/jdk/java/lang/instrument/RetransformRecordTypeAnn/TestRetransformRecord.java b/test/jdk/java/lang/instrument/RetransformRecordTypeAnn/TestRetransformRecord.java new file mode 100644 index 00000000000..e8f1ba176d1 --- /dev/null +++ b/test/jdk/java/lang/instrument/RetransformRecordTypeAnn/TestRetransformRecord.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2026, Datadog, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8376185 + * @summary Class retransformation on a record type annotation + * @comment This is will rewrite the constant pool and process + * @comment the type annotation + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @modules java.compiler + * java.instrument + * @compile ../NamedBuffer.java + * @compile altered/MyRecord.jcod + * @run driver jdk.test.lib.helpers.ClassFileInstaller MyRecord + * @compile MyRecord.java + * @run main RedefineClassHelper + * @run main/othervm -javaagent:redefineagent.jar -Xlog:redefine*=debug TestRetransformRecord + */ + +/* + * This test is loading a record with type annotation first, then by + * calling retransformClasses, we inject a slightly different record classfile + * where just some constants from the constant pools were swapped. + * It triggers, during the retransformation, a rewrite of the constant pool + * calling VM_RedefineClasses::rewrite_cp_refs_in_record_attribute method. + */ +import java.io.File; +import java.io.FileInputStream; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.security.ProtectionDomain; + +public class TestRetransformRecord { + static final String SRC = System.getProperty("test.src"); + static final String DEST = System.getProperty("test.classes"); + + public static void main(String[] args) throws Exception { + MyRecord.parse("foo"); + File clsfile = new File("MyRecord.class"); + byte[] buf = null; + try (FileInputStream str = new FileInputStream(clsfile)) { + buf = NamedBuffer.loadBufferFromStream(str); + } + Instrumentation inst = RedefineClassHelper.instrumentation; + inst.addTransformer(new IdentityTransformer("MyRecord", buf), true); + inst.retransformClasses(MyRecord.class); + System.out.println(MyRecord.parse("foo")); + } +} + +class IdentityTransformer implements ClassFileTransformer { + private final String className; + private final byte[] buffer; + + public IdentityTransformer(String className, byte[] buffer) { + this.className = className; + this.buffer = buffer; + } + + @Override + public byte[] transform(ClassLoader loader, + String classPath, + Class classBeingRedefined, + ProtectionDomain protectionDomain, + byte[] classfileBuffer) { + if (classPath != null && classPath.equals(className.replace('.', '/'))) { + System.out.println("Transforming " + className); + return buffer; + } + return null; + } +} diff --git a/test/jdk/java/lang/instrument/RetransformRecordTypeAnn/altered/MyRecord.jcod b/test/jdk/java/lang/instrument/RetransformRecordTypeAnn/altered/MyRecord.jcod new file mode 100644 index 00000000000..88b4c038a39 --- /dev/null +++ b/test/jdk/java/lang/instrument/RetransformRecordTypeAnn/altered/MyRecord.jcod @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2026, Datadog, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This is a jcod version of the MyRecord classfile. + * Generated from runnning java -jar asmtools.jar jdec MyRecord.class + * Then slightly modified to trigger constant pool rewrite. + * Here the following modifications: + * - constants #10 and #11 were swapped + * - constants #14 and #34 were swapped + */ +class MyRecord { + 0xCAFEBABE; + 0; // minor version + 69; // version + [] { // Constant Pool + ; // first element is empty + Method #2 #3; // #1 + Class #4; // #2 + NameAndType #5 #6; // #3 + Utf8 "java/lang/Record"; // #4 + Utf8 ""; // #5 + Utf8 "()V"; // #6 + Field #8 #9; // #7 + Class #11; // #8 + NameAndType #10 #12; // #9 + Utf8 "filter"; // #10 + Utf8 "MyRecord"; // #11 + Utf8 "Ljava/lang/String;"; // #12 + Class #34; // #13 + Utf8 "LMyTypeUseAnnotation;"; // #14 + String #16; // #15 + Utf8 "Filter cannot be null"; // #16 + Method #13 #18; // #17 + NameAndType #5 #19; // #18 + Utf8 "(Ljava/lang/String;)V"; // #19 + Method #8 #18; // #20 + InvokeDynamic 0s #22; // #21 + NameAndType #23 #24; // #22 + Utf8 "toString"; // #23 + Utf8 "(LMyRecord;)Ljava/lang/String;"; // #24 + InvokeDynamic 0s #26; // #25 + NameAndType #27 #28; // #26 + Utf8 "hashCode"; // #27 + Utf8 "(LMyRecord;)I"; // #28 + InvokeDynamic 0s #30; // #29 + NameAndType #31 #32; // #30 + Utf8 "equals"; // #31 + Utf8 "(LMyRecord;Ljava/lang/Object;)Z"; // #32 + Utf8 "RuntimeVisibleTypeAnnotations"; // #33 + Utf8 "java/lang/IllegalArgumentException"; // #34 + Utf8 "Code"; // #35 + Utf8 "LineNumberTable"; // #36 + Utf8 "LocalVariableTable"; // #37 + Utf8 "this"; // #38 + Utf8 "LMyRecord;"; // #39 + Utf8 "MethodParameters"; // #40 + Utf8 "parse"; // #41 + Utf8 "(Ljava/lang/String;)LMyRecord;"; // #42 + Utf8 "param"; // #43 + Utf8 "StackMapTable"; // #44 + Utf8 "()Ljava/lang/String;"; // #45 + Utf8 "()I"; // #46 + Utf8 "(Ljava/lang/Object;)Z"; // #47 + Utf8 "o"; // #48 + Utf8 "Ljava/lang/Object;"; // #49 + Utf8 "SourceFile"; // #50 + Utf8 "MyRecord.java"; // #51 + Utf8 "RuntimeVisibleAnnotations"; // #52 + Utf8 "LMyTypeAnnotation;"; // #53 + Utf8 "Record"; // #54 + Utf8 "BootstrapMethods"; // #55 + String #10; // #56 + MethodHandle 1b #7; // #57 + MethodHandle 6b #59; // #58 + Method #60 #61; // #59 + Class #62; // #60 + NameAndType #63 #64; // #61 + Utf8 "java/lang/runtime/ObjectMethods"; // #62 + Utf8 "bootstrap"; // #63 + Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;"; // #64 + Utf8 "InnerClasses"; // #65 + Class #67; // #66 + Utf8 "java/lang/invoke/MethodHandles$Lookup"; // #67 + Class #69; // #68 + Utf8 "java/lang/invoke/MethodHandles"; // #69 + Utf8 "Lookup"; // #70 + } + + 0x0031; // access + #8; // this_cpx + #2; // super_cpx + + [] { // Interfaces + } // end of Interfaces + + [] { // Fields + { // field + 0x0012; // access + #10; // name_index + #12; // descriptor_index + [] { // Attributes + Attr(#33) { // RuntimeVisibleTypeAnnotations + [] { // annotations + { // type_annotation + 0x13; // target_type: FIELD + []b { // type_paths + } + #14; + [] { // element_value_pairs + } // element_value_pairs + } // type_annotation + } + } // end of RuntimeVisibleTypeAnnotations + } // end of Attributes + } + } // end of Fields + + [] { // Methods + { // method + 0x0001; // access + #5; // name_index + #19; // descriptor_index + [] { // Attributes + Attr(#35) { // Code + 2; // max_stack + 2; // max_locals + Bytes[]{ + 0x2A 0xB7 0x00 0x01 0x2A 0x2B 0xB5 0x00 0x07 0xB1; + } + [] { // Traps + } // end of Traps + [] { // Attributes + Attr(#36) { // LineNumberTable + [] { // line_number_table + 0 7; + } + } // end of LineNumberTable + ; + Attr(#37) { // LocalVariableTable + [] { // LocalVariableTable + 0 10 38 39 0; + 0 10 11 12 1; + } + } // end of LocalVariableTable + } // end of Attributes + } // end of Code + ; + Attr(#40) { // MethodParameters + []b { // MethodParameters + #10 0x0000; + } + } // end of MethodParameters + ; + Attr(#33) { // RuntimeVisibleTypeAnnotations + [] { // annotations + { // type_annotation + 0x16; // target_type: METHOD_FORMAL_PARAMETER + 0x00; // parameter_index + []b { // type_paths + } + #14; + [] { // element_value_pairs + } // element_value_pairs + } // type_annotation + } + } // end of RuntimeVisibleTypeAnnotations + } // end of Attributes + } + ; + { // method + 0x0009; // access + #41; // name_index + #42; // descriptor_index + [] { // Attributes + Attr(#35) { // Code + 3; // max_stack + 1; // max_locals + Bytes[]{ + 0x2A 0xC7 0x00 0x0D 0xBB 0x00 0x0D 0x59 0x12 0x0F 0xB7 0x00; + 0x11 0xBF 0xBB 0x00 0x08 0x59 0x2A 0xB7 0x00 0x14 0xB0; + } + [] { // Traps + } // end of Traps + [] { // Attributes + Attr(#36) { // LineNumberTable + [] { // line_number_table + 0 9; + 4 10; + 14 12; + } + } // end of LineNumberTable + ; + Attr(#37) { // LocalVariableTable + [] { // LocalVariableTable + 0 23 43 12 0; + } + } // end of LocalVariableTable + ; + Attr(#44) { // StackMapTable + [] { // + 14b; // same_frame + } + } // end of StackMapTable + } // end of Attributes + } // end of Code + } // end of Attributes + } + ; + { // method + 0x0011; // access + #23; // name_index + #45; // descriptor_index + [] { // Attributes + Attr(#35) { // Code + 1; // max_stack + 1; // max_locals + Bytes[]{ + 0x2A 0xBA 0x00 0x15 0x00 0x00 0xB0; + } + [] { // Traps + } // end of Traps + [] { // Attributes + Attr(#36) { // LineNumberTable + [] { // line_number_table + 0 6; + } + } // end of LineNumberTable + ; + Attr(#37) { // LocalVariableTable + [] { // LocalVariableTable + 0 7 38 39 0; + } + } // end of LocalVariableTable + } // end of Attributes + } // end of Code + } // end of Attributes + } + ; + { // method + 0x0011; // access + #27; // name_index + #46; // descriptor_index + [] { // Attributes + Attr(#35) { // Code + 1; // max_stack + 1; // max_locals + Bytes[]{ + 0x2A 0xBA 0x00 0x19 0x00 0x00 0xAC; + } + [] { // Traps + } // end of Traps + [] { // Attributes + Attr(#36) { // LineNumberTable + [] { // line_number_table + 0 6; + } + } // end of LineNumberTable + ; + Attr(#37) { // LocalVariableTable + [] { // LocalVariableTable + 0 7 38 39 0; + } + } // end of LocalVariableTable + } // end of Attributes + } // end of Code + } // end of Attributes + } + ; + { // method + 0x0011; // access + #31; // name_index + #47; // descriptor_index + [] { // Attributes + Attr(#35) { // Code + 2; // max_stack + 2; // max_locals + Bytes[]{ + 0x2A 0x2B 0xBA 0x00 0x1D 0x00 0x00 0xAC } + [] { // Traps + } // end of Traps + [] { // Attributes + Attr(#36) { // LineNumberTable + [] { // line_number_table + 0 6; + } + } // end of LineNumberTable + ; + Attr(#37) { // LocalVariableTable + [] { // LocalVariableTable + 0 8 38 39 0; + 0 8 48 49 1; + } + } // end of LocalVariableTable + } // end of Attributes + } // end of Code + } // end of Attributes + } + ; + { // method + 0x0001; // access + #10; // name_index + #45; // descriptor_index + [] { // Attributes + Attr(#35) { // Code + 1; // max_stack + 1; // max_locals + Bytes[]{ + 0x2A 0xB4 0x00 0x07 0xB0; + } + [] { // Traps + } // end of Traps + [] { // Attributes + Attr(#36) { // LineNumberTable + [] { // line_number_table + 0 6; + } + } // end of LineNumberTable + ; + Attr(#37) { // LocalVariableTable + [] { // LocalVariableTable + 0 5 38 39 0; + } + } // end of LocalVariableTable + } // end of Attributes + } // end of Code + ; + Attr(#33) { // RuntimeVisibleTypeAnnotations + [] { // annotations + { // type_annotation + 0x14; // target_type: METHOD_RETURN + []b { // type_paths + } + #14; + [] { // element_value_pairs + } // element_value_pairs + } // type_annotation + } + } // end of RuntimeVisibleTypeAnnotations + } // end of Attributes + } + } // end of Methods + + [] { // Attributes + Attr(#50) { // SourceFile + #51; + } // end of SourceFile + ; + Attr(#52) { // RuntimeVisibleAnnotations + [] { // annotations + { // annotation + #53; + [] { // element_value_pairs + } // element_value_pairs + } // annotation + } + } // end of RuntimeVisibleAnnotations + ; + Attr(#54) { // Record + [] { // components + { // component + #10; // name_index + #12; // descriptor_index + [] { // Attributes + Attr(#33) { // RuntimeVisibleTypeAnnotations + [] { // annotations + { // type_annotation + 0x13; // target_type: FIELD + []b { // type_paths + } + #14; + [] { // element_value_pairs + } // element_value_pairs + } // type_annotation + } + } // end of RuntimeVisibleTypeAnnotations + } // end of Attributes + } + } + } // end of Record + ; + Attr(#55) { // BootstrapMethods + [] { // bootstrap_methods + { // bootstrap_method + #58; // bootstrap_method_ref + [] { // bootstrap_arguments + #8; + #56; + #57; + } // bootstrap_arguments + } // bootstrap_method + } + } // end of BootstrapMethods + ; + Attr(#65) { // InnerClasses + [] { // classes + #66 #68 #70 25; + } + } // end of InnerClasses + } // end of Attributes +} diff --git a/test/lib/RedefineClassHelper.java b/test/lib/RedefineClassHelper.java index ce27fb33f44..064778b3a2a 100644 --- a/test/lib/RedefineClassHelper.java +++ b/test/lib/RedefineClassHelper.java @@ -107,7 +107,7 @@ public class RedefineClassHelper { * Main method to be invoked before test to create the redefineagent.jar */ public static void main(String[] args) throws Exception { - String manifest = "Premain-Class: RedefineClassHelper\nCan-Redefine-Classes: true\n"; + String manifest = "Premain-Class: RedefineClassHelper\nCan-Redefine-Classes: true\nCan-Retransform-Classes: true\n"; ClassFileInstaller.writeJar("redefineagent.jar", ClassFileInstaller.Manifest.fromString(manifest), "RedefineClassHelper"); } } From 0423483bfdd20e22751edc3b5e5d42f2380bad47 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 25 Mar 2026 07:03:08 +0000 Subject: [PATCH 106/160] 8380772: Rename _total_allocations to _total_allocated_size in ThreadLocalAllocStats Reviewed-by: stefank, aboldtch, tschatzl --- .../gc/shared/threadLocalAllocBuffer.cpp | 24 +++++++++---------- .../gc/shared/threadLocalAllocBuffer.hpp | 6 ++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp index 4c160929f5a..9d995234736 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp @@ -302,7 +302,7 @@ HeapWord* ThreadLocalAllocBuffer::hard_end() { PerfVariable* ThreadLocalAllocStats::_perf_allocating_threads; PerfVariable* ThreadLocalAllocStats::_perf_total_refills; PerfVariable* ThreadLocalAllocStats::_perf_max_refills; -PerfVariable* ThreadLocalAllocStats::_perf_total_allocations; +PerfVariable* ThreadLocalAllocStats::_perf_total_allocated_size; PerfVariable* ThreadLocalAllocStats::_perf_total_gc_waste; PerfVariable* ThreadLocalAllocStats::_perf_max_gc_waste; PerfVariable* ThreadLocalAllocStats::_perf_total_refill_waste; @@ -325,7 +325,7 @@ void ThreadLocalAllocStats::initialize() { _perf_allocating_threads = create_perf_variable("allocThreads", PerfData::U_None, CHECK); _perf_total_refills = create_perf_variable("fills", PerfData::U_None, CHECK); _perf_max_refills = create_perf_variable("maxFills", PerfData::U_None, CHECK); - _perf_total_allocations = create_perf_variable("alloc", PerfData::U_Bytes, CHECK); + _perf_total_allocated_size = create_perf_variable("alloc", PerfData::U_Bytes, CHECK); _perf_total_gc_waste = create_perf_variable("gcWaste", PerfData::U_Bytes, CHECK); _perf_max_gc_waste = create_perf_variable("maxGcWaste", PerfData::U_Bytes, CHECK); _perf_total_refill_waste = create_perf_variable("refillWaste", PerfData::U_Bytes, CHECK); @@ -339,7 +339,7 @@ ThreadLocalAllocStats::ThreadLocalAllocStats() : _allocating_threads(0), _total_refills(0), _max_refills(0), - _total_allocations(0), + _total_allocated_size(0), _total_gc_waste(0), _max_gc_waste(0), _total_refill_waste(0), @@ -352,13 +352,13 @@ unsigned int ThreadLocalAllocStats::allocating_threads_avg() { } void ThreadLocalAllocStats::update_fast_allocations(unsigned int refills, - size_t allocations, - size_t gc_waste, - size_t refill_waste) { + size_t allocated_size, + size_t gc_waste, + size_t refill_waste) { _allocating_threads += 1; _total_refills += refills; _max_refills = MAX2(_max_refills, refills); - _total_allocations += allocations; + _total_allocated_size += allocated_size; _total_gc_waste += gc_waste; _max_gc_waste = MAX2(_max_gc_waste, gc_waste); _total_refill_waste += refill_waste; @@ -374,7 +374,7 @@ void ThreadLocalAllocStats::update(const ThreadLocalAllocStats& other) { _allocating_threads += other._allocating_threads; _total_refills += other._total_refills; _max_refills = MAX2(_max_refills, other._max_refills); - _total_allocations += other._total_allocations; + _total_allocated_size += other._total_allocated_size; _total_gc_waste += other._total_gc_waste; _max_gc_waste = MAX2(_max_gc_waste, other._max_gc_waste); _total_refill_waste += other._total_refill_waste; @@ -387,7 +387,7 @@ void ThreadLocalAllocStats::reset() { _allocating_threads = 0; _total_refills = 0; _max_refills = 0; - _total_allocations = 0; + _total_allocated_size = 0; _total_gc_waste = 0; _max_gc_waste = 0; _total_refill_waste = 0; @@ -397,14 +397,14 @@ void ThreadLocalAllocStats::reset() { } void ThreadLocalAllocStats::publish() { - if (_total_allocations == 0) { + if (_total_allocated_size == 0) { return; } _allocating_threads_avg.sample(_allocating_threads); const size_t waste = _total_gc_waste + _total_refill_waste; - const double waste_percent = percent_of(waste, _total_allocations); + const double waste_percent = percent_of(waste, _total_allocated_size); log_debug(gc, tlab)("TLAB totals: thrds: %d refills: %d max: %d" " slow allocs: %d max %d waste: %4.1f%%" " gc: %zuB max: %zuB" @@ -418,7 +418,7 @@ void ThreadLocalAllocStats::publish() { _perf_allocating_threads ->set_value(_allocating_threads); _perf_total_refills ->set_value(_total_refills); _perf_max_refills ->set_value(_max_refills); - _perf_total_allocations ->set_value(_total_allocations); + _perf_total_allocated_size ->set_value(_total_allocated_size); _perf_total_gc_waste ->set_value(_total_gc_waste); _perf_max_gc_waste ->set_value(_max_gc_waste); _perf_total_refill_waste ->set_value(_total_refill_waste); diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp index eb664d13961..8c99523557e 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp @@ -182,7 +182,7 @@ private: static PerfVariable* _perf_allocating_threads; static PerfVariable* _perf_total_refills; static PerfVariable* _perf_max_refills; - static PerfVariable* _perf_total_allocations; + static PerfVariable* _perf_total_allocated_size; static PerfVariable* _perf_total_gc_waste; static PerfVariable* _perf_max_gc_waste; static PerfVariable* _perf_total_refill_waste; @@ -195,7 +195,7 @@ private: unsigned int _allocating_threads; unsigned int _total_refills; unsigned int _max_refills; - size_t _total_allocations; + size_t _total_allocated_size; size_t _total_gc_waste; size_t _max_gc_waste; size_t _total_refill_waste; @@ -210,7 +210,7 @@ public: ThreadLocalAllocStats(); void update_fast_allocations(unsigned int refills, - size_t allocations, + size_t allocated_size, size_t gc_waste, size_t refill_waste); void update_slow_allocations(unsigned int allocations); From 3bf5022bc6b8bbc544502b3fc100c6debdb1b2c7 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Wed, 25 Mar 2026 07:40:26 +0000 Subject: [PATCH 107/160] 8380641: Thread dump parsing and test improvements 8378946: threadDump.schema.json syntax error, missing comma after owner Reviewed-by: amenkov, sspitsyn --- .../doc-files/threadDump.schema.json | 2 +- .../HotSpotDiagnosticMXBean/DumpThreads.java | 84 +++++--- .../security/provider/acvp/ML_DSA_Test.java | 10 +- test/lib/jdk/test/lib/json/JSONValue.java | 94 +++++++-- .../jdk/test/lib/threaddump/ThreadDump.java | 193 +++++++++--------- 5 files changed, 240 insertions(+), 143 deletions(-) diff --git a/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json b/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json index bf52bb3915d..1da3e3941ef 100644 --- a/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json +++ b/src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json @@ -81,7 +81,7 @@ "owner": { "type": "string", "description": "The thread identifier of the owner when the parkBlocker is an AbstractOwnableSynchronizer." - } + }, "required": [ "object" ] diff --git a/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreads.java b/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreads.java index 77020491c29..3878513d3f2 100644 --- a/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreads.java +++ b/test/jdk/com/sun/management/HotSpotDiagnosticMXBean/DumpThreads.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,6 +49,7 @@ import java.nio.file.FileAlreadyExistsException; import java.nio.file.Path; import java.time.ZonedDateTime; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -238,6 +239,7 @@ class DumpThreads { void testBlockedThread(ThreadFactory factory, boolean pinned) throws Exception { var lock = new Object(); + String lockAsString = Objects.toIdentityString(lock); var started = new CountDownLatch(1); Thread thread = factory.newThread(() -> { @@ -258,9 +260,7 @@ class DumpThreads { thread.start(); started.await(); await(thread, Thread.State.BLOCKED); - long tid = thread.threadId(); - String lockAsString = Objects.toIdentityString(lock); // thread dump in plain text should include thread List lines = dumpThreadsToPlainText(); @@ -308,6 +308,7 @@ class DumpThreads { void testWaitingThread(ThreadFactory factory, boolean pinned) throws Exception { var lock = new Object(); + String lockAsString = Objects.toIdentityString(lock); var started = new CountDownLatch(1); Thread thread = factory.newThread(() -> { @@ -331,9 +332,7 @@ class DumpThreads { thread.start(); started.await(); await(thread, Thread.State.WAITING); - long tid = thread.threadId(); - String lockAsString = Objects.toIdentityString(lock); // thread dump in plain text should include thread List lines = dumpThreadsToPlainText(); @@ -417,7 +416,6 @@ class DumpThreads { thread.start(); started.await(); await(thread, Thread.State.WAITING); - long tid = thread.threadId(); // thread dump in plain text should include thread @@ -460,7 +458,7 @@ class DumpThreads { } /** - * Test thread dump with a thread owning a monitor. + * Test thread dump with a thread owning monitors. */ @ParameterizedTest @MethodSource("threadFactories") @@ -475,19 +473,26 @@ class DumpThreads { } void testThreadOwnsMonitor(ThreadFactory factory, boolean pinned) throws Exception { - var lock = new Object(); - var started = new CountDownLatch(1); + var lock1 = new Object(); + var lock2 = new Object(); + var lock3 = new Object(); + String lock1AsString = Objects.toIdentityString(lock1); + String lock2AsString = Objects.toIdentityString(lock2); + String lock3AsString = Objects.toIdentityString(lock3); + var started = new CountDownLatch(1); Thread thread = factory.newThread(() -> { - synchronized (lock) { - if (pinned) { - VThreadPinner.runPinned(() -> { + synchronized (lock1) { + synchronized (lock2) { + if (pinned) { + VThreadPinner.runPinned(() -> { + started.countDown(); + lockAndRun(lock3, LockSupport::park); + }); + } else { started.countDown(); - LockSupport.park(); - }); - } else { - started.countDown(); - LockSupport.park(); + lockAndRun(lock3, LockSupport::park); + } } } }); @@ -497,16 +502,16 @@ class DumpThreads { thread.start(); started.await(); await(thread, Thread.State.WAITING); - long tid = thread.threadId(); - String lockAsString = Objects.toIdentityString(lock); // thread dump in plain text should include thread List lines = dumpThreadsToPlainText(); ThreadFields fields = findThread(tid, lines); assertNotNull(fields, "thread not found"); assertEquals("WAITING", fields.state()); - assertTrue(contains(lines, "- locked <" + lockAsString)); + assertTrue(contains(lines, "- locked <" + lock1AsString)); + assertTrue(contains(lines, "- locked <" + lock2AsString)); + assertTrue(contains(lines, "- locked <" + lock3AsString)); // thread dump in JSON format should include thread in root container ThreadDump threadDump = dumpThreadsToJson(); @@ -516,18 +521,47 @@ class DumpThreads { assertNotNull(ti, "thread not found"); assertEquals(ti.isVirtual(), thread.isVirtual()); - // the lock should be in the ownedMonitors array - Set ownedMonitors = ti.ownedMonitors().values() + // depth -> list of locks + Map> ownedMonitors = ti.ownedMonitors(); + + // lock -> list of depths + Map> monitorDepths = ownedMonitors.entrySet() .stream() - .flatMap(List::stream) - .collect(Collectors.toSet()); - assertTrue(ownedMonitors.contains(lockAsString), lockAsString + " not found"); + .flatMap(e -> e.getValue() + .stream() + .map(monitor -> Map.entry(monitor, e.getKey()))) + .collect(Collectors.groupingBy( + Map.Entry::getKey, + Collectors.mapping(Map.Entry::getValue, Collectors.toList()) + )); + + // each lock should be owned + List lock1Depths = monitorDepths.getOrDefault(lock1AsString, List.of()); + List lock2Depths = monitorDepths.getOrDefault(lock2AsString, List.of()); + List lock3Depths = monitorDepths.getOrDefault(lock3AsString, List.of()); + assertEquals(1, lock1Depths.size()); + assertEquals(1, lock2Depths.size()); + assertEquals(1, lock3Depths.size()); + + // lock1 and lock2 owned at the same depth, lock3 is the innermost + int depth1 = lock1Depths.get(0); + int depth2 = lock2Depths.get(0); + int depth3 = lock3Depths.get(0); + assertEquals(depth1, depth2); + assertTrue(depth3 < depth1); + } finally { LockSupport.unpark(thread); thread.join(); } } + private void lockAndRun(Object lock, Runnable action) { + synchronized (lock) { + action.run(); + } + } + /** * Test mounted virtual thread. */ diff --git a/test/jdk/sun/security/provider/acvp/ML_DSA_Test.java b/test/jdk/sun/security/provider/acvp/ML_DSA_Test.java index ac56642b8d7..f76f3d8b9a8 100644 --- a/test/jdk/sun/security/provider/acvp/ML_DSA_Test.java +++ b/test/jdk/sun/security/provider/acvp/ML_DSA_Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -88,13 +88,13 @@ public class ML_DSA_Test { for (var t : kat.get("testGroups").asArray()) { var pname = t.get("parameterSet").asString(); System.out.println(">> " + pname + " sign"); - var det = Boolean.parseBoolean(t.get("deterministic").asString()); + var det = t.get("deterministic").asBoolean(); if (t.get("signatureInterface").asString().equals("internal")) { ML_DSA_Impls.version = ML_DSA_Impls.Version.DRAFT; } else { ML_DSA_Impls.version = ML_DSA_Impls.Version.FINAL; } - if (t.get("externalMu").asString().equals("true")) { + if (t.get("externalMu").asBoolean()) { continue; // Not supported } for (var c : t.get("tests").asArray()) { @@ -139,7 +139,7 @@ public class ML_DSA_Test { ML_DSA_Impls.version = ML_DSA_Impls.Version.FINAL; } - if (t.get("externalMu").asString().equals("true")) { + if (t.get("externalMu").asBoolean()) { continue; // Not supported } @@ -157,7 +157,7 @@ public class ML_DSA_Test { public byte[] getEncoded() { return toByteArray(c.get("pk").asString()); } }; // Only ML-DSA sigVer has negative tests - var expected = Boolean.parseBoolean(c.get("testPassed").asString()); + var expected = c.get("testPassed").asBoolean(); var actual = true; try { s.initVerify(pk); diff --git a/test/lib/jdk/test/lib/json/JSONValue.java b/test/lib/jdk/test/lib/json/JSONValue.java index f89d13b3bba..72ed2fd917c 100644 --- a/test/lib/jdk/test/lib/json/JSONValue.java +++ b/test/lib/jdk/test/lib/json/JSONValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; public interface JSONValue { @@ -88,9 +89,6 @@ public interface JSONValue { @Override public String toString() { - if (value == null) { - return "null"; - } var builder = new StringBuilder(); builder.append("\""); @@ -172,6 +170,56 @@ public interface JSONValue { public Iterator iterator() { return values.iterator(); } + + @Override + public List elements() { + return List.copyOf(values); + } + } + + public final class JSONBoolean implements JSONValue { + private static JSONBoolean TRUE = new JSONBoolean(true); + private static JSONBoolean FALSE = new JSONBoolean(false); + + private final boolean value; + + private JSONBoolean(boolean value) { + this.value = value; + } + + @Override + public boolean asBoolean() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static JSONBoolean of(boolean value) { + return value ? TRUE : FALSE; + } + } + + public final class JSONNull implements JSONValue { + private static JSONNull NULL = new JSONNull(); + + private JSONNull() {} + + @Override + public Optional valueOrNull() { + return Optional.empty(); + } + + @Override + public String toString() { + return "null"; + } + + public static JSONNull of() { + return NULL; + } } class JSONParser { @@ -181,8 +229,8 @@ public interface JSONValue { JSONParser() { } - private IllegalStateException failure(String message) { - return new IllegalStateException(String.format("[%d]: %s : %s", pos, message, input)); + private IllegalArgumentException failure(String message) { + return new IllegalArgumentException(String.format("[%d]: %s : %s", pos, message, input)); } private char current() { @@ -220,13 +268,13 @@ public interface JSONValue { } } - private JSONString parseBoolean() { + private JSONBoolean parseBoolean() { if (current() == 't') { expect('r'); expect('u'); expect('e'); advance(); - return new JSONString("true"); + return JSONBoolean.of(true); } if (current() == 'f') { @@ -235,7 +283,7 @@ public interface JSONValue { expect('s'); expect('e'); advance(); - return new JSONString("false"); + return JSONBoolean.of(false); } throw failure("a boolean can only be 'true' or 'false'"); } @@ -400,12 +448,12 @@ public interface JSONValue { return new JSONArray(list); } - public JSONString parseNull() { + public JSONNull parseNull() { expect('u'); expect('l'); expect('l'); advance(); - return new JSONString(null); + return JSONNull.of(); } public JSONObject parseObject() { @@ -531,22 +579,38 @@ public interface JSONValue { } default int size() { - throw new IllegalStateException("Size operation unsupported"); + throw new UnsupportedOperationException("Size operation unsupported"); + } + + default List elements() { + throw new UnsupportedOperationException("Unsupported conversion to array"); } default String asString() { - throw new IllegalStateException("Unsupported conversion to String"); + throw new UnsupportedOperationException("Unsupported conversion to String"); } default JSONArray asArray() { - throw new IllegalStateException("Unsupported conversion to array"); + throw new UnsupportedOperationException("Unsupported conversion to array"); } default JSONObject asObject() { - throw new IllegalStateException("Unsupported conversion to object"); + throw new UnsupportedOperationException("Unsupported conversion to object"); + } + + default boolean asBoolean() { + throw new UnsupportedOperationException("Unsupported conversion to boolean"); } default JSONValue get(String field) { return asObject().get(field); } + + default Optional getOrAbsent(String field) { + return Optional.ofNullable(get(field)); + } + + default Optional valueOrNull() { + return Optional.of(this); + } } diff --git a/test/lib/jdk/test/lib/threaddump/ThreadDump.java b/test/lib/jdk/test/lib/threaddump/ThreadDump.java index ca728e625fc..972d46675f4 100644 --- a/test/lib/jdk/test/lib/threaddump/ThreadDump.java +++ b/test/lib/jdk/test/lib/threaddump/ThreadDump.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,13 +32,17 @@ import java.util.Map; import java.util.Optional; import java.util.OptionalLong; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.test.lib.json.JSONValue; /** * Represents a thread dump that is obtained by parsing JSON text. A thread dump in JSON * format is generated with the {@code com.sun.management.HotSpotDiagnosticMXBean} API or - * using {@code jcmd Thread.dump_to_file -format=json }. + * using {@code jcmd Thread.dump_to_file -format=json }. The thread dump + * format is documented in {@code + * src/jdk.management/share/classes/com/sun/management/doc-files/threadDump.schema.json}. * *

The following is an example thread dump that is parsed by this class. Many of the * objects are collapsed to reduce the size. @@ -127,6 +131,20 @@ public final class ThreadDump { this.threadDumpObj = threadDumpObj; } + /** + * Assert that a JSONValue is a JSONString and parse the string as an int. + */ + private static int parseStringAsInt(JSONValue valueObj) { + return Integer.parseInt(valueObj.asString()); + } + + /** + * Assert that a JSONValue is a JSONString and parse the string as a long. + */ + private static long parseStringAsLong(JSONValue valueObj) { + return Long.parseLong(valueObj.asString()); + } + /** * Represents an element in the threadDump/threadContainers array. */ @@ -149,14 +167,6 @@ public final class ThreadDump { children.add(container); } - /** - * Returns the value of a property of this thread container, as a string. - */ - private String getStringProperty(String propertyName) { - JSONValue value = containerObj.get(propertyName); - return (value != null) ? value.asString() : null; - } - /** * Returns the thread container name. */ @@ -168,10 +178,10 @@ public final class ThreadDump { * Return the thread identifier of the owner or empty OptionalLong if not owned. */ public OptionalLong owner() { - String owner = getStringProperty("owner"); - return (owner != null) - ? OptionalLong.of(Long.parseLong(owner)) - : OptionalLong.empty(); + return containerObj.get("owner") // string or null + .valueOrNull() + .map(v -> OptionalLong.of(parseStringAsLong(v))) + .orElse(OptionalLong.empty()); } /** @@ -192,12 +202,10 @@ public final class ThreadDump { * Returns a stream of {@code ThreadInfo} objects for the threads in this container. */ public Stream threads() { - JSONValue.JSONArray threadsObj = containerObj.get("threads").asArray(); - Set threadInfos = new HashSet<>(); - for (JSONValue threadObj : threadsObj) { - threadInfos.add(new ThreadInfo(threadObj)); - } - return threadInfos.stream(); + return containerObj.get("threads") + .elements() + .stream() + .map(ThreadInfo::new); } /** @@ -237,29 +245,10 @@ public final class ThreadDump { private final JSONValue threadObj; ThreadInfo(JSONValue threadObj) { - this.tid = Long.parseLong(threadObj.get("tid").asString()); + this.tid = parseStringAsLong(threadObj.get("tid")); this.threadObj = threadObj; } - /** - * Returns the value of a property of this thread object, as a string. - */ - private String getStringProperty(String propertyName) { - JSONValue value = threadObj.get(propertyName); - return (value != null) ? value.asString() : null; - } - - /** - * Returns the value of a property of an object in this thread object, as a string. - */ - private String getStringProperty(String objectName, String propertyName) { - if (threadObj.get(objectName) instanceof JSONValue.JSONObject obj - && obj.get(propertyName) instanceof JSONValue value) { - return value.asString(); - } - return null; - } - /** * Returns the thread identifier. */ @@ -271,83 +260,92 @@ public final class ThreadDump { * Returns the thread name. */ public String name() { - return getStringProperty("name"); + return threadObj.get("name").asString(); } /** * Returns the thread state. */ public String state() { - return getStringProperty("state"); + return threadObj.get("state").asString(); } /** * Returns true if virtual thread. */ public boolean isVirtual() { - String s = getStringProperty("virtual"); - return (s != null) ? Boolean.parseBoolean(s) : false; + return threadObj.getOrAbsent("virtual") + .map(JSONValue::asBoolean) + .orElse(false); } /** - * Returns the thread's parkBlocker. + * Returns the thread's parkBlocker or null. */ public String parkBlocker() { - return getStringProperty("parkBlocker", "object"); + return threadObj.getOrAbsent("parkBlocker") + .map(v -> v.get("object").asString()) + .orElse(null); } /** * Returns the owner of the parkBlocker if the parkBlocker is an AbstractOwnableSynchronizer. */ public OptionalLong parkBlockerOwner() { - String owner = getStringProperty("parkBlocker", "owner"); - return (owner != null) - ? OptionalLong.of(Long.parseLong(owner)) - : OptionalLong.empty(); + return threadObj.getOrAbsent("parkBlocker") + .map(v -> OptionalLong.of(parseStringAsLong(v.get("owner")))) + .orElse(OptionalLong.empty()); } /** - * Returns the object that the thread is blocked entering its monitor. + * Returns the object that the thread is blocked entering its monitor or null. */ public String blockedOn() { - return getStringProperty("blockedOn"); + return threadObj.getOrAbsent("blockedOn") + .map(JSONValue::asString) + .orElse(null); } /** - * Return the object that is the therad is waiting on with Object.wait. + * Return the object that is the thread is waiting on with Object.wait or null. */ public String waitingOn() { - return getStringProperty("waitingOn"); + return threadObj.getOrAbsent("waitingOn") + .map(JSONValue::asString) + .orElse(null); } /** * Returns the thread stack. */ public Stream stack() { - JSONValue.JSONArray stackObj = threadObj.get("stack").asArray(); - List stack = new ArrayList<>(); - for (JSONValue steObject : stackObj) { - stack.add(steObject.asString()); - } - return stack.stream(); + return threadObj.get("stack") + .elements() + .stream() + .map(JSONValue::asString); } /** * Return a map of monitors owned. */ public Map> ownedMonitors() { - Map> ownedMonitors = new HashMap<>(); - JSONValue monitorsOwnedObj = threadObj.get("monitorsOwned"); - if (monitorsOwnedObj != null) { - for (JSONValue obj : monitorsOwnedObj.asArray()) { - int depth = Integer.parseInt(obj.get("depth").asString()); - for (JSONValue lock : obj.get("locks").asArray()) { - ownedMonitors.computeIfAbsent(depth, _ -> new ArrayList<>()) - .add(lock.asString()); - } - } - } - return ownedMonitors; + Map> result = new HashMap<>(); + threadObj.getOrAbsent("monitorsOwned") + .map(JSONValue::elements) + .orElse(List.of()) + .forEach(e -> { + int depth = parseStringAsInt(e.get("depth")); + List locks = e.get("locks") + .elements() + .stream() + .map(v -> v.valueOrNull() // string or null + .map(JSONValue::asString) + .orElse(null)) + .toList(); + result.computeIfAbsent(depth, _ -> new ArrayList<>()).addAll(locks); + }); + + return result; } /** @@ -355,10 +353,9 @@ public final class ThreadDump { * its carrier. */ public OptionalLong carrier() { - String s = getStringProperty("carrier"); - return (s != null) - ? OptionalLong.of(Long.parseLong(s)) - : OptionalLong.empty(); + return threadObj.getOrAbsent("carrier") + .map(s -> OptionalLong.of(parseStringAsLong(s))) + .orElse(OptionalLong.empty()); } @Override @@ -388,33 +385,25 @@ public final class ThreadDump { } } - /** - * Returns the value of a property of this thread dump, as a string. - */ - private String getStringProperty(String propertyName) { - JSONValue value = threadDumpObj.get(propertyName); - return (value != null) ? value.asString() : null; - } - /** * Returns the value of threadDump/processId. */ public long processId() { - return Long.parseLong(getStringProperty("processId")); + return parseStringAsLong(threadDumpObj.get("processId")); } /** * Returns the value of threadDump/time. */ public String time() { - return getStringProperty("time"); + return threadDumpObj.get("time").asString(); } /** * Returns the value of threadDump/runtimeVersion. */ public String runtimeVersion() { - return getStringProperty("runtimeVersion"); + return threadDumpObj.get("runtimeVersion").asString(); } /** @@ -449,24 +438,31 @@ public final class ThreadDump { JSONValue threadDumpObj = JSONValue.parse(json).get("threadDump"); // threadContainers array, preserve insertion order (parents are added before children) - Map containerObjs = new LinkedHashMap<>(); - JSONValue threadContainersObj = threadDumpObj.get("threadContainers"); - for (JSONValue containerObj : threadContainersObj.asArray()) { - String name = containerObj.get("container").asString(); - containerObjs.put(name, containerObj); - } + Map containerObjs = threadDumpObj.get("threadContainers") + .elements() + .stream() + .collect(Collectors.toMap( + c -> c.get("container").asString(), + Function.identity(), + (a, b) -> { throw new RuntimeException("Duplicate container"); }, + LinkedHashMap::new + )); // find root and create tree of thread containers ThreadContainer root = null; Map map = new HashMap<>(); for (String name : containerObjs.keySet()) { JSONValue containerObj = containerObjs.get(name); - String parentName = containerObj.get("parent").asString(); - if (parentName == null) { + JSONValue parentObj = containerObj.get("parent"); + if (parentObj instanceof JSONValue.JSONNull) { + if (root != null) { + throw new RuntimeException("More than one root container"); + } root = new ThreadContainer(name, null, containerObj); map.put(name, root); } else { - var parent = map.get(parentName); + String parentName = parentObj.asString(); + ThreadContainer parent = map.get(parentName); if (parent == null) { throw new RuntimeException("Thread container " + name + " found before " + parentName); } @@ -475,7 +471,10 @@ public final class ThreadDump { map.put(name, container); } } + if (root == null) { + throw new RuntimeException("No root container"); + } return new ThreadDump(root, map, threadDumpObj); } -} \ No newline at end of file +} From 1faee07b9546b8ba46e43093267b2b9ccbc7a1dc Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Wed, 25 Mar 2026 08:13:47 +0000 Subject: [PATCH 108/160] 8379362: C2: Buffer overrun in VectorSupport::lanetype2name Reviewed-by: liach, vlivanov, mhaessig --- src/hotspot/share/prims/vectorSupport.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/prims/vectorSupport.cpp b/src/hotspot/share/prims/vectorSupport.cpp index 58f22d38d33..7d80ed327fd 100644 --- a/src/hotspot/share/prims/vectorSupport.cpp +++ b/src/hotspot/share/prims/vectorSupport.cpp @@ -200,7 +200,6 @@ bool VectorSupport::is_unsigned_op(jint id) { } const char* VectorSupport::lanetype2name(LaneType lane_type) { - assert(lane_type >= LT_FLOAT && lane_type <= LT_LONG, ""); const char* lanetype2name[] = { "float", "double", @@ -209,7 +208,11 @@ const char* VectorSupport::lanetype2name(LaneType lane_type) { "int", "long" }; - return lanetype2name[lane_type]; + if (lane_type >= LT_FLOAT && lane_type <= LT_LONG) { + return lanetype2name[lane_type]; + } + assert(false, "unknown lane type: %d", (int)lane_type); + return "illegal"; } int VectorSupport::vop2ideal(jint id, LaneType lt) { From 7d24a096b4cb3fa73e77370e82d24caeada9003b Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Wed, 25 Mar 2026 10:21:07 +0000 Subject: [PATCH 109/160] 8380824: java/net/DatagramSocket/SendReceiveMaxSize.java could also test the loopback interface Reviewed-by: jpai --- .../DatagramSocket/SendReceiveMaxSize.java | 65 ++++++++++++++++++- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java b/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java index ded087d35e8..86936ae41d0 100644 --- a/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java +++ b/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java @@ -22,7 +22,7 @@ */ /* - * @test + * @test id=default * @bug 8242885 8250886 8240901 * @key randomness * @summary This test verifies that on macOS, the send buffer size is configured @@ -34,13 +34,62 @@ * @library /test/lib * @build jdk.test.lib.net.IPSupport * @run testng/othervm SendReceiveMaxSize + */ +/* + * @test id=preferIPv4Stack + * @key randomness + * @summary Check that it is possible to send and receive datagrams of + * maximum size on macOS, using an IPv4 only socket. + * @library /test/lib + * @build jdk.test.lib.net.IPSupport * @run testng/othervm -Djava.net.preferIPv4Stack=true SendReceiveMaxSize + */ +/* + * @test id=preferIPv6Addresses + * @key randomness + * @summary Check that it is possible to send and receive datagrams of + * maximum size on macOS, using a dual socket and prefering + * IPv6 addresses. + * @library /test/lib + * @build jdk.test.lib.net.IPSupport * @run testng/othervm -Djava.net.preferIPv6Addresses=true SendReceiveMaxSize */ +/* + * @test id=preferLoopback + * @key randomness + * @summary Check that it is possible to send and receive datagrams of + * maximum size on macOS, using a dual socket and the loopback + * interface. + * @library /test/lib + * @build jdk.test.lib.net.IPSupport + * @run testng/othervm -Dtest.preferLoopback=true SendReceiveMaxSize + */ +/* + * @test id=preferIPv6Loopback + * @key randomness + * @summary Check that it is possible to send and receive datagrams of + * maximum size on macOS, using a dual socket and the loopback + * interface. + * @library /test/lib + * @build jdk.test.lib.net.IPSupport + * @run testng/othervm -Dtest.preferLoopback=true -Djava.net.preferIPv6Addresses=true SendReceiveMaxSize + */ +/* + * @test id=preferIPv4Loopback + * @key randomness + * @summary Check that it is possible to send and receive datagrams of + * maximum size on macOS, using an IPv4 only socket and the + * loopback interface + * @library /test/lib + * @build jdk.test.lib.net.IPSupport + * @run testng/othervm -Dtest.preferLoopback=true -Djava.net.preferIPv4Stack=true SendReceiveMaxSize + */ import jdk.test.lib.RandomFactory; import jdk.test.lib.Platform; import jdk.test.lib.net.IPSupport; +import jtreg.SkippedException; +import org.testng.SkipException; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -61,6 +110,9 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.expectThrows; public class SendReceiveMaxSize { + + private final static boolean PREFER_LOOPBACK = Boolean.getBoolean("test.preferLoopback"); + private int BUF_LIMIT; private InetAddress HOST_ADDR; private final static int IPV4_SNDBUF = 65507; @@ -75,8 +127,15 @@ public class SendReceiveMaxSize { @BeforeTest public void setUp() throws IOException { - IPSupport.throwSkippedExceptionIfNonOperational(); - HOST_ADDR = InetAddress.getLocalHost(); + try { + // This method throws jtreg.SkippedException, which is + // interpreted as a test failure by testng + IPSupport.throwSkippedExceptionIfNonOperational(); + } catch (SkippedException skip) { + // throws the appropriate TestNG SkipException + throw new SkipException(skip.getMessage(), skip); + } + HOST_ADDR = PREFER_LOOPBACK ? InetAddress.getLoopbackAddress() : InetAddress.getLocalHost(); BUF_LIMIT = (HOST_ADDR instanceof Inet6Address) ? IPV6_SNDBUF : IPV4_SNDBUF; System.out.printf("Host address: %s, Buffer limit: %d%n", HOST_ADDR, BUF_LIMIT); } From 3737cad6d9fc32ccaee5373b1d84d730f6ef42f9 Mon Sep 17 00:00:00 2001 From: Evgeny Astigeevich Date: Wed, 25 Mar 2026 12:46:25 +0000 Subject: [PATCH 110/160] 8370947: Mitigate Neoverse-N1 erratum 1542419 negative impact on GCs and JIT performance Co-authored-by: Axel Boldt-Christmas Reviewed-by: shade, eosterlund, aph, aboldtch --- .../gc/z/zBarrierSetAssembler_aarch64.cpp | 4 +- src/hotspot/cpu/aarch64/globals_aarch64.hpp | 2 + src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp | 7 +- .../cpu/aarch64/vm_version_aarch64.cpp | 45 ++ .../cpu/aarch64/vm_version_aarch64.hpp | 5 + .../linux_aarch64/icache_linux_aarch64.cpp | 28 ++ .../linux_aarch64/icache_linux_aarch64.hpp | 101 ++++- .../vm_version_linux_aarch64.cpp | 2 + src/hotspot/share/asm/codeBuffer.cpp | 3 - src/hotspot/share/code/codeBlob.cpp | 6 + src/hotspot/share/code/nmethod.cpp | 32 +- src/hotspot/share/code/nmethod.hpp | 7 +- src/hotspot/share/code/relocInfo.cpp | 9 - src/hotspot/share/code/relocInfo.hpp | 2 - src/hotspot/share/gc/z/zBarrierSetNMethod.cpp | 14 +- src/hotspot/share/gc/z/zGeneration.cpp | 14 +- src/hotspot/share/gc/z/zMark.cpp | 31 +- src/hotspot/share/gc/z/zNMethod.cpp | 16 +- src/hotspot/share/gc/z/zNMethod.hpp | 2 + src/hotspot/share/runtime/globals.hpp | 2 + src/hotspot/share/runtime/icache.hpp | 23 + ...tDeferredICacheInvalidationCmdOptions.java | 426 ++++++++++++++++++ .../gc/TestDeferredICacheInvalidation.java | 306 +++++++++++++ .../bench/vm/gc/GCPatchingNmethodCost.java | 206 +++++++++ 24 files changed, 1244 insertions(+), 49 deletions(-) create mode 100644 src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp create mode 100644 test/hotspot/jtreg/compiler/runtime/TestDeferredICacheInvalidationCmdOptions.java create mode 100644 test/hotspot/jtreg/gc/TestDeferredICacheInvalidation.java create mode 100644 test/micro/org/openjdk/bench/vm/gc/GCPatchingNmethodCost.java diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index 4f0977a414f..f0885fee93d 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -879,7 +879,9 @@ void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format) { ShouldNotReachHere(); } - ICache::invalidate_word((address)patch_addr); + if (!UseSingleICacheInvalidation) { + ICache::invalidate_word((address)patch_addr); + } } #ifdef COMPILER1 diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp index e6de2c798b1..ba29646a828 100644 --- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp @@ -127,6 +127,8 @@ define_pd_global(intx, InlineSmallCode, 1000); "Branch Protection to use: none, standard, pac-ret") \ product(bool, AlwaysMergeDMB, true, DIAGNOSTIC, \ "Always merge DMB instructions in code emission") \ + product(bool, NeoverseN1ICacheErratumMitigation, false, DIAGNOSTIC, \ + "Enable workaround for Neoverse N1 erratum 1542419") \ // end of ARCH_FLAGS diff --git a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp index dbec2d76d4f..f1b9fb213a2 100644 --- a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp @@ -54,7 +54,12 @@ void Relocation::pd_set_data_value(address x, bool verify_only) { bytes = MacroAssembler::pd_patch_instruction_size(addr(), x); break; } - ICache::invalidate_range(addr(), bytes); + + if (UseSingleICacheInvalidation) { + assert(_binding != nullptr, "expect to be called with RelocIterator in use"); + } else { + ICache::invalidate_range(addr(), bytes); + } } address Relocation::pd_call_destination(address orig_addr) { diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 4423d9c5b58..0a40cd705cd 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -52,6 +52,9 @@ uintptr_t VM_Version::_pac_mask; SpinWait VM_Version::_spin_wait; +bool VM_Version::_cache_dic_enabled; +bool VM_Version::_cache_idc_enabled; + const char* VM_Version::_features_names[MAX_CPU_FEATURES] = { nullptr }; static SpinWait get_spin_wait_desc() { @@ -63,6 +66,19 @@ static SpinWait get_spin_wait_desc() { return spin_wait; } +static bool has_neoverse_n1_errata_1542419() { + const int major_rev_num = VM_Version::cpu_variant(); + const int minor_rev_num = VM_Version::cpu_revision(); + // Neoverse N1: 0xd0c + // Erratum 1542419 affects r3p0, r3p1 and r4p0. + // It is fixed in r4p1 and later revisions, which are not affected. + return (VM_Version::cpu_family() == VM_Version::CPU_ARM && + VM_Version::model_is(0xd0c) && + ((major_rev_num == 3 && minor_rev_num == 0) || + (major_rev_num == 3 && minor_rev_num == 1) || + (major_rev_num == 4 && minor_rev_num == 0))); +} + void VM_Version::initialize() { #define SET_CPU_FEATURE_NAME(id, name, bit) \ _features_names[bit] = XSTR(name); @@ -74,6 +90,9 @@ void VM_Version::initialize() { _supports_atomic_getset8 = true; _supports_atomic_getadd8 = true; + _cache_dic_enabled = false; + _cache_idc_enabled = false; + get_os_cpu_info(); int dcache_line = VM_Version::dcache_line_size(); @@ -661,6 +680,32 @@ void VM_Version::initialize() { clear_feature(CPU_SVE); } + if (FLAG_IS_DEFAULT(UseSingleICacheInvalidation) && is_cache_idc_enabled() && is_cache_dic_enabled()) { + FLAG_SET_DEFAULT(UseSingleICacheInvalidation, true); + } + + if (FLAG_IS_DEFAULT(NeoverseN1ICacheErratumMitigation) && has_neoverse_n1_errata_1542419()) { + FLAG_SET_DEFAULT(NeoverseN1ICacheErratumMitigation, true); + } + + if (NeoverseN1ICacheErratumMitigation) { + if (!has_neoverse_n1_errata_1542419()) { + vm_exit_during_initialization("NeoverseN1ICacheErratumMitigation is set for the CPU not having Neoverse N1 errata 1542419"); + } + if (FLAG_IS_DEFAULT(UseSingleICacheInvalidation)) { + FLAG_SET_DEFAULT(UseSingleICacheInvalidation, true); + } + + if (!UseSingleICacheInvalidation) { + vm_exit_during_initialization("NeoverseN1ICacheErratumMitigation is set but UseSingleICacheInvalidation is not enabled"); + } + } + + if (UseSingleICacheInvalidation + && (!is_cache_idc_enabled() || (!is_cache_dic_enabled() && !NeoverseN1ICacheErratumMitigation))) { + vm_exit_during_initialization("UseSingleICacheInvalidation is set but neither IDC nor DIC nor NeoverseN1ICacheErratumMitigation is enabled"); + } + // Construct the "features" string stringStream ss(512); ss.print("0x%02x:0x%x:0x%03x:%d", _cpu, _variant, _model, _revision); diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index e8681611234..6145990d0d6 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp @@ -58,6 +58,8 @@ protected: // When _prefer_sve_merging_mode_cpy is true, `cpy (imm, zeroing)` is // implemented as `movi; cpy(imm, merging)`. static constexpr bool _prefer_sve_merging_mode_cpy = true; + static bool _cache_dic_enabled; + static bool _cache_idc_enabled; static SpinWait _spin_wait; @@ -253,6 +255,9 @@ public: return vector_length_in_bytes <= 16; } + static bool is_cache_dic_enabled() { return _cache_dic_enabled; } + static bool is_cache_idc_enabled() { return _cache_idc_enabled; } + static void get_cpu_features_name(void* features_buffer, stringStream& ss); // Returns names of features present in features_set1 but not in features_set2 diff --git a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp new file mode 100644 index 00000000000..41cad5af325 --- /dev/null +++ b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp @@ -0,0 +1,28 @@ +/* + * 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. + * + */ + +#include "runtime/icache.hpp" +#include "utilities/globalDefinitions.hpp" + +NOT_PRODUCT(THREAD_LOCAL AArch64ICacheInvalidationContext* AArch64ICacheInvalidationContext::_current_context = nullptr;) diff --git a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp index 8fbaa7a6b6e..b410aebed7b 100644 --- a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp @@ -26,6 +26,10 @@ #ifndef OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP #define OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP +#include "memory/allocation.hpp" +#include "runtime/vm_version.hpp" +#include "utilities/globalDefinitions.hpp" + // Interface for updating the instruction cache. Whenever the VM // modifies code, part of the processor instruction cache potentially // has to be flushed. @@ -37,8 +41,103 @@ class ICache : public AbstractICache { __builtin___clear_cache((char *)addr, (char *)(addr + 4)); } static void invalidate_range(address start, int nbytes) { - __builtin___clear_cache((char *)start, (char *)(start + nbytes)); + if (NeoverseN1ICacheErratumMitigation) { + assert(VM_Version::is_cache_idc_enabled(), + "Expect CTR_EL0.IDC to be enabled for Neoverse N1 with erratum " + "1542419"); + assert(!VM_Version::is_cache_dic_enabled(), + "Expect CTR_EL0.DIC to be disabled for Neoverse N1 with erratum " + "1542419"); + asm volatile("dsb ish \n" + "ic ivau, xzr \n" + "dsb ish \n" + "isb \n" + : : : "memory"); + } else { + __builtin___clear_cache((char *)start, (char *)(start + nbytes)); + } } }; +class AArch64ICacheInvalidationContext : StackObj { + private: + +#ifdef ASSERT + static THREAD_LOCAL AArch64ICacheInvalidationContext* _current_context; +#endif + + bool _has_modified_code; + + public: + NONCOPYABLE(AArch64ICacheInvalidationContext); + + AArch64ICacheInvalidationContext() + : _has_modified_code(false) { + assert(_current_context == nullptr, "nested ICacheInvalidationContext not supported"); +#ifdef ASSERT + _current_context = this; +#endif + } + + ~AArch64ICacheInvalidationContext() { + NOT_PRODUCT(_current_context = nullptr); + + if (!_has_modified_code || !UseSingleICacheInvalidation) { + return; + } + + assert(VM_Version::is_cache_idc_enabled(), "Expect CTR_EL0.IDC to be enabled"); + + asm volatile("dsb ish" : : : "memory"); + + if (NeoverseN1ICacheErratumMitigation) { + assert(!VM_Version::is_cache_dic_enabled(), + "Expect CTR_EL0.DIC to be disabled for Neoverse N1 with erratum " + "1542419"); + + // Errata 1542419: Neoverse N1 cores with the 'COHERENT_ICACHE' feature + // may fetch stale instructions when software depends on + // prefetch-speculation-protection instead of explicit synchronization. + // + // Neoverse-N1 implementation mitigates the errata 1542419 with a + // workaround: + // - Disable coherent icache. + // - Trap IC IVAU instructions. + // - Execute: + // - tlbi vae3is, xzr + // - dsb sy + // - Ignore trapped IC IVAU instructions. + // + // `tlbi vae3is, xzr` invalidates all translation entries (all VAs, all + // possible levels). It waits for all memory accesses using in-scope old + // translation information to complete before it is considered complete. + // + // As this workaround has significant overhead, Arm Neoverse N1 (MP050) + // Software Developer Errata Notice version 29.0 suggests: + // + // "Since one TLB inner-shareable invalidation is enough to avoid this + // erratum, the number of injected TLB invalidations should be minimized + // in the trap handler to mitigate the performance impact due to this + // workaround." + // As the address for icache invalidation is not relevant and + // IC IVAU instruction is ignored, we use XZR in it. + asm volatile( + "ic ivau, xzr \n" + "dsb ish \n" + : + : + : "memory"); + } else { + assert(VM_Version::is_cache_dic_enabled(), "Expect CTR_EL0.DIC to be enabled"); + } + asm volatile("isb" : : : "memory"); + } + + void set_has_modified_code() { + _has_modified_code = true; + } +}; + +#define PD_ICACHE_INVALIDATION_CONTEXT AArch64ICacheInvalidationContext + #endif // OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP diff --git a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp index 1fe06dc640d..dad7161a3cb 100644 --- a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp @@ -169,6 +169,8 @@ void VM_Version::get_os_cpu_info() { _icache_line_size = (1 << (ctr_el0 & 0x0f)) * 4; _dcache_line_size = (1 << ((ctr_el0 >> 16) & 0x0f)) * 4; + _cache_idc_enabled = ((ctr_el0 >> 28) & 0x1) != 0; + _cache_dic_enabled = ((ctr_el0 >> 29) & 0x1) != 0; if (!(dczid_el0 & 0x10)) { _zva_length = 4 << (dczid_el0 & 0xf); diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index ba525588f32..964aec8b501 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -745,9 +745,6 @@ void CodeBuffer::copy_code_to(CodeBlob* dest_blob) { // Done moving code bytes; were they the right size? assert((int)align_up(dest.total_content_size(), oopSize) == dest_blob->content_size(), "sanity"); - - // Flush generated code - ICache::invalidate_range(dest_blob->code_begin(), dest_blob->code_size()); } // Move all my code into another code buffer. Consult applicable diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index fcc0b42a461..d07386bd795 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -331,7 +331,13 @@ RuntimeBlob::RuntimeBlob( : CodeBlob(name, kind, cb, size, header_size, frame_complete, frame_size, oop_maps, caller_must_gc_arguments, align_up(cb->total_relocation_size(), oopSize)) { + if (code_size() == 0) { + // Nothing to copy + return; + } + cb->copy_code_and_locs_to(this); + ICache::invalidate_range(code_begin(), code_size()); } void RuntimeBlob::free(RuntimeBlob* blob) { diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 5a6ed8ab3ed..5cb795a4723 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -1328,6 +1328,7 @@ nmethod::nmethod( code_buffer->copy_values_to(this); post_init(); + ICache::invalidate_range(code_begin(), code_size()); } if (PrintNativeNMethods || PrintDebugInfo || PrintRelocations || PrintDependencies) { @@ -1809,6 +1810,7 @@ nmethod::nmethod( init_immutable_data_ref_count(); post_init(); + ICache::invalidate_range(code_begin(), code_size()); // we use the information of entry points to find out if a method is // static or non static @@ -2036,7 +2038,7 @@ void nmethod::copy_values(GrowableArray* array) { // The code and relocations have already been initialized by the // CodeBlob constructor, so it is valid even at this early point to // iterate over relocations and patch the code. - fix_oop_relocations(nullptr, nullptr, /*initialize_immediates=*/ true); + fix_oop_relocations(/*initialize_immediates=*/ true); } void nmethod::copy_values(GrowableArray* array) { @@ -2048,24 +2050,42 @@ void nmethod::copy_values(GrowableArray* array) { } } -void nmethod::fix_oop_relocations(address begin, address end, bool initialize_immediates) { +bool nmethod::fix_oop_relocations(bool initialize_immediates) { // re-patch all oop-bearing instructions, just in case some oops moved - RelocIterator iter(this, begin, end); + RelocIterator iter(this); + bool modified_code = false; while (iter.next()) { if (iter.type() == relocInfo::oop_type) { oop_Relocation* reloc = iter.oop_reloc(); - if (initialize_immediates && reloc->oop_is_immediate()) { + if (!reloc->oop_is_immediate()) { + // Refresh the oop-related bits of this instruction. + reloc->set_value(reloc->value()); + modified_code = true; + } else if (initialize_immediates) { oop* dest = reloc->oop_addr(); jobject obj = *reinterpret_cast(dest); initialize_immediate_oop(dest, obj); } - // Refresh the oop-related bits of this instruction. - reloc->fix_oop_relocation(); } else if (iter.type() == relocInfo::metadata_type) { metadata_Relocation* reloc = iter.metadata_reloc(); reloc->fix_metadata_relocation(); + modified_code |= !reloc->metadata_is_immediate(); } } + return modified_code; +} + +void nmethod::fix_oop_relocations() { + ICacheInvalidationContext icic; + fix_oop_relocations(&icic); +} + +void nmethod::fix_oop_relocations(ICacheInvalidationContext* icic) { + assert(icic != nullptr, "must provide context to track if code was modified"); + bool modified_code = fix_oop_relocations(/*initialize_immediates=*/ false); + if (modified_code) { + icic->set_has_modified_code(); + } } static void install_post_call_nop_displacement(nmethod* nm, address pc) { diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index 092da181f12..ea8c0e2ad5d 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -41,6 +41,7 @@ class Dependencies; class DirectiveSet; class DebugInformationRecorder; class ExceptionHandlerTable; +class ICacheInvalidationContext; class ImplicitExceptionTable; class JvmtiThreadState; class MetadataClosure; @@ -801,15 +802,15 @@ public: // Relocation support private: - void fix_oop_relocations(address begin, address end, bool initialize_immediates); + bool fix_oop_relocations(bool initialize_immediates); inline void initialize_immediate_oop(oop* dest, jobject handle); protected: address oops_reloc_begin() const; public: - void fix_oop_relocations(address begin, address end) { fix_oop_relocations(begin, end, false); } - void fix_oop_relocations() { fix_oop_relocations(nullptr, nullptr, false); } + void fix_oop_relocations(ICacheInvalidationContext* icic); + void fix_oop_relocations(); bool is_at_poll_return(address pc); bool is_at_poll_or_poll_return(address pc); diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp index 2a6335e2118..25d91edc20f 100644 --- a/src/hotspot/share/code/relocInfo.cpp +++ b/src/hotspot/share/code/relocInfo.cpp @@ -590,15 +590,6 @@ oop oop_Relocation::oop_value() { return *oop_addr(); } - -void oop_Relocation::fix_oop_relocation() { - if (!oop_is_immediate()) { - // get the oop from the pool, and re-insert it into the instruction: - set_value(value()); - } -} - - void oop_Relocation::verify_oop_relocation() { if (!oop_is_immediate()) { // get the oop from the pool, and re-insert it into the instruction: diff --git a/src/hotspot/share/code/relocInfo.hpp b/src/hotspot/share/code/relocInfo.hpp index 6f1778ef479..bb2b2b5693f 100644 --- a/src/hotspot/share/code/relocInfo.hpp +++ b/src/hotspot/share/code/relocInfo.hpp @@ -988,8 +988,6 @@ class oop_Relocation : public DataRelocation { void pack_data_to(CodeSection* dest) override; void unpack_data() override; - void fix_oop_relocation(); // reasserts oop value - void verify_oop_relocation(); address value() override { return *reinterpret_cast(oop_addr()); } diff --git a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp index d80ce4e149d..a439b3a167b 100644 --- a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp @@ -33,6 +33,7 @@ #include "gc/z/zThreadLocalData.hpp" #include "gc/z/zUncoloredRoot.inline.hpp" #include "logging/log.hpp" +#include "runtime/icache.hpp" #include "runtime/threadWXSetters.inline.hpp" bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { @@ -70,12 +71,15 @@ bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { return false; } - // Heal barriers - ZNMethod::nmethod_patch_barriers(nm); + { + ICacheInvalidationContext icic; + // Heal barriers + ZNMethod::nmethod_patch_barriers(nm, &icic); - // Heal oops - ZUncoloredRootProcessWeakOopClosure cl(ZNMethod::color(nm)); - ZNMethod::nmethod_oops_do_inner(nm, &cl); + // Heal oops + ZUncoloredRootProcessWeakOopClosure cl(ZNMethod::color(nm)); + ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); + } const uintptr_t prev_color = ZNMethod::color(nm); const uintptr_t new_color = *ZPointerStoreGoodMaskLowOrderBitsAddr; diff --git a/src/hotspot/share/gc/z/zGeneration.cpp b/src/hotspot/share/gc/z/zGeneration.cpp index 27f352a624f..0f9f4e34a5e 100644 --- a/src/hotspot/share/gc/z/zGeneration.cpp +++ b/src/hotspot/share/gc/z/zGeneration.cpp @@ -58,6 +58,7 @@ #include "prims/jvmtiTagMap.hpp" #include "runtime/continuation.hpp" #include "runtime/handshake.hpp" +#include "runtime/icache.hpp" #include "runtime/safepoint.hpp" #include "runtime/threads.hpp" #include "runtime/vmOperations.hpp" @@ -1434,12 +1435,15 @@ public: virtual void do_nmethod(nmethod* nm) { ZLocker locker(ZNMethod::lock_for_nmethod(nm)); if (_bs_nm->is_armed(nm)) { - // Heal barriers - ZNMethod::nmethod_patch_barriers(nm); + { + ICacheInvalidationContext icic; + // Heal barriers + ZNMethod::nmethod_patch_barriers(nm, &icic); - // Heal oops - ZUncoloredRootProcessOopClosure cl(ZNMethod::color(nm)); - ZNMethod::nmethod_oops_do_inner(nm, &cl); + // Heal oops + ZUncoloredRootProcessOopClosure cl(ZNMethod::color(nm)); + ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); + } log_trace(gc, nmethod)("nmethod: " PTR_FORMAT " visited by old remapping", p2i(nm)); diff --git a/src/hotspot/share/gc/z/zMark.cpp b/src/hotspot/share/gc/z/zMark.cpp index 03701ae9998..ac7d86db240 100644 --- a/src/hotspot/share/gc/z/zMark.cpp +++ b/src/hotspot/share/gc/z/zMark.cpp @@ -59,6 +59,7 @@ #include "oops/oop.inline.hpp" #include "runtime/continuation.hpp" #include "runtime/handshake.hpp" +#include "runtime/icache.hpp" #include "runtime/javaThread.hpp" #include "runtime/prefetch.inline.hpp" #include "runtime/safepointMechanism.hpp" @@ -718,12 +719,15 @@ public: virtual void do_nmethod(nmethod* nm) { ZLocker locker(ZNMethod::lock_for_nmethod(nm)); if (_bs_nm->is_armed(nm)) { - // Heal barriers - ZNMethod::nmethod_patch_barriers(nm); + { + ICacheInvalidationContext icic; + // Heal barriers + ZNMethod::nmethod_patch_barriers(nm, &icic); - // Heal oops - ZUncoloredRootMarkOopClosure cl(ZNMethod::color(nm)); - ZNMethod::nmethod_oops_do_inner(nm, &cl); + // Heal oops + ZUncoloredRootMarkOopClosure cl(ZNMethod::color(nm)); + ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); + } // CodeCache unloading support nm->mark_as_maybe_on_stack(); @@ -753,10 +757,6 @@ public: if (_bs_nm->is_armed(nm)) { const uintptr_t prev_color = ZNMethod::color(nm); - // Heal oops - ZUncoloredRootMarkYoungOopClosure cl(prev_color); - ZNMethod::nmethod_oops_do_inner(nm, &cl); - // Disarm only the young marking, not any potential old marking cycle const uintptr_t old_marked_mask = ZPointerMarkedMask ^ (ZPointerMarkedYoung0 | ZPointerMarkedYoung1); @@ -767,9 +767,16 @@ public: // Check if disarming for young mark, completely disarms the nmethod entry barrier const bool complete_disarm = ZPointer::is_store_good(new_disarm_value_ptr); - if (complete_disarm) { - // We are about to completely disarm the nmethod, must take responsibility to patch all barriers before disarming - ZNMethod::nmethod_patch_barriers(nm); + { + ICacheInvalidationContext icic; + if (complete_disarm) { + // We are about to completely disarm the nmethod, must take responsibility to patch all barriers before disarming + ZNMethod::nmethod_patch_barriers(nm, &icic); + } + + // Heal oops + ZUncoloredRootMarkYoungOopClosure cl(prev_color); + ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); } _bs_nm->guard_with(nm, (int)untype(new_disarm_value_ptr)); diff --git a/src/hotspot/share/gc/z/zNMethod.cpp b/src/hotspot/share/gc/z/zNMethod.cpp index 780bc9e3bf7..a1348b63b6f 100644 --- a/src/hotspot/share/gc/z/zNMethod.cpp +++ b/src/hotspot/share/gc/z/zNMethod.cpp @@ -50,6 +50,7 @@ #include "oops/oop.inline.hpp" #include "runtime/atomicAccess.hpp" #include "runtime/continuation.hpp" +#include "runtime/icache.hpp" #include "utilities/debug.hpp" static ZNMethodData* gc_data(const nmethod* nm) { @@ -245,8 +246,16 @@ void ZNMethod::set_guard_value(nmethod* nm, int value) { } void ZNMethod::nmethod_patch_barriers(nmethod* nm) { + ICacheInvalidationContext icic; + nmethod_patch_barriers(nm, &icic); +} + +void ZNMethod::nmethod_patch_barriers(nmethod* nm, ICacheInvalidationContext* icic) { ZBarrierSetAssembler* const bs_asm = ZBarrierSet::assembler(); ZArrayIterator iter(gc_data(nm)->barriers()); + if (gc_data(nm)->barriers()->is_nonempty()) { + icic->set_has_modified_code(); + } for (ZNMethodDataBarrier barrier; iter.next(&barrier);) { bs_asm->patch_barrier_relocation(barrier._reloc_addr, barrier._reloc_format); } @@ -258,6 +267,11 @@ void ZNMethod::nmethod_oops_do(nmethod* nm, OopClosure* cl) { } void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl) { + ICacheInvalidationContext icic; + nmethod_oops_do_inner(nm, cl, &icic); +} + +void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl, ICacheInvalidationContext* icic) { // Process oops table { oop* const begin = nm->oops_begin(); @@ -283,7 +297,7 @@ void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl) { // Process non-immediate oops if (data->has_non_immediate_oops()) { - nm->fix_oop_relocations(); + nm->fix_oop_relocations(icic); } } diff --git a/src/hotspot/share/gc/z/zNMethod.hpp b/src/hotspot/share/gc/z/zNMethod.hpp index 865ea11e7b9..2779151c576 100644 --- a/src/hotspot/share/gc/z/zNMethod.hpp +++ b/src/hotspot/share/gc/z/zNMethod.hpp @@ -56,9 +56,11 @@ public: static void set_guard_value(nmethod* nm, int value); static void nmethod_patch_barriers(nmethod* nm); + static void nmethod_patch_barriers(nmethod* nm, ICacheInvalidationContext* icic); static void nmethod_oops_do(nmethod* nm, OopClosure* cl); static void nmethod_oops_do_inner(nmethod* nm, OopClosure* cl); + static void nmethod_oops_do_inner(nmethod* nm, OopClosure* cl, ICacheInvalidationContext* icic); static void nmethods_do_begin(bool secondary); static void nmethods_do_end(bool secondary); diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 60feddde09b..296c5dedf8a 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -1978,6 +1978,8 @@ const int ObjectAlignmentInBytes = 8; develop(uint, BinarySearchThreshold, 16, \ "Minimal number of elements in a sorted collection to prefer" \ "binary search over simple linear search." ) \ + product(bool, UseSingleICacheInvalidation, false, DIAGNOSTIC, \ + "Defer multiple ICache invalidation to single invalidation") \ \ // end of RUNTIME_FLAGS diff --git a/src/hotspot/share/runtime/icache.hpp b/src/hotspot/share/runtime/icache.hpp index bc153862323..692a876d9a6 100644 --- a/src/hotspot/share/runtime/icache.hpp +++ b/src/hotspot/share/runtime/icache.hpp @@ -129,4 +129,27 @@ class ICacheStubGenerator : public StubCodeGenerator { void generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub); }; +class DefaultICacheInvalidationContext : StackObj { + public: + NONCOPYABLE(DefaultICacheInvalidationContext); + + DefaultICacheInvalidationContext() {} + + ~DefaultICacheInvalidationContext() {} + + void set_has_modified_code() {} +}; + +#ifndef PD_ICACHE_INVALIDATION_CONTEXT +#define PD_ICACHE_INVALIDATION_CONTEXT DefaultICacheInvalidationContext +#endif // PD_ICACHE_INVALIDATION_CONTEXT + +class ICacheInvalidationContext final : public PD_ICACHE_INVALIDATION_CONTEXT { + private: + NONCOPYABLE(ICacheInvalidationContext); + + public: + using PD_ICACHE_INVALIDATION_CONTEXT::PD_ICACHE_INVALIDATION_CONTEXT; +}; + #endif // SHARE_RUNTIME_ICACHE_HPP diff --git a/test/hotspot/jtreg/compiler/runtime/TestDeferredICacheInvalidationCmdOptions.java b/test/hotspot/jtreg/compiler/runtime/TestDeferredICacheInvalidationCmdOptions.java new file mode 100644 index 00000000000..8bc74be5c62 --- /dev/null +++ b/test/hotspot/jtreg/compiler/runtime/TestDeferredICacheInvalidationCmdOptions.java @@ -0,0 +1,426 @@ +/* + * 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. + * + */ + +package compiler.runtime; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; +import jtreg.SkippedException; + +/* + * @test + * @bug 8370947 + * @summary Test command-line options for UseSingleICacheInvalidation and NeoverseN1ICacheErratumMitigation + * @library /test/lib + * @requires os.arch == "aarch64" + * @requires os.family == "linux" + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.runtime.TestDeferredICacheInvalidationCmdOptions + */ + +public class TestDeferredICacheInvalidationCmdOptions { + + // CPU identifiers + private static final int CPU_ARM = 0x41; + private static final int NEOVERSE_N1_MODEL = 0xd0c; + + private static boolean isAffected; + + public static void main(String[] args) throws Exception { + // Parse CPU features and print CPU info + parseCPUFeatures(); + + System.out.println("Testing UseSingleICacheInvalidation and NeoverseN1ICacheErratumMitigation command-line options..."); + + // Test case 1: Check defaults on Neoverse N1 pre-r4p1 (if applicable) + testCase1_DefaultsOnNeoverseN1(); + + // Test case 2: Check NeoverseN1ICacheErratumMitigation is false on unaffected CPUs + testCase2_DefaultsOnUnaffectedCPUs(); + + // Test case 3: Check if NeoverseN1ICacheErratumMitigation is set to false on affected CPUs, + // UseSingleICacheInvalidation is also set to false + testCase3_ExplicitlyDisableErrataAffectsDeferred(); + + // Test case 4: Check JVM error if UseSingleICacheInvalidation=true + // but NeoverseN1ICacheErratumMitigation=false on affected CPUs + testCase4_ConflictingFlagsOnAffectedCPUs(); + + // Test case 5: Check explicit NeoverseN1ICacheErratumMitigation=true enables UseSingleICacheInvalidation + testCase5_ExplicitlyEnableErrataEnablesDeferred(); + + // Test case 6: Check both flags can be explicitly set to false + testCase6_ExplicitlyDisableBothFlags(); + + // Test case 7: Check UseSingleICacheInvalidation=false with NeoverseN1ICacheErratumMitigation=true + testCase7_ConflictingErrataWithoutDeferred(); + + // Test case 8: Check setting NeoverseN1ICacheErratumMitigation=true on unaffected CPU causes an error + testCase8_EnablingErrataOnUnaffectedCPU(); + + System.out.println("All test cases passed!"); + } + + /** + * Parse CPU features string from WhiteBox.getCPUFeatures() to extract: + * - cpuFamily: 0x41 for ARM + * - cpuVariant: major revision + * - cpuModel: e.g., 0xd0c for Neoverse N1 + * - cpuRevision: minor revision + * - cpuModel2: secondary model (if present, in parentheses) + * + * Sets the static field isAffected and prints CPU info. + * + * Format: 0x%02x:0x%x:0x%03x:%d[(0x%03x)] + * Example: "0x41:0x3:0xd0c:0" or "0x41:0x3:0xd0c:0(0xd0c)" + * + * @throws SkippedException if not running on ARM CPU + */ + private static void parseCPUFeatures() { + WhiteBox wb = WhiteBox.getWhiteBox(); + String cpuFeatures = wb.getCPUFeatures(); + System.out.println("CPU Features string: " + cpuFeatures); + + if (cpuFeatures == null || cpuFeatures.isEmpty()) { + throw new RuntimeException("No CPU features available"); + } + + int commaIndex = cpuFeatures.indexOf(","); + if (commaIndex == -1) { + throw new RuntimeException("Unexpected CPU features format (no comma): " + cpuFeatures); + } + + String cpuPart = cpuFeatures.substring(0, commaIndex).trim(); + + String[] parts = cpuPart.split(":"); + if (parts.length < 4) { + throw new RuntimeException("Unexpected CPU features format: " + cpuPart); + } + + int cpuFamily = Integer.parseInt(parts[0].substring(2), 16); + if (cpuFamily != CPU_ARM) { + throw new SkippedException("Not running on ARM CPU (cpuFamily=0x" + Integer.toHexString(cpuFamily) + ")"); + } + + int cpuVariant = Integer.parseInt(parts[1].substring(2), 16); + int cpuModel = Integer.parseInt(parts[2].substring(2), 16); + int cpuModel2 = 0; + + int model2Start = parts[3].indexOf("("); + String revisionStr = parts[3]; + if (model2Start != -1) { + if (!parts[3].endsWith(")")) { + throw new RuntimeException("Unexpected CPU features format (missing closing parenthesis): " + parts[3]); + } + String model2Str = parts[3].substring(model2Start + 1, parts[3].length() - 1); + cpuModel2 = Integer.parseInt(model2Str.substring(2), 16); + revisionStr = parts[3].substring(0, model2Start); + } + int cpuRevision = Integer.parseInt(revisionStr); + + // Neoverse N1 errata 1542419 affects r3p0, r3p1 and r4p0. + // It is fixed in r4p1 and later revisions. + if (cpuModel == NEOVERSE_N1_MODEL || cpuModel2 == NEOVERSE_N1_MODEL) { + isAffected = (cpuVariant == 3 && cpuRevision == 0) || + (cpuVariant == 3 && cpuRevision == 1) || + (cpuVariant == 4 && cpuRevision == 0); + } + + printCPUInfo(cpuFamily, cpuVariant, cpuModel, cpuModel2, cpuRevision); + } + + private static void printCPUInfo(int cpuFamily, int cpuVariant, int cpuModel, int cpuModel2, int cpuRevision) { + boolean isNeoverseN1 = (cpuFamily == CPU_ARM) && + (cpuModel == NEOVERSE_N1_MODEL || cpuModel2 == NEOVERSE_N1_MODEL); + System.out.println("\n=== CPU Information ==="); + System.out.println("CPU Family: 0x" + Integer.toHexString(cpuFamily)); + System.out.println("CPU Variant: 0x" + Integer.toHexString(cpuVariant)); + System.out.println("CPU Model: 0x" + Integer.toHexString(cpuModel)); + if (cpuModel2 != 0) { + System.out.println("CPU Model2: 0x" + Integer.toHexString(cpuModel2)); + } + System.out.println("CPU Revision: " + cpuRevision); + System.out.println("Is Neoverse N1: " + isNeoverseN1); + System.out.println("Is affected by errata 1542419: " + isAffected); + System.out.println("======================\n"); + } + + /** + * Test case 1: Check the UseSingleICacheInvalidation and NeoverseN1ICacheErratumMitigation + * are set to true for Neoverse N1 pre-r4p1. + */ + private static void testCase1_DefaultsOnNeoverseN1() throws Exception { + if (!isAffected) { + System.out.println("\nTest case 1: Skipping since CPU is not affected by Neoverse N1 errata 1542419"); + return; + } + + System.out.println("\nTest case 1: Check defaults on Neoverse N1 affected revisions"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+PrintFlagsFinal", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + + String output_str = output.getOutput(); + boolean hasSingleEnabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*true.*"); + boolean hasErrataEnabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*true.*"); + + System.out.println("UseSingleICacheInvalidation enabled: " + hasSingleEnabled); + System.out.println("NeoverseN1ICacheErratumMitigation enabled: " + hasErrataEnabled); + + // If running on affected Neoverse N1, both should be true + if (!hasSingleEnabled || !hasErrataEnabled) { + throw new RuntimeException("On affected Neoverse N1, both flags should be enabled by default"); + } + System.out.println("Correctly enabled on affected Neoverse N1"); + } + + /** + * Test case 2: Check NeoverseN1ICacheErratumMitigation is false on unaffected CPUs. + */ + private static void testCase2_DefaultsOnUnaffectedCPUs() throws Exception { + if (isAffected) { + System.out.println("\nTest case 2: Skipping since CPU is affected by Neoverse N1 errata 1542419"); + return; + } + + System.out.println("\nTest case 2: Check NeoverseN1ICacheErratumMitigation is false on unaffected CPUs"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+PrintFlagsFinal", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + + String output_str = output.getOutput(); + boolean hasErrataEnabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*true.*"); + + System.out.println("NeoverseN1ICacheErratumMitigation enabled: " + hasErrataEnabled); + + // On non-Neoverse N1 or unaffected Neoverse N1 CPUs, NeoverseN1ICacheErratumMitigation should be false + if (hasErrataEnabled) { + throw new RuntimeException("On unaffected CPUs, NeoverseN1ICacheErratumMitigation should be disabled"); + } + System.out.println("Correctly disabled on unaffected CPU"); + } + + /** + * Test case 3: Check if NeoverseN1ICacheErratumMitigation is set to false via cmd on affected CPUs, + * UseSingleICacheInvalidation is set to false. + */ + private static void testCase3_ExplicitlyDisableErrataAffectsDeferred() throws Exception { + if (!isAffected) { + System.out.println("\nTest case 3: Skipping since CPU is not affected by Neoverse N1 errata 1542419"); + return; + } + + System.out.println("\nTest case 3: Explicitly disable NeoverseN1ICacheErratumMitigation, check UseSingleICacheInvalidation"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:-NeoverseN1ICacheErratumMitigation", + "-XX:+PrintFlagsFinal", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + + String output_str = output.getOutput(); + boolean hasSingleDisabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*false.*"); + boolean hasErrataDisabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*false.*"); + + System.out.println("NeoverseN1ICacheErratumMitigation disabled: " + hasErrataDisabled); + System.out.println("UseSingleICacheInvalidation disabled: " + hasSingleDisabled); + + if (!hasErrataDisabled) { + throw new RuntimeException("Failed to disable NeoverseN1ICacheErratumMitigation via command line"); + } + + // On affected CPUs, disabling errata should also disable UseSingleICacheInvalidation + if (!hasSingleDisabled) { + throw new RuntimeException("On affected CPU, disabling NeoverseN1ICacheErratumMitigation should also disable UseSingleICacheInvalidation"); + } + System.out.println("Correctly synchronized on affected CPU"); + } + + /** + * Test case 4: Check JVM reports an error if UseSingleICacheInvalidation is set to true + * but NeoverseN1ICacheErratumMitigation is set to false on affected CPUs. + */ + private static void testCase4_ConflictingFlagsOnAffectedCPUs() throws Exception { + if (!isAffected) { + System.out.println("\nTest case 4: Skipping since CPU is not affected by Neoverse N1 errata 1542419"); + return; + } + + System.out.println("\nTest case 4: Try to set UseSingleICacheInvalidation=true with NeoverseN1ICacheErratumMitigation=false"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+UseSingleICacheInvalidation", + "-XX:-NeoverseN1ICacheErratumMitigation", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + if (output.getExitValue() == 0) { + throw new RuntimeException("On affected CPU, conflicting flags should cause error"); + } + output.shouldContain("Error"); + System.out.println("JVM correctly rejected conflicting flags on affected CPU"); + } + + /** + * Test case 5: Check explicit NeoverseN1ICacheErratumMitigation=true enables UseSingleICacheInvalidation. + */ + private static void testCase5_ExplicitlyEnableErrataEnablesDeferred() throws Exception { + if (!isAffected) { + System.out.println("\nTest case 5: Skipping since CPU is not affected by Neoverse N1 errata 1542419"); + return; + } + + System.out.println("\nTest case 5: Explicitly enable NeoverseN1ICacheErratumMitigation, check UseSingleICacheInvalidation"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+NeoverseN1ICacheErratumMitigation", + "-XX:+PrintFlagsFinal", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + + String output_str = output.getOutput(); + boolean hasSingleEnabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*true.*"); + boolean hasErrataEnabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*true.*"); + + System.out.println("NeoverseN1ICacheErratumMitigation enabled: " + hasErrataEnabled); + System.out.println("UseSingleICacheInvalidation enabled: " + hasSingleEnabled); + + if (!hasErrataEnabled) { + throw new RuntimeException("Failed to enable NeoverseN1ICacheErratumMitigation via command line"); + } + + if (!hasSingleEnabled) { + throw new RuntimeException("On affected CPU, enabling NeoverseN1ICacheErratumMitigation should also enable UseSingleICacheInvalidation"); + } + System.out.println("Correctly synchronized on affected CPU"); + } + + /** + * Test case 6: Check both flags can be explicitly set to false. + */ + private static void testCase6_ExplicitlyDisableBothFlags() throws Exception { + System.out.println("\nTest case 6: Explicitly disable both flags"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:-UseSingleICacheInvalidation", + "-XX:-NeoverseN1ICacheErratumMitigation", + "-XX:+PrintFlagsFinal", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + + String output_str = output.getOutput(); + boolean hasSingleDisabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*false.*"); + boolean hasErrataDisabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*false.*"); + + System.out.println("UseSingleICacheInvalidation disabled: " + hasSingleDisabled); + System.out.println("NeoverseN1ICacheErratumMitigation disabled: " + hasErrataDisabled); + + if (!hasErrataDisabled) { + throw new RuntimeException("Failed to disable NeoverseN1ICacheErratumMitigation via command line"); + } + + if (!hasSingleDisabled) { + throw new RuntimeException("Failed to disable UseSingleICacheInvalidation via command line"); + } + + System.out.println("Successfully disabled both flags"); + } + + /** + * Test case 7: Check UseSingleICacheInvalidation=false with NeoverseN1ICacheErratumMitigation=true. + * On affected CPUs, this should error (conflicting requirement). + */ + private static void testCase7_ConflictingErrataWithoutDeferred() throws Exception { + if (!isAffected) { + System.out.println("\nTest case 7: Skipping since CPU is not affected by Neoverse N1 errata 1542419"); + return; + } + System.out.println("\nTest case 7: Try to set NeoverseN1ICacheErratumMitigation=true with UseSingleICacheInvalidation=false"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:-UseSingleICacheInvalidation", + "-XX:+NeoverseN1ICacheErratumMitigation", + "-XX:+PrintFlagsFinal", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + // This should fail on affected CPUs (conflicting requirement) + if (output.getExitValue() == 0) { + throw new RuntimeException("On affected CPU, setting NeoverseN1ICacheErratumMitigation=true with UseSingleICacheInvalidation=false should cause an error"); + } + output.shouldContain("Error"); + System.out.println("JVM correctly rejected conflicting flags on affected CPU"); + } + + /** + * Test case 8: Check setting NeoverseN1ICacheErratumMitigation=true on unaffected CPU causes an error. + */ + private static void testCase8_EnablingErrataOnUnaffectedCPU() throws Exception { + if (isAffected) { + System.out.println("\nTest case 8: Skipping since CPU is affected by Neoverse N1 errata 1542419"); + return; + } + + System.out.println("\nTest case 8: Try to set NeoverseN1ICacheErratumMitigation=true on unaffected CPU"); + + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+NeoverseN1ICacheErratumMitigation", + "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + // This should fail on unaffected CPUs (errata not present) + if (output.getExitValue() == 0) { + throw new RuntimeException("On unaffected CPU, setting NeoverseN1ICacheErratumMitigation=true should cause error"); + } + output.shouldContain("Error"); + System.out.println("JVM correctly rejected enabling errata flag on unaffected CPU"); + } +} diff --git a/test/hotspot/jtreg/gc/TestDeferredICacheInvalidation.java b/test/hotspot/jtreg/gc/TestDeferredICacheInvalidation.java new file mode 100644 index 00000000000..70cd1ab6d8c --- /dev/null +++ b/test/hotspot/jtreg/gc/TestDeferredICacheInvalidation.java @@ -0,0 +1,306 @@ +/* + * 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. + * + */ + +package gc; + +/* + * @test id=parallel + * @bug 8370947 + * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for ParallelGC + * @library /test/lib + * @requires vm.debug + * @requires vm.gc.Parallel + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 + */ + +/* + * @test id=g1 + * @bug 8370947 + * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for G1GC + * @library /test/lib + * @requires vm.debug + * @requires vm.gc.G1 + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 + */ + +/* + * @test id=shenandoah + * @bug 8370947 + * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for ShenandoahGC + * @library /test/lib + * @requires vm.debug + * @requires vm.gc.Shenandoah + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 + */ + +/* + * @test id=genshen + * @bug 8370947 + * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for generational ShenandoahGC + * @library /test/lib + * @requires vm.debug + * @requires vm.gc.Shenandoah + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 + */ + +/* + * @test id=z + * @bug 8370947 + * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for ZGC + * @library /test/lib + * @requires vm.debug + * @requires vm.gc.Z + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 + */ + +/* + * Nmethods have GC barriers and OOPs embedded into their code. GCs can patch nmethod's code + * which requires icache invalidation. Doing invalidation per instruction can be expensive. + * CPU can support hardware dcache and icache coherence. This would allow to defer cache + * invalidation. + * + * There are assertions for deferred cache invalidation. This test checks that all of them + * are passed. + */ + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import jdk.test.whitebox.WhiteBox; + +public class TestDeferredICacheInvalidation { + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + public static class A { + public String s1; + public String s2; + public String s3; + public String s4; + public String s5; + public String s6; + public String s7; + public String s8; + public String s9; + } + + public static A a = new A(); + + private static int compLevel; + + public static class B { + public static void test0() { + } + + public static void test1() { + a.s1 = a.s1 + "1"; + } + + public static void test2() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + } + + public static void test3() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + } + + public static void test4() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + a.s4 = a.s4 + "4"; + } + + public static void test5() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + a.s4 = a.s4 + "4"; + a.s5 = a.s5 + "5"; + } + + public static void test6() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + a.s4 = a.s4 + "4"; + a.s5 = a.s5 + "5"; + a.s6 = a.s6 + "6"; + } + + public static void test7() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + a.s4 = a.s4 + "4"; + a.s5 = a.s5 + "5"; + a.s6 = a.s6 + "6"; + a.s7 = a.s7 + "7"; + } + + public static void test8() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + a.s4 = a.s4 + "4"; + a.s5 = a.s5 + "5"; + a.s6 = a.s6 + "6"; + a.s7 = a.s7 + "7"; + a.s8 = a.s8 + "8"; + } + + public static void test9() { + a.s1 = a.s1 + "1"; + a.s2 = a.s2 + "2"; + a.s3 = a.s3 + "3"; + a.s4 = a.s4 + "4"; + a.s5 = a.s5 + "5"; + a.s6 = a.s6 + "6"; + a.s7 = a.s7 + "7"; + a.s8 = a.s8 + "8"; + a.s9 = a.s9 + "9"; + } + } + + private static void compileMethods() throws Exception { + for (var m : B.class.getDeclaredMethods()) { + if (!m.getName().startsWith("test")) { + continue; + } + m.invoke(null); + WB.markMethodProfiled(m); + WB.enqueueMethodForCompilation(m, compLevel); + while (WB.isMethodQueuedForCompilation(m)) { + Thread.sleep(100); + } + if (WB.getMethodCompilationLevel(m) != compLevel) { + throw new IllegalStateException("Method " + m + " is not compiled at the compilation level: " + compLevel + ". Got: " + WB.getMethodCompilationLevel(m)); + } + } + } + + public static void youngGC() throws Exception { + a = null; + WB.youngGC(); + } + + public static void fullGC() throws Exception { + // Thread synchronization + final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); + final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); + final AtomicBoolean running = new AtomicBoolean(true); + + // Thread 1: GC thread that runs for 1 second with 100ms sleep intervals + Thread gcThread = new Thread(() -> { + final long startTime = System.currentTimeMillis(); + final long duration = 1000; + try { + while (System.currentTimeMillis() - startTime < duration) { + writeLock.lock(); + try { + a = new A(); + WB.fullGC(); + } finally { + writeLock.unlock(); + } + Thread.sleep(100); + } + } catch (InterruptedException e) { + // Thread interrupted, exit + } + running.set(false); + }); + + // Threads 2-11: Test threads that execute test0() through test9() + Thread[] testThreads = new Thread[10]; + for (int i = 0; i < 10; i++) { + final int testIdx = i; + testThreads[i] = new Thread(() -> { + try { + var method = B.class.getDeclaredMethod("test" + testIdx); + while (running.get()) { + readLock.lock(); + try { + method.invoke(null); + } finally { + readLock.unlock(); + } + } + } catch (Exception e) { + e.printStackTrace(); + System.exit(10); + } + }); + } + + // Start all threads + gcThread.start(); + for (Thread t : testThreads) { + t.start(); + } + + // Wait for all threads to complete + gcThread.join(); + for (Thread t : testThreads) { + t.join(); + } + } + + public static void main(String[] args) throws Exception { + if (!Boolean.TRUE.equals(WB.getBooleanVMFlag("UseSingleICacheInvalidation"))) { + System.out.println("Skip. Test requires UseSingleICacheInvalidation enabled."); + return; + } + compLevel = (args[1].equals("C1")) ? 1 : 4; + compileMethods(); + TestDeferredICacheInvalidation.class.getMethod(args[0]).invoke(null); + } +} diff --git a/test/micro/org/openjdk/bench/vm/gc/GCPatchingNmethodCost.java b/test/micro/org/openjdk/bench/vm/gc/GCPatchingNmethodCost.java new file mode 100644 index 00000000000..53fa2378ead --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/gc/GCPatchingNmethodCost.java @@ -0,0 +1,206 @@ +/* + * 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. + * + */ + +package org.openjdk.bench.vm.gc; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.CompilerControl; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import org.openjdk.bench.util.InMemoryJavaCompiler; + +import jdk.test.whitebox.WhiteBox; +import jdk.test.whitebox.code.NMethod; + +/* + * Nmethods have OOPs and GC barriers emmedded into their code. + * GCs patch them which causes invalidation of nmethods' code. + * + * This benchmark can be used to estimate the cost of patching + * OOPs and GC barriers. + * + * We create 5000 nmethods which access fields of a class. + * We measure the time of different GC cycles to see + * the impact of patching nmethods. + * + * The benchmark parameters are method count and accessed field count. + */ + +@BenchmarkMode(Mode.SingleShotTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +@Fork(value = 1, jvmArgsAppend = { + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+WhiteBoxAPI", + "-Xbootclasspath/a:lib-test/wb.jar", + "-XX:-UseCodeCacheFlushing" +}) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +public class GCPatchingNmethodCost { + + private static final int COMP_LEVEL = 1; + private static final String FIELD_USER = "FieldUser"; + + public static Fields fields; + + private static TestMethod[] methods = {}; + private static byte[] BYTE_CODE; + private static WhiteBox WB; + + @Param({"5000"}) + public int methodCount; + + @Param({"0", "2", "4", "8"}) + public int accessedFieldCount; + + public static class Fields { + public String f1; + public String f2; + public String f3; + public String f4; + public String f5; + public String f6; + public String f7; + public String f8; + public String f9; + } + + private static final class TestMethod { + private final Method method; + + public TestMethod(Method method) throws Exception { + this.method = method; + WB.testSetDontInlineMethod(method, true); + } + + public void profile() throws Exception { + method.invoke(null); + WB.markMethodProfiled(method); + } + + public void invoke() throws Exception { + method.invoke(null); + } + + public void compile() throws Exception { + WB.enqueueMethodForCompilation(method, COMP_LEVEL); + while (WB.isMethodQueuedForCompilation(method)) { + Thread.onSpinWait(); + } + if (WB.getMethodCompilationLevel(method) != COMP_LEVEL) { + throw new IllegalStateException("Method " + method + " is not compiled at the compilation level: " + COMP_LEVEL + ". Got: " + WB.getMethodCompilationLevel(method)); + } + } + + public NMethod getNMethod() { + return NMethod.get(method, false); + } + } + + private static ClassLoader createClassLoader() { + return new ClassLoader() { + @Override + public Class loadClass(String name) throws ClassNotFoundException { + if (!name.equals(FIELD_USER)) { + return super.loadClass(name); + } + + return defineClass(name, BYTE_CODE, 0, BYTE_CODE.length); + } + }; + } + + private static void createTestMethods(int accessedFieldCount, int count) throws Exception { + String javaCode = "public class " + FIELD_USER + " {"; + String field = GCPatchingNmethodCost.class.getName() + ".fields.f"; + javaCode += "public static void accessFields() {"; + for (int i = 1; i <= accessedFieldCount; i++) { + javaCode += field + i + "= " + field + i + " + " + i + ";"; + } + javaCode += "}}"; + + BYTE_CODE = InMemoryJavaCompiler.compile(FIELD_USER, javaCode); + + fields = new Fields(); + + methods = new TestMethod[count]; + for (int i = 0; i < count; i++) { + var cl = createClassLoader().loadClass(FIELD_USER); + Method method = cl.getMethod("accessFields"); + methods[i] = new TestMethod(method); + methods[i].profile(); + methods[i].compile(); + } + } + + private static void initWhiteBox() { + WB = WhiteBox.getWhiteBox(); + } + + @Setup(Level.Trial) + public void setupCodeCache() throws Exception { + initWhiteBox(); + createTestMethods(accessedFieldCount, methodCount); + System.gc(); + } + + @Setup(Level.Iteration) + public void setupIteration() { + fields = new Fields(); + } + + @Benchmark + public void youngGC() throws Exception { + fields = null; + WB.youngGC(); + } + + @Benchmark + public void fullGC() throws Exception { + fields = null; + WB.fullGC(); + } + + @Benchmark + public void systemGC() throws Exception { + fields = null; + System.gc(); + } +} From 46e6b26bf8c3124bf4e0c5d26c3eb04e220bd0cb Mon Sep 17 00:00:00 2001 From: Saranya Natarajan Date: Wed, 25 Mar 2026 12:47:41 +0000 Subject: [PATCH 111/160] 8372646: C2: Stress Counted Loop creation Reviewed-by: rcastanedalo, chagedorn, dfenacci --- src/hotspot/share/opto/c2_globals.hpp | 3 +++ src/hotspot/share/opto/compile.cpp | 12 +++++++++--- src/hotspot/share/opto/loopnode.cpp | 15 +++++++++++++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index e0833afa29d..a4f6d04f6a3 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -907,6 +907,9 @@ \ develop(bool, StressLoopPeeling, false, \ "Randomize loop peeling decision") \ + \ + develop(bool, StressCountedLoop, false, \ + "Randomly delay conversion to counted loops") \ // end of C2_FLAGS diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index f1ea8231df9..f34c75656a4 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -741,7 +741,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, if (StressLCM || StressGCM || StressIGVN || StressCCP || StressIncrementalInlining || StressMacroExpansion || StressMacroElimination || StressUnstableIfTraps || - StressBailout || StressLoopPeeling) { + StressBailout || StressLoopPeeling || StressCountedLoop) { initialize_stress_seed(directive); } @@ -2257,7 +2257,9 @@ bool Compile::optimize_loops(PhaseIterGVN& igvn, LoopOptsMode mode) { PhaseIdealLoop::optimize(igvn, mode); _loop_opts_cnt--; if (failing()) return false; - if (major_progress()) print_method(PHASE_PHASEIDEALLOOP_ITERATIONS, 2); + if (major_progress()) { + print_method(PHASE_PHASEIDEALLOOP_ITERATIONS, 2); + } } } return true; @@ -3798,7 +3800,11 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f } break; case Op_Loop: - assert(!n->as_Loop()->is_loop_nest_inner_loop() || _loop_opts_cnt == 0, "should have been turned into a counted loop"); + // When StressCountedLoop is enabled, this loop may intentionally avoid a counted loop conversion. + // This is expected behavior for the stress mode, which exercises alternative compilation paths. + if (!StressCountedLoop) { + assert(!n->as_Loop()->is_loop_nest_inner_loop() || _loop_opts_cnt == 0, "should have been turned into a counted loop"); + } case Op_CountedLoop: case Op_LongCountedLoop: case Op_OuterStripMinedLoop: diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index b2eb0c47458..686a0a05f66 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -2137,7 +2137,6 @@ bool CountedLoopConverter::is_counted_loop() { } assert(_head->Opcode() == Op_Loop || _head->Opcode() == Op_LongCountedLoop, "regular loops only"); - _phase->C->print_method(PHASE_BEFORE_CLOOPS, 3, _head); // =================================================== // We can only convert this loop to a counted loop if we can guarantee that the iv phi will never overflow at runtime. @@ -2411,6 +2410,12 @@ bool CountedLoopConverter::is_counted_loop() { _checked_for_counted_loop = true; #endif +#ifndef PRODUCT + if (StressCountedLoop && (_phase->C->random() % 2 == 0)) { + return false; + } +#endif + return true; } @@ -2553,6 +2558,8 @@ IdealLoopTree* CountedLoopConverter::convert() { PhaseIterGVN* igvn = &_phase->igvn(); + _phase->C->print_method(PHASE_BEFORE_CLOOPS, 3, _head); + if (_should_insert_stride_overflow_limit_check) { insert_stride_overflow_limit_check(); } @@ -4734,7 +4741,11 @@ void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) { } else if (_head->is_LongCountedLoop() || phase->try_convert_to_counted_loop(_head, loop, T_LONG)) { remove_safepoints(phase, true); } else { - assert(!_head->is_Loop() || !_head->as_Loop()->is_loop_nest_inner_loop(), "transformation to counted loop should not fail"); + // When StressCountedLoop is enabled, this loop may intentionally avoid a counted loop conversion. + // This is expected behavior for the stress mode, which exercises alternative compilation paths. + if (!StressCountedLoop) { + assert(!_head->is_Loop() || !_head->as_Loop()->is_loop_nest_inner_loop(), "transformation to counted loop should not fail"); + } if (_parent != nullptr && !_irreducible) { // Not a counted loop. Keep one safepoint. bool keep_one_sfpt = true; From 6466d9809c80bdf1353b36efc67f7977ee4f45b6 Mon Sep 17 00:00:00 2001 From: Frederic Thevenet Date: Wed, 25 Mar 2026 13:11:02 +0000 Subject: [PATCH 112/160] 8380776: No longer necessary to disable calloc-transposed-args warnings when building harfbuzz Reviewed-by: erikj, kbarrett, jwaters, aivanov --- make/modules/java.desktop/lib/ClientLibraries.gmk | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/make/modules/java.desktop/lib/ClientLibraries.gmk b/make/modules/java.desktop/lib/ClientLibraries.gmk index b76cb8dc4e3..13a74ab409f 100644 --- a/make/modules/java.desktop/lib/ClientLibraries.gmk +++ b/make/modules/java.desktop/lib/ClientLibraries.gmk @@ -338,11 +338,8 @@ else # noexcept-type required for GCC 7 builds. Not required for GCC 8+. # expansion-to-defined required for GCC 9 builds. Not required for GCC 10+. # maybe-uninitialized required for GCC 8 builds. Not required for GCC 9+. - # calloc-transposed-args required for GCC 14 builds. (fixed upstream in - # Harfbuzz 032c931e1c0cfb20f18e5acb8ba005775242bd92) HARFBUZZ_DISABLED_WARNINGS_CXX_gcc := class-memaccess noexcept-type \ - expansion-to-defined dangling-reference maybe-uninitialized \ - calloc-transposed-args + expansion-to-defined dangling-reference maybe-uninitialized HARFBUZZ_DISABLED_WARNINGS_clang := missing-field-initializers \ range-loop-analysis unused-variable HARFBUZZ_DISABLED_WARNINGS_microsoft := 4267 4244 From 4dca6e4ca89e3468c48247ddc7fabab769b72ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20Sikstr=C3=B6m?= Date: Wed, 25 Mar 2026 14:01:26 +0000 Subject: [PATCH 113/160] 8380903: [BACKOUT] Mitigate Neoverse-N1 erratum 1542419 negative impact on GCs and JIT performance Reviewed-by: aboldtch --- .../gc/z/zBarrierSetAssembler_aarch64.cpp | 4 +- src/hotspot/cpu/aarch64/globals_aarch64.hpp | 2 - src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp | 7 +- .../cpu/aarch64/vm_version_aarch64.cpp | 45 -- .../cpu/aarch64/vm_version_aarch64.hpp | 5 - .../linux_aarch64/icache_linux_aarch64.cpp | 28 -- .../linux_aarch64/icache_linux_aarch64.hpp | 101 +---- .../vm_version_linux_aarch64.cpp | 2 - src/hotspot/share/asm/codeBuffer.cpp | 3 + src/hotspot/share/code/codeBlob.cpp | 6 - src/hotspot/share/code/nmethod.cpp | 32 +- src/hotspot/share/code/nmethod.hpp | 7 +- src/hotspot/share/code/relocInfo.cpp | 9 + src/hotspot/share/code/relocInfo.hpp | 2 + src/hotspot/share/gc/z/zBarrierSetNMethod.cpp | 14 +- src/hotspot/share/gc/z/zGeneration.cpp | 14 +- src/hotspot/share/gc/z/zMark.cpp | 31 +- src/hotspot/share/gc/z/zNMethod.cpp | 16 +- src/hotspot/share/gc/z/zNMethod.hpp | 2 - src/hotspot/share/runtime/globals.hpp | 2 - src/hotspot/share/runtime/icache.hpp | 23 - ...tDeferredICacheInvalidationCmdOptions.java | 426 ------------------ .../gc/TestDeferredICacheInvalidation.java | 306 ------------- .../bench/vm/gc/GCPatchingNmethodCost.java | 206 --------- 24 files changed, 49 insertions(+), 1244 deletions(-) delete mode 100644 src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp delete mode 100644 test/hotspot/jtreg/compiler/runtime/TestDeferredICacheInvalidationCmdOptions.java delete mode 100644 test/hotspot/jtreg/gc/TestDeferredICacheInvalidation.java delete mode 100644 test/micro/org/openjdk/bench/vm/gc/GCPatchingNmethodCost.java diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index f0885fee93d..4f0977a414f 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -879,9 +879,7 @@ void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format) { ShouldNotReachHere(); } - if (!UseSingleICacheInvalidation) { - ICache::invalidate_word((address)patch_addr); - } + ICache::invalidate_word((address)patch_addr); } #ifdef COMPILER1 diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp index ba29646a828..e6de2c798b1 100644 --- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp @@ -127,8 +127,6 @@ define_pd_global(intx, InlineSmallCode, 1000); "Branch Protection to use: none, standard, pac-ret") \ product(bool, AlwaysMergeDMB, true, DIAGNOSTIC, \ "Always merge DMB instructions in code emission") \ - product(bool, NeoverseN1ICacheErratumMitigation, false, DIAGNOSTIC, \ - "Enable workaround for Neoverse N1 erratum 1542419") \ // end of ARCH_FLAGS diff --git a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp index f1b9fb213a2..dbec2d76d4f 100644 --- a/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/relocInfo_aarch64.cpp @@ -54,12 +54,7 @@ void Relocation::pd_set_data_value(address x, bool verify_only) { bytes = MacroAssembler::pd_patch_instruction_size(addr(), x); break; } - - if (UseSingleICacheInvalidation) { - assert(_binding != nullptr, "expect to be called with RelocIterator in use"); - } else { - ICache::invalidate_range(addr(), bytes); - } + ICache::invalidate_range(addr(), bytes); } address Relocation::pd_call_destination(address orig_addr) { diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 0a40cd705cd..4423d9c5b58 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -52,9 +52,6 @@ uintptr_t VM_Version::_pac_mask; SpinWait VM_Version::_spin_wait; -bool VM_Version::_cache_dic_enabled; -bool VM_Version::_cache_idc_enabled; - const char* VM_Version::_features_names[MAX_CPU_FEATURES] = { nullptr }; static SpinWait get_spin_wait_desc() { @@ -66,19 +63,6 @@ static SpinWait get_spin_wait_desc() { return spin_wait; } -static bool has_neoverse_n1_errata_1542419() { - const int major_rev_num = VM_Version::cpu_variant(); - const int minor_rev_num = VM_Version::cpu_revision(); - // Neoverse N1: 0xd0c - // Erratum 1542419 affects r3p0, r3p1 and r4p0. - // It is fixed in r4p1 and later revisions, which are not affected. - return (VM_Version::cpu_family() == VM_Version::CPU_ARM && - VM_Version::model_is(0xd0c) && - ((major_rev_num == 3 && minor_rev_num == 0) || - (major_rev_num == 3 && minor_rev_num == 1) || - (major_rev_num == 4 && minor_rev_num == 0))); -} - void VM_Version::initialize() { #define SET_CPU_FEATURE_NAME(id, name, bit) \ _features_names[bit] = XSTR(name); @@ -90,9 +74,6 @@ void VM_Version::initialize() { _supports_atomic_getset8 = true; _supports_atomic_getadd8 = true; - _cache_dic_enabled = false; - _cache_idc_enabled = false; - get_os_cpu_info(); int dcache_line = VM_Version::dcache_line_size(); @@ -680,32 +661,6 @@ void VM_Version::initialize() { clear_feature(CPU_SVE); } - if (FLAG_IS_DEFAULT(UseSingleICacheInvalidation) && is_cache_idc_enabled() && is_cache_dic_enabled()) { - FLAG_SET_DEFAULT(UseSingleICacheInvalidation, true); - } - - if (FLAG_IS_DEFAULT(NeoverseN1ICacheErratumMitigation) && has_neoverse_n1_errata_1542419()) { - FLAG_SET_DEFAULT(NeoverseN1ICacheErratumMitigation, true); - } - - if (NeoverseN1ICacheErratumMitigation) { - if (!has_neoverse_n1_errata_1542419()) { - vm_exit_during_initialization("NeoverseN1ICacheErratumMitigation is set for the CPU not having Neoverse N1 errata 1542419"); - } - if (FLAG_IS_DEFAULT(UseSingleICacheInvalidation)) { - FLAG_SET_DEFAULT(UseSingleICacheInvalidation, true); - } - - if (!UseSingleICacheInvalidation) { - vm_exit_during_initialization("NeoverseN1ICacheErratumMitigation is set but UseSingleICacheInvalidation is not enabled"); - } - } - - if (UseSingleICacheInvalidation - && (!is_cache_idc_enabled() || (!is_cache_dic_enabled() && !NeoverseN1ICacheErratumMitigation))) { - vm_exit_during_initialization("UseSingleICacheInvalidation is set but neither IDC nor DIC nor NeoverseN1ICacheErratumMitigation is enabled"); - } - // Construct the "features" string stringStream ss(512); ss.print("0x%02x:0x%x:0x%03x:%d", _cpu, _variant, _model, _revision); diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index 6145990d0d6..e8681611234 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp @@ -58,8 +58,6 @@ protected: // When _prefer_sve_merging_mode_cpy is true, `cpy (imm, zeroing)` is // implemented as `movi; cpy(imm, merging)`. static constexpr bool _prefer_sve_merging_mode_cpy = true; - static bool _cache_dic_enabled; - static bool _cache_idc_enabled; static SpinWait _spin_wait; @@ -255,9 +253,6 @@ public: return vector_length_in_bytes <= 16; } - static bool is_cache_dic_enabled() { return _cache_dic_enabled; } - static bool is_cache_idc_enabled() { return _cache_idc_enabled; } - static void get_cpu_features_name(void* features_buffer, stringStream& ss); // Returns names of features present in features_set1 but not in features_set2 diff --git a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp deleted file mode 100644 index 41cad5af325..00000000000 --- a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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. - * - */ - -#include "runtime/icache.hpp" -#include "utilities/globalDefinitions.hpp" - -NOT_PRODUCT(THREAD_LOCAL AArch64ICacheInvalidationContext* AArch64ICacheInvalidationContext::_current_context = nullptr;) diff --git a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp index b410aebed7b..8fbaa7a6b6e 100644 --- a/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/icache_linux_aarch64.hpp @@ -26,10 +26,6 @@ #ifndef OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP #define OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP -#include "memory/allocation.hpp" -#include "runtime/vm_version.hpp" -#include "utilities/globalDefinitions.hpp" - // Interface for updating the instruction cache. Whenever the VM // modifies code, part of the processor instruction cache potentially // has to be flushed. @@ -41,103 +37,8 @@ class ICache : public AbstractICache { __builtin___clear_cache((char *)addr, (char *)(addr + 4)); } static void invalidate_range(address start, int nbytes) { - if (NeoverseN1ICacheErratumMitigation) { - assert(VM_Version::is_cache_idc_enabled(), - "Expect CTR_EL0.IDC to be enabled for Neoverse N1 with erratum " - "1542419"); - assert(!VM_Version::is_cache_dic_enabled(), - "Expect CTR_EL0.DIC to be disabled for Neoverse N1 with erratum " - "1542419"); - asm volatile("dsb ish \n" - "ic ivau, xzr \n" - "dsb ish \n" - "isb \n" - : : : "memory"); - } else { - __builtin___clear_cache((char *)start, (char *)(start + nbytes)); - } + __builtin___clear_cache((char *)start, (char *)(start + nbytes)); } }; -class AArch64ICacheInvalidationContext : StackObj { - private: - -#ifdef ASSERT - static THREAD_LOCAL AArch64ICacheInvalidationContext* _current_context; -#endif - - bool _has_modified_code; - - public: - NONCOPYABLE(AArch64ICacheInvalidationContext); - - AArch64ICacheInvalidationContext() - : _has_modified_code(false) { - assert(_current_context == nullptr, "nested ICacheInvalidationContext not supported"); -#ifdef ASSERT - _current_context = this; -#endif - } - - ~AArch64ICacheInvalidationContext() { - NOT_PRODUCT(_current_context = nullptr); - - if (!_has_modified_code || !UseSingleICacheInvalidation) { - return; - } - - assert(VM_Version::is_cache_idc_enabled(), "Expect CTR_EL0.IDC to be enabled"); - - asm volatile("dsb ish" : : : "memory"); - - if (NeoverseN1ICacheErratumMitigation) { - assert(!VM_Version::is_cache_dic_enabled(), - "Expect CTR_EL0.DIC to be disabled for Neoverse N1 with erratum " - "1542419"); - - // Errata 1542419: Neoverse N1 cores with the 'COHERENT_ICACHE' feature - // may fetch stale instructions when software depends on - // prefetch-speculation-protection instead of explicit synchronization. - // - // Neoverse-N1 implementation mitigates the errata 1542419 with a - // workaround: - // - Disable coherent icache. - // - Trap IC IVAU instructions. - // - Execute: - // - tlbi vae3is, xzr - // - dsb sy - // - Ignore trapped IC IVAU instructions. - // - // `tlbi vae3is, xzr` invalidates all translation entries (all VAs, all - // possible levels). It waits for all memory accesses using in-scope old - // translation information to complete before it is considered complete. - // - // As this workaround has significant overhead, Arm Neoverse N1 (MP050) - // Software Developer Errata Notice version 29.0 suggests: - // - // "Since one TLB inner-shareable invalidation is enough to avoid this - // erratum, the number of injected TLB invalidations should be minimized - // in the trap handler to mitigate the performance impact due to this - // workaround." - // As the address for icache invalidation is not relevant and - // IC IVAU instruction is ignored, we use XZR in it. - asm volatile( - "ic ivau, xzr \n" - "dsb ish \n" - : - : - : "memory"); - } else { - assert(VM_Version::is_cache_dic_enabled(), "Expect CTR_EL0.DIC to be enabled"); - } - asm volatile("isb" : : : "memory"); - } - - void set_has_modified_code() { - _has_modified_code = true; - } -}; - -#define PD_ICACHE_INVALIDATION_CONTEXT AArch64ICacheInvalidationContext - #endif // OS_CPU_LINUX_AARCH64_ICACHE_AARCH64_HPP diff --git a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp index dad7161a3cb..1fe06dc640d 100644 --- a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp @@ -169,8 +169,6 @@ void VM_Version::get_os_cpu_info() { _icache_line_size = (1 << (ctr_el0 & 0x0f)) * 4; _dcache_line_size = (1 << ((ctr_el0 >> 16) & 0x0f)) * 4; - _cache_idc_enabled = ((ctr_el0 >> 28) & 0x1) != 0; - _cache_dic_enabled = ((ctr_el0 >> 29) & 0x1) != 0; if (!(dczid_el0 & 0x10)) { _zva_length = 4 << (dczid_el0 & 0xf); diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index 964aec8b501..ba525588f32 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -745,6 +745,9 @@ void CodeBuffer::copy_code_to(CodeBlob* dest_blob) { // Done moving code bytes; were they the right size? assert((int)align_up(dest.total_content_size(), oopSize) == dest_blob->content_size(), "sanity"); + + // Flush generated code + ICache::invalidate_range(dest_blob->code_begin(), dest_blob->code_size()); } // Move all my code into another code buffer. Consult applicable diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index d07386bd795..fcc0b42a461 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -331,13 +331,7 @@ RuntimeBlob::RuntimeBlob( : CodeBlob(name, kind, cb, size, header_size, frame_complete, frame_size, oop_maps, caller_must_gc_arguments, align_up(cb->total_relocation_size(), oopSize)) { - if (code_size() == 0) { - // Nothing to copy - return; - } - cb->copy_code_and_locs_to(this); - ICache::invalidate_range(code_begin(), code_size()); } void RuntimeBlob::free(RuntimeBlob* blob) { diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 5cb795a4723..5a6ed8ab3ed 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -1328,7 +1328,6 @@ nmethod::nmethod( code_buffer->copy_values_to(this); post_init(); - ICache::invalidate_range(code_begin(), code_size()); } if (PrintNativeNMethods || PrintDebugInfo || PrintRelocations || PrintDependencies) { @@ -1810,7 +1809,6 @@ nmethod::nmethod( init_immutable_data_ref_count(); post_init(); - ICache::invalidate_range(code_begin(), code_size()); // we use the information of entry points to find out if a method is // static or non static @@ -2038,7 +2036,7 @@ void nmethod::copy_values(GrowableArray* array) { // The code and relocations have already been initialized by the // CodeBlob constructor, so it is valid even at this early point to // iterate over relocations and patch the code. - fix_oop_relocations(/*initialize_immediates=*/ true); + fix_oop_relocations(nullptr, nullptr, /*initialize_immediates=*/ true); } void nmethod::copy_values(GrowableArray* array) { @@ -2050,42 +2048,24 @@ void nmethod::copy_values(GrowableArray* array) { } } -bool nmethod::fix_oop_relocations(bool initialize_immediates) { +void nmethod::fix_oop_relocations(address begin, address end, bool initialize_immediates) { // re-patch all oop-bearing instructions, just in case some oops moved - RelocIterator iter(this); - bool modified_code = false; + RelocIterator iter(this, begin, end); while (iter.next()) { if (iter.type() == relocInfo::oop_type) { oop_Relocation* reloc = iter.oop_reloc(); - if (!reloc->oop_is_immediate()) { - // Refresh the oop-related bits of this instruction. - reloc->set_value(reloc->value()); - modified_code = true; - } else if (initialize_immediates) { + if (initialize_immediates && reloc->oop_is_immediate()) { oop* dest = reloc->oop_addr(); jobject obj = *reinterpret_cast(dest); initialize_immediate_oop(dest, obj); } + // Refresh the oop-related bits of this instruction. + reloc->fix_oop_relocation(); } else if (iter.type() == relocInfo::metadata_type) { metadata_Relocation* reloc = iter.metadata_reloc(); reloc->fix_metadata_relocation(); - modified_code |= !reloc->metadata_is_immediate(); } } - return modified_code; -} - -void nmethod::fix_oop_relocations() { - ICacheInvalidationContext icic; - fix_oop_relocations(&icic); -} - -void nmethod::fix_oop_relocations(ICacheInvalidationContext* icic) { - assert(icic != nullptr, "must provide context to track if code was modified"); - bool modified_code = fix_oop_relocations(/*initialize_immediates=*/ false); - if (modified_code) { - icic->set_has_modified_code(); - } } static void install_post_call_nop_displacement(nmethod* nm, address pc) { diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp index ea8c0e2ad5d..092da181f12 100644 --- a/src/hotspot/share/code/nmethod.hpp +++ b/src/hotspot/share/code/nmethod.hpp @@ -41,7 +41,6 @@ class Dependencies; class DirectiveSet; class DebugInformationRecorder; class ExceptionHandlerTable; -class ICacheInvalidationContext; class ImplicitExceptionTable; class JvmtiThreadState; class MetadataClosure; @@ -802,15 +801,15 @@ public: // Relocation support private: - bool fix_oop_relocations(bool initialize_immediates); + void fix_oop_relocations(address begin, address end, bool initialize_immediates); inline void initialize_immediate_oop(oop* dest, jobject handle); protected: address oops_reloc_begin() const; public: - void fix_oop_relocations(ICacheInvalidationContext* icic); - void fix_oop_relocations(); + void fix_oop_relocations(address begin, address end) { fix_oop_relocations(begin, end, false); } + void fix_oop_relocations() { fix_oop_relocations(nullptr, nullptr, false); } bool is_at_poll_return(address pc); bool is_at_poll_or_poll_return(address pc); diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp index 25d91edc20f..2a6335e2118 100644 --- a/src/hotspot/share/code/relocInfo.cpp +++ b/src/hotspot/share/code/relocInfo.cpp @@ -590,6 +590,15 @@ oop oop_Relocation::oop_value() { return *oop_addr(); } + +void oop_Relocation::fix_oop_relocation() { + if (!oop_is_immediate()) { + // get the oop from the pool, and re-insert it into the instruction: + set_value(value()); + } +} + + void oop_Relocation::verify_oop_relocation() { if (!oop_is_immediate()) { // get the oop from the pool, and re-insert it into the instruction: diff --git a/src/hotspot/share/code/relocInfo.hpp b/src/hotspot/share/code/relocInfo.hpp index bb2b2b5693f..6f1778ef479 100644 --- a/src/hotspot/share/code/relocInfo.hpp +++ b/src/hotspot/share/code/relocInfo.hpp @@ -988,6 +988,8 @@ class oop_Relocation : public DataRelocation { void pack_data_to(CodeSection* dest) override; void unpack_data() override; + void fix_oop_relocation(); // reasserts oop value + void verify_oop_relocation(); address value() override { return *reinterpret_cast(oop_addr()); } diff --git a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp index a439b3a167b..d80ce4e149d 100644 --- a/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp +++ b/src/hotspot/share/gc/z/zBarrierSetNMethod.cpp @@ -33,7 +33,6 @@ #include "gc/z/zThreadLocalData.hpp" #include "gc/z/zUncoloredRoot.inline.hpp" #include "logging/log.hpp" -#include "runtime/icache.hpp" #include "runtime/threadWXSetters.inline.hpp" bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { @@ -71,15 +70,12 @@ bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { return false; } - { - ICacheInvalidationContext icic; - // Heal barriers - ZNMethod::nmethod_patch_barriers(nm, &icic); + // Heal barriers + ZNMethod::nmethod_patch_barriers(nm); - // Heal oops - ZUncoloredRootProcessWeakOopClosure cl(ZNMethod::color(nm)); - ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); - } + // Heal oops + ZUncoloredRootProcessWeakOopClosure cl(ZNMethod::color(nm)); + ZNMethod::nmethod_oops_do_inner(nm, &cl); const uintptr_t prev_color = ZNMethod::color(nm); const uintptr_t new_color = *ZPointerStoreGoodMaskLowOrderBitsAddr; diff --git a/src/hotspot/share/gc/z/zGeneration.cpp b/src/hotspot/share/gc/z/zGeneration.cpp index 0f9f4e34a5e..27f352a624f 100644 --- a/src/hotspot/share/gc/z/zGeneration.cpp +++ b/src/hotspot/share/gc/z/zGeneration.cpp @@ -58,7 +58,6 @@ #include "prims/jvmtiTagMap.hpp" #include "runtime/continuation.hpp" #include "runtime/handshake.hpp" -#include "runtime/icache.hpp" #include "runtime/safepoint.hpp" #include "runtime/threads.hpp" #include "runtime/vmOperations.hpp" @@ -1435,15 +1434,12 @@ public: virtual void do_nmethod(nmethod* nm) { ZLocker locker(ZNMethod::lock_for_nmethod(nm)); if (_bs_nm->is_armed(nm)) { - { - ICacheInvalidationContext icic; - // Heal barriers - ZNMethod::nmethod_patch_barriers(nm, &icic); + // Heal barriers + ZNMethod::nmethod_patch_barriers(nm); - // Heal oops - ZUncoloredRootProcessOopClosure cl(ZNMethod::color(nm)); - ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); - } + // Heal oops + ZUncoloredRootProcessOopClosure cl(ZNMethod::color(nm)); + ZNMethod::nmethod_oops_do_inner(nm, &cl); log_trace(gc, nmethod)("nmethod: " PTR_FORMAT " visited by old remapping", p2i(nm)); diff --git a/src/hotspot/share/gc/z/zMark.cpp b/src/hotspot/share/gc/z/zMark.cpp index ac7d86db240..03701ae9998 100644 --- a/src/hotspot/share/gc/z/zMark.cpp +++ b/src/hotspot/share/gc/z/zMark.cpp @@ -59,7 +59,6 @@ #include "oops/oop.inline.hpp" #include "runtime/continuation.hpp" #include "runtime/handshake.hpp" -#include "runtime/icache.hpp" #include "runtime/javaThread.hpp" #include "runtime/prefetch.inline.hpp" #include "runtime/safepointMechanism.hpp" @@ -719,15 +718,12 @@ public: virtual void do_nmethod(nmethod* nm) { ZLocker locker(ZNMethod::lock_for_nmethod(nm)); if (_bs_nm->is_armed(nm)) { - { - ICacheInvalidationContext icic; - // Heal barriers - ZNMethod::nmethod_patch_barriers(nm, &icic); + // Heal barriers + ZNMethod::nmethod_patch_barriers(nm); - // Heal oops - ZUncoloredRootMarkOopClosure cl(ZNMethod::color(nm)); - ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); - } + // Heal oops + ZUncoloredRootMarkOopClosure cl(ZNMethod::color(nm)); + ZNMethod::nmethod_oops_do_inner(nm, &cl); // CodeCache unloading support nm->mark_as_maybe_on_stack(); @@ -757,6 +753,10 @@ public: if (_bs_nm->is_armed(nm)) { const uintptr_t prev_color = ZNMethod::color(nm); + // Heal oops + ZUncoloredRootMarkYoungOopClosure cl(prev_color); + ZNMethod::nmethod_oops_do_inner(nm, &cl); + // Disarm only the young marking, not any potential old marking cycle const uintptr_t old_marked_mask = ZPointerMarkedMask ^ (ZPointerMarkedYoung0 | ZPointerMarkedYoung1); @@ -767,16 +767,9 @@ public: // Check if disarming for young mark, completely disarms the nmethod entry barrier const bool complete_disarm = ZPointer::is_store_good(new_disarm_value_ptr); - { - ICacheInvalidationContext icic; - if (complete_disarm) { - // We are about to completely disarm the nmethod, must take responsibility to patch all barriers before disarming - ZNMethod::nmethod_patch_barriers(nm, &icic); - } - - // Heal oops - ZUncoloredRootMarkYoungOopClosure cl(prev_color); - ZNMethod::nmethod_oops_do_inner(nm, &cl, &icic); + if (complete_disarm) { + // We are about to completely disarm the nmethod, must take responsibility to patch all barriers before disarming + ZNMethod::nmethod_patch_barriers(nm); } _bs_nm->guard_with(nm, (int)untype(new_disarm_value_ptr)); diff --git a/src/hotspot/share/gc/z/zNMethod.cpp b/src/hotspot/share/gc/z/zNMethod.cpp index a1348b63b6f..780bc9e3bf7 100644 --- a/src/hotspot/share/gc/z/zNMethod.cpp +++ b/src/hotspot/share/gc/z/zNMethod.cpp @@ -50,7 +50,6 @@ #include "oops/oop.inline.hpp" #include "runtime/atomicAccess.hpp" #include "runtime/continuation.hpp" -#include "runtime/icache.hpp" #include "utilities/debug.hpp" static ZNMethodData* gc_data(const nmethod* nm) { @@ -246,16 +245,8 @@ void ZNMethod::set_guard_value(nmethod* nm, int value) { } void ZNMethod::nmethod_patch_barriers(nmethod* nm) { - ICacheInvalidationContext icic; - nmethod_patch_barriers(nm, &icic); -} - -void ZNMethod::nmethod_patch_barriers(nmethod* nm, ICacheInvalidationContext* icic) { ZBarrierSetAssembler* const bs_asm = ZBarrierSet::assembler(); ZArrayIterator iter(gc_data(nm)->barriers()); - if (gc_data(nm)->barriers()->is_nonempty()) { - icic->set_has_modified_code(); - } for (ZNMethodDataBarrier barrier; iter.next(&barrier);) { bs_asm->patch_barrier_relocation(barrier._reloc_addr, barrier._reloc_format); } @@ -267,11 +258,6 @@ void ZNMethod::nmethod_oops_do(nmethod* nm, OopClosure* cl) { } void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl) { - ICacheInvalidationContext icic; - nmethod_oops_do_inner(nm, cl, &icic); -} - -void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl, ICacheInvalidationContext* icic) { // Process oops table { oop* const begin = nm->oops_begin(); @@ -297,7 +283,7 @@ void ZNMethod::nmethod_oops_do_inner(nmethod* nm, OopClosure* cl, ICacheInvalida // Process non-immediate oops if (data->has_non_immediate_oops()) { - nm->fix_oop_relocations(icic); + nm->fix_oop_relocations(); } } diff --git a/src/hotspot/share/gc/z/zNMethod.hpp b/src/hotspot/share/gc/z/zNMethod.hpp index 2779151c576..865ea11e7b9 100644 --- a/src/hotspot/share/gc/z/zNMethod.hpp +++ b/src/hotspot/share/gc/z/zNMethod.hpp @@ -56,11 +56,9 @@ public: static void set_guard_value(nmethod* nm, int value); static void nmethod_patch_barriers(nmethod* nm); - static void nmethod_patch_barriers(nmethod* nm, ICacheInvalidationContext* icic); static void nmethod_oops_do(nmethod* nm, OopClosure* cl); static void nmethod_oops_do_inner(nmethod* nm, OopClosure* cl); - static void nmethod_oops_do_inner(nmethod* nm, OopClosure* cl, ICacheInvalidationContext* icic); static void nmethods_do_begin(bool secondary); static void nmethods_do_end(bool secondary); diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 296c5dedf8a..60feddde09b 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -1978,8 +1978,6 @@ const int ObjectAlignmentInBytes = 8; develop(uint, BinarySearchThreshold, 16, \ "Minimal number of elements in a sorted collection to prefer" \ "binary search over simple linear search." ) \ - product(bool, UseSingleICacheInvalidation, false, DIAGNOSTIC, \ - "Defer multiple ICache invalidation to single invalidation") \ \ // end of RUNTIME_FLAGS diff --git a/src/hotspot/share/runtime/icache.hpp b/src/hotspot/share/runtime/icache.hpp index 692a876d9a6..bc153862323 100644 --- a/src/hotspot/share/runtime/icache.hpp +++ b/src/hotspot/share/runtime/icache.hpp @@ -129,27 +129,4 @@ class ICacheStubGenerator : public StubCodeGenerator { void generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub); }; -class DefaultICacheInvalidationContext : StackObj { - public: - NONCOPYABLE(DefaultICacheInvalidationContext); - - DefaultICacheInvalidationContext() {} - - ~DefaultICacheInvalidationContext() {} - - void set_has_modified_code() {} -}; - -#ifndef PD_ICACHE_INVALIDATION_CONTEXT -#define PD_ICACHE_INVALIDATION_CONTEXT DefaultICacheInvalidationContext -#endif // PD_ICACHE_INVALIDATION_CONTEXT - -class ICacheInvalidationContext final : public PD_ICACHE_INVALIDATION_CONTEXT { - private: - NONCOPYABLE(ICacheInvalidationContext); - - public: - using PD_ICACHE_INVALIDATION_CONTEXT::PD_ICACHE_INVALIDATION_CONTEXT; -}; - #endif // SHARE_RUNTIME_ICACHE_HPP diff --git a/test/hotspot/jtreg/compiler/runtime/TestDeferredICacheInvalidationCmdOptions.java b/test/hotspot/jtreg/compiler/runtime/TestDeferredICacheInvalidationCmdOptions.java deleted file mode 100644 index 8bc74be5c62..00000000000 --- a/test/hotspot/jtreg/compiler/runtime/TestDeferredICacheInvalidationCmdOptions.java +++ /dev/null @@ -1,426 +0,0 @@ -/* - * 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. - * - */ - -package compiler.runtime; - -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.process.ProcessTools; -import jdk.test.whitebox.WhiteBox; -import jtreg.SkippedException; - -/* - * @test - * @bug 8370947 - * @summary Test command-line options for UseSingleICacheInvalidation and NeoverseN1ICacheErratumMitigation - * @library /test/lib - * @requires os.arch == "aarch64" - * @requires os.family == "linux" - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.runtime.TestDeferredICacheInvalidationCmdOptions - */ - -public class TestDeferredICacheInvalidationCmdOptions { - - // CPU identifiers - private static final int CPU_ARM = 0x41; - private static final int NEOVERSE_N1_MODEL = 0xd0c; - - private static boolean isAffected; - - public static void main(String[] args) throws Exception { - // Parse CPU features and print CPU info - parseCPUFeatures(); - - System.out.println("Testing UseSingleICacheInvalidation and NeoverseN1ICacheErratumMitigation command-line options..."); - - // Test case 1: Check defaults on Neoverse N1 pre-r4p1 (if applicable) - testCase1_DefaultsOnNeoverseN1(); - - // Test case 2: Check NeoverseN1ICacheErratumMitigation is false on unaffected CPUs - testCase2_DefaultsOnUnaffectedCPUs(); - - // Test case 3: Check if NeoverseN1ICacheErratumMitigation is set to false on affected CPUs, - // UseSingleICacheInvalidation is also set to false - testCase3_ExplicitlyDisableErrataAffectsDeferred(); - - // Test case 4: Check JVM error if UseSingleICacheInvalidation=true - // but NeoverseN1ICacheErratumMitigation=false on affected CPUs - testCase4_ConflictingFlagsOnAffectedCPUs(); - - // Test case 5: Check explicit NeoverseN1ICacheErratumMitigation=true enables UseSingleICacheInvalidation - testCase5_ExplicitlyEnableErrataEnablesDeferred(); - - // Test case 6: Check both flags can be explicitly set to false - testCase6_ExplicitlyDisableBothFlags(); - - // Test case 7: Check UseSingleICacheInvalidation=false with NeoverseN1ICacheErratumMitigation=true - testCase7_ConflictingErrataWithoutDeferred(); - - // Test case 8: Check setting NeoverseN1ICacheErratumMitigation=true on unaffected CPU causes an error - testCase8_EnablingErrataOnUnaffectedCPU(); - - System.out.println("All test cases passed!"); - } - - /** - * Parse CPU features string from WhiteBox.getCPUFeatures() to extract: - * - cpuFamily: 0x41 for ARM - * - cpuVariant: major revision - * - cpuModel: e.g., 0xd0c for Neoverse N1 - * - cpuRevision: minor revision - * - cpuModel2: secondary model (if present, in parentheses) - * - * Sets the static field isAffected and prints CPU info. - * - * Format: 0x%02x:0x%x:0x%03x:%d[(0x%03x)] - * Example: "0x41:0x3:0xd0c:0" or "0x41:0x3:0xd0c:0(0xd0c)" - * - * @throws SkippedException if not running on ARM CPU - */ - private static void parseCPUFeatures() { - WhiteBox wb = WhiteBox.getWhiteBox(); - String cpuFeatures = wb.getCPUFeatures(); - System.out.println("CPU Features string: " + cpuFeatures); - - if (cpuFeatures == null || cpuFeatures.isEmpty()) { - throw new RuntimeException("No CPU features available"); - } - - int commaIndex = cpuFeatures.indexOf(","); - if (commaIndex == -1) { - throw new RuntimeException("Unexpected CPU features format (no comma): " + cpuFeatures); - } - - String cpuPart = cpuFeatures.substring(0, commaIndex).trim(); - - String[] parts = cpuPart.split(":"); - if (parts.length < 4) { - throw new RuntimeException("Unexpected CPU features format: " + cpuPart); - } - - int cpuFamily = Integer.parseInt(parts[0].substring(2), 16); - if (cpuFamily != CPU_ARM) { - throw new SkippedException("Not running on ARM CPU (cpuFamily=0x" + Integer.toHexString(cpuFamily) + ")"); - } - - int cpuVariant = Integer.parseInt(parts[1].substring(2), 16); - int cpuModel = Integer.parseInt(parts[2].substring(2), 16); - int cpuModel2 = 0; - - int model2Start = parts[3].indexOf("("); - String revisionStr = parts[3]; - if (model2Start != -1) { - if (!parts[3].endsWith(")")) { - throw new RuntimeException("Unexpected CPU features format (missing closing parenthesis): " + parts[3]); - } - String model2Str = parts[3].substring(model2Start + 1, parts[3].length() - 1); - cpuModel2 = Integer.parseInt(model2Str.substring(2), 16); - revisionStr = parts[3].substring(0, model2Start); - } - int cpuRevision = Integer.parseInt(revisionStr); - - // Neoverse N1 errata 1542419 affects r3p0, r3p1 and r4p0. - // It is fixed in r4p1 and later revisions. - if (cpuModel == NEOVERSE_N1_MODEL || cpuModel2 == NEOVERSE_N1_MODEL) { - isAffected = (cpuVariant == 3 && cpuRevision == 0) || - (cpuVariant == 3 && cpuRevision == 1) || - (cpuVariant == 4 && cpuRevision == 0); - } - - printCPUInfo(cpuFamily, cpuVariant, cpuModel, cpuModel2, cpuRevision); - } - - private static void printCPUInfo(int cpuFamily, int cpuVariant, int cpuModel, int cpuModel2, int cpuRevision) { - boolean isNeoverseN1 = (cpuFamily == CPU_ARM) && - (cpuModel == NEOVERSE_N1_MODEL || cpuModel2 == NEOVERSE_N1_MODEL); - System.out.println("\n=== CPU Information ==="); - System.out.println("CPU Family: 0x" + Integer.toHexString(cpuFamily)); - System.out.println("CPU Variant: 0x" + Integer.toHexString(cpuVariant)); - System.out.println("CPU Model: 0x" + Integer.toHexString(cpuModel)); - if (cpuModel2 != 0) { - System.out.println("CPU Model2: 0x" + Integer.toHexString(cpuModel2)); - } - System.out.println("CPU Revision: " + cpuRevision); - System.out.println("Is Neoverse N1: " + isNeoverseN1); - System.out.println("Is affected by errata 1542419: " + isAffected); - System.out.println("======================\n"); - } - - /** - * Test case 1: Check the UseSingleICacheInvalidation and NeoverseN1ICacheErratumMitigation - * are set to true for Neoverse N1 pre-r4p1. - */ - private static void testCase1_DefaultsOnNeoverseN1() throws Exception { - if (!isAffected) { - System.out.println("\nTest case 1: Skipping since CPU is not affected by Neoverse N1 errata 1542419"); - return; - } - - System.out.println("\nTest case 1: Check defaults on Neoverse N1 affected revisions"); - - ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+PrintFlagsFinal", - "-version"); - - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldHaveExitValue(0); - - String output_str = output.getOutput(); - boolean hasSingleEnabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*true.*"); - boolean hasErrataEnabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*true.*"); - - System.out.println("UseSingleICacheInvalidation enabled: " + hasSingleEnabled); - System.out.println("NeoverseN1ICacheErratumMitigation enabled: " + hasErrataEnabled); - - // If running on affected Neoverse N1, both should be true - if (!hasSingleEnabled || !hasErrataEnabled) { - throw new RuntimeException("On affected Neoverse N1, both flags should be enabled by default"); - } - System.out.println("Correctly enabled on affected Neoverse N1"); - } - - /** - * Test case 2: Check NeoverseN1ICacheErratumMitigation is false on unaffected CPUs. - */ - private static void testCase2_DefaultsOnUnaffectedCPUs() throws Exception { - if (isAffected) { - System.out.println("\nTest case 2: Skipping since CPU is affected by Neoverse N1 errata 1542419"); - return; - } - - System.out.println("\nTest case 2: Check NeoverseN1ICacheErratumMitigation is false on unaffected CPUs"); - - ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+PrintFlagsFinal", - "-version"); - - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldHaveExitValue(0); - - String output_str = output.getOutput(); - boolean hasErrataEnabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*true.*"); - - System.out.println("NeoverseN1ICacheErratumMitigation enabled: " + hasErrataEnabled); - - // On non-Neoverse N1 or unaffected Neoverse N1 CPUs, NeoverseN1ICacheErratumMitigation should be false - if (hasErrataEnabled) { - throw new RuntimeException("On unaffected CPUs, NeoverseN1ICacheErratumMitigation should be disabled"); - } - System.out.println("Correctly disabled on unaffected CPU"); - } - - /** - * Test case 3: Check if NeoverseN1ICacheErratumMitigation is set to false via cmd on affected CPUs, - * UseSingleICacheInvalidation is set to false. - */ - private static void testCase3_ExplicitlyDisableErrataAffectsDeferred() throws Exception { - if (!isAffected) { - System.out.println("\nTest case 3: Skipping since CPU is not affected by Neoverse N1 errata 1542419"); - return; - } - - System.out.println("\nTest case 3: Explicitly disable NeoverseN1ICacheErratumMitigation, check UseSingleICacheInvalidation"); - - ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( - "-XX:+UnlockDiagnosticVMOptions", - "-XX:-NeoverseN1ICacheErratumMitigation", - "-XX:+PrintFlagsFinal", - "-version"); - - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldHaveExitValue(0); - - String output_str = output.getOutput(); - boolean hasSingleDisabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*false.*"); - boolean hasErrataDisabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*false.*"); - - System.out.println("NeoverseN1ICacheErratumMitigation disabled: " + hasErrataDisabled); - System.out.println("UseSingleICacheInvalidation disabled: " + hasSingleDisabled); - - if (!hasErrataDisabled) { - throw new RuntimeException("Failed to disable NeoverseN1ICacheErratumMitigation via command line"); - } - - // On affected CPUs, disabling errata should also disable UseSingleICacheInvalidation - if (!hasSingleDisabled) { - throw new RuntimeException("On affected CPU, disabling NeoverseN1ICacheErratumMitigation should also disable UseSingleICacheInvalidation"); - } - System.out.println("Correctly synchronized on affected CPU"); - } - - /** - * Test case 4: Check JVM reports an error if UseSingleICacheInvalidation is set to true - * but NeoverseN1ICacheErratumMitigation is set to false on affected CPUs. - */ - private static void testCase4_ConflictingFlagsOnAffectedCPUs() throws Exception { - if (!isAffected) { - System.out.println("\nTest case 4: Skipping since CPU is not affected by Neoverse N1 errata 1542419"); - return; - } - - System.out.println("\nTest case 4: Try to set UseSingleICacheInvalidation=true with NeoverseN1ICacheErratumMitigation=false"); - - ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UseSingleICacheInvalidation", - "-XX:-NeoverseN1ICacheErratumMitigation", - "-version"); - - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - - if (output.getExitValue() == 0) { - throw new RuntimeException("On affected CPU, conflicting flags should cause error"); - } - output.shouldContain("Error"); - System.out.println("JVM correctly rejected conflicting flags on affected CPU"); - } - - /** - * Test case 5: Check explicit NeoverseN1ICacheErratumMitigation=true enables UseSingleICacheInvalidation. - */ - private static void testCase5_ExplicitlyEnableErrataEnablesDeferred() throws Exception { - if (!isAffected) { - System.out.println("\nTest case 5: Skipping since CPU is not affected by Neoverse N1 errata 1542419"); - return; - } - - System.out.println("\nTest case 5: Explicitly enable NeoverseN1ICacheErratumMitigation, check UseSingleICacheInvalidation"); - - ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+NeoverseN1ICacheErratumMitigation", - "-XX:+PrintFlagsFinal", - "-version"); - - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldHaveExitValue(0); - - String output_str = output.getOutput(); - boolean hasSingleEnabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*true.*"); - boolean hasErrataEnabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*true.*"); - - System.out.println("NeoverseN1ICacheErratumMitigation enabled: " + hasErrataEnabled); - System.out.println("UseSingleICacheInvalidation enabled: " + hasSingleEnabled); - - if (!hasErrataEnabled) { - throw new RuntimeException("Failed to enable NeoverseN1ICacheErratumMitigation via command line"); - } - - if (!hasSingleEnabled) { - throw new RuntimeException("On affected CPU, enabling NeoverseN1ICacheErratumMitigation should also enable UseSingleICacheInvalidation"); - } - System.out.println("Correctly synchronized on affected CPU"); - } - - /** - * Test case 6: Check both flags can be explicitly set to false. - */ - private static void testCase6_ExplicitlyDisableBothFlags() throws Exception { - System.out.println("\nTest case 6: Explicitly disable both flags"); - - ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( - "-XX:+UnlockDiagnosticVMOptions", - "-XX:-UseSingleICacheInvalidation", - "-XX:-NeoverseN1ICacheErratumMitigation", - "-XX:+PrintFlagsFinal", - "-version"); - - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldHaveExitValue(0); - - String output_str = output.getOutput(); - boolean hasSingleDisabled = output_str.matches("(?s).*bool\\s+UseSingleICacheInvalidation\\s*=\\s*false.*"); - boolean hasErrataDisabled = output_str.matches("(?s).*bool\\s+NeoverseN1ICacheErratumMitigation\\s*=\\s*false.*"); - - System.out.println("UseSingleICacheInvalidation disabled: " + hasSingleDisabled); - System.out.println("NeoverseN1ICacheErratumMitigation disabled: " + hasErrataDisabled); - - if (!hasErrataDisabled) { - throw new RuntimeException("Failed to disable NeoverseN1ICacheErratumMitigation via command line"); - } - - if (!hasSingleDisabled) { - throw new RuntimeException("Failed to disable UseSingleICacheInvalidation via command line"); - } - - System.out.println("Successfully disabled both flags"); - } - - /** - * Test case 7: Check UseSingleICacheInvalidation=false with NeoverseN1ICacheErratumMitigation=true. - * On affected CPUs, this should error (conflicting requirement). - */ - private static void testCase7_ConflictingErrataWithoutDeferred() throws Exception { - if (!isAffected) { - System.out.println("\nTest case 7: Skipping since CPU is not affected by Neoverse N1 errata 1542419"); - return; - } - System.out.println("\nTest case 7: Try to set NeoverseN1ICacheErratumMitigation=true with UseSingleICacheInvalidation=false"); - - ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( - "-XX:+UnlockDiagnosticVMOptions", - "-XX:-UseSingleICacheInvalidation", - "-XX:+NeoverseN1ICacheErratumMitigation", - "-XX:+PrintFlagsFinal", - "-version"); - - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - - // This should fail on affected CPUs (conflicting requirement) - if (output.getExitValue() == 0) { - throw new RuntimeException("On affected CPU, setting NeoverseN1ICacheErratumMitigation=true with UseSingleICacheInvalidation=false should cause an error"); - } - output.shouldContain("Error"); - System.out.println("JVM correctly rejected conflicting flags on affected CPU"); - } - - /** - * Test case 8: Check setting NeoverseN1ICacheErratumMitigation=true on unaffected CPU causes an error. - */ - private static void testCase8_EnablingErrataOnUnaffectedCPU() throws Exception { - if (isAffected) { - System.out.println("\nTest case 8: Skipping since CPU is affected by Neoverse N1 errata 1542419"); - return; - } - - System.out.println("\nTest case 8: Try to set NeoverseN1ICacheErratumMitigation=true on unaffected CPU"); - - ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+NeoverseN1ICacheErratumMitigation", - "-version"); - - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - - // This should fail on unaffected CPUs (errata not present) - if (output.getExitValue() == 0) { - throw new RuntimeException("On unaffected CPU, setting NeoverseN1ICacheErratumMitigation=true should cause error"); - } - output.shouldContain("Error"); - System.out.println("JVM correctly rejected enabling errata flag on unaffected CPU"); - } -} diff --git a/test/hotspot/jtreg/gc/TestDeferredICacheInvalidation.java b/test/hotspot/jtreg/gc/TestDeferredICacheInvalidation.java deleted file mode 100644 index 70cd1ab6d8c..00000000000 --- a/test/hotspot/jtreg/gc/TestDeferredICacheInvalidation.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * 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. - * - */ - -package gc; - -/* - * @test id=parallel - * @bug 8370947 - * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for ParallelGC - * @library /test/lib - * @requires vm.debug - * @requires vm.gc.Parallel - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1 - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2 - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseParallelGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 - */ - -/* - * @test id=g1 - * @bug 8370947 - * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for G1GC - * @library /test/lib - * @requires vm.debug - * @requires vm.gc.G1 - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1 - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2 - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 - */ - -/* - * @test id=shenandoah - * @bug 8370947 - * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for ShenandoahGC - * @library /test/lib - * @requires vm.debug - * @requires vm.gc.Shenandoah - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 - */ - -/* - * @test id=genshen - * @bug 8370947 - * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for generational ShenandoahGC - * @library /test/lib - * @requires vm.debug - * @requires vm.gc.Shenandoah - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1 - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2 - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=generational -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 - */ - -/* - * @test id=z - * @bug 8370947 - * @summary Check no assertion is triggered when UseSingleICacheInvalidation is enabled for ZGC - * @library /test/lib - * @requires vm.debug - * @requires vm.gc.Z - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C1 - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C1 - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation youngGC C2 - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseZGC -XX:-UseCodeCacheFlushing gc.TestDeferredICacheInvalidation fullGC C2 - */ - -/* - * Nmethods have GC barriers and OOPs embedded into their code. GCs can patch nmethod's code - * which requires icache invalidation. Doing invalidation per instruction can be expensive. - * CPU can support hardware dcache and icache coherence. This would allow to defer cache - * invalidation. - * - * There are assertions for deferred cache invalidation. This test checks that all of them - * are passed. - */ - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import jdk.test.whitebox.WhiteBox; - -public class TestDeferredICacheInvalidation { - - private static final WhiteBox WB = WhiteBox.getWhiteBox(); - - public static class A { - public String s1; - public String s2; - public String s3; - public String s4; - public String s5; - public String s6; - public String s7; - public String s8; - public String s9; - } - - public static A a = new A(); - - private static int compLevel; - - public static class B { - public static void test0() { - } - - public static void test1() { - a.s1 = a.s1 + "1"; - } - - public static void test2() { - a.s1 = a.s1 + "1"; - a.s2 = a.s2 + "2"; - } - - public static void test3() { - a.s1 = a.s1 + "1"; - a.s2 = a.s2 + "2"; - a.s3 = a.s3 + "3"; - } - - public static void test4() { - a.s1 = a.s1 + "1"; - a.s2 = a.s2 + "2"; - a.s3 = a.s3 + "3"; - a.s4 = a.s4 + "4"; - } - - public static void test5() { - a.s1 = a.s1 + "1"; - a.s2 = a.s2 + "2"; - a.s3 = a.s3 + "3"; - a.s4 = a.s4 + "4"; - a.s5 = a.s5 + "5"; - } - - public static void test6() { - a.s1 = a.s1 + "1"; - a.s2 = a.s2 + "2"; - a.s3 = a.s3 + "3"; - a.s4 = a.s4 + "4"; - a.s5 = a.s5 + "5"; - a.s6 = a.s6 + "6"; - } - - public static void test7() { - a.s1 = a.s1 + "1"; - a.s2 = a.s2 + "2"; - a.s3 = a.s3 + "3"; - a.s4 = a.s4 + "4"; - a.s5 = a.s5 + "5"; - a.s6 = a.s6 + "6"; - a.s7 = a.s7 + "7"; - } - - public static void test8() { - a.s1 = a.s1 + "1"; - a.s2 = a.s2 + "2"; - a.s3 = a.s3 + "3"; - a.s4 = a.s4 + "4"; - a.s5 = a.s5 + "5"; - a.s6 = a.s6 + "6"; - a.s7 = a.s7 + "7"; - a.s8 = a.s8 + "8"; - } - - public static void test9() { - a.s1 = a.s1 + "1"; - a.s2 = a.s2 + "2"; - a.s3 = a.s3 + "3"; - a.s4 = a.s4 + "4"; - a.s5 = a.s5 + "5"; - a.s6 = a.s6 + "6"; - a.s7 = a.s7 + "7"; - a.s8 = a.s8 + "8"; - a.s9 = a.s9 + "9"; - } - } - - private static void compileMethods() throws Exception { - for (var m : B.class.getDeclaredMethods()) { - if (!m.getName().startsWith("test")) { - continue; - } - m.invoke(null); - WB.markMethodProfiled(m); - WB.enqueueMethodForCompilation(m, compLevel); - while (WB.isMethodQueuedForCompilation(m)) { - Thread.sleep(100); - } - if (WB.getMethodCompilationLevel(m) != compLevel) { - throw new IllegalStateException("Method " + m + " is not compiled at the compilation level: " + compLevel + ". Got: " + WB.getMethodCompilationLevel(m)); - } - } - } - - public static void youngGC() throws Exception { - a = null; - WB.youngGC(); - } - - public static void fullGC() throws Exception { - // Thread synchronization - final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); - final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); - final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); - final AtomicBoolean running = new AtomicBoolean(true); - - // Thread 1: GC thread that runs for 1 second with 100ms sleep intervals - Thread gcThread = new Thread(() -> { - final long startTime = System.currentTimeMillis(); - final long duration = 1000; - try { - while (System.currentTimeMillis() - startTime < duration) { - writeLock.lock(); - try { - a = new A(); - WB.fullGC(); - } finally { - writeLock.unlock(); - } - Thread.sleep(100); - } - } catch (InterruptedException e) { - // Thread interrupted, exit - } - running.set(false); - }); - - // Threads 2-11: Test threads that execute test0() through test9() - Thread[] testThreads = new Thread[10]; - for (int i = 0; i < 10; i++) { - final int testIdx = i; - testThreads[i] = new Thread(() -> { - try { - var method = B.class.getDeclaredMethod("test" + testIdx); - while (running.get()) { - readLock.lock(); - try { - method.invoke(null); - } finally { - readLock.unlock(); - } - } - } catch (Exception e) { - e.printStackTrace(); - System.exit(10); - } - }); - } - - // Start all threads - gcThread.start(); - for (Thread t : testThreads) { - t.start(); - } - - // Wait for all threads to complete - gcThread.join(); - for (Thread t : testThreads) { - t.join(); - } - } - - public static void main(String[] args) throws Exception { - if (!Boolean.TRUE.equals(WB.getBooleanVMFlag("UseSingleICacheInvalidation"))) { - System.out.println("Skip. Test requires UseSingleICacheInvalidation enabled."); - return; - } - compLevel = (args[1].equals("C1")) ? 1 : 4; - compileMethods(); - TestDeferredICacheInvalidation.class.getMethod(args[0]).invoke(null); - } -} diff --git a/test/micro/org/openjdk/bench/vm/gc/GCPatchingNmethodCost.java b/test/micro/org/openjdk/bench/vm/gc/GCPatchingNmethodCost.java deleted file mode 100644 index 53fa2378ead..00000000000 --- a/test/micro/org/openjdk/bench/vm/gc/GCPatchingNmethodCost.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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. - * - */ - -package org.openjdk.bench.vm.gc; - -import java.lang.reflect.Method; -import java.util.*; -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.CompilerControl; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -import org.openjdk.bench.util.InMemoryJavaCompiler; - -import jdk.test.whitebox.WhiteBox; -import jdk.test.whitebox.code.NMethod; - -/* - * Nmethods have OOPs and GC barriers emmedded into their code. - * GCs patch them which causes invalidation of nmethods' code. - * - * This benchmark can be used to estimate the cost of patching - * OOPs and GC barriers. - * - * We create 5000 nmethods which access fields of a class. - * We measure the time of different GC cycles to see - * the impact of patching nmethods. - * - * The benchmark parameters are method count and accessed field count. - */ - -@BenchmarkMode(Mode.SingleShotTime) -@OutputTimeUnit(TimeUnit.MILLISECONDS) -@State(Scope.Benchmark) -@Fork(value = 1, jvmArgsAppend = { - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+WhiteBoxAPI", - "-Xbootclasspath/a:lib-test/wb.jar", - "-XX:-UseCodeCacheFlushing" -}) -@Warmup(iterations = 5) -@Measurement(iterations = 5) -public class GCPatchingNmethodCost { - - private static final int COMP_LEVEL = 1; - private static final String FIELD_USER = "FieldUser"; - - public static Fields fields; - - private static TestMethod[] methods = {}; - private static byte[] BYTE_CODE; - private static WhiteBox WB; - - @Param({"5000"}) - public int methodCount; - - @Param({"0", "2", "4", "8"}) - public int accessedFieldCount; - - public static class Fields { - public String f1; - public String f2; - public String f3; - public String f4; - public String f5; - public String f6; - public String f7; - public String f8; - public String f9; - } - - private static final class TestMethod { - private final Method method; - - public TestMethod(Method method) throws Exception { - this.method = method; - WB.testSetDontInlineMethod(method, true); - } - - public void profile() throws Exception { - method.invoke(null); - WB.markMethodProfiled(method); - } - - public void invoke() throws Exception { - method.invoke(null); - } - - public void compile() throws Exception { - WB.enqueueMethodForCompilation(method, COMP_LEVEL); - while (WB.isMethodQueuedForCompilation(method)) { - Thread.onSpinWait(); - } - if (WB.getMethodCompilationLevel(method) != COMP_LEVEL) { - throw new IllegalStateException("Method " + method + " is not compiled at the compilation level: " + COMP_LEVEL + ". Got: " + WB.getMethodCompilationLevel(method)); - } - } - - public NMethod getNMethod() { - return NMethod.get(method, false); - } - } - - private static ClassLoader createClassLoader() { - return new ClassLoader() { - @Override - public Class loadClass(String name) throws ClassNotFoundException { - if (!name.equals(FIELD_USER)) { - return super.loadClass(name); - } - - return defineClass(name, BYTE_CODE, 0, BYTE_CODE.length); - } - }; - } - - private static void createTestMethods(int accessedFieldCount, int count) throws Exception { - String javaCode = "public class " + FIELD_USER + " {"; - String field = GCPatchingNmethodCost.class.getName() + ".fields.f"; - javaCode += "public static void accessFields() {"; - for (int i = 1; i <= accessedFieldCount; i++) { - javaCode += field + i + "= " + field + i + " + " + i + ";"; - } - javaCode += "}}"; - - BYTE_CODE = InMemoryJavaCompiler.compile(FIELD_USER, javaCode); - - fields = new Fields(); - - methods = new TestMethod[count]; - for (int i = 0; i < count; i++) { - var cl = createClassLoader().loadClass(FIELD_USER); - Method method = cl.getMethod("accessFields"); - methods[i] = new TestMethod(method); - methods[i].profile(); - methods[i].compile(); - } - } - - private static void initWhiteBox() { - WB = WhiteBox.getWhiteBox(); - } - - @Setup(Level.Trial) - public void setupCodeCache() throws Exception { - initWhiteBox(); - createTestMethods(accessedFieldCount, methodCount); - System.gc(); - } - - @Setup(Level.Iteration) - public void setupIteration() { - fields = new Fields(); - } - - @Benchmark - public void youngGC() throws Exception { - fields = null; - WB.youngGC(); - } - - @Benchmark - public void fullGC() throws Exception { - fields = null; - WB.fullGC(); - } - - @Benchmark - public void systemGC() throws Exception { - fields = null; - System.gc(); - } -} From 2a6d92fd2725ad0bc26c54275aecf591a83d20a4 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 25 Mar 2026 14:59:18 +0000 Subject: [PATCH 114/160] 8380703: Assertion failure in TestCodeEntryAlignment.java Reviewed-by: kvn, adinn --- src/hotspot/share/runtime/stubRoutines.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index 5246613738e..56d832d3fef 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -219,12 +219,12 @@ static BufferBlob* initialize_stubs(BlobId blob_id, if (STUBGEN_BLOB_FIELD_NAME(blob_name) == nullptr) { \ BlobId blob_id = BlobId:: JOIN3(stubgen, blob_name, id); \ int size = _ ## blob_name ## _code_size; \ - int max_aligned_size = 10; \ + int max_aligned_stubs = StubInfo::stub_count(blob_id); \ const char* timer_msg = "StubRoutines generation " # blob_name " stubs"; \ const char* name = "StubRoutines (" # blob_name " stubs)"; \ const char* assert_msg = "_" # blob_name "_code_size"; \ STUBGEN_BLOB_FIELD_NAME(blob_name) = \ - initialize_stubs(blob_id, size, max_aligned_size, timer_msg, \ + initialize_stubs(blob_id, size, max_aligned_stubs, timer_msg, \ name, assert_msg); \ } \ } From ca0bee78f51d5ff21cb318b9d4c8a702eca28c13 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Wed, 25 Mar 2026 15:19:32 +0000 Subject: [PATCH 115/160] 8374915: Move assertion in GenericTaskQueue<>::pop_global() Reviewed-by: tschatzl, ayang --- src/hotspot/share/gc/shared/taskqueue.inline.hpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/gc/shared/taskqueue.inline.hpp b/src/hotspot/share/gc/shared/taskqueue.inline.hpp index e77645f4fcf..b142aadc580 100644 --- a/src/hotspot/share/gc/shared/taskqueue.inline.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -279,13 +279,11 @@ typename GenericTaskQueue::PopResult GenericTaskQueue::pop_g // Increment top; if it wraps, also increment tag, to distinguish it // from any recent _age for the same top() index. idx_t new_top = increment_index(oldAge.top()); + // Don't use bottom, since a pop_local might have decremented it. + assert_not_underflow(localBot, new_top); idx_t new_tag = oldAge.tag() + ((new_top == 0) ? 1 : 0); Age newAge(new_top, new_tag); bool result = par_set_age(oldAge, newAge); - - // Note that using "bottom" here might fail, since a pop_local might - // have decremented it. - assert_not_underflow(localBot, newAge.top()); return result ? PopResult::Success : PopResult::Contended; } From cf424480f42ac220adee7034e0319cee0e9039db Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Wed, 25 Mar 2026 15:29:34 +0000 Subject: [PATCH 116/160] 8375275: Error handling to raise illegal_parameter or internal_error alert in hybrid key exchange Reviewed-by: wetmore, mpowers --- .../classes/sun/security/ssl/DHasKEM.java | 37 ++++++++++++- .../sun/security/ssl/KAKeyDerivation.java | 53 +++++++++++++++---- 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/DHasKEM.java b/src/java.base/share/classes/sun/security/ssl/DHasKEM.java index 763013f280c..ef5c5b82f06 100644 --- a/src/java.base/share/classes/sun/security/ssl/DHasKEM.java +++ b/src/java.base/share/classes/sun/security/ssl/DHasKEM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -101,7 +101,18 @@ public class DHasKEM implements KEMSpi { return new KEM.Encapsulated( sub(dh, from, to), pkEm, null); + + } catch (IllegalArgumentException e) { + // ECDH validation failure + // all-zero shared secret + throw e; + } catch (InvalidKeyException e) { + // Invalid peer public key + // Convert InvalidKeyException to an unchecked exception + throw new IllegalArgumentException("Invalid peer public key", + e); } catch (Exception e) { + // Unexpected internal failure throw new ProviderException("internal error", e); } } @@ -126,6 +137,11 @@ public class DHasKEM implements KEMSpi { PublicKey pkE = params.DeserializePublicKey(encapsulation); SecretKey dh = params.DH(algorithm, skR, pkE); return sub(dh, from, to); + + } catch (IllegalArgumentException e) { + // ECDH validation failure + // all-zero shared secret + throw e; } catch (IOException | InvalidKeyException e) { throw new DecapsulateException("Cannot decapsulate", e); } catch (Exception e) { @@ -248,7 +264,24 @@ public class DHasKEM implements KEMSpi { KeyAgreement ka = KeyAgreement.getInstance(kaAlgorithm); ka.init(skE); ka.doPhase(pkR, true); - return ka.generateSecret(alg); + SecretKey secret = ka.generateSecret(alg); + + // RFC 8446 section 7.4.2: checks for all-zero + // X25519/X448 shared secret. + if (kaAlgorithm.equals("X25519") || + kaAlgorithm.equals("X448")) { + byte[] s = secret.getEncoded(); + for (byte b : s) { + if (b != 0) { + return secret; + } + } + // Trigger ILLEGAL_PARAMETER alert + throw new IllegalArgumentException( + "All-zero shared secret"); + } + + return secret; } } } diff --git a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java index 39e82b50435..dea86351cc8 100644 --- a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package sun.security.ssl; import sun.security.util.RawKeySpec; +import javax.crypto.DecapsulateException; import javax.crypto.KDF; import javax.crypto.KEM; import javax.crypto.KeyAgreement; @@ -35,6 +36,7 @@ import javax.net.ssl.SSLHandshakeException; import java.io.IOException; import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.Provider; @@ -173,6 +175,9 @@ public class KAKeyDerivation implements SSLKeyDerivation { "encapsulation"); } + // All exceptions thrown during KEM encapsulation are mapped + // to TLS fatal alerts: + // illegal_parameter alert or internal_error alert. try { KeyFactory kf = (provider != null) ? KeyFactory.getInstance(algorithmName, provider) : @@ -189,8 +194,18 @@ public class KAKeyDerivation implements SSLKeyDerivation { SecretKey derived = deriveHandshakeSecret(algorithm, sharedSecret); return new KEM.Encapsulated(derived, enc.encapsulation(), null); - } catch (GeneralSecurityException gse) { - throw new SSLHandshakeException("Could not generate secret", gse); + } catch (IllegalArgumentException | InvalidKeyException e) { + // Peer validation failure + // ECDH all-zero shared secret (RFC 8446 section 7.4.2), + // ML-KEM encapsulation key check failure (FIPS-203 section 7.2) + throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, e); + } catch (GeneralSecurityException e) { + // Cryptographic failure, + // deriveHandshakeSecret failure. + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); + } catch (RuntimeException e) { + // unexpected provider/runtime failure + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); } finally { KeyUtil.destroySecretKeys(sharedSecret); } @@ -208,13 +223,30 @@ public class KAKeyDerivation implements SSLKeyDerivation { // Using KEM: called by the client after receiving the KEM // ciphertext (keyshare) from the server in ServerHello. // The client decapsulates it using its private key. - KEM kem = (provider != null) - ? KEM.getInstance(algorithmName, provider) - : KEM.getInstance(algorithmName); - var decapsulator = kem.newDecapsulator(localPrivateKey); - sharedSecret = decapsulator.decapsulate( - keyshare, 0, decapsulator.secretSize(), - "TlsPremasterSecret"); + + // All exceptions thrown during KEM decapsulation are mapped + // to TLS fatal alerts: + // illegal_parameter alert or internal_error alert. + try { + KEM kem = (provider != null) + ? KEM.getInstance(algorithmName, provider) + : KEM.getInstance(algorithmName); + var decapsulator = kem.newDecapsulator(localPrivateKey); + sharedSecret = decapsulator.decapsulate( + keyshare, 0, decapsulator.secretSize(), + "TlsPremasterSecret"); + } catch (IllegalArgumentException | InvalidKeyException | + DecapsulateException e) { + // Peer validation failure + // ECDH all-zero shared secret (RFC 8446 section 7.4.2) + throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, e); + } catch (GeneralSecurityException e) { + // cryptographic failure + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); + } catch (RuntimeException e) { + // unexpected provider/runtime failure + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); + } } else { // Using traditional DH-style Key Agreement KeyAgreement ka = KeyAgreement.getInstance(algorithmName); @@ -225,6 +257,7 @@ public class KAKeyDerivation implements SSLKeyDerivation { return deriveHandshakeSecret(type, sharedSecret); } catch (GeneralSecurityException gse) { + // deriveHandshakeSecret() failure throw new SSLHandshakeException("Could not generate secret", gse); } finally { KeyUtil.destroySecretKeys(sharedSecret); From 02972a5856a60f27fe6b914b38d3398bf6d2f9c7 Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Wed, 25 Mar 2026 15:52:04 +0000 Subject: [PATCH 117/160] 8379549: sun/security/ssl/SSLSocketImpl/SSLSocketSSLEngineCloseInbound.java failed with SocketException: Connection reset by peer Reviewed-by: wetmore, syan, dcubed --- .../SSLSocketSSLEngineCloseInbound.java | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketSSLEngineCloseInbound.java b/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketSSLEngineCloseInbound.java index e084bdd3367..dc0610ce8c8 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketSSLEngineCloseInbound.java +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketSSLEngineCloseInbound.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ /* * @test * @bug 8273553 8253368 + * @library /test/lib * @summary sun.security.ssl.SSLEngineImpl.closeInbound also has similar error * of JDK-8253368 * @run main/othervm SSLSocketSSLEngineCloseInbound TLSv1.3 @@ -79,12 +80,30 @@ * read() ... ChangeCipherSpec * read() ... Finished */ -import javax.net.ssl.*; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.*; -import java.io.*; -import java.net.*; -import java.security.*; -import java.nio.*; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManagerFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.security.KeyStore; +import java.security.Security; +import java.util.concurrent.CountDownLatch; + +import jdk.test.lib.Utils; public class SSLSocketSSLEngineCloseInbound { @@ -124,6 +143,8 @@ public class SSLSocketSSLEngineCloseInbound { private ByteBuffer cTOs; // "reliable" transport client->server private ByteBuffer sTOc; // "reliable" transport server->client + private final CountDownLatch serverReadyLatch = new CountDownLatch(1); + /* * The following is to set up the keystores/trust material. */ @@ -224,7 +245,7 @@ public class SSLSocketSSLEngineCloseInbound { // server-side socket that will read try (Socket socket = serverSocket.accept()) { - socket.setSoTimeout(500); + socket.setSoTimeout((int)Utils.adjustTimeout(500)); InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); @@ -289,6 +310,8 @@ public class SSLSocketSSLEngineCloseInbound { throw new Exception("Server session is not valid"); } + // Server signals client it has finished writing + serverReadyLatch.countDown(); return; } @@ -315,6 +338,8 @@ public class SSLSocketSSLEngineCloseInbound { } } } finally { + // Release the latch if an exception is thrown. + serverReadyLatch.countDown(); if (serverException != null) { if (clientException != null) { serverException.initCause(clientException); @@ -342,7 +367,8 @@ public class SSLSocketSSLEngineCloseInbound { public void run() { // client-side socket try (SSLSocket sslSocket = (SSLSocket)sslc.getSocketFactory(). - createSocket("localhost", port)) { + createSocket(InetAddress.getLoopbackAddress(), + port)) { clientSocket = sslSocket; OutputStream os = sslSocket.getOutputStream(); @@ -365,9 +391,9 @@ public class SSLSocketSSLEngineCloseInbound { throw new Exception("Client's session is not valid"); } - // Give server a chance to read before we shutdown via - // the try-with-resources block. - Thread.sleep(2000); + // Client waits for server to finish sending data + // before shutdown. + serverReadyLatch.await(); } catch (Exception e) { clientException = e; } From 331c7540175ce1e7f992a49102eaa6ff5b24c42b Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 25 Mar 2026 16:32:44 +0000 Subject: [PATCH 118/160] 8380523: Refactor TLAB slow allocation naming Reviewed-by: stefank, jsikstro --- .../gc/shared/threadLocalAllocBuffer.cpp | 176 +++++++++--------- .../gc/shared/threadLocalAllocBuffer.hpp | 44 ++--- .../shared/threadLocalAllocBuffer.inline.hpp | 2 +- src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 4 +- src/hotspot/share/runtime/vmStructs.cpp | 7 +- 5 files changed, 116 insertions(+), 117 deletions(-) diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp index 9d995234736..f9b8694eb04 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp @@ -37,7 +37,7 @@ #include "utilities/copy.hpp" size_t ThreadLocalAllocBuffer::_max_size = 0; -unsigned int ThreadLocalAllocBuffer::_target_refills = 0; +unsigned int ThreadLocalAllocBuffer::_target_num_refills = 0; ThreadLocalAllocBuffer::ThreadLocalAllocBuffer() : _start(nullptr), @@ -48,10 +48,10 @@ ThreadLocalAllocBuffer::ThreadLocalAllocBuffer() : _desired_size(0), _refill_waste_limit(0), _allocated_before_last_gc(0), - _number_of_refills(0), + _num_refills(0), _refill_waste(0), _gc_waste(0), - _slow_allocations(0), + _num_slow_allocations(0), _allocated_size(0), _allocation_fraction(TLABAllocationWeight) { @@ -81,7 +81,7 @@ void ThreadLocalAllocBuffer::accumulate_and_reset_statistics(ThreadLocalAllocSta print_stats("gc"); - if (_number_of_refills > 0) { + if (_num_refills > 0) { // Update allocation history if a reasonable amount of eden was allocated. bool update_allocation_history = used > 0.5 * capacity; @@ -98,16 +98,16 @@ void ThreadLocalAllocBuffer::accumulate_and_reset_statistics(ThreadLocalAllocSta _allocation_fraction.sample(alloc_frac); } - stats->update_fast_allocations(_number_of_refills, + stats->update_fast_allocations(_num_refills, _allocated_size, _gc_waste, _refill_waste); } else { - assert(_number_of_refills == 0 && _refill_waste == 0 && _gc_waste == 0, + assert(_num_refills == 0 && _refill_waste == 0 && _gc_waste == 0, "tlab stats == 0"); } - stats->update_slow_allocations(_slow_allocations); + stats->update_num_slow_allocations(_num_slow_allocations); reset_statistics(); } @@ -147,7 +147,7 @@ void ThreadLocalAllocBuffer::resize() { assert(ResizeTLAB, "Should not call this otherwise"); size_t alloc = (size_t)(_allocation_fraction.average() * (Universe::heap()->tlab_capacity() / HeapWordSize)); - size_t new_size = alloc / _target_refills; + size_t new_size = alloc / _target_num_refills; new_size = clamp(new_size, min_size(), max_size()); @@ -156,24 +156,24 @@ void ThreadLocalAllocBuffer::resize() { log_trace(gc, tlab)("TLAB new size: thread: " PTR_FORMAT " [id: %2d]" " refills %d alloc: %8.6f desired_size: %zu -> %zu", p2i(thread()), thread()->osthread()->thread_id(), - _target_refills, _allocation_fraction.average(), desired_size(), aligned_new_size); + _target_num_refills, _allocation_fraction.average(), desired_size(), aligned_new_size); set_desired_size(aligned_new_size); set_refill_waste_limit(initial_refill_waste_limit()); } void ThreadLocalAllocBuffer::reset_statistics() { - _number_of_refills = 0; - _refill_waste = 0; - _gc_waste = 0; - _slow_allocations = 0; - _allocated_size = 0; + _num_refills = 0; + _refill_waste = 0; + _gc_waste = 0; + _num_slow_allocations = 0; + _allocated_size = 0; } void ThreadLocalAllocBuffer::fill(HeapWord* start, HeapWord* top, size_t new_size) { - _number_of_refills++; + _num_refills++; _allocated_size += new_size; print_stats("fill"); assert(top <= start + new_size - alignment_reserve(), "size too small"); @@ -205,7 +205,7 @@ void ThreadLocalAllocBuffer::initialize() { size_t capacity = Universe::heap()->tlab_capacity() / HeapWordSize; if (capacity > 0) { // Keep alloc_frac as float and not double to avoid the double to float conversion - float alloc_frac = desired_size() * target_refills() / (float)capacity; + float alloc_frac = desired_size() * target_num_refills() / (float)capacity; _allocation_fraction.sample(alloc_frac); } @@ -219,10 +219,10 @@ void ThreadLocalAllocBuffer::startup_initialization() { // Assuming each thread's active tlab is, on average, // 1/2 full at a GC - _target_refills = 100 / (2 * TLABWasteTargetPercent); - // We need to set initial target refills to 2 to avoid a GC which causes VM + _target_num_refills = 100 / (2 * TLABWasteTargetPercent); + // We need to set the initial target number of refills to 2 to avoid a GC which causes VM // abort during VM initialization. - _target_refills = MAX2(_target_refills, 2U); + _target_num_refills = MAX2(_target_num_refills, 2U); // During jvm startup, the main thread is initialized // before the heap is initialized. So reinitialize it now. @@ -240,10 +240,10 @@ size_t ThreadLocalAllocBuffer::initial_desired_size() { init_sz = TLABSize / HeapWordSize; } else { // Initial size is a function of the average number of allocating threads. - unsigned int nof_threads = ThreadLocalAllocStats::allocating_threads_avg(); + unsigned int num_threads = ThreadLocalAllocStats::num_allocating_threads_avg(); init_sz = (Universe::heap()->tlab_capacity() / HeapWordSize) / - (nof_threads * target_refills()); + (num_threads * target_num_refills()); init_sz = align_object_size(init_sz); } // We can't use clamp() between min_size() and max_size() here because some @@ -271,10 +271,10 @@ void ThreadLocalAllocBuffer::print_stats(const char* tag) { " slow: %dB", tag, p2i(thrd), thrd->osthread()->thread_id(), _desired_size / (K / HeapWordSize), - _slow_allocations, _refill_waste_limit * HeapWordSize, + _num_slow_allocations, _refill_waste_limit * HeapWordSize, _allocation_fraction.average(), _allocation_fraction.average() * tlab_used / K, - _number_of_refills, waste_percent, + _num_refills, waste_percent, _gc_waste * HeapWordSize, _refill_waste * HeapWordSize); } @@ -299,17 +299,17 @@ HeapWord* ThreadLocalAllocBuffer::hard_end() { return _allocation_end + alignment_reserve(); } -PerfVariable* ThreadLocalAllocStats::_perf_allocating_threads; -PerfVariable* ThreadLocalAllocStats::_perf_total_refills; -PerfVariable* ThreadLocalAllocStats::_perf_max_refills; +PerfVariable* ThreadLocalAllocStats::_perf_num_allocating_threads; +PerfVariable* ThreadLocalAllocStats::_perf_total_num_refills; +PerfVariable* ThreadLocalAllocStats::_perf_max_num_refills; PerfVariable* ThreadLocalAllocStats::_perf_total_allocated_size; PerfVariable* ThreadLocalAllocStats::_perf_total_gc_waste; PerfVariable* ThreadLocalAllocStats::_perf_max_gc_waste; PerfVariable* ThreadLocalAllocStats::_perf_total_refill_waste; PerfVariable* ThreadLocalAllocStats::_perf_max_refill_waste; -PerfVariable* ThreadLocalAllocStats::_perf_total_slow_allocations; -PerfVariable* ThreadLocalAllocStats::_perf_max_slow_allocations; -AdaptiveWeightedAverage ThreadLocalAllocStats::_allocating_threads_avg(0); +PerfVariable* ThreadLocalAllocStats::_perf_total_num_slow_allocations; +PerfVariable* ThreadLocalAllocStats::_perf_max_num_slow_allocations; +AdaptiveWeightedAverage ThreadLocalAllocStats::_num_allocating_threads_avg(0); static PerfVariable* create_perf_variable(const char* name, PerfData::Units unit, TRAPS) { ResourceMark rm; @@ -317,47 +317,47 @@ static PerfVariable* create_perf_variable(const char* name, PerfData::Units unit } void ThreadLocalAllocStats::initialize() { - _allocating_threads_avg = AdaptiveWeightedAverage(TLABAllocationWeight); - _allocating_threads_avg.sample(1); // One allocating thread at startup + _num_allocating_threads_avg = AdaptiveWeightedAverage(TLABAllocationWeight); + _num_allocating_threads_avg.sample(1); // One allocating thread at startup if (UsePerfData) { EXCEPTION_MARK; - _perf_allocating_threads = create_perf_variable("allocThreads", PerfData::U_None, CHECK); - _perf_total_refills = create_perf_variable("fills", PerfData::U_None, CHECK); - _perf_max_refills = create_perf_variable("maxFills", PerfData::U_None, CHECK); - _perf_total_allocated_size = create_perf_variable("alloc", PerfData::U_Bytes, CHECK); - _perf_total_gc_waste = create_perf_variable("gcWaste", PerfData::U_Bytes, CHECK); - _perf_max_gc_waste = create_perf_variable("maxGcWaste", PerfData::U_Bytes, CHECK); - _perf_total_refill_waste = create_perf_variable("refillWaste", PerfData::U_Bytes, CHECK); - _perf_max_refill_waste = create_perf_variable("maxRefillWaste", PerfData::U_Bytes, CHECK); - _perf_total_slow_allocations = create_perf_variable("slowAlloc", PerfData::U_None, CHECK); - _perf_max_slow_allocations = create_perf_variable("maxSlowAlloc", PerfData::U_None, CHECK); + _perf_num_allocating_threads = create_perf_variable("allocThreads", PerfData::U_None, CHECK); + _perf_total_num_refills = create_perf_variable("fills", PerfData::U_None, CHECK); + _perf_max_num_refills = create_perf_variable("maxFills", PerfData::U_None, CHECK); + _perf_total_allocated_size = create_perf_variable("alloc", PerfData::U_Bytes, CHECK); + _perf_total_gc_waste = create_perf_variable("gcWaste", PerfData::U_Bytes, CHECK); + _perf_max_gc_waste = create_perf_variable("maxGcWaste", PerfData::U_Bytes, CHECK); + _perf_total_refill_waste = create_perf_variable("refillWaste", PerfData::U_Bytes, CHECK); + _perf_max_refill_waste = create_perf_variable("maxRefillWaste", PerfData::U_Bytes, CHECK); + _perf_total_num_slow_allocations = create_perf_variable("slowAlloc", PerfData::U_None, CHECK); + _perf_max_num_slow_allocations = create_perf_variable("maxSlowAlloc", PerfData::U_None, CHECK); } } ThreadLocalAllocStats::ThreadLocalAllocStats() : - _allocating_threads(0), - _total_refills(0), - _max_refills(0), + _num_allocating_threads(0), + _total_num_refills(0), + _max_num_refills(0), _total_allocated_size(0), _total_gc_waste(0), _max_gc_waste(0), _total_refill_waste(0), _max_refill_waste(0), - _total_slow_allocations(0), - _max_slow_allocations(0) {} + _total_num_slow_allocations(0), + _max_num_slow_allocations(0) {} -unsigned int ThreadLocalAllocStats::allocating_threads_avg() { - return MAX2((unsigned int)(_allocating_threads_avg.average() + 0.5), 1U); +unsigned int ThreadLocalAllocStats::num_allocating_threads_avg() { + return MAX2((unsigned int)(_num_allocating_threads_avg.average() + 0.5), 1U); } -void ThreadLocalAllocStats::update_fast_allocations(unsigned int refills, +void ThreadLocalAllocStats::update_fast_allocations(unsigned int num_refills, size_t allocated_size, size_t gc_waste, size_t refill_waste) { - _allocating_threads += 1; - _total_refills += refills; - _max_refills = MAX2(_max_refills, refills); + _num_allocating_threads += 1; + _total_num_refills += num_refills; + _max_num_refills = MAX2(_max_num_refills, num_refills); _total_allocated_size += allocated_size; _total_gc_waste += gc_waste; _max_gc_waste = MAX2(_max_gc_waste, gc_waste); @@ -365,35 +365,35 @@ void ThreadLocalAllocStats::update_fast_allocations(unsigned int refills, _max_refill_waste = MAX2(_max_refill_waste, refill_waste); } -void ThreadLocalAllocStats::update_slow_allocations(unsigned int allocations) { - _total_slow_allocations += allocations; - _max_slow_allocations = MAX2(_max_slow_allocations, allocations); +void ThreadLocalAllocStats::update_num_slow_allocations(unsigned int num_slow_allocations) { + _total_num_slow_allocations += num_slow_allocations; + _max_num_slow_allocations = MAX2(_max_num_slow_allocations, num_slow_allocations); } void ThreadLocalAllocStats::update(const ThreadLocalAllocStats& other) { - _allocating_threads += other._allocating_threads; - _total_refills += other._total_refills; - _max_refills = MAX2(_max_refills, other._max_refills); - _total_allocated_size += other._total_allocated_size; - _total_gc_waste += other._total_gc_waste; - _max_gc_waste = MAX2(_max_gc_waste, other._max_gc_waste); - _total_refill_waste += other._total_refill_waste; - _max_refill_waste = MAX2(_max_refill_waste, other._max_refill_waste); - _total_slow_allocations += other._total_slow_allocations; - _max_slow_allocations = MAX2(_max_slow_allocations, other._max_slow_allocations); + _num_allocating_threads += other._num_allocating_threads; + _total_num_refills += other._total_num_refills; + _max_num_refills = MAX2(_max_num_refills, other._max_num_refills); + _total_allocated_size += other._total_allocated_size; + _total_gc_waste += other._total_gc_waste; + _max_gc_waste = MAX2(_max_gc_waste, other._max_gc_waste); + _total_refill_waste += other._total_refill_waste; + _max_refill_waste = MAX2(_max_refill_waste, other._max_refill_waste); + _total_num_slow_allocations += other._total_num_slow_allocations; + _max_num_slow_allocations = MAX2(_max_num_slow_allocations, other._max_num_slow_allocations); } void ThreadLocalAllocStats::reset() { - _allocating_threads = 0; - _total_refills = 0; - _max_refills = 0; - _total_allocated_size = 0; - _total_gc_waste = 0; - _max_gc_waste = 0; - _total_refill_waste = 0; - _max_refill_waste = 0; - _total_slow_allocations = 0; - _max_slow_allocations = 0; + _num_allocating_threads = 0; + _total_num_refills = 0; + _max_num_refills = 0; + _total_allocated_size = 0; + _total_gc_waste = 0; + _max_gc_waste = 0; + _total_refill_waste = 0; + _max_refill_waste = 0; + _total_num_slow_allocations = 0; + _max_num_slow_allocations = 0; } void ThreadLocalAllocStats::publish() { @@ -401,7 +401,7 @@ void ThreadLocalAllocStats::publish() { return; } - _allocating_threads_avg.sample(_allocating_threads); + _num_allocating_threads_avg.sample(_num_allocating_threads); const size_t waste = _total_gc_waste + _total_refill_waste; const double waste_percent = percent_of(waste, _total_allocated_size); @@ -409,22 +409,22 @@ void ThreadLocalAllocStats::publish() { " slow allocs: %d max %d waste: %4.1f%%" " gc: %zuB max: %zuB" " slow: %zuB max: %zuB", - _allocating_threads, _total_refills, _max_refills, - _total_slow_allocations, _max_slow_allocations, waste_percent, + _num_allocating_threads, _total_num_refills, _max_num_refills, + _total_num_slow_allocations, _max_num_slow_allocations, waste_percent, _total_gc_waste * HeapWordSize, _max_gc_waste * HeapWordSize, _total_refill_waste * HeapWordSize, _max_refill_waste * HeapWordSize); if (UsePerfData) { - _perf_allocating_threads ->set_value(_allocating_threads); - _perf_total_refills ->set_value(_total_refills); - _perf_max_refills ->set_value(_max_refills); - _perf_total_allocated_size ->set_value(_total_allocated_size); - _perf_total_gc_waste ->set_value(_total_gc_waste); - _perf_max_gc_waste ->set_value(_max_gc_waste); - _perf_total_refill_waste ->set_value(_total_refill_waste); - _perf_max_refill_waste ->set_value(_max_refill_waste); - _perf_total_slow_allocations ->set_value(_total_slow_allocations); - _perf_max_slow_allocations ->set_value(_max_slow_allocations); + _perf_num_allocating_threads ->set_value(_num_allocating_threads); + _perf_total_num_refills ->set_value(_total_num_refills); + _perf_max_num_refills ->set_value(_max_num_refills); + _perf_total_allocated_size ->set_value(_total_allocated_size); + _perf_total_gc_waste ->set_value(_total_gc_waste); + _perf_max_gc_waste ->set_value(_max_gc_waste); + _perf_total_refill_waste ->set_value(_total_refill_waste); + _perf_max_refill_waste ->set_value(_max_refill_waste); + _perf_total_num_slow_allocations ->set_value(_total_num_slow_allocations); + _perf_max_num_slow_allocations ->set_value(_max_num_slow_allocations); } } diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp index 8c99523557e..67bc149013e 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp @@ -56,13 +56,13 @@ private: size_t _refill_waste_limit; // hold onto tlab if free() is larger than this uint64_t _allocated_before_last_gc; // total bytes allocated up until the last gc - static size_t _max_size; // maximum size of any TLAB - static unsigned _target_refills; // expected number of refills between GCs + static size_t _max_size; // maximum size of any TLAB + static unsigned _target_num_refills; // expected number of refills between GCs - unsigned _number_of_refills; + unsigned _num_refills; unsigned _refill_waste; unsigned _gc_waste; - unsigned _slow_allocations; + unsigned _num_slow_allocations; size_t _allocated_size; AdaptiveWeightedAverage _allocation_fraction; // fraction of eden allocated in tlabs @@ -79,7 +79,7 @@ private: size_t initial_refill_waste_limit(); - static int target_refills() { return _target_refills; } + static int target_num_refills() { return _target_num_refills; } size_t initial_desired_size(); size_t remaining(); @@ -98,9 +98,9 @@ private: // statistics - int number_of_refills() const { return _number_of_refills; } - int gc_waste() const { return _gc_waste; } - int slow_allocations() const { return _slow_allocations; } + int num_refills() const { return _num_refills; } + int gc_waste() const { return _gc_waste; } + int num_slow_allocations() const { return _num_slow_allocations; } public: ThreadLocalAllocBuffer(); @@ -179,41 +179,41 @@ public: class ThreadLocalAllocStats : public StackObj { private: - static PerfVariable* _perf_allocating_threads; - static PerfVariable* _perf_total_refills; - static PerfVariable* _perf_max_refills; + static PerfVariable* _perf_num_allocating_threads; + static PerfVariable* _perf_total_num_refills; + static PerfVariable* _perf_max_num_refills; static PerfVariable* _perf_total_allocated_size; static PerfVariable* _perf_total_gc_waste; static PerfVariable* _perf_max_gc_waste; static PerfVariable* _perf_total_refill_waste; static PerfVariable* _perf_max_refill_waste; - static PerfVariable* _perf_total_slow_allocations; - static PerfVariable* _perf_max_slow_allocations; + static PerfVariable* _perf_total_num_slow_allocations; + static PerfVariable* _perf_max_num_slow_allocations; - static AdaptiveWeightedAverage _allocating_threads_avg; + static AdaptiveWeightedAverage _num_allocating_threads_avg; - unsigned int _allocating_threads; - unsigned int _total_refills; - unsigned int _max_refills; + unsigned int _num_allocating_threads; + unsigned int _total_num_refills; + unsigned int _max_num_refills; size_t _total_allocated_size; size_t _total_gc_waste; size_t _max_gc_waste; size_t _total_refill_waste; size_t _max_refill_waste; - unsigned int _total_slow_allocations; - unsigned int _max_slow_allocations; + unsigned int _total_num_slow_allocations; + unsigned int _max_num_slow_allocations; public: static void initialize(); - static unsigned int allocating_threads_avg(); + static unsigned int num_allocating_threads_avg(); ThreadLocalAllocStats(); - void update_fast_allocations(unsigned int refills, + void update_fast_allocations(unsigned int num_refills, size_t allocated_size, size_t gc_waste, size_t refill_waste); - void update_slow_allocations(unsigned int allocations); + void update_num_slow_allocations(unsigned int num_slow_allocations); void update(const ThreadLocalAllocStats& other); void reset(); diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp index 441686c5c4c..727467f98d0 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp @@ -82,7 +82,7 @@ void ThreadLocalAllocBuffer::record_slow_allocation(size_t obj_size) { set_refill_waste_limit(refill_waste_limit() + refill_waste_limit_increment()); - _slow_allocations++; + _num_slow_allocations++; log_develop_trace(gc, tlab)("TLAB: %s thread: " PTR_FORMAT " [id: %2d]" " obj: %zu" diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 74314b0ad61..ac532e1bb4c 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -450,8 +450,8 @@ nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \ - nonstatic_field(ThreadLocalAllocBuffer, _number_of_refills, unsigned) \ - nonstatic_field(ThreadLocalAllocBuffer, _slow_allocations, unsigned) \ + nonstatic_field(ThreadLocalAllocBuffer, _num_refills, unsigned) \ + nonstatic_field(ThreadLocalAllocBuffer, _num_slow_allocations, unsigned) \ \ nonstatic_field(SafepointMechanism::ThreadData, _polling_word, volatile uintptr_t) \ nonstatic_field(SafepointMechanism::ThreadData, _polling_page, volatile uintptr_t) \ diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 93e0ff2f3b6..48f3f078ae8 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -335,11 +335,11 @@ nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \ - static_field(ThreadLocalAllocBuffer, _target_refills, unsigned) \ - nonstatic_field(ThreadLocalAllocBuffer, _number_of_refills, unsigned) \ + static_field(ThreadLocalAllocBuffer, _target_num_refills, unsigned) \ + nonstatic_field(ThreadLocalAllocBuffer, _num_refills, unsigned) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste, unsigned) \ nonstatic_field(ThreadLocalAllocBuffer, _gc_waste, unsigned) \ - nonstatic_field(ThreadLocalAllocBuffer, _slow_allocations, unsigned) \ + nonstatic_field(ThreadLocalAllocBuffer, _num_slow_allocations, unsigned) \ nonstatic_field(VirtualSpace, _low_boundary, char*) \ nonstatic_field(VirtualSpace, _high_boundary, char*) \ nonstatic_field(VirtualSpace, _low, char*) \ @@ -2142,4 +2142,3 @@ void vmStructs_init() { VMStructs::init(); } #endif // ASSERT - From 28529282545f6b59596a445409d59398253176f1 Mon Sep 17 00:00:00 2001 From: Ana-Maria Mihalceanu Date: Wed, 25 Mar 2026 17:20:46 +0000 Subject: [PATCH 119/160] 8380663: Update jcmd man page to include AOT.end_recording diagnostic command Reviewed-by: kevinw, kvn --- src/jdk.jcmd/share/man/jcmd.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/jdk.jcmd/share/man/jcmd.md b/src/jdk.jcmd/share/man/jcmd.md index af3886a915c..23dfa67d864 100644 --- a/src/jdk.jcmd/share/man/jcmd.md +++ b/src/jdk.jcmd/share/man/jcmd.md @@ -134,6 +134,16 @@ The following commands are available: - `-all`: (Optional) Show help for all commands (BOOLEAN, false) . +`AOT.end_recording` +: Ends an in-progress AOT training and records the results to the file(s) specified by `-XX:AOTConfiguration` and/or `-XX:AOTCacheOutput`. + + Impact: Low + + **Note:** + + The JVM must be started in AOT training mode using command-line arguments such as `-XX:AOTMode=record` or `-XX:AOTCacheOutput=`. + The results of the AOT training can be an AOT configuration file, an AOT cache file, or both. + `Compiler.CodeHeap_Analytics` \[*function*\] \[*granularity*\] : Print CodeHeap analytics From 88bdbb78b2565e559d6f96cd099770951f17e8cc Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Wed, 25 Mar 2026 18:26:24 +0000 Subject: [PATCH 120/160] 8380472: Clean up test/jdk/jdk/nio/zipfs/PathOps.java shared file system usage Reviewed-by: lancea, jpai --- test/jdk/jdk/nio/zipfs/PathOps.java | 421 +++++++++++++--------------- 1 file changed, 190 insertions(+), 231 deletions(-) diff --git a/test/jdk/jdk/nio/zipfs/PathOps.java b/test/jdk/jdk/nio/zipfs/PathOps.java index a035d425a3d..86b7946eba2 100644 --- a/test/jdk/jdk/nio/zipfs/PathOps.java +++ b/test/jdk/jdk/nio/zipfs/PathOps.java @@ -21,7 +21,6 @@ * questions. */ -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -30,20 +29,20 @@ import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import java.io.IOException; -import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.ProviderMismatchException; +import java.util.Objects; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test @@ -55,47 +54,48 @@ import static org.junit.jupiter.api.Assertions.fail; */ public class PathOps { - static FileSystem fs; + // create empty JAR file, testing doesn't require any contents + static Path emptyJar; - // This test uses a static file system since some ops tested on - // Path depend on the same underlying `fs` instance @BeforeAll static void setup() throws IOException { - // create empty JAR file, test doesn't require any contents - Path emptyJar = Utils.createJarFile("empty.jar"); - fs = FileSystems.newFileSystem(emptyJar); - } - - @AfterAll - static void cleanup() throws IOException { - fs.close(); + emptyJar = Utils.createJarFile("empty.jar"); } + // Ensure NPEs are thrown for null inputs on Path ops @Test - void nullPointerTest() { - Path path = fs.getPath("foo"); - assertThrows(NullPointerException.class, () -> path.resolve((String) null)); - assertThrows(NullPointerException.class, () -> path.relativize(null)); - assertThrows(NullPointerException.class, () -> path.compareTo(null)); - assertThrows(NullPointerException.class, () -> path.startsWith((Path) null)); - assertThrows(NullPointerException.class, () -> path.endsWith((Path) null)); + void nullPointerTest() throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + Path path = fs.getPath("foo"); + assertThrows(NullPointerException.class, () -> path.resolve((String) null)); + assertThrows(NullPointerException.class, () -> path.relativize(null)); + assertThrows(NullPointerException.class, () -> path.compareTo(null)); + assertThrows(NullPointerException.class, () -> path.startsWith((Path) null)); + assertThrows(NullPointerException.class, () -> path.endsWith((Path) null)); + } } + // Ensure correct behavior when paths are provided by mismatched providers @Test - void mismatchedProvidersTest() { - Path path = fs.getPath("foo"); + void mismatchedProvidersTest() throws IOException { Path other = Paths.get("foo"); - assertThrows(ProviderMismatchException.class, () -> path.compareTo(other)); - assertThrows(ProviderMismatchException.class, () -> path.resolve(other)); - assertThrows(ProviderMismatchException.class, () -> path.relativize(other)); - assertFalse(path.startsWith(other), "providerMismatched startsWith() returns true "); - assertFalse(path.endsWith(other), "providerMismatched endsWith() returns true "); + try (var fs = FileSystems.newFileSystem(emptyJar)) { + Path path = fs.getPath("foo"); + assertThrows(ProviderMismatchException.class, () -> path.compareTo(other)); + assertThrows(ProviderMismatchException.class, () -> path.resolve(other)); + assertThrows(ProviderMismatchException.class, () -> path.relativize(other)); + assertFalse(path.startsWith(other), "providerMismatched startsWith() returns true "); + assertFalse(path.endsWith(other), "providerMismatched endsWith() returns true "); + } } + // Ensure correct construction of paths when given sequence of strings @ParameterizedTest @MethodSource - void constructionTest(String first, String[] more, String expected) { - string(getPath(first, more), expected); + void constructionTest(String first, String[] more, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + string(fs.getPath(first, more), expected); + } } static Stream constructionTest() { @@ -112,22 +112,29 @@ public class PathOps { ); } + // Ensure proper root, parent, and name components @Test - void allComponentsTest() { - var path = getPath("/a/b/c"); - root(path, "/"); - parent(path, "/a/b"); - name(path, "c"); + void allComponentsTest() throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath("/a/b/c"); + root(path, "/"); + parent(path, "/a/b"); + name(path, "c"); + } } + // Ensure correct name count for root only and empty name @ParameterizedTest @MethodSource - void nameCountTest(String first, String root, String parent, String name, int nameCount) { - var path = getPath(first); - root(path, root); - parent(path, parent); - name(path, name); - nameCount(path, nameCount); + void nameCountTest(String first, String root, String parent, String name, int nameCount) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + root(path, root); + parent(path, parent); + name(path, name); + assertNotNull(path); + assertEquals(nameCount, path.getNameCount()); + } } static Stream nameCountTest() { @@ -139,13 +146,16 @@ public class PathOps { ); } + // Ensure correct parent and name behavior for no root and name only @ParameterizedTest @MethodSource - void parentNameTest(String first, String root, String parent, String name) { - var path = getPath(first); - root(path, root); - parent(path, parent); - name(path, name); + void parentNameTest(String first, String root, String parent, String name) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + root(path, root); + parent(path, parent); + name(path, name); + } } static Stream parentNameTest() { @@ -157,10 +167,16 @@ public class PathOps { ); } + // Ensure correct (positive) `startsWith` behavior @ParameterizedTest @MethodSource - void startsWithTest(String first, String prefix) { - starts(getPath(first), prefix); + void startsWithTest(String first, String prefix) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + Path s = fs.getPath(prefix); + assertTrue(path.startsWith(s)); + } } static Stream startsWithTest() { @@ -180,10 +196,16 @@ public class PathOps { ); } + // Ensure correct (negative) `startsWith` behavior @ParameterizedTest @MethodSource - void notStartsWithTest(String first, String prefix) { - notStarts(getPath(first), prefix); + void notStartsWithTest(String first, String prefix) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + Path s = fs.getPath(prefix); + assertFalse(path.startsWith(s)); + } } static Stream notStartsWithTest() { @@ -203,10 +225,16 @@ public class PathOps { ); } + // Ensure correct (positive) `endsWith` behavior @ParameterizedTest @MethodSource - void endsWithTest(String first, String suffix) { - ends(getPath(first), suffix); + void endsWithTest(String first, String suffix) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + Path s = fs.getPath(suffix); + assertTrue(path.endsWith(s)); + } } static Stream endsWithTest() { @@ -231,10 +259,16 @@ public class PathOps { ); } + // Ensure correct (negative) `endsWith` behavior @ParameterizedTest @MethodSource - void notEndsWithTest(String first, String suffix) { - notEnds(getPath(first), suffix); + void notEndsWithTest(String first, String suffix) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + Path s = fs.getPath(suffix); + assertFalse(path.endsWith(s)); + } } static Stream notEndsWithTest() { @@ -248,10 +282,15 @@ public class PathOps { ); } + // Ensure `getName` returns correct String at index @ParameterizedTest @MethodSource - void elementTest(int index, String expected) { - element(getPath("a/b/c"), index, expected); + void elementTest(int index, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath("a/b/c"); + assertNotNull(path); + assertEquals(expected, path.getName(index).toString()); + } } static Stream elementTest() { @@ -262,22 +301,35 @@ public class PathOps { ); } + // Ensure expected behavior for absolute paths @ParameterizedTest @ValueSource(strings = {"/", "/tmp"} ) - void isAbsoluteTest(String first) { - absolute(getPath(first)); + void isAbsoluteTest(String first) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + assertTrue(path.isAbsolute()); + } } + // Ensure expected behavior for non-absolute paths @ParameterizedTest @ValueSource(strings = {"tmp", ""} ) - void notAbsoluteTest(String first) { - notAbsolute(getPath(first)); + void notAbsoluteTest(String first) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + assertFalse(path.isAbsolute()); + } } + // Ensure correct append and replacement behavior for `resolve(String)` @ParameterizedTest @MethodSource - void resolveTest(String first, String other, String expected) { - resolve(getPath(first), other, expected); + void resolveTest(String first, String other, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + resolve(fs.getPath(first), other, expected); + } } static Stream resolveTest() { @@ -298,10 +350,15 @@ public class PathOps { ); } + // Ensure correct append and replacement behavior for `resolve(Path)` @ParameterizedTest @MethodSource - void resolvePathTest(String first, String other, String expected) { - resolvePath(getPath(first), other, expected); + void resolvePathTest(String first, String other, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + assertEquals(expected, path.resolve(fs.getPath(other)).toString()); + } } static Stream resolvePathTest() { @@ -322,10 +379,13 @@ public class PathOps { ); } + // Ensure correct behavior for `resolveSibling` @ParameterizedTest @MethodSource - void resolveSiblingTest(String first, String other, String expected) { - resolveSibling(getPath(first), other, expected); + void resolveSiblingTest(String first, String other, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + resolveSibling(fs.getPath(first), other, expected); + } } static Stream resolveSiblingTest() { @@ -345,18 +405,28 @@ public class PathOps { ); } + // Checking `resolve` and `resolveSibling` behavior for empty path @Test - void resolveSiblingAndResolveTest() { - var path = getPath(""); - resolveSibling(path, "foo", "foo"); - resolveSibling(path, "/foo", "/foo"); - resolve(path, "", ""); + void resolveSiblingAndResolveTest() throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(""); + resolveSibling(path, "foo", "foo"); + resolveSibling(path, "/foo", "/foo"); + resolve(path, "", ""); + } } + // Ensure correct behavior of `relativize`. i.e. Relative path should be + // produced between two given paths @ParameterizedTest @MethodSource - void relativizeTest(String first, String other, String expected) { - relativize(getPath(first), other, expected); + void relativizeTest(String first, String other, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + Path that = fs.getPath(other); + assertEquals(expected, path.relativize(that).toString()); + } } static Stream relativizeTest() { @@ -380,10 +450,15 @@ public class PathOps { ); } + // Ensure correct behavior of `normalize`. i.e. redundant elements should be removed. @ParameterizedTest @MethodSource - void normalizeTest(String first, String expected) { - normalize(getPath(first), expected); + void normalizeTest(String first, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + assertEquals(expected, path.normalize().toString()); + } } static Stream normalizeTest() { @@ -409,10 +484,13 @@ public class PathOps { ); } + // Check IPE is thrown for invalid path Strings @ParameterizedTest @MethodSource - void invalidTest(String first) { - assertThrows(InvalidPathException.class, () -> getPath(first)); + void invalidTest(String first) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + assertThrows(InvalidPathException.class, () -> fs.getPath(first)); + } } static Stream invalidTest() { @@ -426,184 +504,65 @@ public class PathOps { ); } + // Check that repeated forward slash is normalized correctly @Test - void normalizationTest() { - var path = getPath("//foo//bar"); - string(path, "/foo/bar"); - root(path, "/"); - parent(path, "/foo"); - name(path, "bar"); + void normalizationTest() throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath("//foo//bar"); + string(path, "/foo/bar"); + root(path, "/"); + parent(path, "/foo"); + name(path, "bar"); + } } - @Test - void isSameFileTest() { - isSameFile(getPath("/fileDoesNotExist"), "/fileDoesNotExist"); + @Test // Check that identical paths refer to the same file + void isSameFileTest() throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath("/fileDoesNotExist"); + assertNotNull(path); + assertTrue(Files.isSameFile(path, fs.getPath("/fileDoesNotExist"))); + } } + // Regression test for 8139956: Ensure `relativize` of equivalent paths + // produces an empty path -> `getNameCount` returns 1 @Test - void getNameCountTest() { - // 8139956 - System.out.println("check getNameCount"); - int nc = fs.getPath("/").relativize(fs.getPath("/")).getNameCount(); - assertEquals(1, nc, "getNameCount of empty path failed"); + void getNameCountTest() throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + int nc = fs.getPath("/").relativize(fs.getPath("/")).getNameCount(); + assertEquals(1, nc, "getNameCount of empty path failed"); + } } // Utilities for testing - - static void checkPath(Path path) { - assertNotNull(path, "path is null"); - } - - static void check(Object result, String expected) { - if (result == null) { - if (expected == null) return; - } else { - // compare string representations - if (expected != null && result.toString().equals(expected)) - return; - } - fail(); - } - - static void check(Object result, boolean expected) { - check(result, Boolean.toString(expected)); - } - - static void check(Object result, int expected) { - check(result, Integer.toString(expected)); - } - static void root(Path path, String expected) { - System.out.println("check root"); - checkPath(path); - check(path.getRoot(), expected); + assertNotNull(path); + assertEquals(expected, Objects.toString(path.getRoot(), null)); } static void parent(Path path, String expected) { - System.out.println("check parent"); - checkPath(path); - check(path.getParent(), expected); + assertNotNull(path); + assertEquals(expected, Objects.toString(path.getParent(), null)); } static void name(Path path, String expected) { - System.out.println("check name"); - checkPath(path); - check(path.getFileName(), expected); - } - - static void nameCount(Path path, int expected) { - System.out.println("check nameCount"); - checkPath(path); - check(path.getNameCount(), expected); - } - - static void element(Path path, int index, String expected) { - System.out.format("check element %d\n", index); - checkPath(path); - check(path.getName(index), expected); - } - - static void subpath(Path path, int startIndex, int endIndex, String expected) { - System.out.format("test subpath(%d,%d)\n", startIndex, endIndex); - checkPath(path); - check(path.subpath(startIndex, endIndex), expected); - } - - static void starts(Path path, String prefix) { - System.out.format("test startsWith with %s\n", prefix); - checkPath(path); - Path s = fs.getPath(prefix); - check(path.startsWith(s), true); - } - - static void notStarts(Path path, String prefix) { - System.out.format("test not startsWith with %s\n", prefix); - checkPath(path); - Path s = fs.getPath(prefix); - check(path.startsWith(s), false); - } - - static void ends(Path path, String suffix) { - System.out.format("test endsWith %s\n", suffix); - checkPath(path); - Path s = fs.getPath(suffix); - check(path.endsWith(s), true); - } - - static void notEnds(Path path, String suffix) { - System.out.format("test not endsWith %s\n", suffix); - checkPath(path); - Path s = fs.getPath(suffix); - check(path.endsWith(s), false); - } - - static void absolute(Path path) { - System.out.println("check path is absolute"); - checkPath(path); - check(path.isAbsolute(), true); - } - - static void notAbsolute(Path path) { - System.out.println("check path is not absolute"); - checkPath(path); - check(path.isAbsolute(), false); + assertNotNull(path); + assertEquals(expected, Objects.toString(path.getFileName(), null)); } static void resolve(Path path, String other, String expected) { - System.out.format("test resolve %s\n", other); - checkPath(path); - check(path.resolve(other), expected); - } - - static void resolvePath(Path path, String other, String expected) { - System.out.format("test resolve %s\n", other); - checkPath(path); - check(path.resolve(fs.getPath(other)), expected); + assertNotNull(path); + assertEquals(expected, path.resolve(other).toString()); } static void resolveSibling(Path path, String other, String expected) { - System.out.format("test resolveSibling %s\n", other); - checkPath(path); - check(path.resolveSibling(other), expected); - - } - - static void relativize(Path path, String other, String expected) { - System.out.format("test relativize %s\n", other); - checkPath(path); - Path that = fs.getPath(other); - check(path.relativize(that), expected); - - } - - static void normalize(Path path, String expected) { - System.out.println("check normalized path"); - checkPath(path); - check(path.normalize(), expected); - + assertNotNull(path); + assertEquals(expected, path.resolveSibling(other).toString()); } static void string(Path path, String expected) { - System.out.println("check string representation"); - checkPath(path); - check(path, expected); - } - - static void isSameFile(Path path, String target) { - try { - System.out.println("check two paths are same"); - checkPath(path); - check(Files.isSameFile(path, fs.getPath(target)), true); - } catch (IOException ioe) { - fail(); - } - } - - static Path getPath(String s) { - return fs.getPath(s); - } - - static Path getPath(String first, String... more) { - return fs.getPath(first, more); + assertNotNull(path); + assertEquals(expected, path.toString()); } } From 3588fb79691b3829bab214a4bb1517fd382cccaf Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Wed, 25 Mar 2026 22:07:41 +0000 Subject: [PATCH 121/160] 8380825: AOT tests should run in two step training mode by default Reviewed-by: kvn, vlivanov --- .../aotCache/AOTCacheSupportForCustomLoaders.java | 10 ++++++++-- .../runtime/cds/appcds/aotClassLinking/AddOpens.java | 6 +++--- .../runtime/cds/appcds/aotClassLinking/AddReads.java | 8 +++----- .../aotCode/AOTCodeCPUFeatureIncompatibilityTest.java | 11 +++++++---- test/lib/jdk/test/lib/cds/CDSAppTester.java | 11 ++++++++++- 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCacheSupportForCustomLoaders.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCacheSupportForCustomLoaders.java index 9461957ef2c..1a8313a3058 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCacheSupportForCustomLoaders.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCacheSupportForCustomLoaders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,10 +76,12 @@ public class AOTCacheSupportForCustomLoaders { } class AppWithCustomLoaders { + static MyLoader loader; // keep alive so its classes can be cached. + public static void main(String args[]) throws Exception { File custJar = new File("cust.jar"); URL[] urls = new URL[] {custJar.toURI().toURL()}; - MyLoader loader = new MyLoader(urls, AppWithCustomLoaders.class.getClassLoader()); + loader = new MyLoader(urls, AppWithCustomLoaders.class.getClassLoader()); test1(loader); test2(loader); @@ -107,6 +109,7 @@ class AppWithCustomLoaders { } // Test 3: custom loader defines a class from the exact location as a class defined in the boot layer. + static Class test3_c1_keepalive; static void test3(String modulePath) throws Exception { Class c0 = Class.forName("com.test.Foo"); System.out.println(c0); @@ -130,6 +133,9 @@ class AppWithCustomLoaders { System.out.println(c1.getModule().getName()); System.out.println(c1.getClassLoader()); + // Keep c1 alive so it can be cached. + test3_c1_keepalive = c1; + if (!c1.getModule().getName().equals("com.test")) { throw new RuntimeException("Unexpected module: " + c1.getModule()); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddOpens.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddOpens.java index d3626d30c7e..f7e15f8fae0 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddOpens.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddOpens.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -157,7 +157,7 @@ public class AddOpens { out.shouldContain(expectedOutput[0]); out.shouldContain(expectedOutput[1]); } else if (runMode == RunMode.ASSEMBLY) { - out.shouldContain("full module graph: enabled"); + out.shouldMatch("(full module graph: enabled)|(Full module graph = enabled)"); } } } @@ -200,7 +200,7 @@ public class AddOpens { out.shouldContain(expectedOutput[0]); out.shouldContain(expectedOutput[1]); } else if (runMode == RunMode.ASSEMBLY) { - out.shouldContain("full module graph: enabled"); + out.shouldMatch("(full module graph: enabled)|(Full module graph = enabled)"); } } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddReads.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddReads.java index 5c9364aa079..95fc9f0f700 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddReads.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddReads.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,8 +65,6 @@ public class AddReads { "[class,load] com.norequires.Main source: shared objects file"; private static final String sharedClassB = "[class,load] org.astro.World source: shared objects file"; - private static final String fmgEnabled = "full module graph: enabled"; - private static final String fmgDisabled = "full module graph: disabled"; private static final String cannotAccess = "class com.norequires.Main (in module com.norequires) cannot access class org.astro.World (in module org.astro)"; @@ -166,8 +164,8 @@ public class AddReads { out.shouldContain("full module graph: disabled"); out.shouldContain("Mismatched values for property jdk.module.addreads: runtime com.norequires=ALL-UNNAMED dump time com.norequires=org.astro"); } else if (runMode == RunMode.ASSEMBLY) { - out.shouldContain("full module graph: enabled"); - } else { + out.shouldMatch("(full module graph: enabled)|(Full module graph = enabled)"); + } else { out.shouldHaveExitValue(0); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java index 8d1e190d1e3..e9e610330ad 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java @@ -76,15 +76,18 @@ public class AOTCodeCPUFeatureIncompatibilityTest { @Override public String[] vmArgs(RunMode runMode) { if (runMode == RunMode.PRODUCTION) { - return new String[] {vmOption, "-Xlog:aot+codecache+init=debug"}; + return new String[] {vmOption, "-Xlog:aot+codecache*=debug"}; + } else { + return new String[] {"-Xlog:aot+codecache*=debug"}; } - return new String[] {}; } @Override public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { - if (runMode == RunMode.ASSEMBLY) { + if (runMode == RunMode.ASSEMBLY || runMode == RunMode.PRODUCTION) { out.shouldMatch("CPU features recorded in AOTCodeCache:.*" + featureName + ".*"); - } else if (runMode == RunMode.PRODUCTION) { + } + + if (runMode == RunMode.PRODUCTION) { out.shouldMatch("AOT Code Cache disabled: required cpu features are missing:.*" + featureName + ".*"); out.shouldContain("Unable to use AOT Code Cache"); } diff --git a/test/lib/jdk/test/lib/cds/CDSAppTester.java b/test/lib/jdk/test/lib/cds/CDSAppTester.java index 18356dd8aa9..3eac8a35a37 100644 --- a/test/lib/jdk/test/lib/cds/CDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/CDSAppTester.java @@ -480,11 +480,20 @@ abstract public class CDSAppTester { // See JEP 483 public void runAOTWorkflow(String... args) throws Exception { this.workflow = Workflow.AOT; - boolean oneStepTraining = true; // by default use onestep trainning + + // By default use twostep training -- tests are much easier to write this way, as + // the stdout/stderr of the training run is clearly separated from the assembly phase. + // + // Many older test cases written before JEP 514 were not aware of one step treaining + // and may not check the stdout/stderr correctly. + boolean oneStepTraining = false; if (System.getProperty("CDSAppTester.two.step.training") != null) { oneStepTraining = false; } + if (System.getProperty("CDSAppTester.one.step.training") != null) { + oneStepTraining = true; + } if (args.length > 1) { // Tests such as test/hotspot/jtreg/runtime/cds/appcds/aotCache/SpecialCacheNames.java From a55656d2f938ea7ca11b7022f4bfe63f124183cf Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Thu, 26 Mar 2026 01:53:36 +0000 Subject: [PATCH 122/160] 8380789: RISC-V: TestOpaqueConstantBoolNodes.java fails when running without RVV Reviewed-by: fyang --- .../compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java index b8db97f7ecb..c32eba5aa77 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java @@ -26,6 +26,7 @@ * @bug 8374582 * @summary Tests the creation and removal of opaque nodes at range checks points in string intrinsics. * @requires vm.flagless + * @requires os.arch != "riscv64" | vm.cpu.features ~= ".*rvv.*" * @library /test/lib / * @run driver ${test.main.class} */ From ceb109fbd8a382b8c8afa73bdeffc360d352d935 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Thu, 26 Mar 2026 01:59:10 +0000 Subject: [PATCH 123/160] 8380081: jpackage: Incorrect descriptions of win-specific options Reviewed-by: nlisker, almatvee --- .../internal/resources/HelpResources.properties | 14 +++++++++----- src/jdk.jpackage/share/man/jpackage.md | 11 +++++++---- .../jdk/jpackage/internal/cli/help-windows.txt | 14 +++++++++----- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties index 0b2ca83a7d7..7f570e71330 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/resources/HelpResources.properties @@ -405,20 +405,24 @@ help.option.win-help-url=\ \ URL where user can obtain further information or technical support help.option.win-menu=\ -\ Request to add a Start menu shortcut for this application +\ Adds a Start menu shortcut for this application, or requests\n\ +\ to do so if --win-shortcut-prompt is specified help.option.win-menu-group=\ \ Start Menu group this application is placed in help.option.win-per-user-install=\ -\ Request to perform an install on a per-user basis +\ Installs the application on a per-user basis.\n\ +\ Without this option installs per-machine help.option.win-shortcut=\ -\ Request to add desktop shortcut for this application +\ Adds a desktop shortcut for this application, or requests\n\ +\ to do so if --win-shortcut-prompt is specified help.option.win-shortcut-prompt=\ -\ Adds a dialog to enable the user to choose if shortcuts\n\ -\ will be created by installer. +\ Adds a dialog if at least one of --win-menu or --win-shortcut\n\ +\ are specified to enable the user to choose if these shortcuts\n\ +\ will be created by the installer help.option.win-update-url=\ \ URL of available application update information diff --git a/src/jdk.jpackage/share/man/jpackage.md b/src/jdk.jpackage/share/man/jpackage.md index 9ba5949866f..156b05307b7 100644 --- a/src/jdk.jpackage/share/man/jpackage.md +++ b/src/jdk.jpackage/share/man/jpackage.md @@ -410,7 +410,8 @@ The `jpackage` tool will take as input a Java application and a Java run-time im `--win-menu` -: Request to add a Start Menu shortcut for this application +: Adds a Start menu shortcut for this application, or requests to do so + if --win-shortcut-prompt is specified `--win-menu-group` *menu-group-name* @@ -418,15 +419,17 @@ The `jpackage` tool will take as input a Java application and a Java run-time im `--win-per-user-install` -: Request to perform an install on a per-user basis +: Installs the application on a per-user basis. Without this option installs per-machine `--win-shortcut` -: Request to create a desktop shortcut for this application +: Adds a desktop shortcut for this application, or requests to do so + if --win-shortcut-prompt is specified `--win-shortcut-prompt` -: Adds a dialog to enable the user to choose if shortcuts will be created by installer +: Adds a dialog if at least one of --win-menu or --win-shortcut are specified + to enable the user to choose if these shortcuts will be created by the installer `--win-update-url` *url* diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/help-windows.txt b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/help-windows.txt index 7a098b847ad..e30a5bb7f9b 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/help-windows.txt +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/help-windows.txt @@ -189,16 +189,20 @@ Platform dependent options for creating the application package: --win-help-url URL where user can obtain further information or technical support --win-menu [] - Request to add a Start menu shortcut for this application + Adds a Start menu shortcut for this application, or requests + to do so if --win-shortcut-prompt is specified --win-menu-group

Start Menu group this application is placed in --win-per-user-install - Request to perform an install on a per-user basis + Installs the application on a per-user basis. + Without this option installs per-machine --win-shortcut [] - Request to add desktop shortcut for this application + Adds a desktop shortcut for this application, or requests + to do so if --win-shortcut-prompt is specified --win-shortcut-prompt - Adds a dialog to enable the user to choose if shortcuts - will be created by installer. + Adds a dialog if at least one of --win-menu or --win-shortcut + are specified to enable the user to choose if these shortcuts + will be created by the installer --win-update-url URL of available application update information --win-upgrade-uuid From 69deec20163561b40abce1faa4e82217afdc8efd Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Thu, 26 Mar 2026 02:16:00 +0000 Subject: [PATCH 124/160] 8378985: serviceability/sa/TestJhsdbJstackMixedWithXComp.java failed if sender frame is return barrier of Continuation Reviewed-by: cjplummer, mdoerr, fyang --- src/hotspot/share/runtime/vmStructs.cpp | 2 + .../sun/jvm/hotspot/runtime/Continuation.java | 60 +++++++++++++++++++ .../hotspot/runtime/ContinuationEntry.java | 30 ++++++++-- .../jvm/hotspot/runtime/Continuations.java | 33 ++++++++++ .../sun/jvm/hotspot/runtime/Frame.java | 3 +- .../sun/jvm/hotspot/runtime/JavaThread.java | 4 +- .../sun/jvm/hotspot/runtime/StubRoutines.java | 9 ++- .../aarch64/AARCH64ContinuationEntry.java | 43 +++++++++++++ .../hotspot/runtime/aarch64/AARCH64Frame.java | 33 ++++++---- .../runtime/amd64/AMD64ContinuationEntry.java | 43 +++++++++++++ .../jvm/hotspot/runtime/amd64/AMD64Frame.java | 50 ++++++++++++---- .../runtime/ppc64/PPC64ContinuationEntry.java | 43 +++++++++++++ .../jvm/hotspot/runtime/ppc64/PPC64Frame.java | 37 +++++++----- .../riscv64/RISCV64ContinuationEntry.java | 43 +++++++++++++ .../hotspot/runtime/riscv64/RISCV64Frame.java | 37 +++++++----- 15 files changed, 412 insertions(+), 58 deletions(-) create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuation.java create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuations.java create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64ContinuationEntry.java create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64ContinuationEntry.java create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64ContinuationEntry.java create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64ContinuationEntry.java diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 48f3f078ae8..b6294a9a168 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -475,6 +475,7 @@ /***********************************/ \ \ static_field(StubRoutines, _call_stub_return_address, address) \ + static_field(StubRoutines, _cont_returnBarrier, address) \ \ /***************************************/ \ /* PcDesc and other compiled code info */ \ @@ -786,6 +787,7 @@ static_field(Mutex, _mutex_array, Mutex**) \ static_field(Mutex, _num_mutex, int) \ volatile_nonstatic_field(Mutex, _owner, Thread*) \ + nonstatic_field(ContinuationEntry, _parent, ContinuationEntry*) \ static_field(ContinuationEntry, _return_pc, address) //-------------------------------------------------------------------------------- diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuation.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuation.java new file mode 100644 index 00000000000..72ba053f451 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuation.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package sun.jvm.hotspot.runtime; + +import sun.jvm.hotspot.debugger.Address; + + +public class Continuation { + + public static boolean isReturnBarrierEntry(Address senderPC) { + if (!Continuations.enabled()) { + return false; + } + return VM.getVM().getStubRoutines().contReturnBarrier().equals(senderPC); + } + + public static boolean isSPInContinuation(ContinuationEntry entry, Address sp) { + return entry.getEntrySP().greaterThan(sp); + } + + public static ContinuationEntry getContinuationEntryForSP(JavaThread thread, Address sp) { + ContinuationEntry entry = thread.getContEntry(); + while (entry != null && !isSPInContinuation(entry, sp)) { + entry = entry.getParent(); + } + return entry; + } + + public static Frame continuationBottomSender(JavaThread thread, Frame callee, Address senderSP) { + ContinuationEntry ce = getContinuationEntryForSP(thread, callee.getSP()); + Frame entry = ce.toFrame(); + if (callee.isInterpretedFrame()) { + entry.setSP(senderSP); // sp != unextended_sp + } + return entry; + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ContinuationEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ContinuationEntry.java index 73152bdee84..7d8a2ba5993 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ContinuationEntry.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ContinuationEntry.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2025, NTT DATA. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, NTT DATA. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,12 +26,17 @@ package sun.jvm.hotspot.runtime; import sun.jvm.hotspot.debugger.*; -import sun.jvm.hotspot.runtime.*; +import sun.jvm.hotspot.runtime.aarch64.*; +import sun.jvm.hotspot.runtime.amd64.*; +import sun.jvm.hotspot.runtime.ppc64.*; +import sun.jvm.hotspot.runtime.riscv64.*; import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; -public class ContinuationEntry extends VMObject { +public abstract class ContinuationEntry extends VMObject { private static long size; + private static AddressField parentField; private static Address returnPC; static { @@ -41,13 +46,28 @@ public class ContinuationEntry extends VMObject { private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { Type type = db.lookupType("ContinuationEntry"); size = type.getSize(); + parentField = type.getAddressField("_parent"); returnPC = type.getAddressField("_return_pc").getValue(); } + public static ContinuationEntry create(Address addr) { + return switch (VM.getVM().getDebugger().getCPU()) { + case "amd64" -> VMObjectFactory.newObject(AMD64ContinuationEntry.class, addr); + case "aarch64" -> VMObjectFactory.newObject(AARCH64ContinuationEntry.class, addr); + case "riscv64" -> VMObjectFactory.newObject(RISCV64ContinuationEntry.class, addr); + case "ppc64" -> VMObjectFactory.newObject(PPC64ContinuationEntry.class, addr); + default -> throw new UnsupportedPlatformException("Continuation is not yet implemented."); + }; + } + public ContinuationEntry(Address addr) { super(addr); } + public ContinuationEntry getParent() { + return create(parentField.getValue(addr)); + } + public Address getEntryPC() { return returnPC; } @@ -60,4 +80,6 @@ public class ContinuationEntry extends VMObject { return this.getAddress().addOffsetTo(size); } + public abstract Frame toFrame(); + } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuations.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuations.java new file mode 100644 index 00000000000..884f8764ba5 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Continuations.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package sun.jvm.hotspot.runtime; + +public class Continuations { + + public static boolean enabled() { + return VM.getVM().getCommandLineFlag("VMContinuations").getBool(); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java index ee9e0ecdafd..978fb39ad1c 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -138,6 +138,7 @@ public abstract class Frame implements Cloneable { } public abstract Address getSP(); + public abstract void setSP(Address newSP); public abstract Address getID(); public abstract Address getFP(); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java index 826b5cecfd5..c18bcf8cd37 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -343,7 +343,7 @@ public class JavaThread extends Thread { } public ContinuationEntry getContEntry() { - return VMObjectFactory.newObject(ContinuationEntry.class, contEntryField.getValue(addr)); + return ContinuationEntry.create(contEntryField.getValue(addr)); } /** Gets the Java-side thread object for this JavaThread */ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/StubRoutines.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/StubRoutines.java index 38a3103ac50..85d8c8cd3b6 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/StubRoutines.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/StubRoutines.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ import sun.jvm.hotspot.utilities.Observer; public class StubRoutines { private static AddressField callStubReturnAddressField; + private static AddressField contReturnBarrierField; static { VM.registerVMInitializedObserver(new Observer() { @@ -46,6 +47,7 @@ public class StubRoutines { private static synchronized void initialize(TypeDataBase db) { Type type = db.lookupType("StubRoutines"); callStubReturnAddressField = type.getAddressField("_call_stub_return_address"); + contReturnBarrierField = type.getAddressField("_cont_returnBarrier"); } public StubRoutines() { @@ -59,4 +61,9 @@ public class StubRoutines { return (addr.equals(returnPC)); } } + + public Address contReturnBarrier() { + return contReturnBarrierField.getValue(); + } + } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64ContinuationEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64ContinuationEntry.java new file mode 100644 index 00000000000..b373167a37c --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64ContinuationEntry.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package sun.jvm.hotspot.runtime.aarch64; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.ContinuationEntry; +import sun.jvm.hotspot.runtime.Frame; + + +public class AARCH64ContinuationEntry extends ContinuationEntry { + + public AARCH64ContinuationEntry(Address addr) { + super(addr); + } + + @Override + public Frame toFrame() { + return new AARCH64Frame(getEntrySP(), getEntrySP(), getEntryFP(), getEntryPC()); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java index 7233d508cbc..5e73150c6cf 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/aarch64/AARCH64Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2019, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -206,6 +206,11 @@ public class AARCH64Frame extends Frame { public Address getSP() { return raw_sp; } public Address getID() { return raw_sp; } + @Override + public void setSP(Address newSP) { + raw_sp = newSP; + } + // FIXME: not implemented yet public boolean isSignalHandlerFrameDbg() { return false; } public int getSignalNumberDbg() { return 0; } @@ -360,16 +365,6 @@ public class AARCH64Frame extends Frame { map.setLocation(fp, savedFPAddr); } - private Frame senderForContinuationStub(AARCH64RegisterMap map, CodeBlob cb) { - var contEntry = map.getThread().getContEntry(); - - Address senderSP = contEntry.getEntrySP(); - Address senderPC = contEntry.getEntryPC(); - Address senderFP = contEntry.getEntryFP(); - - return new AARCH64Frame(senderSP, senderFP, senderPC); - } - private Frame senderForCompiledFrame(AARCH64RegisterMap map, CodeBlob cb) { if (DEBUG) { System.out.println("senderForCompiledFrame"); @@ -416,6 +411,22 @@ public class AARCH64Frame extends Frame { updateMapWithSavedLink(map, savedFPAddr); } + if (Continuation.isReturnBarrierEntry(senderPC)) { + // We assume WalkContinuation is "WalkContinuation::skip". + // It is same with c'tor arguments of RegisterMap in frame::next_frame(). + // + // HotSpot code in cpu/aarch64/frame_aarch64.inline.hpp: + // + // if (Continuation::is_return_barrier_entry(sender_pc)) { + // if (map->walk_cont()) { // about to walk into an h-stack + // return Continuation::top_frame(*this, map); + // } else { + // return Continuation::continuation_bottom_sender(map->thread(), *this, l_sender_sp); + // } + // } + return Continuation.continuationBottomSender(map.getThread(), this, senderSP); + } + return new AARCH64Frame(senderSP, savedFPAddr.getAddressAt(0), senderPC); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64ContinuationEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64ContinuationEntry.java new file mode 100644 index 00000000000..3cbebfce2f4 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64ContinuationEntry.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package sun.jvm.hotspot.runtime.amd64; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.ContinuationEntry; +import sun.jvm.hotspot.runtime.Frame; + + +public class AMD64ContinuationEntry extends ContinuationEntry { + + public AMD64ContinuationEntry(Address addr) { + super(addr); + } + + @Override + public Frame toFrame() { + return new AMD64Frame(getEntrySP(), getEntrySP(), getEntryFP(), getEntryPC()); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java index fa9d50160e1..2b78157e2b2 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/amd64/AMD64Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -206,6 +206,11 @@ public class AMD64Frame extends Frame { public Address getSP() { return raw_sp; } public Address getID() { return raw_sp; } + @Override + public void setSP(Address newSP) { + raw_sp = newSP; + } + // FIXME: not implemented yet (should be done for Solaris) public boolean isSignalHandlerFrameDbg() { return false; } public int getSignalNumberDbg() { return 0; } @@ -258,6 +263,23 @@ public class AMD64Frame extends Frame { // update it accordingly map.setIncludeArgumentOops(false); + // HotSpot has following code in frame::sender_raw() at frame_x86.inline.hpp, however + // in_cont() should be false. + // + // if (map->in_cont()) { // already in an h-stack + // return map->stack_chunk()->sender(*this, map); + // } + // + // in_cont() returns true if _chunk() is not null. + // + // frame::next_frame() creates RegisterMap instance with 4 arguments. + // It sets RegisterMap::WalkContinuation::skip to final argument (walk_cont), + // therefore _chunk will not be initialized by the following code in c'tor of RegisterMap. + // + // if (walk_cont == WalkContinuation::include && thread != nullptr && thread->last_continuation() != nullptr) { + // _chunk = stackChunkHandle(Thread::current()->handle_area()->allocate_null_handle(), true /* dummy */); + // } + if (isEntryFrame()) return senderForEntryFrame(map); if (isInterpretedFrame()) return senderForInterpreterFrame(map); @@ -360,16 +382,6 @@ public class AMD64Frame extends Frame { map.setLocation(rbp, savedFPAddr); } - private Frame senderForContinuationStub(AMD64RegisterMap map, CodeBlob cb) { - var contEntry = map.getThread().getContEntry(); - - Address senderSP = contEntry.getEntrySP(); - Address senderPC = contEntry.getEntryPC(); - Address senderFP = contEntry.getEntryFP(); - - return new AMD64Frame(senderSP, senderFP, senderPC); - } - private Frame senderForCompiledFrame(AMD64RegisterMap map, CodeBlob cb) { if (DEBUG) { System.out.println("senderForCompiledFrame"); @@ -408,6 +420,22 @@ public class AMD64Frame extends Frame { updateMapWithSavedLink(map, savedFPAddr); } + if (Continuation.isReturnBarrierEntry(senderPC)) { + // We assume WalkContinuation is "WalkContinuation::skip". + // It is same with c'tor arguments of RegisterMap in frame::next_frame(). + // + // HotSpot code in cpu/x86/frame_x86.inline.hpp: + // + // if (Continuation::is_return_barrier_entry(sender_pc)) { + // if (map->walk_cont()) { // about to walk into an h-stack + // return Continuation::top_frame(*this, map); + // } else { + // return Continuation::continuation_bottom_sender(map->thread(), *this, sender_sp); + // } + // } + return Continuation.continuationBottomSender(map.getThread(), this, senderSP); + } + return new AMD64Frame(senderSP, savedFPAddr.getAddressAt(0), senderPC); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64ContinuationEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64ContinuationEntry.java new file mode 100644 index 00000000000..fac71cc9953 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64ContinuationEntry.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package sun.jvm.hotspot.runtime.ppc64; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.ContinuationEntry; +import sun.jvm.hotspot.runtime.Frame; + + +public class PPC64ContinuationEntry extends ContinuationEntry { + + public PPC64ContinuationEntry(Address addr) { + super(addr); + } + + @Override + public Frame toFrame() { + return new PPC64Frame(getEntrySP(), getEntrySP(), getEntryFP(), getEntryPC()); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java index cae034c9613..a663d016011 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ppc64/PPC64Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -198,6 +198,11 @@ public class PPC64Frame extends Frame { public Address getSP() { return raw_sp; } public Address getID() { return raw_sp; } + @Override + public void setSP(Address newSP) { + raw_sp = newSP; + } + // FIXME: not implemented yet (should be done for Solaris/PPC64) public boolean isSignalHandlerFrameDbg() { return false; } public int getSignalNumberDbg() { return 0; } @@ -260,9 +265,7 @@ public class PPC64Frame extends Frame { if (cb != null) { if (cb.isUpcallStub()) { return senderForUpcallStub(map, (UpcallStub)cb); - } else if (cb.isContinuationStub()) { - return senderForContinuationStub(map, cb); - } else { + } else if (cb.getFrameSize() > 0) { return senderForCompiledFrame(map, cb); } } @@ -337,16 +340,6 @@ public class PPC64Frame extends Frame { return new PPC64Frame(sp, unextendedSP, getLink(), getSenderPC()); } - private Frame senderForContinuationStub(PPC64RegisterMap map, CodeBlob cb) { - var contEntry = map.getThread().getContEntry(); - - Address sp = contEntry.getEntrySP(); - Address pc = contEntry.getEntryPC(); - Address fp = contEntry.getEntryFP(); - - return new PPC64Frame(sp, fp, pc); - } - private Frame senderForCompiledFrame(PPC64RegisterMap map, CodeBlob cb) { if (DEBUG) { System.out.println("senderForCompiledFrame"); @@ -379,6 +372,22 @@ public class PPC64Frame extends Frame { } } + if (Continuation.isReturnBarrierEntry(senderPC)) { + // We assume WalkContinuation is "WalkContinuation::skip". + // It is same with c'tor arguments of RegisterMap in frame::next_frame(). + // + // HotSpot code in cpu/ppc/frame_ppc.inline.hpp: + // + // if (Continuation::is_return_barrier_entry(sender_pc)) { + // if (map->walk_cont()) { // about to walk into an h-stack + // return Continuation::top_frame(*this, map); + // } else { + // return Continuation::continuation_bottom_sender(map->thread(), *this, l_sender_sp); + // } + // } + return Continuation.continuationBottomSender(map.getThread(), this, senderSP); + } + return new PPC64Frame(senderSP, getLink(), senderPC); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64ContinuationEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64ContinuationEntry.java new file mode 100644 index 00000000000..ec04498a6c0 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64ContinuationEntry.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package sun.jvm.hotspot.runtime.riscv64; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.ContinuationEntry; +import sun.jvm.hotspot.runtime.Frame; + + +public class RISCV64ContinuationEntry extends ContinuationEntry { + + public RISCV64ContinuationEntry(Address addr) { + super(addr); + } + + @Override + public Frame toFrame() { + return new RISCV64Frame(getEntrySP(), getEntrySP(), getEntryFP(), getEntryPC()); + } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java index 44c8f4c679c..a35c0735979 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/riscv64/RISCV64Frame.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2019, Red Hat Inc. * Copyright (c) 2021, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -201,6 +201,11 @@ public class RISCV64Frame extends Frame { public Address getSP() { return raw_sp; } public Address getID() { return raw_sp; } + @Override + public void setSP(Address newSP) { + raw_sp = newSP; + } + // FIXME: not implemented yet public boolean isSignalHandlerFrameDbg() { return false; } public int getSignalNumberDbg() { return 0; } @@ -264,9 +269,7 @@ public class RISCV64Frame extends Frame { if (cb != null) { if (cb.isUpcallStub()) { return senderForUpcallStub(map, (UpcallStub)cb); - } else if (cb.isContinuationStub()) { - return senderForContinuationStub(map, cb); - } else { + } else if (cb.getFrameSize() > 0) { return senderForCompiledFrame(map, cb); } } @@ -354,16 +357,6 @@ public class RISCV64Frame extends Frame { map.setLocation(fp, savedFPAddr); } - private Frame senderForContinuationStub(RISCV64RegisterMap map, CodeBlob cb) { - var contEntry = map.getThread().getContEntry(); - - Address senderSP = contEntry.getEntrySP(); - Address senderPC = contEntry.getEntryPC(); - Address senderFP = contEntry.getEntryFP(); - - return new RISCV64Frame(senderSP, senderFP, senderPC); - } - private Frame senderForCompiledFrame(RISCV64RegisterMap map, CodeBlob cb) { if (DEBUG) { System.out.println("senderForCompiledFrame"); @@ -406,6 +399,22 @@ public class RISCV64Frame extends Frame { updateMapWithSavedLink(map, savedFPAddr); } + if (Continuation.isReturnBarrierEntry(senderPC)) { + // We assume WalkContinuation is "WalkContinuation::skip". + // It is same with c'tor arguments of RegisterMap in frame::next_frame(). + // + // HotSpot code in cpu/riscv/frame_riscv.inline.hpp: + // + // if (Continuation::is_return_barrier_entry(sender_pc)) { + // if (map->walk_cont()) { // about to walk into an h-stack + // return Continuation::top_frame(*this, map); + // } else { + // return Continuation::continuation_bottom_sender(map->thread(), *this, l_sender_sp); + // } + // } + return Continuation.continuationBottomSender(map.getThread(), this, senderSP); + } + return new RISCV64Frame(senderSP, savedFPAddr.getAddressAt(0), senderPC); } From 960161db82191b679b92094c8bd661e868dfb424 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Thu, 26 Mar 2026 07:22:04 +0000 Subject: [PATCH 125/160] 8380764: Vector classes should have @ValueBased Reviewed-by: liach --- .../share/classes/jdk/incubator/vector/ByteVector128.java | 6 ++++-- .../share/classes/jdk/incubator/vector/ByteVector256.java | 6 ++++-- .../share/classes/jdk/incubator/vector/ByteVector512.java | 6 ++++-- .../share/classes/jdk/incubator/vector/ByteVector64.java | 6 ++++-- .../share/classes/jdk/incubator/vector/ByteVectorMax.java | 6 ++++-- .../share/classes/jdk/incubator/vector/DoubleVector128.java | 6 ++++-- .../share/classes/jdk/incubator/vector/DoubleVector256.java | 6 ++++-- .../share/classes/jdk/incubator/vector/DoubleVector512.java | 6 ++++-- .../share/classes/jdk/incubator/vector/DoubleVector64.java | 6 ++++-- .../share/classes/jdk/incubator/vector/DoubleVectorMax.java | 6 ++++-- .../share/classes/jdk/incubator/vector/FloatVector128.java | 6 ++++-- .../share/classes/jdk/incubator/vector/FloatVector256.java | 6 ++++-- .../share/classes/jdk/incubator/vector/FloatVector512.java | 6 ++++-- .../share/classes/jdk/incubator/vector/FloatVector64.java | 6 ++++-- .../share/classes/jdk/incubator/vector/FloatVectorMax.java | 6 ++++-- .../share/classes/jdk/incubator/vector/IntVector128.java | 6 ++++-- .../share/classes/jdk/incubator/vector/IntVector256.java | 6 ++++-- .../share/classes/jdk/incubator/vector/IntVector512.java | 6 ++++-- .../share/classes/jdk/incubator/vector/IntVector64.java | 6 ++++-- .../share/classes/jdk/incubator/vector/IntVectorMax.java | 6 ++++-- .../share/classes/jdk/incubator/vector/LongVector128.java | 6 ++++-- .../share/classes/jdk/incubator/vector/LongVector256.java | 6 ++++-- .../share/classes/jdk/incubator/vector/LongVector512.java | 6 ++++-- .../share/classes/jdk/incubator/vector/LongVector64.java | 6 ++++-- .../share/classes/jdk/incubator/vector/LongVectorMax.java | 6 ++++-- .../share/classes/jdk/incubator/vector/ShortVector128.java | 6 ++++-- .../share/classes/jdk/incubator/vector/ShortVector256.java | 6 ++++-- .../share/classes/jdk/incubator/vector/ShortVector512.java | 6 ++++-- .../share/classes/jdk/incubator/vector/ShortVector64.java | 6 ++++-- .../share/classes/jdk/incubator/vector/ShortVectorMax.java | 6 ++++-- .../classes/jdk/incubator/vector/X-VectorBits.java.template | 6 ++++-- 31 files changed, 124 insertions(+), 62 deletions(-) diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java index c38e8d0f8a0..360afedbbbb 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ByteVector128 extends ByteVector { static final ByteSpecies VSPECIES = (ByteSpecies) ByteVector.SPECIES_128; @@ -598,7 +600,7 @@ final class ByteVector128 extends ByteVector { } // Mask - + @ValueBased static final class ByteMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -831,7 +833,7 @@ final class ByteVector128 extends ByteVector { } // Shuffle - + @ValueBased static final class ByteShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java index 0eec0c56e37..ca0c59dd49e 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ByteVector256 extends ByteVector { static final ByteSpecies VSPECIES = (ByteSpecies) ByteVector.SPECIES_256; @@ -630,7 +632,7 @@ final class ByteVector256 extends ByteVector { } // Mask - + @ValueBased static final class ByteMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -863,7 +865,7 @@ final class ByteVector256 extends ByteVector { } // Shuffle - + @ValueBased static final class ByteShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java index 138319b60d4..1a0c69153bc 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ByteVector512 extends ByteVector { static final ByteSpecies VSPECIES = (ByteSpecies) ByteVector.SPECIES_512; @@ -694,7 +696,7 @@ final class ByteVector512 extends ByteVector { } // Mask - + @ValueBased static final class ByteMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -927,7 +929,7 @@ final class ByteVector512 extends ByteVector { } // Shuffle - + @ValueBased static final class ByteShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java index d7c7c78534b..50561eca0f8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ByteVector64 extends ByteVector { static final ByteSpecies VSPECIES = (ByteSpecies) ByteVector.SPECIES_64; @@ -582,7 +584,7 @@ final class ByteVector64 extends ByteVector { } // Mask - + @ValueBased static final class ByteMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -815,7 +817,7 @@ final class ByteVector64 extends ByteVector { } // Shuffle - + @ValueBased static final class ByteShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java index 636aa83893a..ee931bbc077 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ByteVectorMax extends ByteVector { static final ByteSpecies VSPECIES = (ByteSpecies) ByteVector.SPECIES_MAX; @@ -568,7 +570,7 @@ final class ByteVectorMax extends ByteVector { } // Mask - + @ValueBased static final class ByteMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -801,7 +803,7 @@ final class ByteVectorMax extends ByteVector { } // Shuffle - + @ValueBased static final class ByteShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java index 1140d377e9b..43c7e3f0c46 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class DoubleVector128 extends DoubleVector { static final DoubleSpecies VSPECIES = (DoubleSpecies) DoubleVector.SPECIES_128; @@ -559,7 +561,7 @@ final class DoubleVector128 extends DoubleVector { } // Mask - + @ValueBased static final class DoubleMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -792,7 +794,7 @@ final class DoubleVector128 extends DoubleVector { } // Shuffle - + @ValueBased static final class DoubleShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java index 59b7913cfcb..5f176854dbd 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class DoubleVector256 extends DoubleVector { static final DoubleSpecies VSPECIES = (DoubleSpecies) DoubleVector.SPECIES_256; @@ -563,7 +565,7 @@ final class DoubleVector256 extends DoubleVector { } // Mask - + @ValueBased static final class DoubleMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -796,7 +798,7 @@ final class DoubleVector256 extends DoubleVector { } // Shuffle - + @ValueBased static final class DoubleShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java index 8ed21953394..0696f48163d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class DoubleVector512 extends DoubleVector { static final DoubleSpecies VSPECIES = (DoubleSpecies) DoubleVector.SPECIES_512; @@ -571,7 +573,7 @@ final class DoubleVector512 extends DoubleVector { } // Mask - + @ValueBased static final class DoubleMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -804,7 +806,7 @@ final class DoubleVector512 extends DoubleVector { } // Shuffle - + @ValueBased static final class DoubleShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java index 7e1a8cf768d..5b74c2c4619 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class DoubleVector64 extends DoubleVector { static final DoubleSpecies VSPECIES = (DoubleSpecies) DoubleVector.SPECIES_64; @@ -557,7 +559,7 @@ final class DoubleVector64 extends DoubleVector { } // Mask - + @ValueBased static final class DoubleMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -790,7 +792,7 @@ final class DoubleVector64 extends DoubleVector { } // Shuffle - + @ValueBased static final class DoubleShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java index 46c090e066e..07d227d641a 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class DoubleVectorMax extends DoubleVector { static final DoubleSpecies VSPECIES = (DoubleSpecies) DoubleVector.SPECIES_MAX; @@ -556,7 +558,7 @@ final class DoubleVectorMax extends DoubleVector { } // Mask - + @ValueBased static final class DoubleMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -789,7 +791,7 @@ final class DoubleVectorMax extends DoubleVector { } // Shuffle - + @ValueBased static final class DoubleShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java index 1e3867e84fc..17c1fdba4fc 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class FloatVector128 extends FloatVector { static final FloatSpecies VSPECIES = (FloatSpecies) FloatVector.SPECIES_128; @@ -563,7 +565,7 @@ final class FloatVector128 extends FloatVector { } // Mask - + @ValueBased static final class FloatMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -796,7 +798,7 @@ final class FloatVector128 extends FloatVector { } // Shuffle - + @ValueBased static final class FloatShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java index f267025972d..7badb71415e 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class FloatVector256 extends FloatVector { static final FloatSpecies VSPECIES = (FloatSpecies) FloatVector.SPECIES_256; @@ -571,7 +573,7 @@ final class FloatVector256 extends FloatVector { } // Mask - + @ValueBased static final class FloatMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -804,7 +806,7 @@ final class FloatVector256 extends FloatVector { } // Shuffle - + @ValueBased static final class FloatShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java index 439e26f0d89..7c0786b7fcd 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class FloatVector512 extends FloatVector { static final FloatSpecies VSPECIES = (FloatSpecies) FloatVector.SPECIES_512; @@ -587,7 +589,7 @@ final class FloatVector512 extends FloatVector { } // Mask - + @ValueBased static final class FloatMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -820,7 +822,7 @@ final class FloatVector512 extends FloatVector { } // Shuffle - + @ValueBased static final class FloatShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java index 9e81a52d27b..fc4877e5ae8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class FloatVector64 extends FloatVector { static final FloatSpecies VSPECIES = (FloatSpecies) FloatVector.SPECIES_64; @@ -559,7 +561,7 @@ final class FloatVector64 extends FloatVector { } // Mask - + @ValueBased static final class FloatMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -792,7 +794,7 @@ final class FloatVector64 extends FloatVector { } // Shuffle - + @ValueBased static final class FloatShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java index 4813f153544..5cfafecdb58 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class FloatVectorMax extends FloatVector { static final FloatSpecies VSPECIES = (FloatSpecies) FloatVector.SPECIES_MAX; @@ -556,7 +558,7 @@ final class FloatVectorMax extends FloatVector { } // Mask - + @ValueBased static final class FloatMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -789,7 +791,7 @@ final class FloatVectorMax extends FloatVector { } // Shuffle - + @ValueBased static final class FloatShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java index cc8f31a4bc2..04b10386127 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class IntVector128 extends IntVector { static final IntSpecies VSPECIES = (IntSpecies) IntVector.SPECIES_128; @@ -574,7 +576,7 @@ final class IntVector128 extends IntVector { } // Mask - + @ValueBased static final class IntMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -807,7 +809,7 @@ final class IntVector128 extends IntVector { } // Shuffle - + @ValueBased static final class IntShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java index 0630cd958f2..20d9df1cd60 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class IntVector256 extends IntVector { static final IntSpecies VSPECIES = (IntSpecies) IntVector.SPECIES_256; @@ -582,7 +584,7 @@ final class IntVector256 extends IntVector { } // Mask - + @ValueBased static final class IntMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -815,7 +817,7 @@ final class IntVector256 extends IntVector { } // Shuffle - + @ValueBased static final class IntShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java index 92eb5a0f2d2..4f3a16e2777 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class IntVector512 extends IntVector { static final IntSpecies VSPECIES = (IntSpecies) IntVector.SPECIES_512; @@ -598,7 +600,7 @@ final class IntVector512 extends IntVector { } // Mask - + @ValueBased static final class IntMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -831,7 +833,7 @@ final class IntVector512 extends IntVector { } // Shuffle - + @ValueBased static final class IntShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java index c3f92285034..dd51669943b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class IntVector64 extends IntVector { static final IntSpecies VSPECIES = (IntSpecies) IntVector.SPECIES_64; @@ -570,7 +572,7 @@ final class IntVector64 extends IntVector { } // Mask - + @ValueBased static final class IntMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -803,7 +805,7 @@ final class IntVector64 extends IntVector { } // Shuffle - + @ValueBased static final class IntShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java index 8d3c251536c..0b785b01aec 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class IntVectorMax extends IntVector { static final IntSpecies VSPECIES = (IntSpecies) IntVector.SPECIES_MAX; @@ -568,7 +570,7 @@ final class IntVectorMax extends IntVector { } // Mask - + @ValueBased static final class IntMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -812,7 +814,7 @@ final class IntVectorMax extends IntVector { } // Shuffle - + @ValueBased static final class IntShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java index f8dad12ff89..594c82ca1fc 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class LongVector128 extends LongVector { static final LongSpecies VSPECIES = (LongSpecies) LongVector.SPECIES_128; @@ -560,7 +562,7 @@ final class LongVector128 extends LongVector { } // Mask - + @ValueBased static final class LongMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -793,7 +795,7 @@ final class LongVector128 extends LongVector { } // Shuffle - + @ValueBased static final class LongShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java index 144e2c1c64d..c3d1ff4c276 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class LongVector256 extends LongVector { static final LongSpecies VSPECIES = (LongSpecies) LongVector.SPECIES_256; @@ -564,7 +566,7 @@ final class LongVector256 extends LongVector { } // Mask - + @ValueBased static final class LongMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -797,7 +799,7 @@ final class LongVector256 extends LongVector { } // Shuffle - + @ValueBased static final class LongShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java index b49d0c7c147..b8c95967a99 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class LongVector512 extends LongVector { static final LongSpecies VSPECIES = (LongSpecies) LongVector.SPECIES_512; @@ -572,7 +574,7 @@ final class LongVector512 extends LongVector { } // Mask - + @ValueBased static final class LongMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -805,7 +807,7 @@ final class LongVector512 extends LongVector { } // Shuffle - + @ValueBased static final class LongShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java index 5e8451695bc..3c9b525f6d0 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class LongVector64 extends LongVector { static final LongSpecies VSPECIES = (LongSpecies) LongVector.SPECIES_64; @@ -558,7 +560,7 @@ final class LongVector64 extends LongVector { } // Mask - + @ValueBased static final class LongMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -791,7 +793,7 @@ final class LongVector64 extends LongVector { } // Shuffle - + @ValueBased static final class LongShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java index 3469da8f2f4..4752959f884 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class LongVectorMax extends LongVector { static final LongSpecies VSPECIES = (LongSpecies) LongVector.SPECIES_MAX; @@ -558,7 +560,7 @@ final class LongVectorMax extends LongVector { } // Mask - + @ValueBased static final class LongMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -791,7 +793,7 @@ final class LongVectorMax extends LongVector { } // Shuffle - + @ValueBased static final class LongShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java index e989cdbdbea..89ec97c6be0 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ShortVector128 extends ShortVector { static final ShortSpecies VSPECIES = (ShortSpecies) ShortVector.SPECIES_128; @@ -582,7 +584,7 @@ final class ShortVector128 extends ShortVector { } // Mask - + @ValueBased static final class ShortMask128 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -815,7 +817,7 @@ final class ShortVector128 extends ShortVector { } // Shuffle - + @ValueBased static final class ShortShuffle128 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java index c74188e22f5..0f5751c27d8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ShortVector256 extends ShortVector { static final ShortSpecies VSPECIES = (ShortSpecies) ShortVector.SPECIES_256; @@ -598,7 +600,7 @@ final class ShortVector256 extends ShortVector { } // Mask - + @ValueBased static final class ShortMask256 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -831,7 +833,7 @@ final class ShortVector256 extends ShortVector { } // Shuffle - + @ValueBased static final class ShortShuffle256 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java index 46b5d652200..3d38dfd88fd 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ShortVector512 extends ShortVector { static final ShortSpecies VSPECIES = (ShortSpecies) ShortVector.SPECIES_512; @@ -630,7 +632,7 @@ final class ShortVector512 extends ShortVector { } // Mask - + @ValueBased static final class ShortMask512 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -863,7 +865,7 @@ final class ShortVector512 extends ShortVector { } // Shuffle - + @ValueBased static final class ShortShuffle512 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java index 66ff3efe522..b319d98f784 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ShortVector64 extends ShortVector { static final ShortSpecies VSPECIES = (ShortSpecies) ShortVector.SPECIES_64; @@ -574,7 +576,7 @@ final class ShortVector64 extends ShortVector { } // Mask - + @ValueBased static final class ShortMask64 extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -807,7 +809,7 @@ final class ShortVector64 extends ShortVector { } // Shuffle - + @ValueBased static final class ShortShuffle64 extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java index b9a9b85126b..69b298857c9 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; // -- This file was mechanically generated: Do not edit! -- // @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class ShortVectorMax extends ShortVector { static final ShortSpecies VSPECIES = (ShortSpecies) ShortVector.SPECIES_MAX; @@ -568,7 +570,7 @@ final class ShortVectorMax extends ShortVector { } // Mask - + @ValueBased static final class ShortMaskMax extends AbstractMask { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -801,7 +803,7 @@ final class ShortVectorMax extends ShortVector { } // Shuffle - + @ValueBased static final class ShortShuffleMax extends AbstractShuffle { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template index bbf02f9c6cd..35041a0b70f 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.function.IntUnaryOperator; +import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; @@ -41,6 +42,7 @@ import static jdk.incubator.vector.VectorOperators.*; #warn This file is preprocessed before being compiled @SuppressWarnings("cast") // warning: redundant cast +@ValueBased final class $vectortype$ extends $abstractvectortype$ { static final $Type$Species VSPECIES = ($Type$Species) $Type$Vector.SPECIES_$BITS$; @@ -855,7 +857,7 @@ final class $vectortype$ extends $abstractvectortype$ { #end[FP] // Mask - + @ValueBased static final class $masktype$ extends AbstractMask<$Boxtype$> { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM @@ -1103,7 +1105,7 @@ final class $vectortype$ extends $abstractvectortype$ { } // Shuffle - + @ValueBased static final class $shuffletype$ extends AbstractShuffle<$Boxtype$> { static final int VLENGTH = VSPECIES.laneCount(); // used by the JVM From c64f7357a536a7577432964ea8ce723c5373a184 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 26 Mar 2026 08:19:31 +0000 Subject: [PATCH 126/160] 8379516: Adjust JVM debug helper exports Reviewed-by: kbarrett, lucy, clanger --- src/hotspot/share/utilities/debug.cpp | 104 +++++++++++++++++--------- 1 file changed, 69 insertions(+), 35 deletions(-) diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index e8533e29460..23e8281f000 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -342,20 +342,20 @@ class Command : public StackObj { int Command::level = 0; -extern "C" DEBUGEXPORT void blob(CodeBlob* cb) { +extern "C" NOINLINE void blob(CodeBlob* cb) { Command c("blob"); cb->print(); } -extern "C" DEBUGEXPORT void dump_vtable(address p) { +extern "C" NOINLINE void dump_vtable(address p) { Command c("dump_vtable"); Klass* k = (Klass*)p; k->vtable().print(); } -extern "C" DEBUGEXPORT void nm(intptr_t p) { +extern "C" NOINLINE void nm(intptr_t p) { // Actually we look through all CodeBlobs (the nm name has been kept for backwards compatibility) Command c("nm"); CodeBlob* cb = CodeCache::find_blob((address)p); @@ -367,7 +367,7 @@ extern "C" DEBUGEXPORT void nm(intptr_t p) { } -extern "C" DEBUGEXPORT void disnm(intptr_t p) { +extern "C" NOINLINE void disnm(intptr_t p) { Command c("disnm"); CodeBlob* cb = CodeCache::find_blob((address) p); if (cb != nullptr) { @@ -382,7 +382,7 @@ extern "C" DEBUGEXPORT void disnm(intptr_t p) { } -extern "C" DEBUGEXPORT void printnm(intptr_t p) { +extern "C" NOINLINE void printnm(intptr_t p) { char buffer[256]; os::snprintf_checked(buffer, sizeof(buffer), "printnm: " INTPTR_FORMAT, p); Command c(buffer); @@ -396,14 +396,14 @@ extern "C" DEBUGEXPORT void printnm(intptr_t p) { } -extern "C" DEBUGEXPORT void universe() { +extern "C" NOINLINE void universe() { Command c("universe"); if (!c.onThread()) return; Universe::print_on(tty); } -extern "C" DEBUGEXPORT void verify() { +extern "C" NOINLINE void verify() { // try to run a verify on the entire system // note: this may not be safe if we're not at a safepoint; for debugging, // this manipulates the safepoint settings to avoid assertion failures @@ -421,7 +421,7 @@ extern "C" DEBUGEXPORT void verify() { } -extern "C" DEBUGEXPORT void pp(void* p) { +extern "C" NOINLINE void pp(void* p) { Command c("pp"); if (!c.onThread()) return; FlagSetting fl(DisplayVMOutput, true); @@ -445,7 +445,7 @@ extern "C" DEBUGEXPORT void pp(void* p) { } -extern "C" DEBUGEXPORT void ps() { // print stack +extern "C" NOINLINE void ps() { // print stack // Prints the stack of the current Java thread Command c("ps"); if (!c.onThread()) return; @@ -477,7 +477,7 @@ extern "C" DEBUGEXPORT void ps() { // print stack } } -extern "C" DEBUGEXPORT void pfl() { +extern "C" NOINLINE void pfl() { // print frame layout Command c("pfl"); if (!c.onThread()) return; @@ -494,7 +494,7 @@ extern "C" DEBUGEXPORT void pfl() { } } -extern "C" DEBUGEXPORT void psf() { // print stack frames +extern "C" NOINLINE void psf() { // print stack frames Command c("psf"); if (!c.onThread()) return; JavaThread* p = JavaThread::active(); @@ -511,21 +511,21 @@ extern "C" DEBUGEXPORT void psf() { // print stack frames } -extern "C" DEBUGEXPORT void threads() { +extern "C" NOINLINE void threads() { Command c("threads"); if (!c.onThread()) return; Threads::print(false, true); } -extern "C" DEBUGEXPORT void psd() { +extern "C" NOINLINE void psd() { Command c("psd"); if (!c.onThread()) return; SystemDictionary::print(); } -extern "C" DEBUGEXPORT void pss() { // print all stacks +extern "C" NOINLINE void pss() { // print all stacks Command c("pss"); if (!c.onThread()) return; Threads::print(true, PRODUCT_ONLY(false) NOT_PRODUCT(true)); @@ -533,7 +533,7 @@ extern "C" DEBUGEXPORT void pss() { // print all stacks // #ifndef PRODUCT -extern "C" DEBUGEXPORT void debug() { // to set things up for compiler debugging +extern "C" NOINLINE void debug() { // to set things up for compiler debugging Command c("debug"); NOT_PRODUCT(WizardMode = true;) PrintCompilation = true; @@ -542,7 +542,7 @@ extern "C" DEBUGEXPORT void debug() { // to set things up for comp } -extern "C" DEBUGEXPORT void ndebug() { // undo debug() +extern "C" NOINLINE void ndebug() { // undo debug() Command c("ndebug"); PrintCompilation = false; PrintInlining = PrintAssembly = false; @@ -550,36 +550,36 @@ extern "C" DEBUGEXPORT void ndebug() { // undo debug() } -extern "C" DEBUGEXPORT void flush() { +extern "C" NOINLINE void flush() { Command c("flush"); tty->flush(); } -extern "C" DEBUGEXPORT void events() { +extern "C" NOINLINE void events() { Command c("events"); Events::print(); } -extern "C" DEBUGEXPORT Method* findm(intptr_t pc) { +extern "C" NOINLINE Method* findm(intptr_t pc) { Command c("findm"); nmethod* nm = CodeCache::find_nmethod((address)pc); return (nm == nullptr) ? (Method*)nullptr : nm->method(); } -extern "C" DEBUGEXPORT nmethod* findnm(intptr_t addr) { +extern "C" NOINLINE nmethod* findnm(intptr_t addr) { Command c("findnm"); return CodeCache::find_nmethod((address)addr); } -extern "C" DEBUGEXPORT void find(intptr_t x) { +extern "C" NOINLINE 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) { +extern "C" NOINLINE void findpc(intptr_t x) { Command c("findpc"); if (!c.onThread()) return; os::print_location(tty, x, true); @@ -591,15 +591,14 @@ extern "C" DEBUGEXPORT void findpc(intptr_t x) { // call findclass("java/lang/Object", 0x3) -> find j.l.Object and disasm all of its methods // call findmethod("*ang/Object*", "wait", 0xff) -> detailed disasm of all "wait" methods in j.l.Object // 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) { +extern "C" NOINLINE 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); } -extern "C" DEBUGEXPORT void findmethod(const char* class_name_pattern, - const char* method_pattern, int flags) { +extern "C" NOINLINE 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); @@ -607,7 +606,7 @@ extern "C" DEBUGEXPORT void findmethod(const char* class_name_pattern, } // Need method pointer to find bcp -extern "C" DEBUGEXPORT void findbcp(intptr_t method, intptr_t bcp) { +extern "C" NOINLINE void findbcp(intptr_t method, intptr_t bcp) { Command c("findbcp"); Method* mh = (Method*)method; if (!mh->is_native()) { @@ -618,7 +617,7 @@ extern "C" DEBUGEXPORT void findbcp(intptr_t method, intptr_t bcp) { } // check and decode a single u5 value -extern "C" DEBUGEXPORT u4 u5decode(intptr_t addr) { +extern "C" NOINLINE u4 u5decode(intptr_t addr) { Command c("u5decode"); u1* arr = (u1*)addr; size_t off = 0, lim = 5; @@ -635,9 +634,7 @@ extern "C" DEBUGEXPORT u4 u5decode(intptr_t addr) { // there is no limit on the count of items printed; the // printing stops when an null is printed or at limit. // See documentation for UNSIGNED5::Reader::print(count). -extern "C" DEBUGEXPORT intptr_t u5p(intptr_t addr, - intptr_t limit, - int count) { +extern "C" NOINLINE intptr_t u5p(intptr_t addr, intptr_t limit, int count) { Command c("u5p"); u1* arr = (u1*)addr; if (limit && limit < addr) limit = addr; @@ -650,10 +647,10 @@ extern "C" DEBUGEXPORT intptr_t u5p(intptr_t addr, // int versions of all methods to avoid having to type type casts in the debugger -void pp(intptr_t p) { pp((void*)p); } -void pp(oop p) { pp((void*)p); } +NOINLINE void pp(intptr_t p) { pp((void*)p); } +NOINLINE void pp(oop p) { pp((void*)p); } -extern "C" DEBUGEXPORT void help() { +extern "C" NOINLINE void help() { Command c("help"); tty->print_cr("basic"); tty->print_cr(" pp(void* p) - try to make sense of p"); @@ -709,7 +706,7 @@ extern "C" DEBUGEXPORT void help() { } #ifndef PRODUCT -extern "C" DEBUGEXPORT void pns(void* sp, void* fp, void* pc) { // print native stack +extern "C" NOINLINE void pns(void* sp, void* fp, void* pc) { // print native stack Command c("pns"); if (!c.onThread()) return; static char buf[O_BUFLEN]; @@ -728,7 +725,7 @@ extern "C" DEBUGEXPORT void pns(void* sp, void* fp, void* pc) { // print native // WARNING: Only intended for use when debugging. Do not leave calls to // pns2() in committed source (product or debug). // -extern "C" DEBUGEXPORT void pns2() { // print native stack +extern "C" NOINLINE void pns2() { // print native stack Command c("pns2"); if (!c.onThread()) return; static char buf[O_BUFLEN]; @@ -739,6 +736,43 @@ extern "C" DEBUGEXPORT void pns2() { // print native stack } #endif +// just an exported helper; to avoid link time elimination of the referenced functions +extern "C" JNIEXPORT void JVM_debug_helpers_keeper(void* p1, void* p2, void* p3, intptr_t ip, oop oh, address adr) { + blob((CodeBlob*)p1); + dump_vtable(adr); + nm(ip); + disnm(ip); + printnm(ip); + universe(); + verify(); + pp(p1); + ps(); + pfl(); + psf(); + threads(); + psd(); + pss(); + debug(); + ndebug(); + flush(); + events(); + findm(ip); + findnm(ip); + find(ip); + findpc(ip); + findclass("", 0); + findmethod("", "", 0); + findbcp(ip, ip); + u5decode(ip); + u5p(ip, ip, 0); + pp(ip); + pp(oh); + help(); +#ifndef PRODUCT + pns(p1, p2, p3); + pns2(); +#endif +} // Returns true iff the address p is readable and *(intptr_t*)p != errvalue extern "C" bool dbg_is_safe(const void* p, intptr_t errvalue) { From aea8947e9df84bf583ef658c5bf5f61ad87b73fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20=C3=96sterlund?= Date: Thu, 26 Mar 2026 09:29:53 +0000 Subject: [PATCH 127/160] 8378176: Concurrent GC worker threads may suffer from priority inversion Reviewed-by: kbarrett, ayang, tschatzl --- src/hotspot/share/gc/shared/concurrentGCThread.cpp | 3 +-- src/hotspot/share/gc/shared/concurrentGCThread.hpp | 2 +- src/hotspot/share/gc/shared/workerThread.cpp | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/gc/shared/concurrentGCThread.cpp b/src/hotspot/share/gc/shared/concurrentGCThread.cpp index ed6c1b4d283..c7765631cd9 100644 --- a/src/hotspot/share/gc/shared/concurrentGCThread.cpp +++ b/src/hotspot/share/gc/shared/concurrentGCThread.cpp @@ -33,9 +33,8 @@ ConcurrentGCThread::ConcurrentGCThread() : _should_terminate(false), _has_terminated(false) {} -void ConcurrentGCThread::create_and_start(ThreadPriority prio) { +void ConcurrentGCThread::create_and_start() { if (os::create_thread(this, os::gc_thread)) { - os::set_priority(this, prio); os::start_thread(this); } } diff --git a/src/hotspot/share/gc/shared/concurrentGCThread.hpp b/src/hotspot/share/gc/shared/concurrentGCThread.hpp index 0c764546045..5322d676493 100644 --- a/src/hotspot/share/gc/shared/concurrentGCThread.hpp +++ b/src/hotspot/share/gc/shared/concurrentGCThread.hpp @@ -36,7 +36,7 @@ private: Atomic _has_terminated; protected: - void create_and_start(ThreadPriority prio = NearMaxPriority); + void create_and_start(); virtual void run_service() = 0; virtual void stop_service() = 0; diff --git a/src/hotspot/share/gc/shared/workerThread.cpp b/src/hotspot/share/gc/shared/workerThread.cpp index 2f6f003608f..2738c98e5c3 100644 --- a/src/hotspot/share/gc/shared/workerThread.cpp +++ b/src/hotspot/share/gc/shared/workerThread.cpp @@ -210,8 +210,6 @@ WorkerThread::WorkerThread(const char* name_prefix, uint name_suffix, WorkerTask } void WorkerThread::run() { - os::set_priority(this, NearMaxPriority); - while (true) { _dispatcher->worker_run_task(); } From ab659d4ee48b33a4bad21857b02f0a29314f2b43 Mon Sep 17 00:00:00 2001 From: Dusan Balek Date: Thu, 26 Mar 2026 10:19:37 +0000 Subject: [PATCH 128/160] 8371817: javac with annotation processor throws AssertionError: Cannot add metadata to this type: METHOD when dealing with local classes Reviewed-by: mcimadamore --- .../com/sun/tools/javac/jvm/ClassReader.java | 9 +- .../classfile/LocalClassesTest.java | 163 ++++++++++++++++++ 2 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 test/langtools/tools/javac/annotations/typeAnnotations/classfile/LocalClassesTest.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 017d740dc0a..08ba0442781 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -1367,9 +1367,7 @@ public class ClassReader { else self.fullname = ClassSymbol.formFullName(self.name, self.owner); - if (m != null) { - ((ClassType)sym.type).setEnclosingType(m.type); - } else if ((self.flags_field & STATIC) == 0) { + if ((self.flags_field & STATIC) == 0 && (m == null || (m.flags_field & STATIC) == 0)) { ((ClassType)sym.type).setEnclosingType(c.type); } else { ((ClassType)sym.type).setEnclosingType(Type.noType); @@ -2687,6 +2685,7 @@ public class ClassReader { // won't pass the "hasOuterInstance" check above, but those that don't have an // enclosing method (i.e. from initializers) will pass that check. boolean local = forceLocal = + currentOwner.owner.kind != TYP || !currentOwner.owner.members().includes(currentOwner, LookupKind.NON_RECURSIVE); if (!currentOwner.name.isEmpty() && !local) type = new MethodType(adjustMethodParams(flags, type.getParameterTypes()), @@ -3038,7 +3037,9 @@ public class ClassReader { * `typevars'. */ protected void enterTypevars(Symbol sym, Type t) { - if (t.getEnclosingType() != null) { + if (sym.owner.kind == MTH) { + enterTypevars(sym.owner, sym.owner.type); + } else if (t.getEnclosingType() != null) { if (!t.getEnclosingType().hasTag(TypeTag.NONE)) { enterTypevars(sym.owner, t.getEnclosingType()); } diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/LocalClassesTest.java b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/LocalClassesTest.java new file mode 100644 index 00000000000..66b55c8d209 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/LocalClassesTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 8371817 + * @summary Check for type annotating types that refer to local classes read + * from classfiles + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit LocalClassesTest + */ + +import com.sun.source.tree.ClassTree; +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskListener; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import toolbox.JavacTask; +import toolbox.ToolBox; + +public class LocalClassesTest { + + ToolBox tb = new ToolBox(); + Path base; + + @Test + void test() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + + Map local2enclosing = new HashMap<>(); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + + public class Test { + public static void m1() { + class Local1 { + @Nullable Local1 l; + } + } + public void m2() { + class Local2 { + @Nullable Local2 l; + } + } + } + + @Target({ElementType.TYPE_USE}) + @interface Nullable {} + """) + .callback(task -> { + task.addTaskListener(new TaskListener() { + @Override + public void finished(TaskEvent e) { + if (e.getKind() == TaskEvent.Kind.ANALYZE) { + Trees trees = Trees.instance(task); + new TreePathScanner<>() { + @Override + public Object visitClass(ClassTree node, Object p) { + if (node.getSimpleName().toString().startsWith("Local")) { + Element el = trees.getElement(getCurrentPath()); + TypeMirror type = trees.getTypeMirror(getCurrentPath()); + local2enclosing.put(el.getSimpleName().toString(), ((DeclaredType) type).getEnclosingType().toString()); + } + return super.visitClass(node, p); + } + }.scan(e.getCompilationUnit(), null); + } + } + }); + }) + .run() + .writeAll(); + + Path classes2 = base.resolve("classes2"); + Files.createDirectories(classes2); + + ProcessorImpl p = new ProcessorImpl(); + new JavacTask(tb) + .options("-cp", classes.toString(), "-d", classes2.toString()) + .processors(p) + .classes("Test$1Local1", "Test$1Local2") + .run() + .writeAll(); + + Assertions.assertEquals(local2enclosing.get("Local1"), p.local2enclosing.get("Local1")); + Assertions.assertEquals(local2enclosing.get("Local2"), p.local2enclosing.get("Local2")); + } + + @SupportedAnnotationTypes("*") + private static class ProcessorImpl extends AbstractProcessor { + private Map local2enclosing = new HashMap<>(); + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (TypeElement te : ElementFilter.typesIn(roundEnv.getRootElements())) { + if (te.getSimpleName().toString().startsWith("Local")) { + local2enclosing.put(te.getSimpleName().toString(), ((DeclaredType) te.asType()).getEnclosingType().toString()); + } + } + return false; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + } + + @BeforeEach + public void setup(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} From da296cbea1603e8b1de46c9daafced76fce921e6 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Thu, 26 Mar 2026 11:08:48 +0000 Subject: [PATCH 129/160] 8363996: Obsolete UseCompressedClassPointers Reviewed-by: rkennke, kvn, adinn, dholmes, mdoerr, iklam, fyang --- src/hotspot/cpu/aarch64/aarch64.ad | 12 +- .../cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 27 +- .../cpu/aarch64/c1_LIRGenerator_aarch64.cpp | 10 +- .../cpu/aarch64/c1_MacroAssembler_aarch64.cpp | 12 +- .../cpu/aarch64/macroAssembler_aarch64.cpp | 74 ++---- src/hotspot/cpu/arm/matcher_arm.hpp | 3 +- src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp | 4 +- src/hotspot/cpu/ppc/macroAssembler_ppc.cpp | 65 ++--- src/hotspot/cpu/ppc/matcher_ppc.hpp | 1 - .../riscv/c1_LIRAssembler_arraycopy_riscv.cpp | 11 +- .../cpu/riscv/c1_LIRAssembler_riscv.cpp | 24 +- .../cpu/riscv/c1_LIRGenerator_riscv.cpp | 10 +- .../cpu/riscv/c1_MacroAssembler_riscv.cpp | 12 +- .../cpu/riscv/c2_MacroAssembler_riscv.cpp | 15 +- .../cpu/riscv/macroAssembler_riscv.cpp | 34 +-- src/hotspot/cpu/riscv/riscv.ad | 9 +- src/hotspot/cpu/riscv/stubGenerator_riscv.cpp | 8 +- src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp | 15 +- .../cpu/s390/c1_MacroAssembler_s390.cpp | 8 +- src/hotspot/cpu/s390/macroAssembler_s390.cpp | 176 ++++++------- src/hotspot/cpu/s390/macroAssembler_s390.hpp | 5 +- src/hotspot/cpu/s390/matcher_s390.hpp | 3 +- src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp | 39 +-- src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp | 10 +- src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp | 9 +- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 38 +-- src/hotspot/cpu/x86/macroAssembler_x86.hpp | 3 +- src/hotspot/cpu/x86/matcher_x86.hpp | 3 +- src/hotspot/cpu/x86/x86.ad | 9 +- src/hotspot/share/cds/aotMapLogger.cpp | 1 - src/hotspot/share/cds/aotMappedHeapLoader.hpp | 2 +- src/hotspot/share/cds/aotMappedHeapWriter.cpp | 2 - src/hotspot/share/cds/aotMetaspace.cpp | 222 ++++++++--------- .../share/cds/aotStreamedHeapWriter.cpp | 1 - src/hotspot/share/cds/archiveBuilder.cpp | 7 +- src/hotspot/share/cds/archiveBuilder.hpp | 2 - src/hotspot/share/cds/archiveUtils.cpp | 4 +- src/hotspot/share/cds/cdsConfig.cpp | 4 - src/hotspot/share/cds/filemap.cpp | 24 +- src/hotspot/share/cds/filemap.hpp | 2 - .../classfile/systemDictionaryShared.cpp | 4 +- src/hotspot/share/code/aotCodeCache.cpp | 7 - src/hotspot/share/code/aotCodeCache.hpp | 11 +- src/hotspot/share/code/compiledIC.cpp | 15 +- .../share/gc/shared/c2/barrierSetC2.cpp | 1 - src/hotspot/share/gc/shared/gcTrace.cpp | 9 +- .../share/gc/shenandoah/shenandoahAsserts.cpp | 32 ++- src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp | 4 +- src/hotspot/share/gc/z/zDebug.gdb | 6 +- .../types/traceid/jfrTraceIdKlassQueue.cpp | 4 +- .../share/jvmci/jvmciCompilerToVMInit.cpp | 10 +- src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 4 +- .../share/memory/classLoaderMetaspace.cpp | 6 +- .../share/memory/classLoaderMetaspace.hpp | 17 +- src/hotspot/share/memory/metaspace.cpp | 233 +++++++++--------- src/hotspot/share/memory/metaspace.hpp | 10 +- .../memory/metaspace/metaspaceReporter.cpp | 212 ++++++++-------- .../memory/metaspace/metaspaceStatistics.cpp | 30 ++- .../memory/metaspace/virtualSpaceNode.cpp | 11 +- src/hotspot/share/nmt/memReporter.cpp | 10 +- src/hotspot/share/oops/arrayOop.hpp | 5 +- src/hotspot/share/oops/compressedKlass.cpp | 33 ++- src/hotspot/share/oops/compressedKlass.hpp | 8 - .../share/oops/compressedKlass.inline.hpp | 4 +- src/hotspot/share/oops/instanceKlass.cpp | 6 +- src/hotspot/share/oops/klass.cpp | 12 +- src/hotspot/share/oops/objLayout.cpp | 10 +- src/hotspot/share/oops/objLayout.hpp | 10 +- src/hotspot/share/oops/objLayout.inline.hpp | 4 +- src/hotspot/share/oops/oop.cpp | 5 +- src/hotspot/share/oops/oop.hpp | 9 +- src/hotspot/share/oops/oop.inline.hpp | 33 +-- src/hotspot/share/opto/cfgnode.cpp | 2 +- src/hotspot/share/opto/chaitin.cpp | 4 +- src/hotspot/share/opto/compile.cpp | 8 +- src/hotspot/share/opto/lcm.cpp | 5 +- src/hotspot/share/opto/memnode.cpp | 4 +- src/hotspot/share/opto/narrowptrnode.cpp | 4 +- src/hotspot/share/opto/type.cpp | 2 +- src/hotspot/share/prims/whitebox.cpp | 1 - src/hotspot/share/runtime/arguments.cpp | 19 +- src/hotspot/share/runtime/globals.hpp | 3 - src/hotspot/share/runtime/os.cpp | 5 +- src/hotspot/share/runtime/vmStructs.cpp | 3 +- src/hotspot/share/services/memoryService.cpp | 12 +- src/hotspot/share/utilities/macros.hpp | 14 +- src/hotspot/share/utilities/vmError.cpp | 15 +- .../classes/sun/jvm/hotspot/oops/Array.java | 6 +- .../sun/jvm/hotspot/oops/Instance.java | 6 +- .../classes/sun/jvm/hotspot/oops/Oop.java | 21 +- .../classes/sun/jvm/hotspot/runtime/VM.java | 18 +- .../gtest/metaspace/test_is_metaspace_obj.cpp | 6 +- .../gtest/metaspace/test_metaspaceUtils.cpp | 61 ++--- test/hotspot/gtest/oops/test_arrayOop.cpp | 20 +- .../gtest/oops/test_compressedKlass.cpp | 30 --- test/hotspot/gtest/oops/test_objArrayOop.cpp | 40 ++- .../arraycopy/TestObjectArrayClone.java | 6 +- .../c1/TestArrayCopyToFromObject.java | 5 +- .../c2/TestReduceAllocationAndLoadKlass.java | 63 ----- .../AllocationMergesTests.java | 22 +- .../compilerToVM/GetResolvedJavaTypeTest.java | 3 +- .../jdk/vm/ci/code/test/DataPatchTest.java | 4 +- .../vm/ci/code/test/TestHotSpotVMConfig.java | 3 +- .../types/TestCheckCastPPBecomesTOP.java | 3 - .../jtreg/compiler/unsafe/OpaqueAccesses.java | 18 +- .../arguments/TestCompressedClassFlags.java | 53 ---- .../gc/g1/TestSharedArchiveWithPreTouch.java | 6 +- .../gc/metaspace/TestMetaspaceMemoryPool.java | 15 +- .../metaspace/TestMetaspacePerfCounters.java | 37 +-- .../TestPerfCountersAndMemoryPools.java | 7 +- .../gc/metaspace/TestSizeTransitions.java | 48 ++-- test/hotspot/jtreg/gtest/ArrayTests.java | 27 +- .../jtreg/gtest/CompressedKlassGtest.java | 10 +- test/hotspot/jtreg/gtest/MetaspaceGtests.java | 11 +- test/hotspot/jtreg/gtest/ObjArrayTests.java | 54 ++-- .../CDSCompressedKPtrs.java | 66 ----- .../CompressedClassPointers.java | 22 +- .../CompressedClassSpaceSize.java | 9 +- .../CompressedKlassPointerAndOops.java | 6 +- .../TestVMConfigInHsErrFile.java | 15 +- .../runtime/FieldLayout/BaseOffsets.java | 42 +--- .../runtime/FieldLayout/FieldDensityTest.java | 5 +- .../FieldLayout/TestOopMapSizeMinimal.java | 53 ++-- .../Metaspace/MaxMetaspaceSizeTest.java | 3 +- .../runtime/Metaspace/PrintMetaspaceDcmd.java | 60 +---- .../cds/appcds/CommandLineFlagCombo.java | 4 +- .../appcds/CommandLineFlagComboNegative.java | 8 +- .../cds/appcds/FillerObjectLoadTest.java | 53 ---- .../appcds/TestCombinedCompressedFlags.java | 87 +------ .../runtime/cds/appcds/TestZGCWithCDS.java | 62 +---- .../cds/appcds/aotCache/OldClassSupport2.java | 7 +- .../cacheObject/DifferentHeapSizes.java | 4 +- .../dynamicArchive/CDSStreamTestDriver.java | 4 +- .../DynamicArchiveTestBase.java | 7 +- .../sharedStrings/IncompatibleOptions.java | 16 +- .../dcmd/vm/ClassLoaderStatsTest.java | 15 +- .../ir_framework/tests/TestIRMatching.java | 19 -- .../GetObjectSizeIntrinsicsTest.java | 5 +- .../TestObjectAllocationInNewTLABEvent.java | 9 +- .../TestObjectAllocationOutsideTLABEvent.java | 9 +- ...ObjectAllocationSampleEventThrottling.java | 9 +- .../TestHeapSummaryEventDefNewSerial.java | 12 +- .../objectcount/ObjectCountEventVerifier.java | 4 +- ...CountAfterGCEventWithG1ConcurrentMark.java | 4 +- ...CountAfterGCEventWithG1FullCollection.java | 4 +- ...bjectCountAfterGCEventWithParallelOld.java | 4 +- ...TestObjectCountAfterGCEventWithSerial.java | 4 +- .../gc/objectcount/TestObjectCountEvent.java | 4 +- test/jtreg-ext/requires/VMProps.java | 11 +- 149 files changed, 952 insertions(+), 2093 deletions(-) delete mode 100644 test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndLoadKlass.java delete mode 100644 test/hotspot/jtreg/gc/arguments/TestCompressedClassFlags.java delete mode 100644 test/hotspot/jtreg/runtime/CDSCompressedKPtrs/CDSCompressedKPtrs.java delete mode 100644 test/hotspot/jtreg/runtime/cds/appcds/FillerObjectLoadTest.java diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index b79030f07e7..05b2514a456 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -2233,15 +2233,9 @@ uint BoxLockNode::size(PhaseRegAlloc *ra_) const { void MachUEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const { st->print_cr("# MachUEPNode"); - if (UseCompressedClassPointers) { - st->print_cr("\tldrw rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); - st->print_cr("\tldrw r10, [rscratch2 + CompiledICData::speculated_klass_offset()]\t# compressed klass"); - st->print_cr("\tcmpw rscratch1, r10"); - } else { - st->print_cr("\tldr rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); - st->print_cr("\tldr r10, [rscratch2 + CompiledICData::speculated_klass_offset()]\t# compressed klass"); - st->print_cr("\tcmp rscratch1, r10"); - } + st->print_cr("\tldrw rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); + st->print_cr("\tldrw r10, [rscratch2 + CompiledICData::speculated_klass_offset()]\t# compressed klass"); + st->print_cr("\tcmpw rscratch1, r10"); st->print_cr("\tbne, SharedRuntime::_ic_miss_stub"); } #endif diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 30048a2079d..e7d8c2d3648 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -59,22 +59,6 @@ const Register SHIFT_count = r0; // where count for shift operations must be #define __ _masm-> -static void select_different_registers(Register preserve, - Register extra, - Register &tmp1, - Register &tmp2) { - if (tmp1 == preserve) { - assert_different_registers(tmp1, tmp2, extra); - tmp1 = extra; - } else if (tmp2 == preserve) { - assert_different_registers(tmp1, tmp2, extra); - tmp2 = extra; - } - assert_different_registers(preserve, tmp1, tmp2); -} - - - static void select_different_registers(Register preserve, Register extra, Register &tmp1, @@ -1269,12 +1253,9 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L } else if (obj == klass_RInfo) { klass_RInfo = dst; } - if (k->is_loaded() && !UseCompressedClassPointers) { - select_different_registers(obj, dst, k_RInfo, klass_RInfo); - } else { - Rtmp1 = op->tmp3()->as_register(); - select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); - } + + Rtmp1 = op->tmp3()->as_register(); + select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); assert_different_registers(obj, k_RInfo, klass_RInfo); diff --git a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp index ad26d494b2d..f10c5197d91 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1287,9 +1287,7 @@ void LIRGenerator::do_CheckCast(CheckCast* x) { } LIR_Opr reg = rlock_result(x); LIR_Opr tmp3 = LIR_OprFact::illegalOpr; - if (!x->klass()->is_loaded() || UseCompressedClassPointers) { - tmp3 = new_register(objectType); - } + tmp3 = new_register(objectType); __ checkcast(reg, obj.result(), x->klass(), new_register(objectType), new_register(objectType), tmp3, x->direct_compare(), info_for_exception, patching_info, stub, @@ -1308,9 +1306,7 @@ void LIRGenerator::do_InstanceOf(InstanceOf* x) { } obj.load_item(); LIR_Opr tmp3 = LIR_OprFact::illegalOpr; - if (!x->klass()->is_loaded() || UseCompressedClassPointers) { - tmp3 = new_register(objectType); - } + tmp3 = new_register(objectType); __ instanceof(reg, obj.result(), x->klass(), new_register(objectType), new_register(objectType), tmp3, x->direct_compare(), patching_info, x->profiled_method(), x->profiled_bci()); diff --git a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp index e934632715c..89a9422ea48 100644 --- a/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2021, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -105,12 +105,8 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register } else { mov(t1, checked_cast(markWord::prototype().value())); str(t1, Address(obj, oopDesc::mark_offset_in_bytes())); - if (UseCompressedClassPointers) { // Take care not to kill klass - encode_klass_not_null(t1, klass); - strw(t1, Address(obj, oopDesc::klass_offset_in_bytes())); - } else { - str(klass, Address(obj, oopDesc::klass_offset_in_bytes())); - } + encode_klass_not_null(t1, klass); // Take care not to kill klass + strw(t1, Address(obj, oopDesc::klass_offset_in_bytes())); } if (len->is_valid()) { @@ -121,7 +117,7 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register // Clear gap/first 4 bytes following the length field. strw(zr, Address(obj, base_offset)); } - } else if (UseCompressedClassPointers && !UseCompactObjectHeaders) { + } else if (!UseCompactObjectHeaders) { store_klass_gap(obj, zr); } } diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 3e3e95be07e..732d94180ae 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -762,7 +762,7 @@ void MacroAssembler::call_VM_base(Register oop_result, assert(java_thread == rthread, "unexpected register"); #ifdef ASSERT // TraceBytecodes does not use r12 but saves it over the call, so don't verify - // if ((UseCompressedOops || UseCompressedClassPointers) && !TraceBytecodes) verify_heapbase("call_VM_base: heap base corrupted?"); + // if (!TraceBytecodes) verify_heapbase("call_VM_base: heap base corrupted?"); #endif // ASSERT assert(java_thread != oop_result , "cannot use the same register for java_thread & oop_result"); @@ -1002,14 +1002,10 @@ int MacroAssembler::ic_check(int end_alignment) { load_narrow_klass_compact(tmp1, receiver); ldrw(tmp2, Address(data, CompiledICData::speculated_klass_offset())); cmpw(tmp1, tmp2); - } else if (UseCompressedClassPointers) { + } else { ldrw(tmp1, Address(receiver, oopDesc::klass_offset_in_bytes())); ldrw(tmp2, Address(data, CompiledICData::speculated_klass_offset())); cmpw(tmp1, tmp2); - } else { - ldr(tmp1, Address(receiver, oopDesc::klass_offset_in_bytes())); - ldr(tmp2, Address(data, CompiledICData::speculated_klass_offset())); - cmp(tmp1, tmp2); } Label dont; @@ -3278,7 +3274,6 @@ int MacroAssembler::pop_p(unsigned int bitset, Register stack) { #ifdef ASSERT void MacroAssembler::verify_heapbase(const char* msg) { #if 0 - assert (UseCompressedOops || UseCompressedClassPointers, "should be compressed"); assert (Universe::heap() != nullptr, "java heap should be initialized"); if (!UseCompressedOops || Universe::ptr_base() == nullptr) { // rheapbase is allocated as general register @@ -5067,13 +5062,10 @@ void MacroAssembler::load_narrow_klass_compact(Register dst, Register src) { void MacroAssembler::load_klass(Register dst, Register src) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(dst, src); - decode_klass_not_null(dst); - } else if (UseCompressedClassPointers) { - ldrw(dst, Address(src, oopDesc::klass_offset_in_bytes())); - decode_klass_not_null(dst); } else { - ldr(dst, Address(src, oopDesc::klass_offset_in_bytes())); + ldrw(dst, Address(src, oopDesc::klass_offset_in_bytes())); } + decode_klass_not_null(dst); } void MacroAssembler::restore_cpu_control_state_after_jni(Register tmp1, Register tmp2) { @@ -5125,25 +5117,21 @@ void MacroAssembler::load_mirror(Register dst, Register method, Register tmp1, R void MacroAssembler::cmp_klass(Register obj, Register klass, Register tmp) { assert_different_registers(obj, klass, tmp); - if (UseCompressedClassPointers) { - if (UseCompactObjectHeaders) { - load_narrow_klass_compact(tmp, obj); - } else { - ldrw(tmp, Address(obj, oopDesc::klass_offset_in_bytes())); - } - if (CompressedKlassPointers::base() == nullptr) { - cmp(klass, tmp, LSL, CompressedKlassPointers::shift()); - return; - } else if (((uint64_t)CompressedKlassPointers::base() & 0xffffffff) == 0 - && CompressedKlassPointers::shift() == 0) { - // Only the bottom 32 bits matter - cmpw(klass, tmp); - return; - } - decode_klass_not_null(tmp); + if (UseCompactObjectHeaders) { + load_narrow_klass_compact(tmp, obj); } else { - ldr(tmp, Address(obj, oopDesc::klass_offset_in_bytes())); + ldrw(tmp, Address(obj, oopDesc::klass_offset_in_bytes())); } + if (CompressedKlassPointers::base() == nullptr) { + cmp(klass, tmp, LSL, CompressedKlassPointers::shift()); + return; + } else if (((uint64_t)CompressedKlassPointers::base() & 0xffffffff) == 0 + && CompressedKlassPointers::shift() == 0) { + // Only the bottom 32 bits matter + cmpw(klass, tmp); + return; + } + decode_klass_not_null(tmp); cmp(klass, tmp); } @@ -5151,36 +5139,25 @@ void MacroAssembler::cmp_klasses_from_objects(Register obj1, Register obj2, Regi if (UseCompactObjectHeaders) { load_narrow_klass_compact(tmp1, obj1); load_narrow_klass_compact(tmp2, obj2); - cmpw(tmp1, tmp2); - } else if (UseCompressedClassPointers) { + } else { ldrw(tmp1, Address(obj1, oopDesc::klass_offset_in_bytes())); ldrw(tmp2, Address(obj2, oopDesc::klass_offset_in_bytes())); - cmpw(tmp1, tmp2); - } else { - ldr(tmp1, Address(obj1, oopDesc::klass_offset_in_bytes())); - ldr(tmp2, Address(obj2, oopDesc::klass_offset_in_bytes())); - cmp(tmp1, tmp2); } + cmpw(tmp1, tmp2); } void MacroAssembler::store_klass(Register dst, Register src) { // FIXME: Should this be a store release? concurrent gcs assumes // klass length is valid if klass field is not null. assert(!UseCompactObjectHeaders, "not with compact headers"); - if (UseCompressedClassPointers) { - encode_klass_not_null(src); - strw(src, Address(dst, oopDesc::klass_offset_in_bytes())); - } else { - str(src, Address(dst, oopDesc::klass_offset_in_bytes())); - } + encode_klass_not_null(src); + strw(src, Address(dst, oopDesc::klass_offset_in_bytes())); } void MacroAssembler::store_klass_gap(Register dst, Register src) { assert(!UseCompactObjectHeaders, "not with compact headers"); - if (UseCompressedClassPointers) { - // Store to klass gap in destination - strw(src, Address(dst, oopDesc::klass_gap_offset_in_bytes())); - } + // Store to klass gap in destination + strw(src, Address(dst, oopDesc::klass_gap_offset_in_bytes())); } // Algorithm must match CompressedOops::encode. @@ -5326,8 +5303,6 @@ MacroAssembler::KlassDecodeMode MacroAssembler::klass_decode_mode() { } MacroAssembler::KlassDecodeMode MacroAssembler::klass_decode_mode(address base, int shift, const size_t range) { - assert(UseCompressedClassPointers, "not using compressed class pointers"); - // KlassDecodeMode shouldn't be set already. assert(_klass_decode_mode == KlassDecodeNone, "set once"); @@ -5457,8 +5432,6 @@ void MacroAssembler::decode_klass_not_null_for_aot(Register dst, Register src) { } void MacroAssembler::decode_klass_not_null(Register dst, Register src) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); - if (AOTCodeCache::is_on_for_dump()) { decode_klass_not_null_for_aot(dst, src); return; @@ -5525,7 +5498,6 @@ void MacroAssembler::set_narrow_oop(Register dst, jobject obj) { } void MacroAssembler::set_narrow_klass(Register dst, Klass* k) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int index = oop_recorder()->find_index(k); diff --git a/src/hotspot/cpu/arm/matcher_arm.hpp b/src/hotspot/cpu/arm/matcher_arm.hpp index 6c818e1f20d..7978a5b7090 100644 --- a/src/hotspot/cpu/arm/matcher_arm.hpp +++ b/src/hotspot/cpu/arm/matcher_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -75,7 +75,6 @@ static bool narrow_klass_use_complex_address() { NOT_LP64(ShouldNotCallThis()); - assert(UseCompressedClassPointers, "only for compressed klass code"); return false; } diff --git a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp index 798451446e5..4d7af0e4a71 100644 --- a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -144,7 +144,7 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register if (len->is_valid()) { stw(len, arrayOopDesc::length_offset_in_bytes(), obj); - } else if (UseCompressedClassPointers && !UseCompactObjectHeaders) { + } else if (!UseCompactObjectHeaders) { // Otherwise length is in the class gap. store_klass_gap(obj); } diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index 14e90ddf185..5fbcce94029 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -3201,23 +3201,17 @@ Register MacroAssembler::encode_klass_not_null(Register dst, Register src) { void MacroAssembler::store_klass(Register dst_oop, Register klass, Register ck) { assert(!UseCompactObjectHeaders, "not with compact headers"); - if (UseCompressedClassPointers) { - Register compressedKlass = encode_klass_not_null(ck, klass); - stw(compressedKlass, oopDesc::klass_offset_in_bytes(), dst_oop); - } else { - std(klass, oopDesc::klass_offset_in_bytes(), dst_oop); - } + Register compressedKlass = encode_klass_not_null(ck, klass); + stw(compressedKlass, oopDesc::klass_offset_in_bytes(), dst_oop); } void MacroAssembler::store_klass_gap(Register dst_oop, Register val) { assert(!UseCompactObjectHeaders, "not with compact headers"); - if (UseCompressedClassPointers) { - if (val == noreg) { - val = R0; - li(val, 0); - } - stw(val, oopDesc::klass_gap_offset_in_bytes(), dst_oop); + if (val == noreg) { + val = R0; + li(val, 0); } + stw(val, oopDesc::klass_gap_offset_in_bytes(), dst_oop); } int MacroAssembler::instr_size_for_decode_klass_not_null() { @@ -3226,17 +3220,13 @@ int MacroAssembler::instr_size_for_decode_klass_not_null() { // Not yet computed? if (computed_size == -1) { - if (!UseCompressedClassPointers) { - computed_size = 0; - } else { - // Determine by scratch emit. - ResourceMark rm; - int code_size = 8 * BytesPerInstWord; - CodeBuffer cb("decode_klass_not_null scratch buffer", code_size, 0); - MacroAssembler* a = new MacroAssembler(&cb); - a->decode_klass_not_null(R11_scratch1); - computed_size = a->offset(); - } + // Determine by scratch emit. + ResourceMark rm; + int code_size = 8 * BytesPerInstWord; + CodeBuffer cb("decode_klass_not_null scratch buffer", code_size, 0); + MacroAssembler* a = new MacroAssembler(&cb); + a->decode_klass_not_null(R11_scratch1); + computed_size = a->offset(); } return computed_size; @@ -3259,18 +3249,14 @@ void MacroAssembler::decode_klass_not_null(Register dst, Register src) { void MacroAssembler::load_klass_no_decode(Register dst, Register src) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(dst, src); - } else if (UseCompressedClassPointers) { - lwz(dst, oopDesc::klass_offset_in_bytes(), src); } else { - ld(dst, oopDesc::klass_offset_in_bytes(), src); + lwz(dst, oopDesc::klass_offset_in_bytes(), src); } } void MacroAssembler::load_klass(Register dst, Register src) { load_klass_no_decode(dst, src); - if (UseCompressedClassPointers) { // also true for UseCompactObjectHeaders - decode_klass_not_null(dst); - } + decode_klass_not_null(dst); } // Loads the obj's Klass* into dst. @@ -3286,18 +3272,13 @@ void MacroAssembler::load_narrow_klass_compact(Register dst, Register src) { void MacroAssembler::cmp_klass(ConditionRegister dst, Register obj, Register klass, Register tmp, Register tmp2) { assert_different_registers(obj, klass, tmp); - if (UseCompressedClassPointers) { - if (UseCompactObjectHeaders) { - load_narrow_klass_compact(tmp, obj); - } else { - lwz(tmp, oopDesc::klass_offset_in_bytes(), obj); - } - Register encoded_klass = encode_klass_not_null(tmp2, klass); - cmpw(dst, tmp, encoded_klass); + if (UseCompactObjectHeaders) { + load_narrow_klass_compact(tmp, obj); } else { - ld(tmp, oopDesc::klass_offset_in_bytes(), obj); - cmpd(dst, tmp, klass); + lwz(tmp, oopDesc::klass_offset_in_bytes(), obj); } + Register encoded_klass = encode_klass_not_null(tmp2, klass); + cmpw(dst, tmp, encoded_klass); } void MacroAssembler::cmp_klasses_from_objects(ConditionRegister dst, Register obj1, Register obj2, Register tmp1, Register tmp2) { @@ -3305,14 +3286,10 @@ void MacroAssembler::cmp_klasses_from_objects(ConditionRegister dst, Register ob load_narrow_klass_compact(tmp1, obj1); load_narrow_klass_compact(tmp2, obj2); cmpw(dst, tmp1, tmp2); - } else if (UseCompressedClassPointers) { + } else { lwz(tmp1, oopDesc::klass_offset_in_bytes(), obj1); lwz(tmp2, oopDesc::klass_offset_in_bytes(), obj2); cmpw(dst, tmp1, tmp2); - } else { - ld(tmp1, oopDesc::klass_offset_in_bytes(), obj1); - ld(tmp2, oopDesc::klass_offset_in_bytes(), obj2); - cmpd(dst, tmp1, tmp2); } } diff --git a/src/hotspot/cpu/ppc/matcher_ppc.hpp b/src/hotspot/cpu/ppc/matcher_ppc.hpp index 2ddbec3e48c..cbe882648b8 100644 --- a/src/hotspot/cpu/ppc/matcher_ppc.hpp +++ b/src/hotspot/cpu/ppc/matcher_ppc.hpp @@ -87,7 +87,6 @@ static bool narrow_klass_use_complex_address() { NOT_LP64(ShouldNotCallThis()); - assert(UseCompressedClassPointers, "only for compressed klass code"); // TODO: PPC port if (MatchDecodeNodes) return true; return false; } diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp index 819d6c05654..58eb1a55553 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_arraycopy_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -196,12 +196,9 @@ void LIR_Assembler::arraycopy_type_check(Register src, Register src_pos, Registe if (UseCompactObjectHeaders) { __ load_narrow_klass_compact(tmp, src); __ load_narrow_klass_compact(t0, dst); - } else if (UseCompressedClassPointers) { + } else { __ lwu(tmp, Address(src, oopDesc::klass_offset_in_bytes())); __ lwu(t0, Address(dst, oopDesc::klass_offset_in_bytes())); - } else { - __ ld(tmp, Address(src, oopDesc::klass_offset_in_bytes())); - __ ld(t0, Address(dst, oopDesc::klass_offset_in_bytes())); } __ bne(tmp, t0, *stub->entry(), /* is_far */ true); } else { @@ -257,9 +254,7 @@ void LIR_Assembler::arraycopy_assert(Register src, Register dst, Register tmp, c // but not necessarily exactly of type default_type. Label known_ok, halt; __ mov_metadata(tmp, default_type->constant_encoding()); - if (UseCompressedClassPointers) { - __ encode_klass_not_null(tmp); - } + __ encode_klass_not_null(tmp); if (basic_type != T_OBJECT) { __ cmp_klass_compressed(dst, tmp, t0, halt, false); diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index 63e2fd015d7..29e5d86d0cc 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -55,20 +55,6 @@ const Register SHIFT_count = x10; // where count for shift operations must be #define __ _masm-> -static void select_different_registers(Register preserve, - Register extra, - Register &tmp1, - Register &tmp2) { - if (tmp1 == preserve) { - assert_different_registers(tmp1, tmp2, extra); - tmp1 = extra; - } else if (tmp2 == preserve) { - assert_different_registers(tmp1, tmp2, extra); - tmp2 = extra; - } - assert_different_registers(preserve, tmp1, tmp2); -} - static void select_different_registers(Register preserve, Register extra, Register &tmp1, @@ -1155,12 +1141,8 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L } else if (obj == klass_RInfo) { klass_RInfo = dst; } - if (k->is_loaded() && !UseCompressedClassPointers) { - select_different_registers(obj, dst, k_RInfo, klass_RInfo); - } else { - Rtmp1 = op->tmp3()->as_register(); - select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); - } + Rtmp1 = op->tmp3()->as_register(); + select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); assert_different_registers(obj, k_RInfo, klass_RInfo); diff --git a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp index 88565d9136f..f290708a231 100644 --- a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -1073,9 +1073,7 @@ void LIRGenerator::do_CheckCast(CheckCast* x) { } LIR_Opr reg = rlock_result(x); LIR_Opr tmp3 = LIR_OprFact::illegalOpr; - if (!x->klass()->is_loaded() || UseCompressedClassPointers) { - tmp3 = new_register(objectType); - } + tmp3 = new_register(objectType); __ checkcast(reg, obj.result(), x->klass(), new_register(objectType), new_register(objectType), tmp3, x->direct_compare(), info_for_exception, patching_info, stub, @@ -1094,9 +1092,7 @@ void LIRGenerator::do_InstanceOf(InstanceOf* x) { } obj.load_item(); LIR_Opr tmp3 = LIR_OprFact::illegalOpr; - if (!x->klass()->is_loaded() || UseCompressedClassPointers) { - tmp3 = new_register(objectType); - } + tmp3 = new_register(objectType); __ instanceof(reg, obj.result(), x->klass(), new_register(objectType), new_register(objectType), tmp3, x->direct_compare(), patching_info, x->profiled_method(), x->profiled_bci()); diff --git a/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp index aeb077ba0a0..abcc070b253 100644 --- a/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_MacroAssembler_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -92,12 +92,8 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register // This assumes that all prototype bits fitr in an int32_t mv(tmp1, checked_cast(markWord::prototype().value())); sd(tmp1, Address(obj, oopDesc::mark_offset_in_bytes())); - if (UseCompressedClassPointers) { // Take care not to kill klass - encode_klass_not_null(tmp1, klass, tmp2); - sw(tmp1, Address(obj, oopDesc::klass_offset_in_bytes())); - } else { - sd(klass, Address(obj, oopDesc::klass_offset_in_bytes())); - } + encode_klass_not_null(tmp1, klass, tmp2); + sw(tmp1, Address(obj, oopDesc::klass_offset_in_bytes())); } if (len->is_valid()) { @@ -108,7 +104,7 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register // Clear gap/first 4 bytes following the length field. sw(zr, Address(obj, base_offset)); } - } else if (UseCompressedClassPointers && !UseCompactObjectHeaders) { + } else if (!UseCompactObjectHeaders) { store_klass_gap(obj, zr); } } diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index 72a90ddde1f..0d06fd469de 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -1175,8 +1175,7 @@ void C2_MacroAssembler::string_compare_long_same_encoding(Register result, Regis Label TAIL_CHECK, TAIL, NEXT_WORD, DIFFERENCE; const int base_offset = arrayOopDesc::base_offset_in_bytes(T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); const int minCharsInWord = isLL ? wordSize : wordSize / 2; @@ -1269,8 +1268,7 @@ void C2_MacroAssembler::string_compare_long_different_encoding(Register result, Label TAIL, NEXT_WORD, DIFFERENCE; const int base_offset = arrayOopDesc::base_offset_in_bytes(T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); Register strL = isLU ? str1 : str2; Register strU = isLU ? str2 : str1; @@ -1485,8 +1483,7 @@ void C2_MacroAssembler::arrays_equals(Register a1, Register a2, int length_offset = arrayOopDesc::length_offset_in_bytes(); int base_offset = arrayOopDesc::base_offset_in_bytes(elem_size == 2 ? T_CHAR : T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); Register cnt1 = tmp3; Register cnt2 = tmp1; // cnt2 only used in array length compare @@ -1611,8 +1608,7 @@ void C2_MacroAssembler::string_equals(Register a1, Register a2, int base_offset = arrayOopDesc::base_offset_in_bytes(T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); BLOCK_COMMENT("string_equals {"); @@ -2699,8 +2695,7 @@ void C2_MacroAssembler::arrays_equals_v(Register a1, Register a2, Register resul int length_offset = arrayOopDesc::length_offset_in_bytes(); int base_offset = arrayOopDesc::base_offset_in_bytes(elem_size == 2 ? T_CHAR : T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); BLOCK_COMMENT("arrays_equals_v {"); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 4f5e7afc166..b0305fa2977 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2024, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -3514,10 +3514,8 @@ void MacroAssembler::orptr(Address adr, RegisterOrConstant src, Register tmp1, R void MacroAssembler::cmp_klass_compressed(Register oop, Register trial_klass, Register tmp, Label &L, bool equal) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(tmp, oop); - } else if (UseCompressedClassPointers) { - lwu(tmp, Address(oop, oopDesc::klass_offset_in_bytes())); } else { - ld(tmp, Address(oop, oopDesc::klass_offset_in_bytes())); + lwu(tmp, Address(oop, oopDesc::klass_offset_in_bytes())); } if (equal) { beq(trial_klass, tmp, L); @@ -3741,11 +3739,9 @@ void MacroAssembler::load_klass(Register dst, Register src, Register tmp) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(dst, src); decode_klass_not_null(dst, tmp); - } else if (UseCompressedClassPointers) { + } else { lwu(dst, Address(src, oopDesc::klass_offset_in_bytes())); decode_klass_not_null(dst, tmp); - } else { - ld(dst, Address(src, oopDesc::klass_offset_in_bytes())); } } @@ -3753,20 +3749,15 @@ void MacroAssembler::store_klass(Register dst, Register src, Register tmp) { // FIXME: Should this be a store release? concurrent gcs assumes // klass length is valid if klass field is not null. assert(!UseCompactObjectHeaders, "not with compact headers"); - if (UseCompressedClassPointers) { - encode_klass_not_null(src, tmp); - sw(src, Address(dst, oopDesc::klass_offset_in_bytes())); - } else { - sd(src, Address(dst, oopDesc::klass_offset_in_bytes())); - } + encode_klass_not_null(src, tmp); + sw(src, Address(dst, oopDesc::klass_offset_in_bytes())); + } void MacroAssembler::store_klass_gap(Register dst, Register src) { assert(!UseCompactObjectHeaders, "not with compact headers"); - if (UseCompressedClassPointers) { - // Store to klass gap in destination - sw(src, Address(dst, oopDesc::klass_gap_offset_in_bytes())); - } + // Store to klass gap in destination + sw(src, Address(dst, oopDesc::klass_gap_offset_in_bytes())); } void MacroAssembler::decode_klass_not_null(Register r, Register tmp) { @@ -3775,7 +3766,6 @@ void MacroAssembler::decode_klass_not_null(Register r, Register tmp) { } void MacroAssembler::decode_klass_not_null(Register dst, Register src, Register tmp) { - assert(UseCompressedClassPointers, "should only be used for compressed headers"); assert_different_registers(dst, tmp); assert_different_registers(src, tmp); @@ -3806,8 +3796,6 @@ void MacroAssembler::encode_klass_not_null(Register r, Register tmp) { } void MacroAssembler::encode_klass_not_null(Register dst, Register src, Register tmp) { - assert(UseCompressedClassPointers, "should only be used for compressed headers"); - if (CompressedKlassPointers::base() == nullptr) { if (CompressedKlassPointers::shift() != 0) { srli(dst, src, CompressedKlassPointers::shift()); @@ -5337,7 +5325,6 @@ void MacroAssembler::set_narrow_oop(Register dst, jobject obj) { } void MacroAssembler::set_narrow_klass(Register dst, Klass* k) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int index = oop_recorder()->find_index(k); @@ -5417,12 +5404,9 @@ int MacroAssembler::ic_check(int end_alignment) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(tmp1, receiver); lwu(tmp2, Address(data, CompiledICData::speculated_klass_offset())); - } else if (UseCompressedClassPointers) { + } else { lwu(tmp1, Address(receiver, oopDesc::klass_offset_in_bytes())); lwu(tmp2, Address(data, CompiledICData::speculated_klass_offset())); - } else { - ld(tmp1, Address(receiver, oopDesc::klass_offset_in_bytes())); - ld(tmp2, Address(data, CompiledICData::speculated_klass_offset())); } Label ic_hit; diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 54c0d9c0955..e236d03e6d2 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1801,13 +1801,8 @@ void MachUEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const { assert_cond(st != nullptr); st->print_cr("# MachUEPNode"); - if (UseCompressedClassPointers) { - st->print_cr("\tlwu t1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); - st->print_cr("\tlwu t2, [t0 + CompiledICData::speculated_klass_offset()]\t# compressed klass"); - } else { - st->print_cr("\tld t1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); - st->print_cr("\tld t2, [t0 + CompiledICData::speculated_klass_offset()]\t# compressed klass"); - } + st->print_cr("\tlwu t1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); + st->print_cr("\tlwu t2, [t0 + CompiledICData::speculated_klass_offset()]\t# compressed klass"); st->print_cr("\tbeq t1, t2, ic_hit"); st->print_cr("\tj, SharedRuntime::_ic_miss_stub\t # Inline cache check"); st->print_cr("\tic_hit:"); diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 127ac9f6951..964c6d98e9c 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2025, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2025, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -3070,8 +3070,7 @@ class StubGenerator: public StubCodeGenerator { const Register tmp = x30, tmpLval = x12; int base_offset = arrayOopDesc::base_offset_in_bytes(T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); #ifdef ASSERT if (AvoidUnalignedAccesses) { @@ -3128,8 +3127,7 @@ class StubGenerator: public StubCodeGenerator { tmp1 = x28, tmp2 = x29, tmp3 = x30, tmp4 = x12; int base_offset = arrayOopDesc::base_offset_in_bytes(T_BYTE); - assert((base_offset % (UseCompactObjectHeaders ? 4 : - (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be"); + assert((base_offset % (UseCompactObjectHeaders ? 4 : 8)) == 0, "Must be"); Register strU = isLU ? str2 : str1, strL = isLU ? str1 : str2, diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index 93d6051aa76..e1d8d062c23 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -2251,9 +2251,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { // but not necessarily exactly of type default_type. NearLabel known_ok, halt; metadata2reg(default_type->constant_encoding(), tmp); - if (UseCompressedClassPointers) { - __ encode_klass_not_null(tmp); - } + __ encode_klass_not_null(tmp); if (basic_type != T_OBJECT) { __ cmp_klass(tmp, dst, Z_R1_scratch); @@ -2540,13 +2538,8 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L // Get object class. // Not a safepoint as obj null check happens earlier. if (op->fast_check()) { - if (UseCompressedClassPointers) { - __ load_klass(klass_RInfo, obj); - __ compareU64_and_branch(k_RInfo, klass_RInfo, Assembler::bcondNotEqual, *failure_target); - } else { - __ z_cg(k_RInfo, Address(obj, oopDesc::klass_offset_in_bytes())); - __ branch_optimized(Assembler::bcondNotEqual, *failure_target); - } + __ load_klass(klass_RInfo, obj); + __ compareU64_and_branch(k_RInfo, klass_RInfo, Assembler::bcondNotEqual, *failure_target); // Successful cast, fall through to profile or jump. } else { bool need_slow_path = !k->is_loaded() || diff --git a/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp index 993c1a1b552..813143938f9 100644 --- a/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -107,10 +107,10 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register } if (len->is_valid()) { - // Length will be in the klass gap, if one exists. + // Length will be in the klass gap. z_st(len, Address(obj, arrayOopDesc::length_offset_in_bytes())); - } else if (UseCompressedClassPointers && !UseCompactObjectHeaders) { - store_klass_gap(Rzero, obj); // Zero klass gap for compressed oops. + } else if (!UseCompactObjectHeaders) { + store_klass_gap(Rzero, obj); // Zero klass gap. } } diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index 6e132f895bd..de3608e74ba 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -1237,7 +1237,6 @@ void MacroAssembler::load_narrow_oop(Register t, narrowOop a) { // Load narrow klass constant, compression required. void MacroAssembler::load_narrow_klass(Register t, Klass* k) { - assert(UseCompressedClassPointers, "must be on to call this method"); narrowKlass encoded_k = CompressedKlassPointers::encode(k); load_const_32to64(t, encoded_k, false /*sign_extend*/); } @@ -1255,7 +1254,6 @@ void MacroAssembler::compare_immediate_narrow_oop(Register oop1, narrowOop oop2) // Compare narrow oop in reg with narrow oop constant, no decompression. void MacroAssembler::compare_immediate_narrow_klass(Register klass1, Klass* klass2) { - assert(UseCompressedClassPointers, "must be on to call this method"); narrowKlass encoded_k = CompressedKlassPointers::encode(klass2); Assembler::z_clfi(klass1, encoded_k); @@ -1348,8 +1346,6 @@ int MacroAssembler::patch_load_narrow_oop(address pos, oop o) { // Patching the immediate value of CPU version dependent load_narrow_klass sequence. // The passed ptr must NOT be in compressed format! int MacroAssembler::patch_load_narrow_klass(address pos, Klass* k) { - assert(UseCompressedClassPointers, "Can only patch compressed klass pointers"); - narrowKlass nk = CompressedKlassPointers::encode(k); return patch_load_const_32to64(pos, nk); } @@ -1364,8 +1360,6 @@ int MacroAssembler::patch_compare_immediate_narrow_oop(address pos, oop o) { // Patching the immediate value of CPU version dependent compare_immediate_narrow_klass sequence. // The passed ptr must NOT be in compressed format! int MacroAssembler::patch_compare_immediate_narrow_klass(address pos, Klass* k) { - assert(UseCompressedClassPointers, "Can only patch compressed klass pointers"); - narrowKlass nk = CompressedKlassPointers::encode(k); return patch_compare_immediate_32(pos, nk); } @@ -2235,10 +2229,8 @@ int MacroAssembler::ic_check(int end_alignment) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(R1_scratch, R2_receiver); - } else if (UseCompressedClassPointers) { - z_llgf(R1_scratch, Address(R2_receiver, oopDesc::klass_offset_in_bytes())); } else { - z_lg(R1_scratch, Address(R2_receiver, oopDesc::klass_offset_in_bytes())); + z_llgf(R1_scratch, Address(R2_receiver, oopDesc::klass_offset_in_bytes())); } z_cg(R1_scratch, Address(R9_data, in_bytes(CompiledICData::speculated_klass_offset()))); z_bre(success); @@ -3916,7 +3908,6 @@ void MacroAssembler::encode_klass_not_null(Register dst, Register src) { address base = CompressedKlassPointers::base(); int shift = CompressedKlassPointers::shift(); bool need_zero_extend = base != nullptr; - assert(UseCompressedClassPointers, "only for compressed klass ptrs"); BLOCK_COMMENT("cKlass encoder {"); @@ -4013,7 +4004,6 @@ int MacroAssembler::instr_size_for_decode_klass_not_null() { address base = CompressedKlassPointers::base(); int shift_size = CompressedKlassPointers::shift() == 0 ? 0 : 6; /* sllg */ int addbase_size = 0; - assert(UseCompressedClassPointers, "only for compressed klass ptrs"); if (base != nullptr) { unsigned int base_h = ((unsigned long)base)>>32; @@ -4043,7 +4033,6 @@ void MacroAssembler::decode_klass_not_null(Register dst) { address base = CompressedKlassPointers::base(); int shift = CompressedKlassPointers::shift(); int beg_off = offset(); - assert(UseCompressedClassPointers, "only for compressed klass ptrs"); BLOCK_COMMENT("cKlass decoder (const size) {"); @@ -4085,7 +4074,6 @@ void MacroAssembler::decode_klass_not_null(Register dst) { void MacroAssembler::decode_klass_not_null(Register dst, Register src) { address base = CompressedKlassPointers::base(); int shift = CompressedKlassPointers::shift(); - assert(UseCompressedClassPointers, "only for compressed klass ptrs"); BLOCK_COMMENT("cKlass decoder {"); @@ -4125,13 +4113,9 @@ void MacroAssembler::decode_klass_not_null(Register dst, Register src) { } void MacroAssembler::load_klass(Register klass, Address mem) { - if (UseCompressedClassPointers) { - z_llgf(klass, mem); - // Attention: no null check here! - decode_klass_not_null(klass); - } else { - z_lg(klass, mem); - } + z_llgf(klass, mem); + // Attention: no null check here! + decode_klass_not_null(klass); } // Loads the obj's Klass* into dst. @@ -4154,10 +4138,8 @@ void MacroAssembler::cmp_klass(Register klass, Register obj, Register tmp) { assert_different_registers(klass, obj, tmp); load_narrow_klass_compact(tmp, obj); z_cr(klass, tmp); - } else if (UseCompressedClassPointers) { - z_c(klass, Address(obj, oopDesc::klass_offset_in_bytes())); } else { - z_cg(klass, Address(obj, oopDesc::klass_offset_in_bytes())); + z_c(klass, Address(obj, oopDesc::klass_offset_in_bytes())); } BLOCK_COMMENT("} cmp_klass"); } @@ -4170,12 +4152,9 @@ void MacroAssembler::cmp_klasses_from_objects(Register obj1, Register obj2, Regi load_narrow_klass_compact(tmp1, obj1); load_narrow_klass_compact(tmp2, obj2); z_cr(tmp1, tmp2); - } else if (UseCompressedClassPointers) { + } else { z_l(tmp1, Address(obj1, oopDesc::klass_offset_in_bytes())); z_c(tmp1, Address(obj2, oopDesc::klass_offset_in_bytes())); - } else { - z_lg(tmp1, Address(obj1, oopDesc::klass_offset_in_bytes())); - z_cg(tmp1, Address(obj2, oopDesc::klass_offset_in_bytes())); } BLOCK_COMMENT("} cmp_klasses_from_objects"); } @@ -4184,36 +4163,28 @@ void MacroAssembler::load_klass(Register klass, Register src_oop) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(klass, src_oop); decode_klass_not_null(klass); - } else if (UseCompressedClassPointers) { + } else { z_llgf(klass, oopDesc::klass_offset_in_bytes(), src_oop); decode_klass_not_null(klass); - } else { - z_lg(klass, oopDesc::klass_offset_in_bytes(), src_oop); } } void MacroAssembler::store_klass(Register klass, Register dst_oop, Register ck) { assert(!UseCompactObjectHeaders, "Don't use with compact headers"); - if (UseCompressedClassPointers) { - assert_different_registers(dst_oop, klass, Z_R0); - if (ck == noreg) ck = klass; - encode_klass_not_null(ck, klass); - z_st(ck, Address(dst_oop, oopDesc::klass_offset_in_bytes())); - } else { - z_stg(klass, Address(dst_oop, oopDesc::klass_offset_in_bytes())); - } + assert_different_registers(dst_oop, klass, Z_R0); + if (ck == noreg) ck = klass; + encode_klass_not_null(ck, klass); + z_st(ck, Address(dst_oop, oopDesc::klass_offset_in_bytes())); } void MacroAssembler::store_klass_gap(Register s, Register d) { assert(!UseCompactObjectHeaders, "Don't use with compact headers"); - if (UseCompressedClassPointers) { - assert(s != d, "not enough registers"); - // Support s = noreg. - if (s != noreg) { - z_st(s, Address(d, oopDesc::klass_gap_offset_in_bytes())); - } else { - z_mvhi(Address(d, oopDesc::klass_gap_offset_in_bytes()), 0); - } + assert(s != d, "not enough registers"); + // Support s = noreg. + if (s != noreg) { + z_st(s, Address(d, oopDesc::klass_gap_offset_in_bytes())); + } else { + z_mvhi(Address(d, oopDesc::klass_gap_offset_in_bytes()), 0); } } @@ -4227,67 +4198,64 @@ void MacroAssembler::compare_klass_ptr(Register Rop1, int64_t disp, Register Rba BLOCK_COMMENT("compare klass ptr {"); - if (UseCompressedClassPointers) { - const int shift = CompressedKlassPointers::shift(); - address base = CompressedKlassPointers::base(); + const int shift = CompressedKlassPointers::shift(); + address base = CompressedKlassPointers::base(); - if (UseCompactObjectHeaders) { - assert(shift >= 3, "cKlass encoder detected bad shift"); - } else { - assert((shift == 0) || (shift == 3), "cKlass encoder detected bad shift"); - } - assert_different_registers(Rop1, Z_R0); - assert_different_registers(Rop1, Rbase, Z_R1); - - // First encode register oop and then compare with cOop in memory. - // This sequence saves an unnecessary cOop load and decode. - if (base == nullptr) { - if (shift == 0) { - z_cl(Rop1, disp, Rbase); // Unscaled - } else { - z_srlg(Z_R0, Rop1, shift); // ZeroBased - z_cl(Z_R0, disp, Rbase); - } - } else { // HeapBased -#ifdef ASSERT - bool used_R0 = true; - bool used_R1 = true; -#endif - Register current = Rop1; - Label done; - - if (maybenull) { // null pointer must be preserved! - z_ltgr(Z_R0, current); - z_bre(done); - current = Z_R0; - } - - unsigned int base_h = ((unsigned long)base)>>32; - unsigned int base_l = (unsigned int)((unsigned long)base); - if ((base_h != 0) && (base_l == 0) && VM_Version::has_HighWordInstr()) { - lgr_if_needed(Z_R0, current); - z_aih(Z_R0, -((int)base_h)); // Base has no set bits in lower half. - } else if ((base_h == 0) && (base_l != 0)) { - lgr_if_needed(Z_R0, current); - z_agfi(Z_R0, -(int)base_l); - } else { - int pow2_offset = get_oop_base_complement(Z_R1, ((uint64_t)(intptr_t)base)); - add2reg_with_index(Z_R0, pow2_offset, Z_R1, Rop1); // Subtract base by adding complement. - } - - if (shift != 0) { - z_srlg(Z_R0, Z_R0, shift); - } - bind(done); - z_cl(Z_R0, disp, Rbase); -#ifdef ASSERT - if (used_R0) preset_reg(Z_R0, 0xb05bUL, 2); - if (used_R1) preset_reg(Z_R1, 0xb06bUL, 2); -#endif - } + if (UseCompactObjectHeaders) { + assert(shift >= 3, "cKlass encoder detected bad shift"); } else { - z_clg(Rop1, disp, Z_R0, Rbase); + assert((shift == 0) || (shift == 3), "cKlass encoder detected bad shift"); } + assert_different_registers(Rop1, Z_R0); + assert_different_registers(Rop1, Rbase, Z_R1); + + // First encode register oop and then compare with cOop in memory. + // This sequence saves an unnecessary cOop load and decode. + if (base == nullptr) { + if (shift == 0) { + z_cl(Rop1, disp, Rbase); // Unscaled + } else { + z_srlg(Z_R0, Rop1, shift); // ZeroBased + z_cl(Z_R0, disp, Rbase); + } + } else { // HeapBased +#ifdef ASSERT + bool used_R0 = true; + bool used_R1 = true; +#endif + Register current = Rop1; + Label done; + + if (maybenull) { // null pointer must be preserved! + z_ltgr(Z_R0, current); + z_bre(done); + current = Z_R0; + } + + unsigned int base_h = ((unsigned long)base)>>32; + unsigned int base_l = (unsigned int)((unsigned long)base); + if ((base_h != 0) && (base_l == 0) && VM_Version::has_HighWordInstr()) { + lgr_if_needed(Z_R0, current); + z_aih(Z_R0, -((int)base_h)); // Base has no set bits in lower half. + } else if ((base_h == 0) && (base_l != 0)) { + lgr_if_needed(Z_R0, current); + z_agfi(Z_R0, -(int)base_l); + } else { + int pow2_offset = get_oop_base_complement(Z_R1, ((uint64_t)(intptr_t)base)); + add2reg_with_index(Z_R0, pow2_offset, Z_R1, Rop1); // Subtract base by adding complement. + } + + if (shift != 0) { + z_srlg(Z_R0, Z_R0, shift); + } + bind(done); + z_cl(Z_R0, disp, Rbase); +#ifdef ASSERT + if (used_R0) preset_reg(Z_R0, 0xb05bUL, 2); + if (used_R1) preset_reg(Z_R1, 0xb06bUL, 2); +#endif + } + BLOCK_COMMENT("} compare klass ptr"); } diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index da24ae80d45..32e484d4790 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * Copyright (c) 2024 IBM Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -842,8 +842,7 @@ class MacroAssembler: public Assembler { void store_klass(Register klass, Register dst_oop, Register ck = noreg); // Klass will get compressed if ck not provided. void store_klass_gap(Register s, Register dst_oop); void load_narrow_klass_compact(Register dst, Register src); - // Compares the Klass pointer of an object to a given Klass (which might be narrow, - // depending on UseCompressedClassPointers). + // Compares the narrow Klass pointer of an object to a given narrow Klass void cmp_klass(Register klass, Register obj, Register tmp); // Compares the Klass pointer of two objects obj1 and obj2. Result is in the condition flags. // Uses tmp1 and tmp2 as temporary registers. diff --git a/src/hotspot/cpu/s390/matcher_s390.hpp b/src/hotspot/cpu/s390/matcher_s390.hpp index 99461e33e3c..b04a6566d41 100644 --- a/src/hotspot/cpu/s390/matcher_s390.hpp +++ b/src/hotspot/cpu/s390/matcher_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -82,7 +82,6 @@ static bool narrow_klass_use_complex_address() { NOT_LP64(ShouldNotCallThis()); - assert(UseCompressedClassPointers, "only for compressed klass code"); // TODO HS25: z port if (MatchDecodeNodes) return true; return false; } diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index d9be0fdcc8d..5397a230642 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -77,23 +77,6 @@ const Register SHIFT_count = rcx; // where count for shift operations must be #define __ _masm-> - -static void select_different_registers(Register preserve, - Register extra, - Register &tmp1, - Register &tmp2) { - if (tmp1 == preserve) { - assert_different_registers(tmp1, tmp2, extra); - tmp1 = extra; - } else if (tmp2 == preserve) { - assert_different_registers(tmp1, tmp2, extra); - tmp2 = extra; - } - assert_different_registers(preserve, tmp1, tmp2); -} - - - static void select_different_registers(Register preserve, Register extra, Register &tmp1, @@ -1309,12 +1292,8 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L } else if (obj == klass_RInfo) { klass_RInfo = dst; } - if (k->is_loaded() && !UseCompressedClassPointers) { - select_different_registers(obj, dst, k_RInfo, klass_RInfo); - } else { - Rtmp1 = op->tmp3()->as_register(); - select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); - } + Rtmp1 = op->tmp3()->as_register(); + select_different_registers(obj, dst, k_RInfo, klass_RInfo, Rtmp1); assert_different_registers(obj, k_RInfo, klass_RInfo); @@ -1348,12 +1327,8 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L if (op->fast_check()) { // get object class // not a safepoint as obj null check happens earlier - if (UseCompressedClassPointers) { - __ load_klass(Rtmp1, obj, tmp_load_klass); - __ cmpptr(k_RInfo, Rtmp1); - } else { - __ cmpptr(k_RInfo, Address(obj, oopDesc::klass_offset_in_bytes())); - } + __ load_klass(Rtmp1, obj, tmp_load_klass); + __ cmpptr(k_RInfo, Rtmp1); __ jcc(Assembler::notEqual, *failure_target); // successful cast, fall through to profile or jump } else { @@ -2651,9 +2626,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { // but not necessarily exactly of type default_type. Label known_ok, halt; __ mov_metadata(tmp, default_type->constant_encoding()); - if (UseCompressedClassPointers) { - __ encode_klass_not_null(tmp, rscratch1); - } + __ encode_klass_not_null(tmp, rscratch1); if (basic_type != T_OBJECT) { __ cmp_klass(tmp, dst, tmp2); diff --git a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp index 5459e8df229..f448e4ee17f 100644 --- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1291,9 +1291,7 @@ void LIRGenerator::do_CheckCast(CheckCast* x) { } LIR_Opr reg = rlock_result(x); LIR_Opr tmp3 = LIR_OprFact::illegalOpr; - if (!x->klass()->is_loaded() || UseCompressedClassPointers) { - tmp3 = new_register(objectType); - } + tmp3 = new_register(objectType); __ checkcast(reg, obj.result(), x->klass(), new_register(objectType), new_register(objectType), tmp3, x->direct_compare(), info_for_exception, patching_info, stub, @@ -1313,9 +1311,7 @@ void LIRGenerator::do_InstanceOf(InstanceOf* x) { } obj.load_item(); LIR_Opr tmp3 = LIR_OprFact::illegalOpr; - if (!x->klass()->is_loaded() || UseCompressedClassPointers) { - tmp3 = new_register(objectType); - } + tmp3 = new_register(objectType); __ instanceof(reg, obj.result(), x->klass(), new_register(objectType), new_register(objectType), tmp3, x->direct_compare(), patching_info, x->profiled_method(), x->profiled_bci()); diff --git a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp index 88e2e6c8ba9..7adaea48ff1 100644 --- a/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -85,14 +85,11 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register if (UseCompactObjectHeaders) { movptr(t1, Address(klass, Klass::prototype_header_offset())); movptr(Address(obj, oopDesc::mark_offset_in_bytes()), t1); - } else if (UseCompressedClassPointers) { // Take care not to kill klass + } else { // Take care not to kill klass movptr(Address(obj, oopDesc::mark_offset_in_bytes()), checked_cast(markWord::prototype().value())); movptr(t1, klass); encode_klass_not_null(t1, rscratch1); movl(Address(obj, oopDesc::klass_offset_in_bytes()), t1); - } else { - movptr(Address(obj, oopDesc::mark_offset_in_bytes()), checked_cast(markWord::prototype().value())); - movptr(Address(obj, oopDesc::klass_offset_in_bytes()), klass); } if (len->is_valid()) { @@ -104,7 +101,7 @@ void C1_MacroAssembler::initialize_header(Register obj, Register klass, Register xorl(t1, t1); movl(Address(obj, base_offset), t1); } - } else if (UseCompressedClassPointers && !UseCompactObjectHeaders) { + } else if (!UseCompactObjectHeaders) { xorptr(t1, t1); store_klass_gap(obj, t1); } diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 1d77be26bd9..a0f08145d55 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -985,12 +985,9 @@ int MacroAssembler::ic_check(int end_alignment) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(temp, receiver); cmpl(temp, Address(data, CompiledICData::speculated_klass_offset())); - } else if (UseCompressedClassPointers) { + } else { movl(temp, Address(receiver, oopDesc::klass_offset_in_bytes())); cmpl(temp, Address(data, CompiledICData::speculated_klass_offset())); - } else { - movptr(temp, Address(receiver, oopDesc::klass_offset_in_bytes())); - cmpptr(temp, Address(data, CompiledICData::speculated_klass_offset())); } // if inline cache check fails, then jump to runtime routine @@ -5384,11 +5381,9 @@ void MacroAssembler::load_klass(Register dst, Register src, Register tmp) { if (UseCompactObjectHeaders) { load_narrow_klass_compact(dst, src); decode_klass_not_null(dst, tmp); - } else if (UseCompressedClassPointers) { + } else { movl(dst, Address(src, oopDesc::klass_offset_in_bytes())); decode_klass_not_null(dst, tmp); - } else { - movptr(dst, Address(src, oopDesc::klass_offset_in_bytes())); } } @@ -5396,12 +5391,8 @@ void MacroAssembler::store_klass(Register dst, Register src, Register tmp) { assert(!UseCompactObjectHeaders, "not with compact headers"); assert_different_registers(src, tmp); assert_different_registers(dst, tmp); - if (UseCompressedClassPointers) { - encode_klass_not_null(src, tmp); - movl(Address(dst, oopDesc::klass_offset_in_bytes()), src); - } else { - movptr(Address(dst, oopDesc::klass_offset_in_bytes()), src); - } + encode_klass_not_null(src, tmp); + movl(Address(dst, oopDesc::klass_offset_in_bytes()), src); } void MacroAssembler::cmp_klass(Register klass, Register obj, Register tmp) { @@ -5410,10 +5401,8 @@ void MacroAssembler::cmp_klass(Register klass, Register obj, Register tmp) { assert_different_registers(klass, obj, tmp); load_narrow_klass_compact(tmp, obj); cmpl(klass, tmp); - } else if (UseCompressedClassPointers) { - cmpl(klass, Address(obj, oopDesc::klass_offset_in_bytes())); } else { - cmpptr(klass, Address(obj, oopDesc::klass_offset_in_bytes())); + cmpl(klass, Address(obj, oopDesc::klass_offset_in_bytes())); } } @@ -5424,12 +5413,9 @@ void MacroAssembler::cmp_klasses_from_objects(Register obj1, Register obj2, Regi load_narrow_klass_compact(tmp1, obj1); load_narrow_klass_compact(tmp2, obj2); cmpl(tmp1, tmp2); - } else if (UseCompressedClassPointers) { + } else { movl(tmp1, Address(obj1, oopDesc::klass_offset_in_bytes())); cmpl(tmp1, Address(obj2, oopDesc::klass_offset_in_bytes())); - } else { - movptr(tmp1, Address(obj1, oopDesc::klass_offset_in_bytes())); - cmpptr(tmp1, Address(obj2, oopDesc::klass_offset_in_bytes())); } } @@ -5478,10 +5464,8 @@ void MacroAssembler::store_heap_oop_null(Address dst) { void MacroAssembler::store_klass_gap(Register dst, Register src) { assert(!UseCompactObjectHeaders, "Don't use with compact headers"); - if (UseCompressedClassPointers) { - // Store to klass gap in destination - movl(Address(dst, oopDesc::klass_gap_offset_in_bytes()), src); - } + // Store to klass gap in destination + movl(Address(dst, oopDesc::klass_gap_offset_in_bytes()), src); } #ifdef ASSERT @@ -5671,7 +5655,6 @@ void MacroAssembler::decode_klass_not_null(Register r, Register tmp) { BLOCK_COMMENT("decode_klass_not_null {"); assert_different_registers(r, tmp); // Note: it will change flags - assert(UseCompressedClassPointers, "should only be used for compressed headers"); // Cannot assert, unverified entry point counts instructions (see .ad file) // vtableStubs also counts instructions in pd_code_size_limit. // Also do not verify_oop as this is called by verify_oop. @@ -5693,7 +5676,6 @@ void MacroAssembler::decode_and_move_klass_not_null(Register dst, Register src) BLOCK_COMMENT("decode_and_move_klass_not_null {"); assert_different_registers(src, dst); // Note: it will change flags - assert (UseCompressedClassPointers, "should only be used for compressed headers"); // Cannot assert, unverified entry point counts instructions (see .ad file) // vtableStubs also counts instructions in pd_code_size_limit. // Also do not verify_oop as this is called by verify_oop. @@ -5750,7 +5732,6 @@ void MacroAssembler::set_narrow_oop(Address dst, jobject obj) { } void MacroAssembler::set_narrow_klass(Register dst, Klass* k) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int klass_index = oop_recorder()->find_index(k); RelocationHolder rspec = metadata_Relocation::spec(klass_index); @@ -5758,7 +5739,6 @@ void MacroAssembler::set_narrow_klass(Register dst, Klass* k) { } void MacroAssembler::set_narrow_klass(Address dst, Klass* k) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int klass_index = oop_recorder()->find_index(k); RelocationHolder rspec = metadata_Relocation::spec(klass_index); @@ -5784,7 +5764,6 @@ void MacroAssembler::cmp_narrow_oop(Address dst, jobject obj) { } void MacroAssembler::cmp_narrow_klass(Register dst, Klass* k) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int klass_index = oop_recorder()->find_index(k); RelocationHolder rspec = metadata_Relocation::spec(klass_index); @@ -5792,7 +5771,6 @@ void MacroAssembler::cmp_narrow_klass(Register dst, Klass* k) { } void MacroAssembler::cmp_narrow_klass(Address dst, Klass* k) { - assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int klass_index = oop_recorder()->find_index(k); RelocationHolder rspec = metadata_Relocation::spec(klass_index); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index 8469deaa8be..3bdd1e4477a 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -351,8 +351,7 @@ class MacroAssembler: public Assembler { void load_klass(Register dst, Register src, Register tmp); void store_klass(Register dst, Register src, Register tmp); - // Compares the Klass pointer of an object to a given Klass (which might be narrow, - // depending on UseCompressedClassPointers). + // Compares the narrow Klass pointer of an object to a given narrow Klass. void cmp_klass(Register klass, Register obj, Register tmp); // Compares the Klass pointer of two objects obj1 and obj2. Result is in the condition flags. diff --git a/src/hotspot/cpu/x86/matcher_x86.hpp b/src/hotspot/cpu/x86/matcher_x86.hpp index f7973a8564e..62a5d2827bc 100644 --- a/src/hotspot/cpu/x86/matcher_x86.hpp +++ b/src/hotspot/cpu/x86/matcher_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -75,7 +75,6 @@ } static bool narrow_klass_use_complex_address() { - assert(UseCompressedClassPointers, "only for compressed klass code"); return (CompressedKlassPointers::shift() <= 3); } diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 2fd4e5516fc..f31d64f3d7e 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -2605,13 +2605,8 @@ uint BoxLockNode::size(PhaseRegAlloc *ra_) const #ifndef PRODUCT void MachUEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const { - if (UseCompressedClassPointers) { - st->print_cr("movl rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); - st->print_cr("\tcmpl rscratch1, [rax + CompiledICData::speculated_klass_offset()]\t # Inline cache check"); - } else { - st->print_cr("movq rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); - st->print_cr("\tcmpq rscratch1, [rax + CompiledICData::speculated_klass_offset()]\t # Inline cache check"); - } + st->print_cr("movl rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()]\t# compressed klass"); + st->print_cr("\tcmpl rscratch1, [rax + CompiledICData::speculated_klass_offset()]\t # Inline cache check"); st->print_cr("\tjne SharedRuntime::_ic_miss_stub"); } #endif diff --git a/src/hotspot/share/cds/aotMapLogger.cpp b/src/hotspot/share/cds/aotMapLogger.cpp index 98336ff9b1f..9f338826fd6 100644 --- a/src/hotspot/share/cds/aotMapLogger.cpp +++ b/src/hotspot/share/cds/aotMapLogger.cpp @@ -589,7 +589,6 @@ public: } Klass* real_klass() { - assert(UseCompressedClassPointers, "heap archiving requires UseCompressedClassPointers"); return _data._klass; } diff --git a/src/hotspot/share/cds/aotMappedHeapLoader.hpp b/src/hotspot/share/cds/aotMappedHeapLoader.hpp index 7c5ca1b1f9e..10f5ce3124f 100644 --- a/src/hotspot/share/cds/aotMappedHeapLoader.hpp +++ b/src/hotspot/share/cds/aotMappedHeapLoader.hpp @@ -54,7 +54,7 @@ public: // Can this VM map archived heap region? Currently only G1+compressed{oops,cp} static bool can_map() { - CDS_JAVA_HEAP_ONLY(return (UseG1GC && UseCompressedClassPointers);) + CDS_JAVA_HEAP_ONLY(return UseG1GC;) NOT_CDS_JAVA_HEAP(return false;) } diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp index 3456c845938..8f810ef5244 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp @@ -450,7 +450,6 @@ int AOTMappedHeapWriter::filler_array_length(size_t fill_bytes) { } HeapWord* AOTMappedHeapWriter::init_filler_array_at_buffer_top(int array_length, size_t fill_bytes) { - assert(UseCompressedClassPointers, "Archived heap only supported for compressed klasses"); Klass* oak = Universe::objectArrayKlass(); // already relocated to point to archived klass HeapWord* mem = offset_to_buffered_address(_buffer_used); memset(mem, 0, fill_bytes); @@ -724,7 +723,6 @@ template void AOTMappedHeapWriter::mark_oop_pointer(T* buffered_add } void AOTMappedHeapWriter::update_header_for_requested_obj(oop requested_obj, oop src_obj, Klass* src_klass) { - assert(UseCompressedClassPointers, "Archived heap only supported for compressed klasses"); narrowKlass nk = ArchiveBuilder::current()->get_requested_narrow_klass(src_klass); address buffered_addr = requested_addr_to_buffered_addr(cast_from_oop
(requested_obj)); diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 76c37194882..55e9f93b3ab 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -250,9 +250,9 @@ static bool shared_base_too_high(char* specified_base, char* aligned_base, size_ static char* compute_shared_base(size_t cds_max) { char* specified_base = (char*)SharedBaseAddress; size_t alignment = AOTMetaspace::core_region_alignment(); - if (UseCompressedClassPointers && CompressedKlassPointers::needs_class_space()) { - alignment = MAX2(alignment, Metaspace::reserve_alignment()); - } +#if INCLUDE_CLASS_SPACE + alignment = MAX2(alignment, Metaspace::reserve_alignment()); +#endif if (SharedBaseAddress == 0) { // Special meaning of -XX:SharedBaseAddress=0 -> Always map archive at os-selected address. @@ -1637,32 +1637,29 @@ MapArchiveResult AOTMetaspace::map_archives(FileMapInfo* static_mapinfo, FileMap aot_log_debug(aot)("Failed to reserve spaces (use_requested_addr=%u)", (unsigned)use_requested_addr); } else { - if (Metaspace::using_class_space()) { - prot_zone_size = protection_zone_size(); - } + CLASS_SPACE_ONLY(prot_zone_size = protection_zone_size();) -#ifdef ASSERT // Some sanity checks after reserving address spaces for archives // and class space. assert(archive_space_rs.is_reserved(), "Sanity"); - if (Metaspace::using_class_space()) { - assert(archive_space_rs.base() == mapped_base_address && - archive_space_rs.size() > protection_zone_size(), - "Archive space must lead and include the protection zone"); - // Class space must closely follow the archive space. Both spaces - // must be aligned correctly. - assert(class_space_rs.is_reserved() && class_space_rs.size() > 0, - "A class space should have been reserved"); - assert(class_space_rs.base() >= archive_space_rs.end(), - "class space should follow the cds archive space"); - assert(is_aligned(archive_space_rs.base(), - core_region_alignment()), - "Archive space misaligned"); - assert(is_aligned(class_space_rs.base(), - Metaspace::reserve_alignment()), - "class space misaligned"); - } -#endif // ASSERT + +#if INCLUDE_CLASS_SPACE + assert(archive_space_rs.base() == mapped_base_address && + archive_space_rs.size() > protection_zone_size(), + "Archive space must lead and include the protection zone"); + // Class space must closely follow the archive space. Both spaces + // must be aligned correctly. + assert(class_space_rs.is_reserved() && class_space_rs.size() > 0, + "A class space should have been reserved"); + assert(class_space_rs.base() >= archive_space_rs.end(), + "class space should follow the cds archive space"); + assert(is_aligned(archive_space_rs.base(), + core_region_alignment()), + "Archive space misaligned"); + assert(is_aligned(class_space_rs.base(), + Metaspace::reserve_alignment()), + "class space misaligned"); +#endif // INCLUDE_CLASS_SPACE aot_log_info(aot)("Reserved archive_space_rs [" INTPTR_FORMAT " - " INTPTR_FORMAT "] (%zu) bytes%s", p2i(archive_space_rs.base()), p2i(archive_space_rs.end()), archive_space_rs.size(), @@ -1764,62 +1761,60 @@ MapArchiveResult AOTMetaspace::map_archives(FileMapInfo* static_mapinfo, FileMap if (result == MAP_ARCHIVE_SUCCESS) { SharedBaseAddress = (size_t)mapped_base_address; -#ifdef _LP64 - if (Metaspace::using_class_space()) { - assert(prot_zone_size > 0 && - *(mapped_base_address) == 'P' && - *(mapped_base_address + prot_zone_size - 1) == 'P', - "Protection zone was overwritten?"); - // Set up ccs in metaspace. - Metaspace::initialize_class_space(class_space_rs); +#if INCLUDE_CLASS_SPACE + assert(prot_zone_size > 0 && + *(mapped_base_address) == 'P' && + *(mapped_base_address + prot_zone_size - 1) == 'P', + "Protection zone was overwritten?"); + // Set up ccs in metaspace. + Metaspace::initialize_class_space(class_space_rs); - // Set up compressed Klass pointer encoding: the encoding range must - // cover both archive and class space. - const address klass_range_start = (address)mapped_base_address; - const size_t klass_range_size = (address)class_space_rs.end() - klass_range_start; - if (INCLUDE_CDS_JAVA_HEAP || UseCompactObjectHeaders) { - // The CDS archive may contain narrow Klass IDs that were precomputed at archive generation time: - // - every archived java object header (only if INCLUDE_CDS_JAVA_HEAP) - // - every archived Klass' prototype (only if +UseCompactObjectHeaders) - // - // In order for those IDs to still be valid, we need to dictate base and shift: base should be the - // mapping start (including protection zone), shift should be the shift used at archive generation time. - CompressedKlassPointers::initialize_for_given_encoding( - klass_range_start, klass_range_size, - klass_range_start, ArchiveBuilder::precomputed_narrow_klass_shift() // precomputed encoding, see ArchiveBuilder - ); - assert(CompressedKlassPointers::base() == klass_range_start, "must be"); - } else { - // Let JVM freely choose encoding base and shift - CompressedKlassPointers::initialize(klass_range_start, klass_range_size); - assert(CompressedKlassPointers::base() == nullptr || - CompressedKlassPointers::base() == klass_range_start, "must be"); - } - // Establish protection zone, but only if we need one - if (CompressedKlassPointers::base() == klass_range_start) { - CompressedKlassPointers::establish_protection_zone(klass_range_start, prot_zone_size); - } + // Set up compressed Klass pointer encoding: the encoding range must + // cover both archive and class space. + const address klass_range_start = (address)mapped_base_address; + const size_t klass_range_size = (address)class_space_rs.end() - klass_range_start; + if (INCLUDE_CDS_JAVA_HEAP || UseCompactObjectHeaders) { + // The CDS archive may contain narrow Klass IDs that were precomputed at archive generation time: + // - every archived java object header (only if INCLUDE_CDS_JAVA_HEAP) + // - every archived Klass' prototype (only if +UseCompactObjectHeaders) + // + // In order for those IDs to still be valid, we need to dictate base and shift: base should be the + // mapping start (including protection zone), shift should be the shift used at archive generation time. + CompressedKlassPointers::initialize_for_given_encoding( + klass_range_start, klass_range_size, + klass_range_start, ArchiveBuilder::precomputed_narrow_klass_shift() // precomputed encoding, see ArchiveBuilder + ); + assert(CompressedKlassPointers::base() == klass_range_start, "must be"); + } else { + // Let JVM freely choose encoding base and shift + CompressedKlassPointers::initialize(klass_range_start, klass_range_size); + assert(CompressedKlassPointers::base() == nullptr || + CompressedKlassPointers::base() == klass_range_start, "must be"); + } + // Establish protection zone, but only if we need one + if (CompressedKlassPointers::base() == klass_range_start) { + CompressedKlassPointers::establish_protection_zone(klass_range_start, prot_zone_size); + } - if (static_mapinfo->can_use_heap_region()) { - if (static_mapinfo->object_streaming_mode()) { - HeapShared::initialize_loading_mode(HeapArchiveMode::_streaming); - } else { - // map_or_load_heap_region() compares the current narrow oop and klass encodings - // with the archived ones, so it must be done after all encodings are determined. - static_mapinfo->map_or_load_heap_region(); - HeapShared::initialize_loading_mode(HeapArchiveMode::_mapping); - } + if (static_mapinfo->can_use_heap_region()) { + if (static_mapinfo->object_streaming_mode()) { + HeapShared::initialize_loading_mode(HeapArchiveMode::_streaming); } else { - FileMapRegion* r = static_mapinfo->region_at(AOTMetaspace::hp); - if (r->used() > 0) { - AOTMetaspace::report_loading_error("Cannot use CDS heap data."); - } - if (!CDSConfig::is_dumping_static_archive()) { - CDSConfig::stop_using_full_module_graph("No CDS heap data"); - } + // map_or_load_heap_region() compares the current narrow oop and klass encodings + // with the archived ones, so it must be done after all encodings are determined. + static_mapinfo->map_or_load_heap_region(); + HeapShared::initialize_loading_mode(HeapArchiveMode::_mapping); + } + } else { + FileMapRegion* r = static_mapinfo->region_at(AOTMetaspace::hp); + if (r->used() > 0) { + AOTMetaspace::report_loading_error("Cannot use CDS heap data."); + } + if (!CDSConfig::is_dumping_static_archive()) { + CDSConfig::stop_using_full_module_graph("No CDS heap data"); } } -#endif // _LP64 +#endif // INCLUDE_CLASS_SPACE log_info(aot)("initial optimized module handling: %s", CDSConfig::is_using_optimized_module_handling() ? "enabled" : "disabled"); log_info(aot)("initial full module graph: %s", CDSConfig::is_using_full_module_graph() ? "enabled" : "disabled"); } else { @@ -1852,8 +1847,13 @@ MapArchiveResult AOTMetaspace::map_archives(FileMapInfo* static_mapinfo, FileMap // (The gap may result from different alignment requirements between metaspace // and CDS) // -// If UseCompressedClassPointers is disabled, only one address space will be -// reserved: +// The range encompassing both spaces will be suitable to en/decode narrow Klass +// pointers: the base will be valid for encoding the range [Base, End) and not +// surpass the max. range for that encoding. +// +// On 32-bit, a "narrow" Klass is just the pointer itself, and the Klass encoding +// range encompasses the whole address range. Consequently, we can "decode" and +// "encode" any pointer anywhere, and so are free to place the CDS archive anywhere: // // +-- Base address End // | | @@ -1867,27 +1867,21 @@ MapArchiveResult AOTMetaspace::map_archives(FileMapInfo* static_mapinfo, FileMap // use_archive_base_addr address is false, this base address is determined // by the platform. // -// If UseCompressedClassPointers=1, the range encompassing both spaces will be -// suitable to en/decode narrow Klass pointers: the base will be valid for -// encoding, the range [Base, End) and not surpass the max. range for that encoding. -// // Return: // // - On success: // - total_space_rs will be reserved as whole for archive_space_rs and -// class_space_rs if UseCompressedClassPointers is true. +// class_space_rs on 64-bit. // On Windows, try reserve archive_space_rs and class_space_rs // separately first if use_archive_base_addr is true. // - archive_space_rs will be reserved and large enough to host static and // if needed dynamic archive: [Base, A). // archive_space_rs.base and size will be aligned to CDS reserve // granularity. -// - class_space_rs: If UseCompressedClassPointers=1, class_space_rs will -// be reserved. Its start address will be aligned to metaspace reserve -// alignment, which may differ from CDS alignment. It will follow the cds -// archive space, close enough such that narrow class pointer encoding -// covers both spaces. -// If UseCompressedClassPointers=0, class_space_rs remains unreserved. +// - class_space_rs: On 64-bit, class_space_rs will be reserved. Its start +// address will be aligned to metaspace reserve alignment, which may differ +// from CDS alignment. It will follow the cds archive space, close enough +// such that narrow class pointer encoding covers both spaces. // - On error: null is returned and the spaces remain unreserved. char* AOTMetaspace::reserve_address_space_for_archives(FileMapInfo* static_mapinfo, FileMapInfo* dynamic_mapinfo, @@ -1903,32 +1897,34 @@ char* AOTMetaspace::reserve_address_space_for_archives(FileMapInfo* static_mapin size_t archive_end_offset = (dynamic_mapinfo == nullptr) ? static_mapinfo->mapping_end_offset() : dynamic_mapinfo->mapping_end_offset(); size_t archive_space_size = align_up(archive_end_offset, archive_space_alignment); - if (!Metaspace::using_class_space()) { - // Get the simple case out of the way first: - // no compressed class space, simple allocation. +#if !INCLUDE_CLASS_SPACE - // When running without class space, requested archive base should be aligned to cds core alignment. - assert(is_aligned(base_address, archive_space_alignment), - "Archive base address unaligned: " PTR_FORMAT ", needs alignment: %zu.", - p2i(base_address), archive_space_alignment); + // Get the simple case out of the way first: + // no compressed class space, simple allocation. - archive_space_rs = MemoryReserver::reserve((char*)base_address, - archive_space_size, - archive_space_alignment, - os::vm_page_size(), - mtNone); - if (archive_space_rs.is_reserved()) { - assert(base_address == nullptr || - (address)archive_space_rs.base() == base_address, "Sanity"); - // Register archive space with NMT. - MemTracker::record_virtual_memory_tag(archive_space_rs, mtClassShared); - return archive_space_rs.base(); - } - return nullptr; + // When running without class space, requested archive base should be aligned to cds core alignment. + assert(is_aligned(base_address, archive_space_alignment), + "Archive base address unaligned: " PTR_FORMAT ", needs alignment: %zu.", + p2i(base_address), archive_space_alignment); + + archive_space_rs = MemoryReserver::reserve((char*)base_address, + archive_space_size, + archive_space_alignment, + os::vm_page_size(), + mtNone); + if (archive_space_rs.is_reserved()) { + assert(base_address == nullptr || + (address)archive_space_rs.base() == base_address, "Sanity"); + // Register archive space with NMT. + MemTracker::record_virtual_memory_tag(archive_space_rs, mtClassShared); + return archive_space_rs.base(); } -#ifdef _LP64 + return nullptr; +#else + + // INCLUDE_CLASS_SPACE=1 // Complex case: two spaces adjacent to each other, both to be addressable // with narrow class pointers. // We reserve the whole range spanning both spaces, then split that range up. @@ -2040,11 +2036,7 @@ char* AOTMetaspace::reserve_address_space_for_archives(FileMapInfo* static_mapin return archive_space_rs.base(); -#else - ShouldNotReachHere(); - return nullptr; -#endif - +#endif // INCLUDE_CLASS_SPACE } void AOTMetaspace::release_reserved_spaces(ReservedSpace& total_space_rs, diff --git a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp index ad363f21fdb..25bef10a673 100644 --- a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp @@ -369,7 +369,6 @@ template void AOTStreamedHeapWriter::map_oop_field_in_buffer(oop ob } void AOTStreamedHeapWriter::update_header_for_buffered_addr(address buffered_addr, oop src_obj, Klass* src_klass) { - assert(UseCompressedClassPointers, "Archived heap only supported for compressed klasses"); narrowKlass nk = ArchiveBuilder::current()->get_requested_narrow_klass(src_klass); markWord mw = markWord::prototype(); diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index b2d6600e44f..21eef3d7b0b 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -1092,20 +1092,17 @@ class RelocateBufferToRequested : public BitMapClosure { } }; -#ifdef _LP64 int ArchiveBuilder::precomputed_narrow_klass_shift() { - // Legacy Mode: - // We use 32 bits for narrowKlass, which should cover the full 4G Klass range. Shift can be 0. + // Standard Mode: + // We use 32 bits for narrowKlass, which should cover a full 4G Klass range. Shift can be 0. // CompactObjectHeader Mode: // narrowKlass is much smaller, and we use the highest possible shift value to later get the maximum // Klass encoding range. // // Note that all of this may change in the future, if we decide to correct the pre-calculated // narrow Klass IDs at archive load time. - assert(UseCompressedClassPointers, "Only needed for compressed class pointers"); return UseCompactObjectHeaders ? CompressedKlassPointers::max_shift() : 0; } -#endif // _LP64 void ArchiveBuilder::relocate_to_requested() { if (!ro_region()->is_packed()) { diff --git a/src/hotspot/share/cds/archiveBuilder.hpp b/src/hotspot/share/cds/archiveBuilder.hpp index b3667ea11b4..6a9df87092b 100644 --- a/src/hotspot/share/cds/archiveBuilder.hpp +++ b/src/hotspot/share/cds/archiveBuilder.hpp @@ -484,7 +484,6 @@ public: void print_stats(); void report_out_of_space(const char* name, size_t needed_bytes); -#ifdef _LP64 // The CDS archive contains pre-computed narrow Klass IDs. It carries them in the headers of // archived heap objects. With +UseCompactObjectHeaders, it also carries them in prototypes // in Klass. @@ -504,7 +503,6 @@ public: // TinyClassPointer Mode: // We use the highest possible shift value to maximize the encoding range size. static int precomputed_narrow_klass_shift(); -#endif // _LP64 }; diff --git a/src/hotspot/share/cds/archiveUtils.cpp b/src/hotspot/share/cds/archiveUtils.cpp index f79b1e134e8..6e0608e196b 100644 --- a/src/hotspot/share/cds/archiveUtils.cpp +++ b/src/hotspot/share/cds/archiveUtils.cpp @@ -360,8 +360,8 @@ char* DumpRegion::allocate_metaspace_obj(size_t num_bytes, address src, Metaspac bool is_instance_class = is_class && ((Klass*)src)->is_instance_klass(); #ifdef _LP64 - // More strict alignments needed for UseCompressedClassPointers - if (is_class && UseCompressedClassPointers) { + // More strict alignments needed for Klass objects + if (is_class) { size_t klass_alignment = checked_cast(nth_bit(ArchiveBuilder::precomputed_narrow_klass_shift())); alignment = MAX2(alignment, klass_alignment); precond(is_aligned(alignment, SharedSpaceObjectAlignment)); diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 9d191bf0121..ecf3c6d2231 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -891,10 +891,6 @@ const char* CDSConfig::type_of_archive_being_written() { // If an incompatible VM options is found, return a text message that explains why static const char* check_options_incompatible_with_dumping_heap() { #if INCLUDE_CDS_JAVA_HEAP - if (!UseCompressedClassPointers) { - return "UseCompressedClassPointers must be true"; - } - return nullptr; #else return "JVM not configured for writing Java heap objects"; diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index a07f13cefca..38502b2b2d8 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -225,15 +225,9 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment, } #endif _compressed_oops = UseCompressedOops; - _compressed_class_ptrs = UseCompressedClassPointers; - if (UseCompressedClassPointers) { -#ifdef _LP64 - _narrow_klass_pointer_bits = CompressedKlassPointers::narrow_klass_pointer_bits(); - _narrow_klass_shift = ArchiveBuilder::precomputed_narrow_klass_shift(); -#endif - } else { - _narrow_klass_pointer_bits = _narrow_klass_shift = -1; - } + _narrow_klass_pointer_bits = CompressedKlassPointers::narrow_klass_pointer_bits(); + _narrow_klass_shift = ArchiveBuilder::precomputed_narrow_klass_shift(); + // Which JIT compier is used _compiler_type = (u1)CompilerConfig::compiler_type(); _type_profile_level = TypeProfileLevel; @@ -295,7 +289,6 @@ void FileMapHeader::print(outputStream* st) { st->print_cr("- max_heap_size: %zu", _max_heap_size); st->print_cr("- narrow_oop_mode: %d", _narrow_oop_mode); st->print_cr("- compressed_oops: %d", _compressed_oops); - st->print_cr("- compressed_class_ptrs: %d", _compressed_class_ptrs); st->print_cr("- narrow_klass_pointer_bits: %d", _narrow_klass_pointer_bits); st->print_cr("- narrow_klass_shift: %d", _narrow_klass_shift); st->print_cr("- cloned_vtables: %u", cast_to_u4(_cloned_vtables)); @@ -1926,11 +1919,12 @@ bool FileMapHeader::validate() { _has_platform_or_app_classes = false; } - aot_log_info(aot)("The %s was created with UseCompressedOops = %d, UseCompressedClassPointers = %d, UseCompactObjectHeaders = %d", - file_type, compressed_oops(), compressed_class_pointers(), compact_headers()); - if (compressed_oops() != UseCompressedOops || compressed_class_pointers() != UseCompressedClassPointers) { - aot_log_warning(aot)("Unable to use %s.\nThe saved state of UseCompressedOops and UseCompressedClassPointers is " - "different from runtime, CDS will be disabled.", file_type); + aot_log_info(aot)("The %s was created with UseCompressedOops = %d, UseCompactObjectHeaders = %d", + file_type, compressed_oops(), compact_headers()); + if (compressed_oops() != UseCompressedOops) { + aot_log_warning(aot)("Unable to use %s.\nThe saved state of UseCompressedOops (%d) is " + "different from runtime (%d), CDS will be disabled.", file_type, + compressed_oops(), UseCompressedOops); return false; } diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp index 56b88df378a..bae08bd5bc7 100644 --- a/src/hotspot/share/cds/filemap.hpp +++ b/src/hotspot/share/cds/filemap.hpp @@ -120,7 +120,6 @@ private: CompressedOops::Mode _narrow_oop_mode; // compressed oop encoding mode bool _object_streaming_mode; // dump was created for object streaming bool _compressed_oops; // save the flag UseCompressedOops - bool _compressed_class_ptrs; // save the flag UseCompressedClassPointers int _narrow_klass_pointer_bits; // save number of bits in narrowKlass int _narrow_klass_shift; // save shift width used to pre-compute narrowKlass IDs in archived heap objects narrowPtr _cloned_vtables; // The address of the first cloned vtable @@ -200,7 +199,6 @@ public: bool has_platform_or_app_classes() const { return _has_platform_or_app_classes; } bool has_aot_linked_classes() const { return _has_aot_linked_classes; } bool compressed_oops() const { return _compressed_oops; } - bool compressed_class_pointers() const { return _compressed_class_ptrs; } int narrow_klass_pointer_bits() const { return _narrow_klass_pointer_bits; } int narrow_klass_shift() const { return _narrow_klass_shift; } bool has_full_module_graph() const { return _has_full_module_graph; } diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 5947050e31a..58d432a628c 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -89,11 +89,9 @@ DEBUG_ONLY(bool SystemDictionaryShared::_class_loading_may_happen = true;) #ifdef ASSERT static void check_klass_after_loading(const Klass* k) { -#ifdef _LP64 - if (k != nullptr && UseCompressedClassPointers) { + if (k != nullptr) { CompressedKlassPointers::check_encodable(k); } -#endif } #endif diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index b29cf906736..4ad5e12d808 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -388,9 +388,6 @@ void AOTCodeCache::Config::record(uint cpu_features_offset) { if (UseCompressedOops) { _flags |= compressedOops; } - if (UseCompressedClassPointers) { - _flags |= compressedClassPointers; - } if (UseTLAB) { _flags |= useTLAB; } @@ -474,10 +471,6 @@ bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const { return false; } - if (((_flags & compressedClassPointers) != 0) != UseCompressedClassPointers) { - log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with UseCompressedClassPointers = %s", UseCompressedClassPointers ? "false" : "true"); - return false; - } if (_compressedKlassShift != (uint)CompressedKlassPointers::shift()) { log_debug(aot, codecache, init)("AOT Code Cache disabled: it was created with CompressedKlassPointers::shift() = %d vs current %d", _compressedKlassShift, CompressedKlassPointers::shift()); return false; diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index bd8721fea8c..7996388faa6 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -177,12 +177,11 @@ protected: none = 0, debugVM = 1, compressedOops = 2, - compressedClassPointers = 4, - useTLAB = 8, - systemClassAssertions = 16, - userClassAssertions = 32, - enableContendedPadding = 64, - restrictContendedPadding = 128 + useTLAB = 4, + systemClassAssertions = 8, + userClassAssertions = 16, + enableContendedPadding = 32, + restrictContendedPadding = 64 }; uint _flags; uint _cpu_features_offset; // offset in the cache where cpu features are stored diff --git a/src/hotspot/share/code/compiledIC.cpp b/src/hotspot/share/code/compiledIC.cpp index 5f5c9711441..07d96d6cd44 100644 --- a/src/hotspot/share/code/compiledIC.cpp +++ b/src/hotspot/share/code/compiledIC.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,11 +76,7 @@ CompiledICData::CompiledICData() // Inline cache callsite info is initialized once the first time it is resolved void CompiledICData::initialize(CallInfo* call_info, Klass* receiver_klass) { _speculated_method = call_info->selected_method(); - if (UseCompressedClassPointers) { - _speculated_klass = (uintptr_t)CompressedKlassPointers::encode_not_null(receiver_klass); - } else { - _speculated_klass = (uintptr_t)receiver_klass; - } + _speculated_klass = (uintptr_t)CompressedKlassPointers::encode_not_null(receiver_klass); if (call_info->call_kind() == CallInfo::itable_call) { assert(call_info->resolved_method() != nullptr, "virtual or interface method must be found"); _itable_defc_klass = call_info->resolved_method()->method_holder(); @@ -133,12 +129,7 @@ Klass* CompiledICData::speculated_klass() const { if (is_speculated_klass_unloaded()) { return nullptr; } - - if (UseCompressedClassPointers) { - return CompressedKlassPointers::decode_not_null((narrowKlass)_speculated_klass); - } else { - return (Klass*)_speculated_klass; - } + return CompressedKlassPointers::decode_not_null((narrowKlass)_speculated_klass); } //----------------------------------------------------------------------------- diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp index a888ea81707..afe7d2acfa7 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp @@ -708,7 +708,6 @@ int BarrierSetC2::arraycopy_payload_base_offset(bool is_array) { // 12 - 64-bit VM, compressed klass // 16 - 64-bit VM, normal klass if (base_off % BytesPerLong != 0) { - assert(UseCompressedClassPointers, ""); assert(!UseCompactObjectHeaders, ""); if (is_array) { // Exclude length to copy by 8 bytes words. diff --git a/src/hotspot/share/gc/shared/gcTrace.cpp b/src/hotspot/share/gc/shared/gcTrace.cpp index bad9c707b1e..5d0627e779e 100644 --- a/src/hotspot/share/gc/shared/gcTrace.cpp +++ b/src/hotspot/share/gc/shared/gcTrace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -122,11 +122,10 @@ void GCTracer::report_gc_heap_summary(GCWhen::Type when, const GCHeapSummary& he void GCTracer::report_metaspace_summary(GCWhen::Type when, const MetaspaceSummary& summary) const { send_meta_space_summary_event(when, summary); - send_metaspace_chunk_free_list_summary(when, Metaspace::NonClassType, summary.metaspace_chunk_free_list_summary()); - if (UseCompressedClassPointers) { - send_metaspace_chunk_free_list_summary(when, Metaspace::ClassType, summary.class_chunk_free_list_summary()); - } +#if INCLUDE_CLASS_SPACE + send_metaspace_chunk_free_list_summary(when, Metaspace::ClassType, summary.class_chunk_free_list_summary()); +#endif } void YoungGCTracer::report_gc_end_impl(const Ticks& timestamp, TimePartitions* time_partitions) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp index baeaffb9c7b..268f5b13035 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp @@ -559,26 +559,24 @@ bool ShenandoahAsserts::extract_klass_safely(oop obj, narrowKlass& nk, const Kla if (!os::is_readable_pointer(obj)) { return false; } - if (UseCompressedClassPointers) { - if (UseCompactObjectHeaders) { // look in forwardee - markWord mark = obj->mark(); - if (mark.is_marked()) { - oop fwd = cast_to_oop(mark.clear_lock_bits().to_pointer()); - if (!os::is_readable_pointer(fwd)) { - return false; - } - mark = fwd->mark(); + + if (UseCompactObjectHeaders) { // look in forwardee + markWord mark = obj->mark(); + if (mark.is_marked()) { + oop fwd = cast_to_oop(mark.clear_lock_bits().to_pointer()); + if (!os::is_readable_pointer(fwd)) { + return false; } - nk = mark.narrow_klass(); - } else { - nk = obj->narrow_klass(); + mark = fwd->mark(); } - if (!CompressedKlassPointers::is_valid_narrow_klass_id(nk)) { - return false; - } - k = CompressedKlassPointers::decode_not_null_without_asserts(nk); + nk = mark.narrow_klass(); } else { - k = obj->klass(); + nk = obj->narrow_klass(); } + if (!CompressedKlassPointers::is_valid_narrow_klass_id(nk)) { + return false; + } + k = CompressedKlassPointers::decode_not_null_without_asserts(nk); + return k != nullptr; } diff --git a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp index 650918e2d30..0a3dac1e100 100644 --- a/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp +++ b/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -442,7 +442,7 @@ void ZBarrierSetC2::clone_at_expansion(PhaseMacroExpand* phase, ArrayCopyNode* a assert(src_offset == dest_offset, "should be equal"); const jlong offset = src_offset->get_long(); if (offset != arrayOopDesc::base_offset_in_bytes(T_OBJECT)) { - assert(!UseCompressedClassPointers || UseCompactObjectHeaders, "should only happen without compressed class pointers"); + assert(UseCompactObjectHeaders, "should only happen with COH"); assert((arrayOopDesc::base_offset_in_bytes(T_OBJECT) - offset) == BytesPerLong, "unexpected offset"); length = phase->transform_later(new SubLNode(length, phase->longcon(1))); // Size is in longs src_offset = phase->longcon(arrayOopDesc::base_offset_in_bytes(T_OBJECT)); diff --git a/src/hotspot/share/gc/z/zDebug.gdb b/src/hotspot/share/gc/z/zDebug.gdb index d502eea7ce3..df087c4a42d 100644 --- a/src/hotspot/share/gc/z/zDebug.gdb +++ b/src/hotspot/share/gc/z/zDebug.gdb @@ -50,11 +50,7 @@ define zpo end printf "\t Page: %llu\n", ((uintptr_t)$obj & ZAddressOffsetMask) >> ZGranuleSizeShift x/16gx $obj - if (UseCompressedClassPointers) - set $klass = (Klass*)(void*)((uintptr_t)CompressedKlassPointers::_base +((uintptr_t)$obj->_metadata->_compressed_klass << CompressedKlassPointers::_shift)) - else - set $klass = $obj->_metadata->_klass - end + set $klass = (Klass*)(void*)((uintptr_t)CompressedKlassPointers::_base +((uintptr_t)$obj->_compressed_klass << CompressedKlassPointers::_shift)) printf "Mark: 0x%016llx\tKlass: %s\n", (uintptr_t)$obj->_mark, (char*)$klass->_name->_body end diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdKlassQueue.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdKlassQueue.cpp index 9c57374d6c6..eab7de20545 100644 --- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdKlassQueue.cpp +++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdKlassQueue.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -75,7 +75,7 @@ static size_t element_size(bool compressed) { } static bool can_compress_element(traceid id) { - return Metaspace::using_class_space() && id < uncompressed_threshold; + return INCLUDE_CLASS_SPACE == 1 && id < uncompressed_threshold; } static size_t element_size(const Klass* klass) { diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index 6214f6a2746..6048c19d911 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -206,13 +206,8 @@ void CompilerToVM::Data::initialize(JVMCI_TRAPS) { Universe_narrow_oop_base = nullptr; Universe_narrow_oop_shift = 0; } - if (UseCompressedClassPointers) { - Universe_narrow_klass_base = CompressedKlassPointers::base(); - Universe_narrow_klass_shift = CompressedKlassPointers::shift(); - } else { - Universe_narrow_klass_base = nullptr; - Universe_narrow_klass_shift = 0; - } + Universe_narrow_klass_base = CompressedKlassPointers::base(); + Universe_narrow_klass_shift = CompressedKlassPointers::shift(); Universe_non_oop_bits = Universe::non_oop_word(); Universe_verify_oop_mask = Universe::verify_oop_mask(); Universe_verify_oop_bits = Universe::verify_oop_bits(); @@ -390,7 +385,6 @@ JVMCIObjectArray CompilerToVM::initialize_intrinsics(JVMCI_TRAPS) { X86_ONLY(do_int_flag(UseAVX)) \ do_bool_flag(UseCRC32Intrinsics) \ do_bool_flag(UseAdler32Intrinsics) \ - do_bool_flag(UseCompressedClassPointers) \ do_bool_flag(UseCompressedOops) \ X86_ONLY(do_bool_flag(UseCountLeadingZerosInstruction)) \ X86_ONLY(do_bool_flag(UseCountTrailingZerosInstruction)) \ diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index ac532e1bb4c..1fdf98588fd 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -342,7 +342,7 @@ volatile_nonstatic_field(ObjectMonitor, _succ, int64_t) \ \ volatile_nonstatic_field(oopDesc, _mark, markWord) \ - volatile_nonstatic_field(oopDesc, _metadata._klass, Klass*) \ + volatile_nonstatic_field(oopDesc, _compressed_klass, narrowKlass) \ \ static_field(StubRoutines, _verify_oop_count, jint) \ \ diff --git a/src/hotspot/share/memory/classLoaderMetaspace.cpp b/src/hotspot/share/memory/classLoaderMetaspace.cpp index c1ff172071d..af3b8b1b77f 100644 --- a/src/hotspot/share/memory/classLoaderMetaspace.cpp +++ b/src/hotspot/share/memory/classLoaderMetaspace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -162,10 +162,12 @@ void ClassLoaderMetaspace::deallocate(MetaWord* ptr, size_t word_size) { MetaBlock bl(ptr, word_size); // Add to class arena only if block is usable for encodable Klass storage. MetaspaceArena* receiving_arena = non_class_space_arena(); - if (Metaspace::using_class_space() && Metaspace::is_in_class_space(ptr) && +#if INCLUDE_CLASS_SPACE + if (Metaspace::is_in_class_space(ptr) && is_aligned(ptr, class_space_arena()->allocation_alignment_bytes())) { receiving_arena = class_space_arena(); } +#endif receiving_arena->deallocate(bl); DEBUG_ONLY(InternalStats::inc_num_deallocs();) } diff --git a/src/hotspot/share/memory/classLoaderMetaspace.hpp b/src/hotspot/share/memory/classLoaderMetaspace.hpp index aa43e171708..dc782611e98 100644 --- a/src/hotspot/share/memory/classLoaderMetaspace.hpp +++ b/src/hotspot/share/memory/classLoaderMetaspace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,9 +40,10 @@ namespace metaspace { // A ClassLoaderMetaspace manages MetaspaceArena(s) for a CLD. // -// A CLD owns one MetaspaceArena if UseCompressedClassPointers is false. Otherwise -// it owns two - one for the Klass* objects from the class space, one for the other -// types of MetaspaceObjs from the non-class space. +// 64-bit: +// +// A CLD owns two MetaspaceArenas - one for the Klass* objects from the class space, +// one for the other types of MetaspaceObjs from the non-class space. // // +------+ +----------------------+ +-------------------+ // | CLD | ---> | ClassLoaderMetaspace | ----> | (non class) Arena | @@ -58,6 +59,11 @@ namespace metaspace { // ^ // alloc top // +// 32-bit: +// +// A CLD owns just one MetaspaceArena. In that arena all metadata - Klass and other - +// are placed. + class ClassLoaderMetaspace : public CHeapObj { friend class metaspace::ClmsTester; // for gtests @@ -67,11 +73,10 @@ class ClassLoaderMetaspace : public CHeapObj { const Metaspace::MetaspaceType _space_type; // Arena for allocations from non-class metaspace - // (resp. for all allocations if -XX:-UseCompressedClassPointers). metaspace::MetaspaceArena* _non_class_space_arena; // Arena for allocations from class space - // (null if -XX:-UseCompressedClassPointers). + // (null for 32-bit). metaspace::MetaspaceArena* _class_space_arena; Mutex* lock() const { return _lock; } diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index e686b324004..da6ebc991c8 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2021 SAP SE. All rights reserved. * Copyright (c) 2023, 2025, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -166,33 +166,33 @@ void MetaspaceUtils::print_metaspace_change(const MetaspaceCombinedStats& pre_me // it is a constant (to uninformed users, often confusingly large). For non-class space, it would // be interesting since free chunks can be uncommitted, but for now it is left out. - if (Metaspace::using_class_space()) { - log_info(gc, metaspace)(HEAP_CHANGE_FORMAT" " - HEAP_CHANGE_FORMAT" " - HEAP_CHANGE_FORMAT, - HEAP_CHANGE_FORMAT_ARGS("Metaspace", - pre_meta_values.used(), - pre_meta_values.committed(), - meta_values.used(), - meta_values.committed()), - HEAP_CHANGE_FORMAT_ARGS("NonClass", - pre_meta_values.non_class_used(), - pre_meta_values.non_class_committed(), - meta_values.non_class_used(), - meta_values.non_class_committed()), - HEAP_CHANGE_FORMAT_ARGS("Class", - pre_meta_values.class_used(), - pre_meta_values.class_committed(), - meta_values.class_used(), - meta_values.class_committed())); - } else { - log_info(gc, metaspace)(HEAP_CHANGE_FORMAT, - HEAP_CHANGE_FORMAT_ARGS("Metaspace", - pre_meta_values.used(), - pre_meta_values.committed(), - meta_values.used(), - meta_values.committed())); - } +#if INCLUDE_CLASS_SPACE + log_info(gc, metaspace)(HEAP_CHANGE_FORMAT" " + HEAP_CHANGE_FORMAT" " + HEAP_CHANGE_FORMAT, + HEAP_CHANGE_FORMAT_ARGS("Metaspace", + pre_meta_values.used(), + pre_meta_values.committed(), + meta_values.used(), + meta_values.committed()), + HEAP_CHANGE_FORMAT_ARGS("NonClass", + pre_meta_values.non_class_used(), + pre_meta_values.non_class_committed(), + meta_values.non_class_used(), + meta_values.non_class_committed()), + HEAP_CHANGE_FORMAT_ARGS("Class", + pre_meta_values.class_used(), + pre_meta_values.class_committed(), + meta_values.class_used(), + meta_values.class_committed())); +#else + log_info(gc, metaspace)(HEAP_CHANGE_FORMAT, + HEAP_CHANGE_FORMAT_ARGS("Metaspace", + pre_meta_values.used(), + pre_meta_values.committed(), + meta_values.used(), + meta_values.committed())); +#endif // INCLUDE_CLASS_SPACE } // This will print out a basic metaspace usage report but @@ -226,41 +226,36 @@ void MetaspaceUtils::print_on(outputStream* out) { stats.committed()/K, stats.reserved()/K); - if (Metaspace::using_class_space()) { - StreamIndentor si(out, 1); - out->print("class space "); - out->fill_to(17); - out->print_cr("used %zuK, " - "committed %zuK, " - "reserved %zuK", - stats.class_space_stats().used()/K, - stats.class_space_stats().committed()/K, - stats.class_space_stats().reserved()/K); - } +#if INCLUDE_CLASS_SPACE + StreamIndentor si(out, 1); + out->print("class space "); + out->fill_to(17); + out->print_cr("used %zuK, " + "committed %zuK, " + "reserved %zuK", + stats.class_space_stats().used()/K, + stats.class_space_stats().committed()/K, + stats.class_space_stats().reserved()/K); +#endif // INCLUDE_CLASS_SPACE } #ifdef ASSERT void MetaspaceUtils::verify() { if (Metaspace::initialized()) { - // Verify non-class chunkmanager... ChunkManager* cm = ChunkManager::chunkmanager_nonclass(); cm->verify(); - // ... and space list. VirtualSpaceList* vsl = VirtualSpaceList::vslist_nonclass(); vsl->verify(); - if (Metaspace::using_class_space()) { - // If we use compressed class pointers, verify class chunkmanager... - cm = ChunkManager::chunkmanager_class(); - cm->verify(); - - // ... and class spacelist. - vsl = VirtualSpaceList::vslist_class(); - vsl->verify(); - } +#if INCLUDE_CLASS_SPACE + cm = ChunkManager::chunkmanager_class(); + cm->verify(); + vsl = VirtualSpaceList::vslist_class(); + vsl->verify(); +#endif // INCLUDE_CLASS_SPACE } } #endif @@ -387,7 +382,8 @@ void MetaspaceGC::post_initialize() { bool MetaspaceGC::can_expand(size_t word_size, bool is_class) { // Check if the compressed class space is full. - if (is_class && Metaspace::using_class_space()) { +#if INCLUDE_CLASS_SPACE + if (is_class) { size_t class_committed = MetaspaceUtils::committed_bytes(Metaspace::ClassType); if (class_committed + word_size * BytesPerWord > CompressedClassSpaceSize) { log_trace(gc, metaspace, freelist)("Cannot expand %s metaspace by %zu words (CompressedClassSpaceSize = %zu words)", @@ -395,6 +391,7 @@ bool MetaspaceGC::can_expand(size_t word_size, bool is_class) { return false; } } +#endif // INCLUDE_CLASS_SPACE // Check if the user has imposed a limit on the metaspace memory. size_t committed_bytes = MetaspaceUtils::committed_bytes(); @@ -548,7 +545,7 @@ const void* Metaspace::_class_space_end = nullptr; bool Metaspace::initialized() { return metaspace::MetaspaceContext::context_nonclass() != nullptr - LP64_ONLY(&& (using_class_space() ? Metaspace::class_space_is_initialized() : true)); + CLASS_SPACE_ONLY(&& Metaspace::class_space_is_initialized()); } #ifdef _LP64 @@ -566,9 +563,9 @@ void Metaspace::print_compressed_class_space(outputStream* st) { // Given a prereserved space, use that to set up the compressed class space list. void Metaspace::initialize_class_space(ReservedSpace rs) { + STATIC_ASSERT(INCLUDE_CLASS_SPACE == 1); assert(rs.size() >= CompressedClassSpaceSize, "%zu != %zu", rs.size(), CompressedClassSpaceSize); - assert(using_class_space(), "Must be using class space"); assert(rs.size() == CompressedClassSpaceSize, "%zu != %zu", rs.size(), CompressedClassSpaceSize); @@ -658,49 +655,51 @@ void Metaspace::ergo_initialize() { MaxMetaspaceSize = MAX2(MaxMetaspaceSize, commit_alignment()); - if (UseCompressedClassPointers) { - // Let Class Space not be larger than 80% of MaxMetaspaceSize. Note that is - // grossly over-dimensioned for most usage scenarios; typical ratio of - // class space : non class space usage is about 1:6. With many small classes, - // it can get as low as 1:2. It is not a big deal though since ccs is only - // reserved and will be committed on demand only. - const size_t max_ccs_size = 8 * (MaxMetaspaceSize / 10); +#if INCLUDE_CLASS_SPACE - // Sanity check. - const size_t max_klass_range = CompressedKlassPointers::max_klass_range_size(); - assert(max_klass_range >= reserve_alignment(), - "Klass range (%zu) must cover at least a full root chunk (%zu)", - max_klass_range, reserve_alignment()); + // Let Class Space not be larger than 80% of MaxMetaspaceSize. Note that is + // grossly over-dimensioned for most usage scenarios; typical ratio of + // class space : non class space usage is about 1:6. With many small classes, + // it can get as low as 1:2. It is not a big deal though since ccs is only + // reserved and will be committed on demand only. + const size_t max_ccs_size = 8 * (MaxMetaspaceSize / 10); - size_t adjusted_ccs_size = MIN3(CompressedClassSpaceSize, max_ccs_size, max_klass_range); + // Sanity check. + const size_t max_klass_range = CompressedKlassPointers::max_klass_range_size(); + assert(max_klass_range >= reserve_alignment(), + "Klass range (%zu) must cover at least a full root chunk (%zu)", + max_klass_range, reserve_alignment()); - // CCS must be aligned to root chunk size, and be at least the size of one - // root chunk. - adjusted_ccs_size = align_up(adjusted_ccs_size, reserve_alignment()); - adjusted_ccs_size = MAX2(adjusted_ccs_size, reserve_alignment()); + size_t adjusted_ccs_size = MIN3(CompressedClassSpaceSize, max_ccs_size, max_klass_range); - // Print a warning if the adjusted size differs from the users input - if (CompressedClassSpaceSize != adjusted_ccs_size) { - #define X "CompressedClassSpaceSize adjusted from user input " \ - "%zu bytes to %zu bytes", CompressedClassSpaceSize, adjusted_ccs_size - if (FLAG_IS_CMDLINE(CompressedClassSpaceSize)) { - log_warning(metaspace)(X); - } else { - log_info(metaspace)(X); - } - #undef X - } - - // Note: re-adjusting may have us left with a CompressedClassSpaceSize - // larger than MaxMetaspaceSize for very small values of MaxMetaspaceSize. - // Lets just live with that, its not a big deal. - if (adjusted_ccs_size != CompressedClassSpaceSize) { - FLAG_SET_ERGO(CompressedClassSpaceSize, adjusted_ccs_size); - log_info(metaspace)("Setting CompressedClassSpaceSize to %zu.", - CompressedClassSpaceSize); + // CCS must be aligned to root chunk size, and be at least the size of one + // root chunk. + adjusted_ccs_size = align_up(adjusted_ccs_size, reserve_alignment()); + adjusted_ccs_size = MAX2(adjusted_ccs_size, reserve_alignment()); + + // Print a warning if the adjusted size differs from the users input + if (CompressedClassSpaceSize != adjusted_ccs_size) { + #define X "CompressedClassSpaceSize adjusted from user input " \ + "%zu bytes to %zu bytes", CompressedClassSpaceSize, adjusted_ccs_size + if (FLAG_IS_CMDLINE(CompressedClassSpaceSize)) { + log_warning(metaspace)(X); + } else { + log_info(metaspace)(X); } + #undef X } + // Note: re-adjusting may have us left with a CompressedClassSpaceSize + // larger than MaxMetaspaceSize for very small values of MaxMetaspaceSize. + // Lets just live with that, its not a big deal. + if (adjusted_ccs_size != CompressedClassSpaceSize) { + FLAG_SET_ERGO(CompressedClassSpaceSize, adjusted_ccs_size); + log_info(metaspace)("Setting CompressedClassSpaceSize to %zu.", + CompressedClassSpaceSize); + } + +#endif // INCLUDE_CLASS_SPACE + // Set MetaspaceSize, MinMetaspaceExpansion and MaxMetaspaceExpansion if (MetaspaceSize > MaxMetaspaceSize) { MetaspaceSize = MaxMetaspaceSize; @@ -724,15 +723,12 @@ void Metaspace::global_initialize() { AOTMetaspace::initialize_for_static_dump(); } - // If UseCompressedClassPointers=1, we have two cases: + // We have two cases: // a) if CDS is active (runtime, Xshare=on), it will create the class space - // for us, initialize it and set up CompressedKlassPointers encoding. - // Class space will be reserved above the mapped archives. + // for us. It then will set up encoding to cover both CDS archive space and class space. // b) if CDS either deactivated (Xshare=off) or a static dump is to be done (Xshare:dump), - // we will create the class space on our own. It will be placed above the java heap, - // since we assume it has been placed in low - // address regions. We may rethink this (see JDK-8244943). Failing that, - // it will be placed anywhere. + // we will create the class space on our own and set up encoding to only cover the + // class space. #if INCLUDE_CDS // case (a) @@ -746,9 +742,9 @@ void Metaspace::global_initialize() { } #endif // INCLUDE_CDS -#ifdef _LP64 +#if INCLUDE_CLASS_SPACE - if (using_class_space() && !class_space_is_initialized()) { + if (!class_space_is_initialized()) { assert(!CDSConfig::is_using_archive(), "CDS archive is not mapped at this point"); // case (b) (No CDS) @@ -835,28 +831,23 @@ void Metaspace::global_initialize() { } #else - // +UseCompressedClassPointers on 32-bit: does not need class space. Klass can live wherever. - if (UseCompressedClassPointers) { - const address start = (address)os::vm_min_address(); // but not in the zero page - const address end = (address)CompressedKlassPointers::max_klass_range_size(); - CompressedKlassPointers::initialize(start, end - start); - } -#endif // __LP64 + // 32-bit: + const address start = (address)os::vm_min_address(); // but not in the zero page + const address end = (address)CompressedKlassPointers::max_klass_range_size(); + CompressedKlassPointers::initialize(start, end - start); +#endif // INCLUDE_CLASS_SPACE // Initialize non-class virtual space list, and its chunk manager: MetaspaceContext::initialize_nonclass_space_context(); _tracer = new MetaspaceTracer(); - if (UseCompressedClassPointers) { - // Note: "cds" would be a better fit but keep this for backward compatibility. - LogTarget(Info, gc, metaspace) lt; - if (lt.is_enabled()) { - LogStream ls(lt); - CDS_ONLY(AOTMetaspace::print_on(&ls);) - Metaspace::print_compressed_class_space(&ls); - CompressedKlassPointers::print_mode(&ls); - } + LogTarget(Info, gc, metaspace) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + CDS_ONLY(AOTMetaspace::print_on(&ls);) + Metaspace::print_compressed_class_space(&ls); + CompressedKlassPointers::print_mode(&ls); } } @@ -888,15 +879,13 @@ MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size, MetaWord* result = loader_data->metaspace_non_null()->allocate(word_size, mdtype); if (result != nullptr) { -#ifdef ASSERT - if (using_class_space() && mdtype == ClassType) { + if (INCLUDE_CLASS_SPACE == 1 && mdtype == ClassType) { assert(is_in_class_space(result) && is_aligned(result, CompressedKlassPointers::klass_alignment_in_bytes()), "Sanity"); } else { assert((is_in_class_space(result) || is_in_nonclass_metaspace(result)) && is_aligned(result, Metaspace::min_allocation_alignment_bytes), "Sanity"); } -#endif // Zero initialize. Copy::fill_to_words((HeapWord*)result, word_size, 0); log_trace(metaspace)("Metaspace::allocate: type %d return " PTR_FORMAT ".", (int)type, p2i(result)); @@ -1017,12 +1006,12 @@ void Metaspace::purge(bool classes_unloaded) { if (cm != nullptr) { cm->purge(); } - if (using_class_space()) { - cm = ChunkManager::chunkmanager_class(); - if (cm != nullptr) { - cm->purge(); - } +#if INCLUDE_CLASS_SPACE + cm = ChunkManager::chunkmanager_class(); + if (cm != nullptr) { + cm->purge(); } +#endif // INCLUDE_CLASS_SPACE } // Try to satisfy queued metaspace allocation requests. diff --git a/src/hotspot/share/memory/metaspace.hpp b/src/hotspot/share/memory/metaspace.hpp index 01ef4b4dd49..96f5a2459ce 100644 --- a/src/hotspot/share/memory/metaspace.hpp +++ b/src/hotspot/share/memory/metaspace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2021 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -164,18 +164,12 @@ public: static void print_compressed_class_space(outputStream* st) NOT_LP64({}); - // Return TRUE only if UseCompressedClassPointers is True. - static bool using_class_space() { - return NOT_LP64(false) LP64_ONLY(UseCompressedClassPointers); - } - static bool is_class_space_allocation(MetadataType mdType) { - return mdType == ClassType && using_class_space(); + return CLASS_SPACE_ONLY(mdType == ClassType) NOT_CLASS_SPACE(false); } static bool initialized(); }; - #endif // SHARE_MEMORY_METASPACE_HPP diff --git a/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp b/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp index 3cff2a50d03..f6683f50fd1 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp +++ b/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -59,39 +59,39 @@ static void print_vs(outputStream* out, size_t scale) { const size_t committed_nc = RunningCounters::committed_words_nonclass(); const int num_nodes_nc = VirtualSpaceList::vslist_nonclass()->num_nodes(); - if (Metaspace::using_class_space()) { - const size_t reserved_c = RunningCounters::reserved_words_class(); - const size_t committed_c = RunningCounters::committed_words_class(); - const int num_nodes_c = VirtualSpaceList::vslist_class()->num_nodes(); +#if INCLUDE_CLASS_SPACE + const size_t reserved_c = RunningCounters::reserved_words_class(); + const size_t committed_c = RunningCounters::committed_words_class(); + const int num_nodes_c = VirtualSpaceList::vslist_class()->num_nodes(); - out->print(" Non-class space: "); - print_scaled_words(out, reserved_nc, scale, 7); - out->print(" reserved, "); - print_scaled_words_and_percentage(out, committed_nc, reserved_nc, scale, 7); - out->print(" committed, "); - out->print(" %d nodes.", num_nodes_nc); - out->cr(); - out->print(" Class space: "); - print_scaled_words(out, reserved_c, scale, 7); - out->print(" reserved, "); - print_scaled_words_and_percentage(out, committed_c, reserved_c, scale, 7); - out->print(" committed, "); - out->print(" %d nodes.", num_nodes_c); - out->cr(); - out->print(" Both: "); - print_scaled_words(out, reserved_c + reserved_nc, scale, 7); - out->print(" reserved, "); - print_scaled_words_and_percentage(out, committed_c + committed_nc, reserved_c + reserved_nc, scale, 7); - out->print(" committed. "); - out->cr(); - } else { - print_scaled_words(out, reserved_nc, scale, 7); - out->print(" reserved, "); - print_scaled_words_and_percentage(out, committed_nc, reserved_nc, scale, 7); - out->print(" committed, "); - out->print(" %d nodes.", num_nodes_nc); - out->cr(); - } + out->print(" Non-class space: "); + print_scaled_words(out, reserved_nc, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_nc, reserved_nc, scale, 7); + out->print(" committed, "); + out->print(" %d nodes.", num_nodes_nc); + out->cr(); + out->print(" Class space: "); + print_scaled_words(out, reserved_c, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_c, reserved_c, scale, 7); + out->print(" committed, "); + out->print(" %d nodes.", num_nodes_c); + out->cr(); + out->print(" Both: "); + print_scaled_words(out, reserved_c + reserved_nc, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_c + committed_nc, reserved_c + reserved_nc, scale, 7); + out->print(" committed. "); + out->cr(); +#else + print_scaled_words(out, reserved_nc, scale, 7); + out->print(" reserved, "); + print_scaled_words_and_percentage(out, committed_nc, reserved_nc, scale, 7); + out->print(" committed, "); + out->print(" %d nodes.", num_nodes_nc); + out->cr(); +#endif // INCLUDE_CLASS_SPACE } static void print_settings(outputStream* out, size_t scale) { @@ -102,12 +102,12 @@ static void print_settings(outputStream* out, size_t scale) { print_human_readable_size(out, MaxMetaspaceSize, scale); } out->cr(); - if (Metaspace::using_class_space()) { - out->print("CompressedClassSpaceSize: "); - print_human_readable_size(out, CompressedClassSpaceSize, scale); - } else { - out->print("No class space"); - } +#if INCLUDE_CLASS_SPACE + out->print("CompressedClassSpaceSize: "); + print_human_readable_size(out, CompressedClassSpaceSize, scale); +#else + out->print("No class space"); +#endif // INCLUDE_CLASS_SPACE out->cr(); out->print("Initial GC threshold: "); print_human_readable_size(out, MetaspaceSize, scale); @@ -117,9 +117,7 @@ static void print_settings(outputStream* out, size_t scale) { out->cr(); out->print_cr("CDS: %s", (CDSConfig::is_using_archive() ? "on" : (CDSConfig::is_dumping_static_archive() ? "dump" : "off"))); Settings::print_on(out); -#ifdef _LP64 CompressedKlassPointers::print_mode(out); -#endif } // This will print out a basic metaspace usage report but @@ -131,9 +129,7 @@ void MetaspaceReporter::print_basic_report(outputStream* out, size_t scale) { } out->cr(); out->print_cr("Usage:"); - if (Metaspace::using_class_space()) { - out->print(" Non-class: "); - } + CLASS_SPACE_ONLY(out->print(" Non-class: ");) // Note: since we want to purely rely on counters, without any locking or walking the CLDG, // for Usage stats (statistics over in-use chunks) all we can print is the @@ -144,37 +140,35 @@ void MetaspaceReporter::print_basic_report(outputStream* out, size_t scale) { print_scaled_words(out, used_nc, scale, 5); out->print(" used."); out->cr(); - if (Metaspace::using_class_space()) { - const size_t used_c = MetaspaceUtils::used_words(Metaspace::ClassType); - out->print(" Class: "); - print_scaled_words(out, used_c, scale, 5); - out->print(" used."); - out->cr(); - out->print(" Both: "); - const size_t used = used_nc + used_c; - print_scaled_words(out, used, scale, 5); - out->print(" used."); - out->cr(); - } +#if INCLUDE_CLASS_SPACE + const size_t used_c = MetaspaceUtils::used_words(Metaspace::ClassType); + out->print(" Class: "); + print_scaled_words(out, used_c, scale, 5); + out->print(" used."); + out->cr(); + out->print(" Both: "); + const size_t used = used_nc + used_c; + print_scaled_words(out, used, scale, 5); + out->print(" used."); + out->cr(); +#endif // INCLUDE_CLASS_SPACE out->cr(); out->print_cr("Virtual space:"); print_vs(out, scale); out->cr(); out->print_cr("Chunk freelists:"); - if (Metaspace::using_class_space()) { - out->print(" Non-Class: "); - } + CLASS_SPACE_ONLY(out->print(" Non-Class: ");) print_scaled_words(out, ChunkManager::chunkmanager_nonclass()->total_word_size(), scale); out->cr(); - if (Metaspace::using_class_space()) { - out->print(" Class: "); - print_scaled_words(out, ChunkManager::chunkmanager_class()->total_word_size(), scale); - out->cr(); - out->print(" Both: "); - print_scaled_words(out, ChunkManager::chunkmanager_nonclass()->total_word_size() + - ChunkManager::chunkmanager_class()->total_word_size(), scale); - out->cr(); - } +#if INCLUDE_CLASS_SPACE + out->print(" Class: "); + print_scaled_words(out, ChunkManager::chunkmanager_class()->total_word_size(), scale); + out->cr(); + out->print(" Both: "); + print_scaled_words(out, ChunkManager::chunkmanager_nonclass()->total_word_size() + + ChunkManager::chunkmanager_class()->total_word_size(), scale); + out->cr(); +#endif // INCLUDE_CLASS_SPACE out->cr(); // Print basic settings @@ -256,70 +250,70 @@ void MetaspaceReporter::print_report(outputStream* out, size_t scale, int flags) // -- Print VirtualSpaceList details. if ((flags & (int)Option::ShowVSList) > 0) { out->cr(); - out->print_cr("Virtual space list%s:", Metaspace::using_class_space() ? "s" : ""); - - if (Metaspace::using_class_space()) { - out->print_cr(" Non-Class:"); - } +#if INCLUDE_CLASS_SPACE + out->print_cr("Virtual space lists:"); + out->print_cr(" Non-Class:"); VirtualSpaceList::vslist_nonclass()->print_on(out); out->cr(); - if (Metaspace::using_class_space()) { - out->print_cr(" Class:"); - VirtualSpaceList::vslist_class()->print_on(out); - out->cr(); - } + out->print_cr(" Class:"); + VirtualSpaceList::vslist_class()->print_on(out); + out->cr(); +#else + out->print_cr("Virtual space list:"); + VirtualSpaceList::vslist_nonclass()->print_on(out); + out->cr(); +#endif // INCLUDE_CLASS_SPACE } out->cr(); //////////// Freelists (ChunkManager) section /////////////////////////// - out->cr(); - out->print_cr("Chunk freelist%s:", Metaspace::using_class_space() ? "s" : ""); - ChunkManagerStats non_class_cm_stat; ChunkManagerStats class_cm_stat; ChunkManagerStats total_cm_stat; ChunkManager::chunkmanager_nonclass()->add_to_statistics(&non_class_cm_stat); - if (Metaspace::using_class_space()) { - ChunkManager::chunkmanager_nonclass()->add_to_statistics(&non_class_cm_stat); - ChunkManager::chunkmanager_class()->add_to_statistics(&class_cm_stat); - total_cm_stat.add(non_class_cm_stat); - total_cm_stat.add(class_cm_stat); +#if INCLUDE_CLASS_SPACE + ChunkManager::chunkmanager_class()->add_to_statistics(&class_cm_stat); + total_cm_stat.add(non_class_cm_stat); + total_cm_stat.add(class_cm_stat); - out->print_cr(" Non-Class:"); - non_class_cm_stat.print_on(out, scale); - out->cr(); - out->print_cr(" Class:"); - class_cm_stat.print_on(out, scale); - out->cr(); - out->print_cr(" Both:"); - total_cm_stat.print_on(out, scale); - out->cr(); - } else { - ChunkManager::chunkmanager_nonclass()->add_to_statistics(&non_class_cm_stat); - non_class_cm_stat.print_on(out, scale); - out->cr(); - } + out->print_cr("Chunk freelists:"); + out->cr(); + out->print_cr(" Non-Class:"); + non_class_cm_stat.print_on(out, scale); + out->cr(); + out->print_cr(" Class:"); + class_cm_stat.print_on(out, scale); + out->cr(); + out->print_cr(" Both:"); + total_cm_stat.print_on(out, scale); + out->cr(); +#else + out->print_cr("Chunk freelist:"); + ChunkManager::chunkmanager_nonclass()->add_to_statistics(&non_class_cm_stat); + non_class_cm_stat.print_on(out, scale); + out->cr(); +#endif // INCLUDE_CLASS_SPACE // -- Print Chunkmanager details. if ((flags & (int)Option::ShowChunkFreeList) > 0) { out->cr(); out->print_cr("Chunk freelist details:"); - if (Metaspace::using_class_space()) { - out->print_cr(" Non-Class:"); - } +#if INCLUDE_CLASS_SPACE + out->print_cr(" Non-Class:"); ChunkManager::chunkmanager_nonclass()->print_on(out); out->cr(); - if (Metaspace::using_class_space()) { - out->print_cr(" Class:"); - ChunkManager::chunkmanager_class()->print_on(out); - out->cr(); - } + out->print_cr(" Class:"); + ChunkManager::chunkmanager_class()->print_on(out); + out->cr(); +#else + ChunkManager::chunkmanager_nonclass()->print_on(out); + out->cr(); +#endif // INCLUDE_CLASS_SPACE } out->cr(); - //////////// Waste section /////////////////////////// // As a convenience, print a summary of common waste. out->cr(); diff --git a/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp b/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp index aab46d64db5..d90e8ed090d 100644 --- a/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp +++ b/src/hotspot/share/memory/metaspace/metaspaceStatistics.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -205,28 +205,26 @@ ArenaStats ClmsStats::totals() const { void ClmsStats::print_on(outputStream* st, size_t scale, bool detailed) const { StreamIndentor si(st, 2); st->cr(); - if (Metaspace::using_class_space()) { - st->print("Non-Class: "); - } + CLASS_SPACE_ONLY(st->print("Non-Class: ");) _arena_stats_nonclass.print_on(st, scale, detailed); if (detailed) { st->cr(); } - if (Metaspace::using_class_space()) { +#if INCLUDE_CLASS_SPACE + st->cr(); + st->print(" Class: "); + _arena_stats_class.print_on(st, scale, detailed); + if (detailed) { st->cr(); - st->print(" Class: "); - _arena_stats_class.print_on(st, scale, detailed); - if (detailed) { - st->cr(); - } - st->cr(); - st->print(" Both: "); - totals().print_on(st, scale, detailed); - if (detailed) { - st->cr(); - } } st->cr(); + st->print(" Both: "); + totals().print_on(st, scale, detailed); + if (detailed) { + st->cr(); + } +#endif // INCLUDE_CLASS_SPACE + st->cr(); } #ifdef ASSERT diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp index df4e507b104..a934a628582 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2021 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -259,12 +259,11 @@ VirtualSpaceNode* VirtualSpaceNode::create_node(size_t word_size, } #ifndef _LP64 - // On 32-bit, with +UseCompressedClassPointers, the whole address space is the encoding range. We therefore - // don't need a class space. However, as a pragmatic workaround for pesty overflow problems on 32-bit, we leave - // a small area at the end of the address space out of the encoding range. We just assume no Klass will ever live + // On 32-bit, the whole address space is the encoding range. We therefore don't need a class space. + // However, as a pragmatic workaround for pesty overflow problems on 32-bit, we leave a small area + // at the end of the address space out of the encoding range. We just assume no Klass will ever live // there (it won't, for no OS we support on 32-bit has user-addressable memory up there). - assert(!UseCompressedClassPointers || - rs.end() <= (char*)CompressedKlassPointers::max_klass_range_size(), "Weirdly high address"); + assert(rs.end() <= (char*)CompressedKlassPointers::max_klass_range_size(), "Weirdly high address"); #endif // _LP64 MemTracker::record_virtual_memory_tag(rs, mtMetaspace); diff --git a/src/hotspot/share/nmt/memReporter.cpp b/src/hotspot/share/nmt/memReporter.cpp index 27a94ec7bc0..429db6ad31f 100644 --- a/src/hotspot/share/nmt/memReporter.cpp +++ b/src/hotspot/share/nmt/memReporter.cpp @@ -272,9 +272,7 @@ void MemSummaryReporter::report_summary_of_tag(MemTag mem_tag, } else if (mem_tag == mtClass) { // Metadata information report_metadata(Metaspace::NonClassType); - if (Metaspace::using_class_space()) { - report_metadata(Metaspace::ClassType); - } + CLASS_SPACE_ONLY(report_metadata(Metaspace::ClassType);) } out->cr(); } @@ -754,9 +752,9 @@ void MemSummaryDiffReporter::diff_summary_of_tag(MemTag mem_tag, void MemSummaryDiffReporter::print_metaspace_diff(const MetaspaceCombinedStats& current_ms, const MetaspaceCombinedStats& early_ms) const { print_metaspace_diff("Metadata", current_ms.non_class_space_stats(), early_ms.non_class_space_stats()); - if (Metaspace::using_class_space()) { - print_metaspace_diff("Class space", current_ms.class_space_stats(), early_ms.class_space_stats()); - } +#if INCLUDE_CLASS_SPACE + print_metaspace_diff("Class space", current_ms.class_space_stats(), early_ms.class_space_stats()); +#endif } void MemSummaryDiffReporter::print_metaspace_diff(const char* header, diff --git a/src/hotspot/share/oops/arrayOop.hpp b/src/hotspot/share/oops/arrayOop.hpp index f0c476a2486..836a1b9250d 100644 --- a/src/hotspot/share/oops/arrayOop.hpp +++ b/src/hotspot/share/oops/arrayOop.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -80,8 +80,7 @@ private: // The _length field is not declared in C++. It is allocated after the // mark-word when using compact headers (+UseCompactObjectHeaders), otherwise - // after the compressed Klass* when running with compressed class-pointers - // (+UseCompressedClassPointers), or else after the full Klass*. + // after the compressed Klass*. static int length_offset_in_bytes() { return oopDesc::base_offset_in_bytes(); } diff --git a/src/hotspot/share/oops/compressedKlass.cpp b/src/hotspot/share/oops/compressedKlass.cpp index b32d10c74d2..ca1c46d4095 100644 --- a/src/hotspot/share/oops/compressedKlass.cpp +++ b/src/hotspot/share/oops/compressedKlass.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,7 +95,7 @@ void CompressedKlassPointers::sanity_check_after_initialization() { // We should need a class space if address space is larger than what narrowKlass can address const bool should_need_class_space = (BytesPerWord * BitsPerByte) > narrow_klass_pointer_bits(); - ASSERT_HERE(should_need_class_space == needs_class_space()); + ASSERT_HERE(should_need_class_space == (INCLUDE_CLASS_SPACE ? true : false)); const size_t klass_align = klass_alignment_in_bytes(); @@ -318,24 +318,19 @@ void CompressedKlassPointers::initialize(address addr, size_t len) { } void CompressedKlassPointers::print_mode(outputStream* st) { - st->print_cr("UseCompressedClassPointers %d, UseCompactObjectHeaders %d", - UseCompressedClassPointers, UseCompactObjectHeaders); - if (UseCompressedClassPointers) { - st->print_cr("Narrow klass pointer bits %d, Max shift %d", - _narrow_klass_pointer_bits, _max_shift); - st->print_cr("Narrow klass base: " PTR_FORMAT ", Narrow klass shift: %d", - p2i(base()), shift()); - st->print_cr("Encoding Range: " RANGE2FMT, RANGE2FMTARGS(_base, encoding_range_end())); - st->print_cr("Klass Range: " RANGE2FMT, RANGE2FMTARGS(_klass_range_start, _klass_range_end)); - st->print_cr("Klass ID Range: [%u - %u) (%u)", _lowest_valid_narrow_klass_id, _highest_valid_narrow_klass_id + 1, - _highest_valid_narrow_klass_id + 1 - _lowest_valid_narrow_klass_id); - if (_protection_zone_size > 0) { - st->print_cr("Protection zone: " RANGEFMT, RANGEFMTARGS(_base, _protection_zone_size)); - } else { - st->print_cr("No protection zone."); - } + st->print_cr("UseCompactObjectHeaders %d", UseCompactObjectHeaders); + st->print_cr("Narrow klass pointer bits %d, Max shift %d", + _narrow_klass_pointer_bits, _max_shift); + st->print_cr("Narrow klass base: " PTR_FORMAT ", Narrow klass shift: %d", + p2i(base()), shift()); + st->print_cr("Encoding Range: " RANGE2FMT, RANGE2FMTARGS(_base, encoding_range_end())); + st->print_cr("Klass Range: " RANGE2FMT, RANGE2FMTARGS(_klass_range_start, _klass_range_end)); + st->print_cr("Klass ID Range: [%u - %u) (%u)", _lowest_valid_narrow_klass_id, _highest_valid_narrow_klass_id + 1, + _highest_valid_narrow_klass_id + 1 - _lowest_valid_narrow_klass_id); + if (_protection_zone_size > 0) { + st->print_cr("Protection zone: " RANGEFMT, RANGEFMTARGS(_base, _protection_zone_size)); } else { - st->print_cr("UseCompressedClassPointers off"); + st->print_cr("No protection zone."); } } diff --git a/src/hotspot/share/oops/compressedKlass.hpp b/src/hotspot/share/oops/compressedKlass.hpp index 98a9eafdbf4..fe1ce9e07ae 100644 --- a/src/hotspot/share/oops/compressedKlass.hpp +++ b/src/hotspot/share/oops/compressedKlass.hpp @@ -98,7 +98,6 @@ class Klass; // If compressed klass pointers then use narrowKlass. typedef juint narrowKlass; -// For UseCompressedClassPointers. class CompressedKlassPointers : public AllStatic { friend class VMStructs; friend class ArchiveBuilder; @@ -161,7 +160,6 @@ public: // Initialization sequence: // 1) Parse arguments. The following arguments take a role: - // - UseCompressedClassPointers // - UseCompactObjectHeaders // - Xshare on off dump // - CompressedClassSpaceSize @@ -192,12 +190,6 @@ public: // resulting from the current encoding settings (base, shift), capped to a certain max. value. static size_t max_klass_range_size(); - // On 64-bit, we need the class space to confine Klass structures to the encoding range, which is determined - // by bit size of narrowKlass IDs and the shift. On 32-bit, we support compressed class pointer only - // "pro-forma": narrowKlass have the same size as addresses (32 bits), and therefore the encoding range is - // equal to the address space size. Here, we don't need a class space. - static constexpr bool needs_class_space() { return LP64_ONLY(true) NOT_LP64(false); } - // Reserve a range of memory that is to contain Klass strucutures which are referenced by narrow Klass IDs. // If optimize_for_zero_base is true, the implementation will attempt to reserve optimized for zero-based encoding. static char* reserve_address_space_for_compressed_classes(size_t size, bool aslr, bool optimize_for_zero_base); diff --git a/src/hotspot/share/oops/compressedKlass.inline.hpp b/src/hotspot/share/oops/compressedKlass.inline.hpp index 65732b3b289..834264286bc 100644 --- a/src/hotspot/share/oops/compressedKlass.inline.hpp +++ b/src/hotspot/share/oops/compressedKlass.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -75,7 +75,6 @@ inline narrowKlass CompressedKlassPointers::encode(const Klass* v) { #ifdef ASSERT inline void CompressedKlassPointers::check_encodable(const void* addr) { - assert(UseCompressedClassPointers, "Only call for +UseCCP"); assert(addr != nullptr, "Null Klass?"); assert(is_encodable(addr), "Address " PTR_FORMAT " is not encodable (Klass range: " RANGEFMT ", klass alignment: %d)", @@ -84,7 +83,6 @@ inline void CompressedKlassPointers::check_encodable(const void* addr) { inline void CompressedKlassPointers::check_valid_narrow_klass_id(narrowKlass nk) { check_init(_base); - assert(UseCompressedClassPointers, "Only call for +UseCCP"); assert(nk > 0, "narrow Klass ID is 0"); const uint64_t nk_mask = ~right_n_bits(narrow_klass_pointer_bits()); assert(((uint64_t)nk & nk_mask) == 0, "narrow klass id bit spillover (%u)", nk); diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 919afbf3abd..d3333e72c2a 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -486,10 +486,8 @@ InstanceKlass* InstanceKlass::allocate_instance_klass(const ClassFileParser& par ik = new (loader_data, size, THREAD) InstanceKlass(parser); } - if (ik != nullptr && UseCompressedClassPointers) { - assert(CompressedKlassPointers::is_encodable(ik), - "Klass " PTR_FORMAT "needs a narrow Klass ID, but is not encodable", p2i(ik)); - } + assert(ik == nullptr || CompressedKlassPointers::is_encodable(ik), + "Klass " PTR_FORMAT "needs a narrow Klass ID, but is not encodable", p2i(ik)); // Check for pending exception before adding to the loader data and incrementing // class count. Can get OOM here. diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index 001e9eba790..84a1766a702 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1055,14 +1055,8 @@ void Klass::verify_on(outputStream* st) { // This can be expensive, but it is worth checking that this klass is actually // in the CLD graph but not in production. -#ifdef ASSERT - if (UseCompressedClassPointers) { - // Stricter checks for both correct alignment and placement - CompressedKlassPointers::check_encodable(this); - } else { - assert(Metaspace::contains((address)this), "Should be"); - } -#endif // ASSERT + // Stricter checks for both correct alignment and placement + DEBUG_ONLY(CompressedKlassPointers::check_encodable(this)); guarantee(this->is_klass(),"should be klass"); diff --git a/src/hotspot/share/oops/objLayout.cpp b/src/hotspot/share/oops/objLayout.cpp index b8cd8249da1..2c426a7ddff 100644 --- a/src/hotspot/share/oops/objLayout.cpp +++ b/src/hotspot/share/oops/objLayout.cpp @@ -38,21 +38,17 @@ void ObjLayout::initialize() { _klass_mode = Compact; _oop_base_offset_in_bytes = sizeof(markWord); _oop_has_klass_gap = false; - } else if (UseCompressedClassPointers) { + } else { _klass_mode = Compressed; _oop_base_offset_in_bytes = sizeof(markWord) + sizeof(narrowKlass); _oop_has_klass_gap = true; - } else { - _klass_mode = Uncompressed; - _oop_base_offset_in_bytes = sizeof(markWord) + sizeof(Klass*); - _oop_has_klass_gap = false; } #else assert(_klass_mode == Undefined, "ObjLayout initialized twice"); assert(!UseCompactObjectHeaders, "COH unsupported on 32-bit"); - // We support +-UseCompressedClassPointers on 32-bit, but the layout + // We support narrow Klass pointers on 32-bit, but the layout // is exactly the same as it was with uncompressed klass pointers - _klass_mode = UseCompressedClassPointers ? Compressed : Uncompressed; + _klass_mode = Compressed; _oop_base_offset_in_bytes = sizeof(markWord) + sizeof(Klass*); _oop_has_klass_gap = false; #endif diff --git a/src/hotspot/share/oops/objLayout.hpp b/src/hotspot/share/oops/objLayout.hpp index e434524d4b0..37ed0b7a532 100644 --- a/src/hotspot/share/oops/objLayout.hpp +++ b/src/hotspot/share/oops/objLayout.hpp @@ -27,8 +27,8 @@ /* * This class helps to avoid loading more than one flag in some - * operations that require checking UseCompressedClassPointers, - * UseCompactObjectHeaders and possibly more. + * operations that require checking UseCompactObjectHeaders and - in the future - + * possibly more. * * This is important on some performance critical paths, e.g. where * the Klass* is accessed frequently, especially by GC oop iterators @@ -37,12 +37,10 @@ class ObjLayout { public: enum Mode { - // +UseCompactObjectHeaders (implies +UseCompressedClassPointers) + // +UseCompactObjectHeaders Compact, - // +UseCompressedClassPointers (-UseCompactObjectHeaders) + // -UseCompactObjectHeaders (compressed Klass pointers) Compressed, - // -UseCompressedClassPointers (-UseCompactObjectHeaders) - Uncompressed, // Not yet initialized Undefined }; diff --git a/src/hotspot/share/oops/objLayout.inline.hpp b/src/hotspot/share/oops/objLayout.inline.hpp index 6aa9e39ce28..adad490378d 100644 --- a/src/hotspot/share/oops/objLayout.inline.hpp +++ b/src/hotspot/share/oops/objLayout.inline.hpp @@ -32,10 +32,8 @@ inline ObjLayout::Mode ObjLayout::klass_mode() { assert(_klass_mode != Undefined, "KlassMode not yet initialized"); if (UseCompactObjectHeaders) { assert(_klass_mode == Compact, "Klass mode does not match flags"); - } else if (UseCompressedClassPointers) { - assert(_klass_mode == Compressed, "Klass mode does not match flags"); } else { - assert(_klass_mode == Uncompressed, "Klass mode does not match flags"); + assert(_klass_mode == Compressed, "Klass mode does not match flags"); } #endif return _klass_mode; diff --git a/src/hotspot/share/oops/oop.cpp b/src/hotspot/share/oops/oop.cpp index 5f453241c3d..415732af4f6 100644 --- a/src/hotspot/share/oops/oop.cpp +++ b/src/hotspot/share/oops/oop.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -152,8 +152,7 @@ bool oopDesc::is_typeArray_noinline() const { return is_typeArray(); } #if INCLUDE_CDS_JAVA_HEAP void oopDesc::set_narrow_klass(narrowKlass nk) { assert(CDSConfig::is_dumping_heap(), "Used by CDS only. Do not abuse!"); - assert(UseCompressedClassPointers, "must be"); - _metadata._compressed_klass = nk; + _compressed_klass = nk; } #endif diff --git a/src/hotspot/share/oops/oop.hpp b/src/hotspot/share/oops/oop.hpp index 0dc6590750e..d6cc71a60d8 100644 --- a/src/hotspot/share/oops/oop.hpp +++ b/src/hotspot/share/oops/oop.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,10 +49,7 @@ class oopDesc { friend class JVMCIVMStructs; private: volatile markWord _mark; - union _metadata { - Klass* _klass; - narrowKlass _compressed_klass; - } _metadata; + narrowKlass _compressed_klass; // There may be ordering constraints on the initialization of fields that // make use of the C++ copy/assign incorrect. @@ -338,7 +335,7 @@ class oopDesc { } else #endif { - return (int)offset_of(oopDesc, _metadata._klass); + return (int)offset_of(oopDesc, _compressed_klass); } } static int klass_gap_offset_in_bytes() { diff --git a/src/hotspot/share/oops/oop.inline.hpp b/src/hotspot/share/oops/oop.inline.hpp index b445eae933b..d5cb80e1122 100644 --- a/src/hotspot/share/oops/oop.inline.hpp +++ b/src/hotspot/share/oops/oop.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -99,9 +99,9 @@ Klass* oopDesc::klass() const { case ObjLayout::Compact: return mark().klass(); case ObjLayout::Compressed: - return CompressedKlassPointers::decode_not_null(_metadata._compressed_klass); + return CompressedKlassPointers::decode_not_null(_compressed_klass); default: - return _metadata._klass; + ShouldNotReachHere(); } } @@ -110,9 +110,9 @@ Klass* oopDesc::klass_or_null() const { case ObjLayout::Compact: return mark().klass_or_null(); case ObjLayout::Compressed: - return CompressedKlassPointers::decode(_metadata._compressed_klass); + return CompressedKlassPointers::decode(_compressed_klass); default: - return _metadata._klass; + ShouldNotReachHere(); } } @@ -121,11 +121,11 @@ Klass* oopDesc::klass_or_null_acquire() const { case ObjLayout::Compact: return mark_acquire().klass(); case ObjLayout::Compressed: { - narrowKlass narrow_klass = AtomicAccess::load_acquire(&_metadata._compressed_klass); + narrowKlass narrow_klass = AtomicAccess::load_acquire(&_compressed_klass); return CompressedKlassPointers::decode(narrow_klass); } default: - return AtomicAccess::load_acquire(&_metadata._klass); + ShouldNotReachHere(); } } @@ -134,9 +134,9 @@ Klass* oopDesc::klass_without_asserts() const { case ObjLayout::Compact: return mark().klass_without_asserts(); case ObjLayout::Compressed: - return CompressedKlassPointers::decode_without_asserts(_metadata._compressed_klass); + return CompressedKlassPointers::decode_without_asserts(_compressed_klass); default: - return _metadata._klass; + ShouldNotReachHere(); } } @@ -145,7 +145,7 @@ narrowKlass oopDesc::narrow_klass() const { case ObjLayout::Compact: return mark().narrow_klass(); case ObjLayout::Compressed: - return _metadata._compressed_klass; + return _compressed_klass; default: ShouldNotReachHere(); } @@ -154,23 +154,14 @@ narrowKlass oopDesc::narrow_klass() const { void oopDesc::set_klass(Klass* k) { assert(Universe::is_bootstrapping() || (k != nullptr && k->is_klass()), "incorrect Klass"); assert(!UseCompactObjectHeaders, "don't set Klass* with compact headers"); - if (UseCompressedClassPointers) { - _metadata._compressed_klass = CompressedKlassPointers::encode_not_null(k); - } else { - _metadata._klass = k; - } + _compressed_klass = CompressedKlassPointers::encode_not_null(k); } void oopDesc::release_set_klass(HeapWord* mem, Klass* k) { assert(Universe::is_bootstrapping() || (k != nullptr && k->is_klass()), "incorrect Klass"); assert(!UseCompactObjectHeaders, "don't set Klass* with compact headers"); char* raw_mem = ((char*)mem + klass_offset_in_bytes()); - if (UseCompressedClassPointers) { - AtomicAccess::release_store((narrowKlass*)raw_mem, - CompressedKlassPointers::encode_not_null(k)); - } else { - AtomicAccess::release_store((Klass**)raw_mem, k); - } + AtomicAccess::release_store((narrowKlass*)raw_mem, CompressedKlassPointers::encode_not_null(k)); } void oopDesc::set_klass_gap(HeapWord* mem, int v) { diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index a251a253ed1..0915f79a503 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -2689,7 +2689,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { #ifdef _LP64 // Push DecodeN/DecodeNKlass down through phi. // The rest of phi graph will transform by split EncodeP node though phis up. - if ((UseCompressedOops || UseCompressedClassPointers) && can_reshape && progress == nullptr) { + if (can_reshape && progress == nullptr) { bool may_push = true; bool has_decodeN = false; bool is_decodeN = false; diff --git a/src/hotspot/share/opto/chaitin.cpp b/src/hotspot/share/opto/chaitin.cpp index 3ba3ffc1045..ef017ee15ec 100644 --- a/src/hotspot/share/opto/chaitin.cpp +++ b/src/hotspot/share/opto/chaitin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2633,7 +2633,7 @@ void PhaseChaitin::verify_base_ptrs(ResourceArea* a) const { #ifdef _LP64 (UseCompressedOops && check->as_Mach()->ideal_Opcode() == Op_CastPP) || (UseCompressedOops && check->as_Mach()->ideal_Opcode() == Op_DecodeN) || - (UseCompressedClassPointers && check->as_Mach()->ideal_Opcode() == Op_DecodeNKlass) || + (check->as_Mach()->ideal_Opcode() == Op_DecodeNKlass) || #endif // _LP64 check->as_Mach()->ideal_Opcode() == Op_LoadP || check->as_Mach()->ideal_Opcode() == Op_LoadKlass))) { diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index f34c75656a4..7ab384a29c7 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -3414,8 +3414,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f Node *addp = n->in(AddPNode::Address); assert(n->as_AddP()->address_input_has_same_base(), "Base pointers must match (addp %u)", addp->_idx ); #ifdef _LP64 - if ((UseCompressedOops || UseCompressedClassPointers) && - addp->Opcode() == Op_ConP && + if (addp->Opcode() == Op_ConP && addp == n->in(AddPNode::Base) && n->in(AddPNode::Offset)->is_Con()) { // If the transformation of ConP to ConN+DecodeN is beneficial depends @@ -3428,7 +3427,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f bool is_klass = t->isa_klassptr() != nullptr; if ((is_oop && UseCompressedOops && Matcher::const_oop_prefer_decode() ) || - (is_klass && UseCompressedClassPointers && Matcher::const_klass_prefer_decode() && + (is_klass && Matcher::const_klass_prefer_decode() && t->isa_klassptr()->exact_klass()->is_in_encoding_range())) { Node* nn = nullptr; @@ -3975,8 +3974,7 @@ void Compile::final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_R } // Skip next transformation if compressed oops are not used. - if ((UseCompressedOops && !Matcher::gen_narrow_oop_implicit_null_checks()) || - (!UseCompressedOops && !UseCompressedClassPointers)) + if (UseCompressedOops && !Matcher::gen_narrow_oop_implicit_null_checks()) return; // Go over safepoints nodes to skip DecodeN/DecodeNKlass nodes for debug edges. diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp index 53a503866fa..450d267e821 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -306,8 +306,7 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo // cannot reason about it; is probably not implicit null exception } else { const TypePtr* tptr; - if ((UseCompressedOops && CompressedOops::shift() == 0) || - (UseCompressedClassPointers && CompressedKlassPointers::shift() == 0)) { + if ((UseCompressedOops && CompressedOops::shift() == 0) || CompressedKlassPointers::shift() == 0) { // 32-bits narrow oop can be the base of address expressions tptr = base->get_ptr_type(); } else { diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 3cd553e4bd1..46f6729c82f 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -2486,7 +2486,6 @@ Node* LoadKlassNode::make(PhaseGVN& gvn, Node* mem, Node* adr, const TypePtr* at assert(adr_type != nullptr, "expecting TypeKlassPtr"); #ifdef _LP64 if (adr_type->is_ptr_to_narrowklass()) { - assert(UseCompressedClassPointers, "no compressed klasses"); Node* load_klass = gvn.transform(new LoadNKlassNode(mem, adr, at, tk->make_narrowklass(), MemNode::unordered)); return new DecodeNKlassNode(load_klass, load_klass->bottom_type()->make_ptr()); } @@ -2816,8 +2815,7 @@ StoreNode* StoreNode::make(PhaseGVN& gvn, Node* ctl, Node* mem, Node* adr, const val = gvn.transform(new EncodePNode(val, val->bottom_type()->make_narrowoop())); return new StoreNNode(ctl, mem, adr, adr_type, val, mo); } else if (adr->bottom_type()->is_ptr_to_narrowklass() || - (UseCompressedClassPointers && val->bottom_type()->isa_klassptr() && - adr->bottom_type()->isa_rawptr())) { + (val->bottom_type()->isa_klassptr() && adr->bottom_type()->isa_rawptr())) { val = gvn.transform(new EncodePKlassNode(val, val->bottom_type()->make_narrowklass())); return new StoreNKlassNode(ctl, mem, adr, adr_type, val, mo); } diff --git a/src/hotspot/share/opto/narrowptrnode.cpp b/src/hotspot/share/opto/narrowptrnode.cpp index 7f86b8caecf..8b91bfaa944 100644 --- a/src/hotspot/share/opto/narrowptrnode.cpp +++ b/src/hotspot/share/opto/narrowptrnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -102,7 +102,7 @@ const Type* EncodePKlassNode::Value(PhaseGVN* phase) const { if (t == Type::TOP) return Type::TOP; assert (t != TypePtr::NULL_PTR, "null klass?"); - assert(UseCompressedClassPointers && t->isa_klassptr(), "only klass ptr here"); + assert(t->isa_klassptr(), "only klass ptr here"); return t->make_narrowklass(); } diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index aab2ea3cd3b..308ec819773 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.cpp @@ -3489,7 +3489,7 @@ TypeOopPtr::TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, const TypeInterfaces* inter #ifdef _LP64 if (_offset > 0 || _offset == Type::OffsetTop || _offset == Type::OffsetBot) { if (_offset == oopDesc::klass_offset_in_bytes()) { - _is_ptr_to_narrowklass = UseCompressedClassPointers; + _is_ptr_to_narrowklass = true; } else if (klass() == nullptr) { // Array with unknown body type assert(this->isa_aryptr(), "only arrays without klass"); diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 5df1461c0fd..a13d0ba47c8 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -333,7 +333,6 @@ WB_ENTRY(void, WB_ReadFromNoaccessArea(JNIEnv* env, jobject o)) WB_END WB_ENTRY(void, WB_DecodeNKlassAndAccessKlass(JNIEnv* env, jobject o, jint nKlass)) - assert(UseCompressedClassPointers, "Should only call for UseCompressedClassPointers"); const narrowKlass nk = (narrowKlass)nKlass; const Klass* const k = CompressedKlassPointers::decode_not_null_without_asserts(nKlass); printf("WB_DecodeNKlassAndAccessKlass: nk %u k " PTR_FORMAT "\n", nk, p2i(k)); diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index dac34017e45..1d79c4d0488 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -1554,7 +1554,7 @@ void Arguments::set_heap_size() { } #ifdef _LP64 - if (UseCompressedOops || UseCompressedClassPointers) { + if (UseCompressedOops) { // HeapBaseMinAddress can be greater than default but not less than. if (!FLAG_IS_DEFAULT(HeapBaseMinAddress)) { if (HeapBaseMinAddress < DefaultHeapBaseMinAddress) { @@ -1567,9 +1567,7 @@ void Arguments::set_heap_size() { FLAG_SET_ERGO(HeapBaseMinAddress, DefaultHeapBaseMinAddress); } } - } - if (UseCompressedOops) { uintptr_t heap_end = HeapBaseMinAddress + MaxHeapSize; uintptr_t max_coop_heap = max_heap_for_compressed_oops(); @@ -3782,10 +3780,6 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) { void Arguments::set_compact_headers_flags() { #ifdef _LP64 - if (UseCompactObjectHeaders && FLAG_IS_CMDLINE(UseCompressedClassPointers) && !UseCompressedClassPointers) { - warning("Compact object headers require compressed class pointers. Disabling compact object headers."); - FLAG_SET_DEFAULT(UseCompactObjectHeaders, false); - } if (UseCompactObjectHeaders && !UseObjectMonitorTable) { // If UseCompactObjectHeaders is on the command line, turn on UseObjectMonitorTable. if (FLAG_IS_CMDLINE(UseCompactObjectHeaders)) { @@ -3799,9 +3793,6 @@ void Arguments::set_compact_headers_flags() { FLAG_SET_DEFAULT(UseObjectMonitorTable, true); } } - if (UseCompactObjectHeaders && !UseCompressedClassPointers) { - FLAG_SET_DEFAULT(UseCompressedClassPointers, true); - } #endif } @@ -3817,9 +3808,7 @@ jint Arguments::apply_ergo() { set_compact_headers_flags(); - if (UseCompressedClassPointers) { - CompressedKlassPointers::pre_initialize(); - } + CompressedKlassPointers::pre_initialize(); CDSConfig::ergo_initialize(); @@ -3864,10 +3853,6 @@ jint Arguments::apply_ergo() { DebugNonSafepoints = true; } - if (FLAG_IS_CMDLINE(CompressedClassSpaceSize) && !UseCompressedClassPointers) { - warning("Setting CompressedClassSpaceSize has no effect when compressed class pointers are not used"); - } - // Treat the odd case where local verification is enabled but remote // verification is not as if both were enabled. if (BytecodeVerificationLocal && !BytecodeVerificationRemote) { diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 60feddde09b..b5c19d8aa36 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -1368,9 +1368,6 @@ const int ObjectAlignmentInBytes = 8; "Maximum size of Metaspaces (in bytes)") \ constraint(MaxMetaspaceSizeConstraintFunc,AfterErgo) \ \ - product(bool, UseCompressedClassPointers, true, \ - "(Deprecated) Use 32-bit class pointers.") \ - \ product(size_t, CompressedClassSpaceSize, 1*G, \ "Maximum size of class area in Metaspace when compressed " \ "class pointers are used") \ diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 129f8f76e73..d55cf454256 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -1285,7 +1285,7 @@ void os::print_location(outputStream* st, intptr_t x, bool verbose) { bool accessible = is_readable_pointer(addr); // Check if addr points into the narrow Klass protection zone - if (UseCompressedClassPointers && CompressedKlassPointers::is_in_protection_zone(addr)) { + if (CompressedKlassPointers::is_in_protection_zone(addr)) { st->print_cr(PTR_FORMAT " points into nKlass protection zone", p2i(addr)); return; } @@ -1339,8 +1339,9 @@ void os::print_location(outputStream* st, intptr_t x, bool verbose) { } // Compressed klass needs to be decoded first. + // Todo: questionable for COH - can we do this better? #ifdef _LP64 - if (UseCompressedClassPointers && ((uintptr_t)addr &~ (uintptr_t)max_juint) == 0) { + if (((uintptr_t)addr &~ (uintptr_t)max_juint) == 0) { narrowKlass narrow_klass = (narrowKlass)(uintptr_t)addr; Klass* k = CompressedKlassPointers::decode_without_asserts(narrow_klass); diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index b6294a9a168..ad9463443b2 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -163,8 +163,7 @@ /******************************************************************/ \ \ volatile_nonstatic_field(oopDesc, _mark, markWord) \ - volatile_nonstatic_field(oopDesc, _metadata._klass, Klass*) \ - volatile_nonstatic_field(oopDesc, _metadata._compressed_klass, narrowKlass) \ + volatile_nonstatic_field(oopDesc, _compressed_klass, narrowKlass) \ static_field(BarrierSet, _barrier_set, BarrierSet*) \ nonstatic_field(ArrayKlass, _dimension, const int) \ volatile_nonstatic_field(ArrayKlass, _higher_dimension, ObjArrayKlass*) \ diff --git a/src/hotspot/share/services/memoryService.cpp b/src/hotspot/share/services/memoryService.cpp index f64da3c5477..4636f125079 100644 --- a/src/hotspot/share/services/memoryService.cpp +++ b/src/hotspot/share/services/memoryService.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -119,11 +119,11 @@ void MemoryService::add_metaspace_memory_pools() { mgr->add_pool(_metaspace_pool); _pools_list->append(_metaspace_pool); - if (UseCompressedClassPointers) { - _compressed_class_pool = new CompressedKlassSpacePool(); - mgr->add_pool(_compressed_class_pool); - _pools_list->append(_compressed_class_pool); - } +#if INCLUDE_CLASS_SPACE + _compressed_class_pool = new CompressedKlassSpacePool(); + mgr->add_pool(_compressed_class_pool); + _pools_list->append(_compressed_class_pool); +#endif _managers_list->append(mgr); } diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp index a03255b5cf3..3621f675ecb 100644 --- a/src/hotspot/share/utilities/macros.hpp +++ b/src/hotspot/share/utilities/macros.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -586,6 +586,18 @@ #define BIG_ENDIAN_ONLY(code) code #endif +#ifdef _LP64 +#define INCLUDE_CLASS_SPACE 1 +#define CLASS_SPACE_ONLY(x) x +#define NOT_CLASS_SPACE(x) +#else +// On 32-bit we use fake "narrow class pointers" which are really just 32-bit pointers, +// but we don't use a class space (would cause too much address space fragmentation) +#define INCLUDE_CLASS_SPACE 0 +#define CLASS_SPACE_ONLY(x) +#define NOT_CLASS_SPACE(x) x +#endif + #define define_pd_global(type, name, value) const type pd_##name = value; // Helper macros for constructing file names for includes. diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 48fae6868ab..d78fd331b56 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -533,8 +533,7 @@ static void report_vm_version(outputStream* st, char* buf, int buflen) { "", "", #endif UseCompressedOops ? ", compressed oops" : "", - UseCompactObjectHeaders ? ", compact obj headers" - : (UseCompressedClassPointers ? ", compressed class ptrs" : ""), + UseCompactObjectHeaders ? ", compact obj headers" : "", GCConfig::hs_err_name(), VM_Version::vm_platform_string() ); @@ -1215,7 +1214,7 @@ void VMError::report(outputStream* st, bool _verbose) { CompressedOops::print_mode(st); st->cr(); - STEP_IF("printing compressed klass pointers mode", _verbose && UseCompressedClassPointers) + STEP_IF("printing compressed klass pointers mode", _verbose) CDS_ONLY(AOTMetaspace::print_on(st);) Metaspace::print_compressed_class_space(st); CompressedKlassPointers::print_mode(st); @@ -1437,12 +1436,10 @@ void VMError::print_vm_info(outputStream* st) { #endif // STEP("printing compressed class ptrs mode") - if (UseCompressedClassPointers) { - CDS_ONLY(AOTMetaspace::print_on(st);) - Metaspace::print_compressed_class_space(st); - CompressedKlassPointers::print_mode(st); - st->cr(); - } + CDS_ONLY(AOTMetaspace::print_on(st);) + Metaspace::print_compressed_class_space(st); + CompressedKlassPointers::print_mode(st); + st->cr(); // Take heap lock over heap, GC and metaspace printing so that information // is consistent. diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Array.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Array.java index c4eeaf4a367..61067e63707 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Array.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Array.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -83,10 +83,8 @@ public class Array extends Oop { } if (VM.getVM().isCompactObjectHeadersEnabled()) { lengthOffsetInBytes = Oop.getHeaderSize(); - } else if (VM.getVM().isCompressedKlassPointersEnabled()) { - lengthOffsetInBytes = typeSize - VM.getVM().getIntSize(); } else { - lengthOffsetInBytes = typeSize; + lengthOffsetInBytes = typeSize - VM.getVM().getIntSize(); } return lengthOffsetInBytes; } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Instance.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Instance.java index 66efbe3484a..fea4fdaabc2 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Instance.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Instance.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,10 +57,8 @@ public class Instance extends Oop { public static long getHeaderSize() { if (VM.getVM().isCompactObjectHeadersEnabled()) { return Oop.getHeaderSize(); - } else if (VM.getVM().isCompressedKlassPointersEnabled()) { - return typeSize - VM.getVM().getIntSize(); } else { - return typeSize; + return typeSize - VM.getVM().getIntSize(); } } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java index 75ad4ab1d66..951499974fa 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,8 +51,7 @@ public class Oop { headerSize = markType.getSize(); } else { headerSize = type.getSize(); - klass = new MetadataField(type.getAddressField("_metadata._klass"), 0); - compressedKlass = new NarrowKlassField(type.getAddressField("_metadata._compressed_klass"), 0); + compressedKlass = new NarrowKlassField(type.getAddressField("_compressed_klass"), 0); } } @@ -75,7 +74,6 @@ public class Oop { public static long getHeaderSize() { return headerSize; } // Header size in bytes. private static CIntField mark; - private static MetadataField klass; private static NarrowKlassField compressedKlass; // Accessors for declared fields @@ -83,12 +81,9 @@ public class Oop { public Klass getKlass() { if (VM.getVM().isCompactObjectHeadersEnabled()) { - assert(VM.getVM().isCompressedKlassPointersEnabled()); return getMark().getKlass(); - } else if (VM.getVM().isCompressedKlassPointersEnabled()) { - return (Klass)compressedKlass.getValue(getHandle()); } else { - return (Klass)klass.getValue(getHandle()); + return (Klass)compressedKlass.getValue(getHandle()); } } @@ -157,11 +152,7 @@ public class Oop { if (doVMFields) { visitor.doCInt(mark, true); if (!VM.getVM().isCompactObjectHeadersEnabled()) { - if (VM.getVM().isCompressedKlassPointersEnabled()) { - visitor.doMetadata(compressedKlass, true); - } else { - visitor.doMetadata(klass, true); - } + visitor.doMetadata(compressedKlass, true); } } } @@ -220,10 +211,8 @@ public class Oop { if (VM.getVM().isCompactObjectHeadersEnabled()) { Mark mark = new Mark(handle); return mark.getKlass(); - } else if (VM.getVM().isCompressedKlassPointersEnabled()) { - return (Klass)Metadata.instantiateWrapperFor(handle.getCompKlassAddressAt(compressedKlass.getOffset())); } else { - return (Klass)Metadata.instantiateWrapperFor(handle.getAddressAt(klass.getOffset())); + return (Klass)Metadata.instantiateWrapperFor(handle.getCompKlassAddressAt(compressedKlass.getOffset())); } } }; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java index 1607563150a..2ec96121934 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -144,7 +144,6 @@ public class VM { private static CIntegerType boolType; private Boolean sharingEnabled; private Boolean compressedOopsEnabled; - private Boolean compressedKlassPointersEnabled; private Boolean compactObjectHeadersEnabled; // command line flags supplied to VM - see struct JVMFlag in jvmFlag.hpp @@ -515,11 +514,7 @@ public class VM { heapOopSize = (int)getOopSize(); } - if (isCompressedKlassPointersEnabled()) { - klassPtrSize = (int)getIntSize(); - } else { - klassPtrSize = (int)getOopSize(); // same as an oop - } + klassPtrSize = (int)getIntSize(); } /** This could be used by a reflective runtime system */ @@ -938,15 +933,6 @@ public class VM { return compressedOopsEnabled.booleanValue(); } - public boolean isCompressedKlassPointersEnabled() { - if (compressedKlassPointersEnabled == null) { - Flag flag = getCommandLineFlag("UseCompressedClassPointers"); - compressedKlassPointersEnabled = (flag == null) ? Boolean.FALSE: - (flag.getBool()? Boolean.TRUE: Boolean.FALSE); - } - return compressedKlassPointersEnabled.booleanValue(); - } - public boolean isCompactObjectHeadersEnabled() { if (compactObjectHeadersEnabled == null) { Flag flag = getCommandLineFlag("UseCompactObjectHeaders"); diff --git a/test/hotspot/gtest/metaspace/test_is_metaspace_obj.cpp b/test/hotspot/gtest/metaspace/test_is_metaspace_obj.cpp index a35888c62e2..c1b4abfb355 100644 --- a/test/hotspot/gtest/metaspace/test_is_metaspace_obj.cpp +++ b/test/hotspot/gtest/metaspace/test_is_metaspace_obj.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -65,8 +65,8 @@ public: // Test VirtualSpaceList::contains const VirtualSpaceList* const vslist = - (mdType == Metaspace::ClassType && Metaspace::using_class_space()) ? - VirtualSpaceList::vslist_class() : VirtualSpaceList::vslist_nonclass(); + (mdType == Metaspace::ClassType && INCLUDE_CLASS_SPACE) ? + VirtualSpaceList::vslist_class() : VirtualSpaceList::vslist_nonclass(); ASSERT_TRUE(vslist->contains((MetaWord*)p)); diff --git a/test/hotspot/gtest/metaspace/test_metaspaceUtils.cpp b/test/hotspot/gtest/metaspace/test_metaspaceUtils.cpp index 49ab816c4a0..68deb283784 100644 --- a/test/hotspot/gtest/metaspace/test_metaspaceUtils.cpp +++ b/test/hotspot/gtest/metaspace/test_metaspaceUtils.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -37,15 +37,15 @@ TEST_VM(metaspace, MetaspaceUtils_reserved) { EXPECT_LE(reserved_metadata, reserved); } +#if INCLUDE_CLASS_SPACE TEST_VM(metaspace, MetaspaceUtils_reserved_compressed_class_pointers) { - if (UseCompressedClassPointers && CompressedKlassPointers::needs_class_space()) { - size_t reserved = MetaspaceUtils::reserved_bytes(); - EXPECT_GT(reserved, 0UL); - size_t reserved_class = MetaspaceUtils::reserved_bytes(Metaspace::ClassType); - EXPECT_GT(reserved_class, 0UL); - EXPECT_LE(reserved_class, reserved); - } + size_t reserved = MetaspaceUtils::reserved_bytes(); + EXPECT_GT(reserved, 0UL); + size_t reserved_class = MetaspaceUtils::reserved_bytes(Metaspace::ClassType); + EXPECT_GT(reserved_class, 0UL); + EXPECT_LE(reserved_class, reserved); } +#endif // INCLUDE_CLASS_SPACE TEST_VM(metaspace, MetaspaceUtils_committed) { size_t committed = MetaspaceUtils::committed_bytes(); @@ -59,30 +59,15 @@ TEST_VM(metaspace, MetaspaceUtils_committed) { EXPECT_LE(committed_metadata, committed); } +#if INCLUDE_CLASS_SPACE TEST_VM(metaspace, MetaspaceUtils_committed_compressed_class_pointers) { - if (UseCompressedClassPointers && CompressedKlassPointers::needs_class_space()) { - size_t committed = MetaspaceUtils::committed_bytes(); - EXPECT_GT(committed, 0UL); - size_t committed_class = MetaspaceUtils::committed_bytes(Metaspace::ClassType); - EXPECT_GT(committed_class, 0UL); - EXPECT_LE(committed_class, committed); - } -} - -TEST_VM(metaspace, MetaspaceUtils_non_compressed_class_pointers) { - if (UseCompressedClassPointers) { - return; - } - + size_t committed = MetaspaceUtils::committed_bytes(); + EXPECT_GT(committed, 0UL); size_t committed_class = MetaspaceUtils::committed_bytes(Metaspace::ClassType); - EXPECT_EQ(committed_class, 0UL); - - size_t used_class = MetaspaceUtils::used_bytes(Metaspace::ClassType); - EXPECT_EQ(used_class, 0UL); - - size_t reserved_class = MetaspaceUtils::reserved_bytes(Metaspace::ClassType); - EXPECT_EQ(reserved_class, 0UL); + EXPECT_GT(committed_class, 0UL); + EXPECT_LE(committed_class, committed); } +#endif // INCLUDE_CLASS_SPACE static void check_metaspace_stats_are_consistent(const MetaspaceStats& stats) { EXPECT_LE(stats.committed(), stats.reserved()); @@ -102,13 +87,13 @@ TEST_VM(MetaspaceUtils, MetaspaceUtils_get_statistics) { check_metaspace_stats_are_not_null(combined_stats.non_class_space_stats()); check_metaspace_stats_are_consistent(combined_stats.non_class_space_stats()); - if (CompressedKlassPointers::needs_class_space() && UseCompressedClassPointers) { - check_metaspace_stats_are_not_null(combined_stats.class_space_stats()); - check_metaspace_stats_are_consistent(combined_stats.class_space_stats()); - } else { - // if we don't have a class space, combined stats should equal non-class stats - EXPECT_EQ(combined_stats.non_class_space_stats().reserved(), combined_stats.reserved()); - EXPECT_EQ(combined_stats.non_class_space_stats().committed(), combined_stats.committed()); - EXPECT_EQ(combined_stats.non_class_space_stats().used(), combined_stats.used()); - } +#if INCLUDE_CLASS_SPACE + check_metaspace_stats_are_not_null(combined_stats.class_space_stats()); + check_metaspace_stats_are_consistent(combined_stats.class_space_stats()); +#else + // if we don't have a class space, combined stats should equal non-class stats + EXPECT_EQ(combined_stats.non_class_space_stats().reserved(), combined_stats.reserved()); + EXPECT_EQ(combined_stats.non_class_space_stats().committed(), combined_stats.committed()); + EXPECT_EQ(combined_stats.non_class_space_stats().used(), combined_stats.used()); +#endif // INCLUDE_CLASS_SPACE } diff --git a/test/hotspot/gtest/oops/test_arrayOop.cpp b/test/hotspot/gtest/oops/test_arrayOop.cpp index 33163de427c..bb5f54b3f58 100644 --- a/test/hotspot/gtest/oops/test_arrayOop.cpp +++ b/test/hotspot/gtest/oops/test_arrayOop.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -97,7 +97,7 @@ TEST_VM(arrayOopDesc, base_offset) { EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_OBJECT), 16); EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_ARRAY), 16); } - } else if (UseCompressedClassPointers) { + } else { EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_BOOLEAN), 16); EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_BYTE), 16); EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_SHORT), 16); @@ -108,22 +108,6 @@ TEST_VM(arrayOopDesc, base_offset) { EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_DOUBLE), 16); EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_OBJECT), 16); EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_ARRAY), 16); - } else { - EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_BOOLEAN), 20); - EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_BYTE), 20); - EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_SHORT), 20); - EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_CHAR), 20); - EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_INT), 20); - EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_FLOAT), 20); - EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_LONG), 24); - EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_DOUBLE), 24); - if (UseCompressedOops) { - EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_OBJECT), 20); - EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_ARRAY), 20); - } else { - EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_OBJECT), 24); - EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_ARRAY), 24); - } } #else EXPECT_EQ(arrayOopDesc::base_offset_in_bytes(T_BOOLEAN), 12); diff --git a/test/hotspot/gtest/oops/test_compressedKlass.cpp b/test/hotspot/gtest/oops/test_compressedKlass.cpp index dcdb335adb5..5a7323edc03 100644 --- a/test/hotspot/gtest/oops/test_compressedKlass.cpp +++ b/test/hotspot/gtest/oops/test_compressedKlass.cpp @@ -29,9 +29,6 @@ #include "unittest.hpp" TEST_VM(CompressedKlass, basics) { - if (!UseCompressedClassPointers) { - return; - } ASSERT_LE(CompressedKlassPointers::base(), CompressedKlassPointers::klass_range_start()); ASSERT_LT(CompressedKlassPointers::klass_range_start(), CompressedKlassPointers::klass_range_end()); ASSERT_LE(CompressedKlassPointers::klass_range_end(), CompressedKlassPointers::encoding_range_end()); @@ -54,22 +51,7 @@ TEST_VM(CompressedKlass, basics) { #endif // _LP64 } -TEST_VM(CompressedKlass, ccp_off) { - if (UseCompressedClassPointers) { - return; - } - ASSERT_EQ(CompressedKlassPointers::klass_range_start(), (address)nullptr); - ASSERT_EQ(CompressedKlassPointers::klass_range_end(), (address)nullptr); - // We should be able to call CompressedKlassPointers::is_encodable, and it should - // always return false - ASSERT_FALSE(CompressedKlassPointers::is_encodable((address)0x12345)); -} - - TEST_VM(CompressedKlass, test_too_low_address) { - if (!UseCompressedClassPointers) { - return; - } address really_low = (address) 32; ASSERT_FALSE(CompressedKlassPointers::is_encodable(really_low)); address low = CompressedKlassPointers::klass_range_start() - 1; @@ -77,9 +59,6 @@ TEST_VM(CompressedKlass, test_too_low_address) { } TEST_VM(CompressedKlass, test_too_high_address) { - if (!UseCompressedClassPointers) { - return; - } address really_high = (address) UINTPTR_MAX; ASSERT_FALSE(CompressedKlassPointers::is_encodable(really_high)); address high = CompressedKlassPointers::klass_range_end(); @@ -87,9 +66,6 @@ TEST_VM(CompressedKlass, test_too_high_address) { } TEST_VM(CompressedKlass, test_unaligned_address) { - if (!UseCompressedClassPointers) { - return; - } const size_t alignment = CompressedKlassPointers::klass_alignment_in_bytes(); address addr = CompressedKlassPointers::klass_range_start() + alignment - 1; ASSERT_FALSE(CompressedKlassPointers::is_encodable(addr)); @@ -103,9 +79,6 @@ TEST_VM(CompressedKlass, test_unaligned_address) { } TEST_VM(CompressedKlass, test_good_address) { - if (!UseCompressedClassPointers) { - return; - } const size_t alignment = CompressedKlassPointers::klass_alignment_in_bytes(); address addr = CompressedKlassPointers::klass_range_start(); ASSERT_TRUE(CompressedKlassPointers::is_encodable(addr)); @@ -114,9 +87,6 @@ TEST_VM(CompressedKlass, test_good_address) { } TEST_VM(CompressedKlass, test_is_valid_narrow_klass) { - if (!UseCompressedClassPointers) { - return; - } ASSERT_FALSE(CompressedKlassPointers::is_valid_narrow_klass_id(0)); narrowKlass nk_jlC = CompressedKlassPointers::encode((Klass*)vmClasses::Class_klass()); ASSERT_TRUE(CompressedKlassPointers::is_valid_narrow_klass_id(nk_jlC)); diff --git a/test/hotspot/gtest/oops/test_objArrayOop.cpp b/test/hotspot/gtest/oops/test_objArrayOop.cpp index 22c9b2efc11..f6a53a3031f 100644 --- a/test/hotspot/gtest/oops/test_objArrayOop.cpp +++ b/test/hotspot/gtest/oops/test_objArrayOop.cpp @@ -27,35 +27,29 @@ TEST_VM(objArrayOop, osize) { static const struct { - int objal; bool ccp; bool coops; bool coh; int result; + int objal; bool coops; bool coh; int result; } x[] = { -// ObjAligInB, UseCCP, UseCoops, UseCOH, object size in heap words +// ObjAligInB, UseCoops, UseCOH, object size in heap words #ifdef _LP64 - { 8, false, false, false, 4 }, // 20 byte header, 8 byte oops - { 8, false, true, false, 3 }, // 20 byte header, 4 byte oops - { 8, true, false, false, 3 }, // 16 byte header, 8 byte oops - { 8, true, true, false, 3 }, // 16 byte header, 4 byte oops - { 8, true, false, true, 3 }, // 12 byte header, 8 byte oops - { 8, true, true, true, 2 }, // 12 byte header, 4 byte oops - { 16, false, false, false, 4 }, // 20 byte header, 8 byte oops, 16-byte align - { 16, false, true, false, 4 }, // 20 byte header, 4 byte oops, 16-byte align - { 16, true, false, false, 4 }, // 16 byte header, 8 byte oops, 16-byte align - { 16, true, true, false, 4 }, // 16 byte header, 4 byte oops, 16-byte align - { 16, true, false, true, 4 }, // 12 byte header, 8 byte oops, 16-byte align - { 16, true, true, true, 2 }, // 12 byte header, 4 byte oops, 16-byte align - { 256, false, false, false, 32 }, // 20 byte header, 8 byte oops, 256-byte align - { 256, false, true, false, 32 }, // 20 byte header, 4 byte oops, 256-byte align - { 256, true, false, false, 32 }, // 16 byte header, 8 byte oops, 256-byte align - { 256, true, true, false, 32 }, // 16 byte header, 4 byte oops, 256-byte align - { 256, true, false, true, 32 }, // 12 byte header, 8 byte oops, 256-byte align - { 256, true, true, true, 32 }, // 12 byte header, 4 byte oops, 256-byte align + { 8, false, false, 3 }, // 16 byte header, 8 byte oops + { 8, true, false, 3 }, // 16 byte header, 4 byte oops + { 8, false, true, 3 }, // 12 byte header, 8 byte oops + { 8, true, true, 2 }, // 12 byte header, 4 byte oops + { 16, false, false, 4 }, // 16 byte header, 8 byte oops, 16-byte align + { 16, true, false, 4 }, // 16 byte header, 4 byte oops, 16-byte align + { 16, false, true, 4 }, // 12 byte header, 8 byte oops, 16-byte align + { 16, true, true, 2 }, // 12 byte header, 4 byte oops, 16-byte align + { 256, false, false, 32 }, // 16 byte header, 8 byte oops, 256-byte align + { 256, true, false, 32 }, // 16 byte header, 4 byte oops, 256-byte align + { 256, false, true, 32 }, // 12 byte header, 8 byte oops, 256-byte align + { 256, true, true, 32 }, // 12 byte header, 4 byte oops, 256-byte align #else - { 8, false, false, false, 4 }, // 12 byte header, 4 byte oops, wordsize 4 + { 8, false, false, 4 }, // 12 byte header, 4 byte oops, wordsize 4 #endif - { -1, false, false, false, -1 } + { -1, false, false, -1 } }; for (int i = 0; x[i].result != -1; i++) { - if (x[i].objal == (int)ObjectAlignmentInBytes && x[i].ccp == UseCompressedClassPointers && x[i].coops == UseCompressedOops && + if (x[i].objal == (int)ObjectAlignmentInBytes && x[i].coops == UseCompressedOops && x[i].coh == UseCompactObjectHeaders) { EXPECT_EQ(objArrayOopDesc::object_size(1), (size_t)x[i].result); } diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestObjectArrayClone.java b/test/hotspot/jtreg/compiler/arraycopy/TestObjectArrayClone.java index b7e5b135c64..595205055ee 100644 --- a/test/hotspot/jtreg/compiler/arraycopy/TestObjectArrayClone.java +++ b/test/hotspot/jtreg/compiler/arraycopy/TestObjectArrayClone.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,10 +34,6 @@ * @run main/othervm -XX:CompileCommand=compileonly,compiler.arraycopy.TestObjectArrayClone::testClone* * -XX:CompileCommand=compileonly,jdk.internal.reflect.GeneratedMethodAccessor*::invoke * compiler.arraycopy.TestObjectArrayClone - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-UseCompressedClassPointers -Xmx128m - * -XX:CompileCommand=compileonly,compiler.arraycopy.TestObjectArrayClone::testClone* - * -XX:CompileCommand=compileonly,jdk.internal.reflect.GeneratedMethodAccessor*::invoke - * compiler.arraycopy.TestObjectArrayClone * @run main/othervm -Xbatch -XX:-UseTypeProfile * -XX:CompileCommand=compileonly,compiler.arraycopy.TestObjectArrayClone::testClone* * -XX:CompileCommand=compileonly,jdk.internal.reflect.GeneratedMethodAccessor*::invoke diff --git a/test/hotspot/jtreg/compiler/c1/TestArrayCopyToFromObject.java b/test/hotspot/jtreg/compiler/c1/TestArrayCopyToFromObject.java index 98c8c24dd3a..176226806a6 100644 --- a/test/hotspot/jtreg/compiler/c1/TestArrayCopyToFromObject.java +++ b/test/hotspot/jtreg/compiler/c1/TestArrayCopyToFromObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,7 @@ * @test * @bug 8160591 * @summary C1-generated code for System.arraycopy() does not throw an ArrayStoreException if 'dst' is no a "proper" array (i.e., it is java.lang.Object) - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xcomp -XX:-UseCompressedClassPointers -XX:CompileOnly=TestArrayCopyToFromObject::test TestArrayCopyToFromObject - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xcomp -XX:+UseCompressedClassPointers -XX:CompileOnly=TestArrayCopyToFromObject::test TestArrayCopyToFromObject + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xcomp -XX:CompileOnly=TestArrayCopyToFromObject::test TestArrayCopyToFromObject */ public class TestArrayCopyToFromObject { diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndLoadKlass.java b/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndLoadKlass.java deleted file mode 100644 index a8997535aae..00000000000 --- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndLoadKlass.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * 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 8330795 - * @summary Check that Reduce Allocation Merges doesn't crash when CompressedClassPointers - * is disabled and there is an access to Klass "field" through the phi. - * @requires vm.bits == 64 & vm.flagless & vm.compiler2.enabled & vm.opt.final.EliminateAllocations - * @run main/othervm -XX:CompileCommand=dontinline,*TestReduceAllocationAndLoadKlass*::test - * -XX:CompileCommand=compileonly,*TestReduceAllocationAndLoadKlass*::test - * -XX:CompileCommand=compileonly,*Shape*::*init* - * -XX:CompileCommand=compileonly,*Point*::*init* - * -XX:CompileCommand=exclude,*TestReduceAllocationAndLoadKlass*::dummy - * -XX:-TieredCompilation - * -XX:-UseCompressedClassPointers - * -Xbatch - * -Xcomp - * compiler.c2.TestReduceAllocationAndLoadKlass - */ - -package compiler.c2; - -public class TestReduceAllocationAndLoadKlass { - public static void main(String[] args) { - Point p = new Point(); - Line q = new Line(); - - test(true); - test(false); - } - - static Class test(boolean cond) { - Object p = cond ? dummy() : new Line(); - return p.getClass(); - } - - static Point dummy() { return new Point(); } - - static class Shape { } - static class Point extends Shape { } - static class Line extends Shape { } -} diff --git a/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java b/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java index 24065bd97b9..2c84ad2676e 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/scalarReplacement/AllocationMergesTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,26 +46,13 @@ public class AllocationMergesTests { "-XX:+TraceReduceAllocationMerges", "-XX:+DeoptimizeALot", "-XX:+UseCompressedOops", - "-XX:+UseCompressedClassPointers", "-XX:CompileCommand=inline,*::charAt*", "-XX:CompileCommand=inline,*PicturePositions::*", "-XX:CompileCommand=inline,*Point::*", "-XX:CompileCommand=inline,*Nested::*", "-XX:CompileCommand=exclude,*::dummy*"); - Scenario scenario1 = new Scenario(1, "-XX:+UnlockDiagnosticVMOptions", - "-XX:+ReduceAllocationMerges", - "-XX:+TraceReduceAllocationMerges", - "-XX:+DeoptimizeALot", - "-XX:+UseCompressedOops", - "-XX:-UseCompressedClassPointers", - "-XX:CompileCommand=inline,*::charAt*", - "-XX:CompileCommand=inline,*PicturePositions::*", - "-XX:CompileCommand=inline,*Point::*", - "-XX:CompileCommand=inline,*Nested::*", - "-XX:CompileCommand=exclude,*::dummy*"); - - Scenario scenario2 = new Scenario(2, "-XX:+UnlockDiagnosticVMOptions", + Scenario scenario1 = new Scenario(2, "-XX:+UnlockDiagnosticVMOptions", "-XX:+ReduceAllocationMerges", "-XX:+TraceReduceAllocationMerges", "-XX:+DeoptimizeALot", @@ -76,12 +63,11 @@ public class AllocationMergesTests { "-XX:CompileCommand=inline,*Nested::*", "-XX:CompileCommand=exclude,*::dummy*"); - Scenario scenario3 = new Scenario(3, "-XX:+UnlockDiagnosticVMOptions", + Scenario scenario2 = new Scenario(3, "-XX:+UnlockDiagnosticVMOptions", "-XX:+ReduceAllocationMerges", "-XX:+TraceReduceAllocationMerges", "-XX:+DeoptimizeALot", "-XX:+UseCompressedOops", - "-XX:+UseCompressedClassPointers", "-XX:-OptimizePtrCompare", "-XX:+VerifyReduceAllocationMerges", "-XX:CompileCommand=inline,*::charAt*", @@ -90,7 +76,7 @@ public class AllocationMergesTests { "-XX:CompileCommand=inline,*Nested::*", "-XX:CompileCommand=exclude,*::dummy*"); - framework.addScenarios(scenario0, scenario1, scenario2, scenario3).start(); + framework.addScenarios(scenario0, scenario1, scenario2).start(); } // ------------------ No Scalar Replacement Should Happen in The Tests Below ------------------- // diff --git a/test/hotspot/jtreg/compiler/jvmci/compilerToVM/GetResolvedJavaTypeTest.java b/test/hotspot/jtreg/compiler/jvmci/compilerToVM/GetResolvedJavaTypeTest.java index e0302e7b5aa..1e2e2d50232 100644 --- a/test/hotspot/jtreg/compiler/jvmci/compilerToVM/GetResolvedJavaTypeTest.java +++ b/test/hotspot/jtreg/compiler/jvmci/compilerToVM/GetResolvedJavaTypeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -159,7 +159,6 @@ public class GetResolvedJavaTypeTest { /* a compressed parameter for tested method is set to false because unsafe.getKlassPointer always returns uncompressed pointer */ private static final boolean COMPRESSED = false; - // = WB.getBooleanVMFlag("UseCompressedClassPointers"); private static long getPtrToKlass() { Field field; diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/DataPatchTest.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/DataPatchTest.java index adbad3e0b4d..b0c7d11edfe 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/DataPatchTest.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/DataPatchTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -119,7 +119,6 @@ public class DataPatchTest extends CodeInstallationTest { @Test public void testInlineNarrowMetadata() { - Assume.assumeTrue(config.useCompressedClassPointers); test(asm -> { ResolvedJavaType type = metaAccess.lookupJavaType(getConstClass()); HotSpotConstant hub = (HotSpotConstant) constantReflection.asObjectHub(type); @@ -144,7 +143,6 @@ public class DataPatchTest extends CodeInstallationTest { @Test public void testNarrowMetadataInDataSection() { - Assume.assumeTrue(config.useCompressedClassPointers); test(asm -> { ResolvedJavaType type = metaAccess.lookupJavaType(getConstClass()); HotSpotConstant hub = (HotSpotConstant) constantReflection.asObjectHub(type); diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/TestHotSpotVMConfig.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/TestHotSpotVMConfig.java index a26872b96ae..4b0065d0e70 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/TestHotSpotVMConfig.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/TestHotSpotVMConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * 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,6 @@ public class TestHotSpotVMConfig extends HotSpotVMConfigAccess { } public final boolean useCompressedOops = getFlag("UseCompressedOops", Boolean.class); - public final boolean useCompressedClassPointers = getFlag("UseCompressedClassPointers", Boolean.class); public final long narrowOopBase = getFieldValue("CompilerToVM::Data::Universe_narrow_oop_base", Long.class, "address"); public final int narrowOopShift = getFieldValue("CompilerToVM::Data::Universe_narrow_oop_shift", Integer.class, "int"); diff --git a/test/hotspot/jtreg/compiler/types/TestCheckCastPPBecomesTOP.java b/test/hotspot/jtreg/compiler/types/TestCheckCastPPBecomesTOP.java index 5c57e479307..38ab9121b47 100644 --- a/test/hotspot/jtreg/compiler/types/TestCheckCastPPBecomesTOP.java +++ b/test/hotspot/jtreg/compiler/types/TestCheckCastPPBecomesTOP.java @@ -30,9 +30,6 @@ * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation * -XX:CompileOnly=TestCheckCastPPBecomesTOP::test1 -XX:LoopMaxUnroll=0 * -XX:CompileCommand=dontinline,TestCheckCastPPBecomesTOP::notInlined -XX:+UseParallelGC TestCheckCastPPBecomesTOP - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation - * -XX:CompileOnly=TestCheckCastPPBecomesTOP::test1 -XX:LoopMaxUnroll=0 - * -XX:CompileCommand=dontinline,TestCheckCastPPBecomesTOP::notInlined -XX:+UseParallelGC -XX:-UseCompressedClassPointers TestCheckCastPPBecomesTOP * */ diff --git a/test/hotspot/jtreg/compiler/unsafe/OpaqueAccesses.java b/test/hotspot/jtreg/compiler/unsafe/OpaqueAccesses.java index 9a31cbbb057..00c3ce5fb7f 100644 --- a/test/hotspot/jtreg/compiler/unsafe/OpaqueAccesses.java +++ b/test/hotspot/jtreg/compiler/unsafe/OpaqueAccesses.java @@ -247,28 +247,14 @@ public class OpaqueAccesses { "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UnlockDiagnosticVMOptions", "-XX:-TieredCompilation", "-Xbatch", - "-XX:+UseCompressedOops", "-XX:+UseCompressedClassPointers", + "-XX:+UseCompressedOops", "-XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test*" ); TestFramework.runWithFlags( "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UnlockDiagnosticVMOptions", "-XX:-TieredCompilation", "-Xbatch", - "-XX:+UseCompressedOops", "-XX:-UseCompressedClassPointers", - "-XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test*" - ); - TestFramework.runWithFlags( - "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", - "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UnlockDiagnosticVMOptions", - "-XX:-TieredCompilation", "-Xbatch", - "-XX:-UseCompressedOops", "-XX:+UseCompressedClassPointers", - "-XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test*" - ); - TestFramework.runWithFlags( - "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", - "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UnlockDiagnosticVMOptions", - "-XX:-TieredCompilation", "-Xbatch", - "-XX:-UseCompressedOops", "-XX:-UseCompressedClassPointers", + "-XX:-UseCompressedOops", "-XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test*" ); } diff --git a/test/hotspot/jtreg/gc/arguments/TestCompressedClassFlags.java b/test/hotspot/jtreg/gc/arguments/TestCompressedClassFlags.java deleted file mode 100644 index b152fc8c936..00000000000 --- a/test/hotspot/jtreg/gc/arguments/TestCompressedClassFlags.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package gc.arguments; - -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.Platform; - -/* - * @test - * @bug 8015107 - * @summary Tests that VM prints a warning when -XX:CompressedClassSpaceSize - * is used together with -XX:-UseCompressedClassPointers - * @library /test/lib - * @library / - * @modules java.base/jdk.internal.misc - * java.management - * @requires vm.opt.CompressedClassSpaceSize == null & vm.opt.UseCompressedClassPointers == null - * @run driver gc.arguments.TestCompressedClassFlags - */ -public class TestCompressedClassFlags { - public static void main(String[] args) throws Exception { - if (Platform.is64bit()) { - OutputAnalyzer output = GCArguments.executeTestJava( - "-XX:CompressedClassSpaceSize=1g", - "-XX:-UseCompressedClassPointers", - "-version"); - output.shouldContain("warning"); - output.shouldNotContain("error"); - output.shouldHaveExitValue(0); - } - } -} diff --git a/test/hotspot/jtreg/gc/g1/TestSharedArchiveWithPreTouch.java b/test/hotspot/jtreg/gc/g1/TestSharedArchiveWithPreTouch.java index 1156a2166c1..bba3c850213 100644 --- a/test/hotspot/jtreg/gc/g1/TestSharedArchiveWithPreTouch.java +++ b/test/hotspot/jtreg/gc/g1/TestSharedArchiveWithPreTouch.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,7 +53,7 @@ public class TestSharedArchiveWithPreTouch { List dump_args = new ArrayList(BaseOptions); if (Platform.is64bit()) { - dump_args.addAll(0, Arrays.asList(new String[] { "-XX:+UseCompressedClassPointers", "-XX:+UseCompressedOops" })); + dump_args.addFirst("-XX:+UseCompressedOops" ); } dump_args.addAll(Arrays.asList(new String[] { "-Xshare:dump", "-Xlog:cds" })); @@ -66,7 +66,7 @@ public class TestSharedArchiveWithPreTouch { List load_args = new ArrayList(BaseOptions); if (Platform.is64bit()) { - load_args.addAll(0, Arrays.asList(new String[] { "-XX:+UseCompressedClassPointers", "-XX:+UseCompressedOops" })); + load_args.addFirst("-XX:+UseCompressedOops" ); } load_args.addAll(Arrays.asList(new String[] { "-Xshare:on", "-version" })); diff --git a/test/hotspot/jtreg/gc/metaspace/TestMetaspaceMemoryPool.java b/test/hotspot/jtreg/gc/metaspace/TestMetaspaceMemoryPool.java index 71cf2003ddd..2f4ce3df8b2 100644 --- a/test/hotspot/jtreg/gc/metaspace/TestMetaspaceMemoryPool.java +++ b/test/hotspot/jtreg/gc/metaspace/TestMetaspaceMemoryPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,10 +37,9 @@ import static jdk.test.lib.Asserts.*; * @library / * @modules java.base/jdk.internal.misc * java.management - * @run main/othervm -XX:-UseCompressedOops gc.metaspace.TestMetaspaceMemoryPool - * @run main/othervm -XX:-UseCompressedOops -XX:MaxMetaspaceSize=60m gc.metaspace.TestMetaspaceMemoryPool - * @run main/othervm -XX:+UseCompressedOops -XX:+UseCompressedClassPointers gc.metaspace.TestMetaspaceMemoryPool - * @run main/othervm -XX:+UseCompressedOops -XX:+UseCompressedClassPointers -XX:CompressedClassSpaceSize=60m gc.metaspace.TestMetaspaceMemoryPool + * @run main/othervm gc.metaspace.TestMetaspaceMemoryPool + * @run main/othervm -XX:MaxMetaspaceSize=60m gc.metaspace.TestMetaspaceMemoryPool + * @run main/othervm -XX:CompressedClassSpaceSize=60m gc.metaspace.TestMetaspaceMemoryPool */ public class TestMetaspaceMemoryPool { @@ -51,10 +50,8 @@ public class TestMetaspaceMemoryPool { verifyMemoryPool(getMemoryPool("Metaspace"), isMetaspaceMaxDefined); if (Platform.is64bit()) { - if (InputArguments.contains("-XX:+UseCompressedOops")) { - MemoryPoolMXBean cksPool = getMemoryPool("Compressed Class Space"); - verifyMemoryPool(cksPool, true); - } + MemoryPoolMXBean cksPool = getMemoryPool("Compressed Class Space"); + verifyMemoryPool(cksPool, true); } } diff --git a/test/hotspot/jtreg/gc/metaspace/TestMetaspacePerfCounters.java b/test/hotspot/jtreg/gc/metaspace/TestMetaspacePerfCounters.java index b0f779109e1..5ead40c9068 100644 --- a/test/hotspot/jtreg/gc/metaspace/TestMetaspacePerfCounters.java +++ b/test/hotspot/jtreg/gc/metaspace/TestMetaspacePerfCounters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,8 +48,7 @@ import gc.testlibrary.PerfCounters; * java.compiler * java.management/sun.management * jdk.internal.jvmstat/sun.jvmstat.monitor - * @run main/othervm -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:+UsePerfData -XX:+UseSerialGC gc.metaspace.TestMetaspacePerfCounters - * @run main/othervm -XX:+UseCompressedOops -XX:+UseCompressedClassPointers -XX:+UsePerfData -XX:+UseSerialGC gc.metaspace.TestMetaspacePerfCounters + * @run main/othervm -XX:+UseCompressedOops -XX:+UsePerfData -XX:+UseSerialGC gc.metaspace.TestMetaspacePerfCounters */ /* @test id=Parallel-64 @@ -63,8 +62,7 @@ import gc.testlibrary.PerfCounters; * java.compiler * java.management/sun.management * jdk.internal.jvmstat/sun.jvmstat.monitor - * @run main/othervm -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:+UsePerfData -XX:+UseParallelGC gc.metaspace.TestMetaspacePerfCounters - * @run main/othervm -XX:+UseCompressedOops -XX:+UseCompressedClassPointers -XX:+UsePerfData -XX:+UseParallelGC gc.metaspace.TestMetaspacePerfCounters + * @run main/othervm -XX:+UseCompressedOops -XX:+UsePerfData -XX:+UseParallelGC gc.metaspace.TestMetaspacePerfCounters */ /* @test id=G1-64 @@ -78,8 +76,7 @@ import gc.testlibrary.PerfCounters; * java.compiler * java.management/sun.management * jdk.internal.jvmstat/sun.jvmstat.monitor - * @run main/othervm -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:+UsePerfData -XX:+UseG1GC gc.metaspace.TestMetaspacePerfCounters - * @run main/othervm -XX:+UseCompressedOops -XX:+UseCompressedClassPointers -XX:+UsePerfData -XX:+UseG1GC gc.metaspace.TestMetaspacePerfCounters + * @run main/othervm -XX:+UseCompressedOops -XX:+UsePerfData -XX:+UseG1GC gc.metaspace.TestMetaspacePerfCounters */ /* @test id=Shenandoah-64 @@ -93,8 +90,7 @@ import gc.testlibrary.PerfCounters; * java.compiler * java.management/sun.management * jdk.internal.jvmstat/sun.jvmstat.monitor - * @run main/othervm -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:+UsePerfData -XX:+UseShenandoahGC gc.metaspace.TestMetaspacePerfCounters - * @run main/othervm -XX:+UseCompressedOops -XX:+UseCompressedClassPointers -XX:+UsePerfData -XX:+UseShenandoahGC gc.metaspace.TestMetaspacePerfCounters + * @run main/othervm -XX:+UseCompressedOops -XX:+UsePerfData -XX:+UseShenandoahGC gc.metaspace.TestMetaspacePerfCounters */ /* @test id=Epsilon-64 @@ -108,8 +104,7 @@ import gc.testlibrary.PerfCounters; * java.compiler * java.management/sun.management * jdk.internal.jvmstat/sun.jvmstat.monitor - * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:+UsePerfData -XX:+UseEpsilonGC gc.metaspace.TestMetaspacePerfCounters - * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseCompressedOops -XX:+UseCompressedClassPointers -XX:+UsePerfData -XX:+UseEpsilonGC gc.metaspace.TestMetaspacePerfCounters + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseCompressedOops -XX:+UsePerfData -XX:+UseEpsilonGC gc.metaspace.TestMetaspacePerfCounters */ /* @test id=Serial-32 @@ -227,14 +222,8 @@ public class TestMetaspacePerfCounters { String ccs = "sun.gc.compressedclassspace"; checkPerfCounters(metaspace); - - if (isUsingCompressedClassPointers()) { - checkPerfCounters(ccs); - checkUsedIncreasesWhenLoadingClass(ccs); - } else { - checkEmptyPerfCounters(ccs); - checkUsedIncreasesWhenLoadingClass(metaspace); - } + checkPerfCounters(ccs); + checkUsedIncreasesWhenLoadingClass(ccs); } private static void checkPerfCounters(String ns) throws Exception { @@ -262,13 +251,6 @@ public class TestMetaspacePerfCounters { assertGTE(snap1.maxCapacity, snap1.capacity); } - private static void checkEmptyPerfCounters(String ns) throws Exception { - for (PerfCounter counter : countersInNamespace(ns)) { - String msg = "Expected " + counter.getName() + " to equal 0"; - assertEQ(counter.longValue(), 0L, msg); - } - } - private static void checkUsedIncreasesWhenLoadingClass(String ns) throws Exception { // Need to ensure that used is up to date and that all unreachable // classes are unloaded before doing this check. @@ -296,7 +278,4 @@ public class TestMetaspacePerfCounters { return ByteCodeLoader.load(name, byteCode); } - private static boolean isUsingCompressedClassPointers() { - return Platform.is64bit() && InputArguments.contains("-XX:+UseCompressedClassPointers"); - } } diff --git a/test/hotspot/jtreg/gc/metaspace/TestPerfCountersAndMemoryPools.java b/test/hotspot/jtreg/gc/metaspace/TestPerfCountersAndMemoryPools.java index fb32c439940..6d5cdf181b9 100644 --- a/test/hotspot/jtreg/gc/metaspace/TestPerfCountersAndMemoryPools.java +++ b/test/hotspot/jtreg/gc/metaspace/TestPerfCountersAndMemoryPools.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,8 +40,7 @@ import gc.testlibrary.PerfCounters; * @modules java.base/jdk.internal.misc * java.management * jdk.internal.jvmstat/sun.jvmstat.monitor - * @run main/othervm -Xlog:class+load,class+unload=trace -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:+UseSerialGC -XX:+UsePerfData -Xint gc.metaspace.TestPerfCountersAndMemoryPools - * @run main/othervm -Xlog:class+load,class+unload=trace -XX:+UseCompressedOops -XX:+UseCompressedClassPointers -XX:+UseSerialGC -XX:+UsePerfData -Xint gc.metaspace.TestPerfCountersAndMemoryPools + * @run main/othervm -Xlog:class+load,class+unload=trace -XX:+UseCompressedOops -XX:+UseSerialGC -XX:+UsePerfData -Xint gc.metaspace.TestPerfCountersAndMemoryPools */ /* @test TestPerfCountersAndMemoryPools @@ -60,7 +59,7 @@ public class TestPerfCountersAndMemoryPools { public static void main(String[] args) throws Exception { checkMemoryUsage("Metaspace", "sun.gc.metaspace"); - if (InputArguments.contains("-XX:+UseCompressedClassPointers") && Platform.is64bit()) { + if (Platform.is64bit()) { checkMemoryUsage("Compressed Class Space", "sun.gc.compressedclassspace"); } } diff --git a/test/hotspot/jtreg/gc/metaspace/TestSizeTransitions.java b/test/hotspot/jtreg/gc/metaspace/TestSizeTransitions.java index 4237b8ce1dd..db698782e8f 100644 --- a/test/hotspot/jtreg/gc/metaspace/TestSizeTransitions.java +++ b/test/hotspot/jtreg/gc/metaspace/TestSizeTransitions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, Twitter, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -35,24 +35,21 @@ import java.util.List; * @requires vm.gc.Serial * @summary Tests that the metaspace size transition logging is done correctly. * @library /test/lib - * @run driver gc.metaspace.TestSizeTransitions false -XX:+UseSerialGC - * @run driver gc.metaspace.TestSizeTransitions true -XX:+UseSerialGC + * @run driver gc.metaspace.TestSizeTransitions -XX:+UseSerialGC */ /* @test TestSizeTransitionsParallel * @requires vm.gc.Parallel * @summary Tests that the metaspace size transition logging is done correctly. * @library /test/lib - * @run driver gc.metaspace.TestSizeTransitions false -XX:+UseParallelGC - * @run driver gc.metaspace.TestSizeTransitions true -XX:+UseParallelGC + * @run driver gc.metaspace.TestSizeTransitions -XX:+UseParallelGC */ /* @test TestSizeTransitionsG1 * @requires vm.gc.G1 * @summary Tests that the metaspace size transition logging is done correctly. * @library /test/lib - * @run driver gc.metaspace.TestSizeTransitions false -XX:+UseG1GC - * @run driver gc.metaspace.TestSizeTransitions true -XX:+UseG1GC + * @run driver gc.metaspace.TestSizeTransitions -XX:+UseG1GC */ public class TestSizeTransitions { @@ -76,13 +73,13 @@ public class TestSizeTransitions { private static final String SIZE_TRANSITION_REGEX = "\\d+K\\(\\d+K\\)->\\d+K\\(\\d+K\\)"; // matches -coops metaspace size transitions - private static final String NO_COMPRESSED_KLASS_POINTERS_REGEX = + private static final String WITHOUT_CLASS_SPACE_REGEX = String.format("^%s.* Metaspace: %s$", LOG_TAGS_REGEX, SIZE_TRANSITION_REGEX); // matches +coops metaspace size transitions - private static final String COMPRESSED_KLASS_POINTERS_REGEX = + private static final String WITH_CLASS_SPACE_REGEX = String.format("^%s.* Metaspace: %s NonClass: %s Class: %s$", LOG_TAGS_REGEX, SIZE_TRANSITION_REGEX, @@ -90,26 +87,11 @@ public class TestSizeTransitions { SIZE_TRANSITION_REGEX); public static void main(String... args) throws Exception { - // args: - if (args.length != 2) { - throw new RuntimeException("wrong number of args: " + args.length); + if (args.length == 0) { + throw new RuntimeException("expected jvm args: " + args.length); } - final boolean hasCompressedKlassPointers = Platform.is64bit(); - final boolean useCompressedKlassPointers = Boolean.parseBoolean(args[0]); - final String gcArg = args[1]; - - if (!hasCompressedKlassPointers && useCompressedKlassPointers) { - // No need to run this configuration. - System.out.println("Skipping test."); - return; - } - - List jvmArgs = new ArrayList<>(); - if (hasCompressedKlassPointers) { - jvmArgs.add(useCompressedKlassPointers ? "-XX:+UseCompressedClassPointers" : "-XX:-UseCompressedClassPointers"); - } - jvmArgs.add(gcArg); + List jvmArgs = new ArrayList<>(List.of(args)); jvmArgs.add("-Xmx256m"); jvmArgs.add("-Xlog:gc,gc+metaspace=info"); jvmArgs.add(TestSizeTransitions.Run.class.getName()); @@ -123,12 +105,14 @@ public class TestSizeTransitions { System.out.println(output.getStdout()); output.shouldHaveExitValue(0); - if (useCompressedKlassPointers) { - output.stdoutShouldMatch(COMPRESSED_KLASS_POINTERS_REGEX); - output.stdoutShouldNotMatch(NO_COMPRESSED_KLASS_POINTERS_REGEX); + // 32-bit uses narrow Pointers but no class space + final boolean hasClassSpace = Platform.is64bit(); + if (hasClassSpace) { + output.stdoutShouldMatch(WITH_CLASS_SPACE_REGEX); + output.stdoutShouldNotMatch(WITHOUT_CLASS_SPACE_REGEX); } else { - output.stdoutShouldMatch(NO_COMPRESSED_KLASS_POINTERS_REGEX); - output.stdoutShouldNotMatch(COMPRESSED_KLASS_POINTERS_REGEX); + output.stdoutShouldMatch(WITHOUT_CLASS_SPACE_REGEX); + output.stdoutShouldNotMatch(WITH_CLASS_SPACE_REGEX); } } } diff --git a/test/hotspot/jtreg/gtest/ArrayTests.java b/test/hotspot/jtreg/gtest/ArrayTests.java index b1afa4795d2..d3a8498d5eb 100644 --- a/test/hotspot/jtreg/gtest/ArrayTests.java +++ b/test/hotspot/jtreg/gtest/ArrayTests.java @@ -26,31 +26,18 @@ * This tests object array sizes by running gtests with different settings. */ -/* @test id=with-coops-with-ccp - * @summary Run object array size tests with compressed oops and compressed class pointers +/* @test id=with-coops + * @summary Run object array size tests with compressed oops * @library /test/lib * @modules java.base/jdk.internal.misc * java.xml - * @run main/native GTestWrapper --gtest_filter=arrayOop -XX:+UseCompressedClassPointers -XX:+UseCompressedOops + * @run main/native GTestWrapper --gtest_filter=arrayOop -XX:+UseCompressedOops */ -/* @test id=with-coops-no-ccp - * @summary Run object array size tests with compressed oops and compressed class pointers + +/* @test id=no-coops + * @summary Run object array size tests with compressed oops * @library /test/lib * @modules java.base/jdk.internal.misc * java.xml - * @run main/native GTestWrapper --gtest_filter=arrayOop -XX:-UseCompressedClassPointers -XX:+UseCompressedOops - */ -/* @test id=no-coops-with-ccp - * @summary Run object array size tests with compressed oops and compressed class pointers - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.xml - * @run main/native GTestWrapper --gtest_filter=arrayOop -XX:+UseCompressedClassPointers -XX:-UseCompressedOops - */ -/* @test id=no-coops-no-ccp - * @summary Run object array size tests with compressed oops and compressed class pointers - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.xml - * @run main/native GTestWrapper --gtest_filter=arrayOop -XX:-UseCompressedClassPointers -XX:-UseCompressedOops + * @run main/native GTestWrapper --gtest_filter=arrayOop -XX:-UseCompressedOops */ diff --git a/test/hotspot/jtreg/gtest/CompressedKlassGtest.java b/test/hotspot/jtreg/gtest/CompressedKlassGtest.java index ba801724558..24f1dff5a30 100644 --- a/test/hotspot/jtreg/gtest/CompressedKlassGtest.java +++ b/test/hotspot/jtreg/gtest/CompressedKlassGtest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2024, Red Hat, Inc. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,14 +39,6 @@ * @run main/native GTestWrapper --gtest_filter=CompressedKlass* -XX:-UseCompactObjectHeaders -Xlog:metaspace* -Xmx6g -Xms128m -Xshare:off -XX:CompressedClassSpaceSize=128m */ -/* @test id=ccp_off - * @library /test/lib - * @requires vm.bits == "64" - * @modules java.base/jdk.internal.misc - * java.xml - * @run main/native GTestWrapper --gtest_filter=CompressedKlass* -XX:-UseCompressedClassPointers -Xlog:metaspace* -Xmx6g -Xms128m - */ - /* @test id=use-zero-based-encoding-coh * @library /test/lib * @requires vm.bits == "64" diff --git a/test/hotspot/jtreg/gtest/MetaspaceGtests.java b/test/hotspot/jtreg/gtest/MetaspaceGtests.java index 38fef145125..26faa2c7c2d 100644 --- a/test/hotspot/jtreg/gtest/MetaspaceGtests.java +++ b/test/hotspot/jtreg/gtest/MetaspaceGtests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -40,15 +40,6 @@ * @run main/native GTestWrapper --gtest_filter=metaspace* -XX:+UnlockDiagnosticVMOptions -XX:VerifyMetaspaceInterval=1 */ -/* @test id=no-ccs - * @summary Run metaspace-related gtests with compressed class pointers off - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.xml - * @requires vm.bits == 64 - * @requires vm.flagless - * @run main/native GTestWrapper --gtest_filter=metaspace* -XX:+UnlockDiagnosticVMOptions -XX:-UseCompressedClassPointers - */ /* @test id=UseCompactObjectHeaders * @summary Run metaspace-related gtests with tiny classpointers diff --git a/test/hotspot/jtreg/gtest/ObjArrayTests.java b/test/hotspot/jtreg/gtest/ObjArrayTests.java index baae1840417..ede85948335 100644 --- a/test/hotspot/jtreg/gtest/ObjArrayTests.java +++ b/test/hotspot/jtreg/gtest/ObjArrayTests.java @@ -26,60 +26,34 @@ * This tests object array sizes by running gtests with different settings. */ -/* @test id=with-coops-with-ccp - * @summary Run object array size tests with compressed oops and compressed class pointers +/* @test id=with-coops + * @summary Run object array size tests with compressed oops * @library /test/lib * @modules java.base/jdk.internal.misc * java.xml - * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:+UseCompressedClassPointers -XX:+UseCompressedOops - */ -/* @test id=with-coops-no-ccp - * @summary Run object array size tests with compressed oops and compressed class pointers - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.xml - * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:-UseCompressedClassPointers -XX:+UseCompressedOops - */ -/* @test id=no-coops-with-ccp - * @summary Run object array size tests with compressed oops and compressed class pointers - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.xml - * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:+UseCompressedClassPointers -XX:-UseCompressedOops - */ -/* @test id=no-coops-no-ccp - * @summary Run object array size tests with compressed oops and compressed class pointers - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.xml - * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:-UseCompressedClassPointers -XX:-UseCompressedOops + * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:+UseCompressedOops */ -/* @test id=with-coops-with-ccp-large-align - * @summary Run object array size tests with compressed oops and compressed class pointers +/* @test id=no-coops + * @summary Run object array size tests with compressed oops * @library /test/lib * @modules java.base/jdk.internal.misc * java.xml - * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:ObjAlignmentInBytes=256 + * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:-UseCompressedOops */ -/* @test id=with-coops-no-ccp-large-align - * @summary Run object array size tests with compressed oops and compressed class pointers + +/* @test id=with-coops-large-align + * @summary Run object array size tests with compressed oops * @library /test/lib * @modules java.base/jdk.internal.misc * java.xml - * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:-UseCompressedClassPointers -XX:+UseCompressedOops -XX:ObjAlignmentInBytes=256 + * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:+UseCompressedOops -XX:ObjAlignmentInBytes=256 */ -/* @test id=no-coops-with-ccp-large-align - * @summary Run object array size tests with compressed oops and compressed class pointers + +/* @test id=no-coops-large-align + * @summary Run object array size tests with compressed oops * @library /test/lib * @modules java.base/jdk.internal.misc * java.xml - * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:+UseCompressedClassPointers -XX:-UseCompressedOops -XX:ObjAlignmentInBytes=256 - */ -/* @test id=no-coops-no-ccp-large-align - * @summary Run object array size tests with compressed oops and compressed class pointers - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.xml - * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:-UseCompressedClassPointers -XX:-UseCompressedOops -XX:ObjAlignmentInBytes=256 + * @run main/native GTestWrapper --gtest_filter=objArrayOop -XX:-UseCompressedOops -XX:ObjAlignmentInBytes=256 */ diff --git a/test/hotspot/jtreg/runtime/CDSCompressedKPtrs/CDSCompressedKPtrs.java b/test/hotspot/jtreg/runtime/CDSCompressedKPtrs/CDSCompressedKPtrs.java deleted file mode 100644 index 51b0469ce46..00000000000 --- a/test/hotspot/jtreg/runtime/CDSCompressedKPtrs/CDSCompressedKPtrs.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * @test - * @requires vm.cds - * @requires vm.bits == 64 - * @requires vm.flagless - * @bug 8003424 - * @summary Testing UseCompressedClassPointers with CDS - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.management - * @run driver CDSCompressedKPtrs - */ - -import jdk.test.lib.Platform; -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; -import jtreg.SkippedException; - -public class CDSCompressedKPtrs { - public static void main(String[] args) throws Exception { - ProcessBuilder pb; - pb = ProcessTools.createLimitedTestJavaProcessBuilder( - "-XX:+UseCompressedClassPointers", "-XX:+UseCompressedOops", - "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./CDSCompressedKPtrs.jsa", "-Xshare:dump", "-Xlog:cds"); - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - try { - output.shouldContain("Loading classes to share"); - output.shouldHaveExitValue(0); - - pb = ProcessTools.createLimitedTestJavaProcessBuilder( - "-XX:+UseCompressedClassPointers", "-XX:+UseCompressedOops", - "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./CDSCompressedKPtrs.jsa", "-Xshare:on", "-version"); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("sharing"); - output.shouldHaveExitValue(0); - - } catch (RuntimeException e) { - output.shouldContain("Unable to use shared archive"); - output.shouldHaveExitValue(1); - throw new SkippedException("CDS was turned off"); - } - } -} diff --git a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointers.java b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointers.java index e4b6f2a0548..fe1f8cb92fc 100644 --- a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointers.java +++ b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,7 +40,6 @@ import jdk.test.lib.Platform; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; -import jtreg.SkippedException; public class CompressedClassPointers { @@ -218,7 +217,6 @@ public class CompressedClassPointers { public static void smallHeapTestNoCoop() throws Exception { ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( "-XX:-UseCompressedOops", - "-XX:+UseCompressedClassPointers", "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedBaseAddress=8g", "-Xmx128m", @@ -236,7 +234,6 @@ public class CompressedClassPointers { public static void smallHeapTestWith1GNoCoop() throws Exception { ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( "-XX:-UseCompressedOops", - "-XX:+UseCompressedClassPointers", "-XX:+UnlockDiagnosticVMOptions", "-XX:CompressedClassSpaceSize=1g", "-Xmx128m", @@ -258,7 +255,6 @@ public class CompressedClassPointers { public static void largeHeapTestNoCoop() throws Exception { ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( "-XX:-UseCompressedOops", - "-XX:+UseCompressedClassPointers", "-XX:+UnlockDiagnosticVMOptions", "-XX:+UnlockExperimentalVMOptions", "-Xmx30g", @@ -280,7 +276,6 @@ public class CompressedClassPointers { public static void largePagesTestNoCoop() throws Exception { ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( "-XX:-UseCompressedOops", - "-XX:+UseCompressedClassPointers", "-XX:+UnlockDiagnosticVMOptions", "-Xmx128m", "-XX:+UseLargePages", @@ -291,23 +286,10 @@ public class CompressedClassPointers { output.shouldHaveExitValue(0); } - public static void heapBaseMinAddressTestNoCoop() throws Exception { - ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( - "-XX:-UseCompressedOops", - "-XX:+UseCompressedClassPointers", - "-XX:HeapBaseMinAddress=1m", - "-Xlog:gc+heap+coops=debug", - "-version"); - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldContain("HeapBaseMinAddress must be at least"); - output.shouldHaveExitValue(0); - } - public static void sharingTestNoCoop() throws Exception { // Test small heaps ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( "-XX:-UseCompressedOops", - "-XX:+UseCompressedClassPointers", "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./CompressedClassPointers.jsa", "-Xmx128m", @@ -325,7 +307,6 @@ public class CompressedClassPointers { pb = ProcessTools.createLimitedTestJavaProcessBuilder( "-XX:-UseCompressedOops", - "-XX:+UseCompressedClassPointers", "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./CompressedClassPointers.jsa", "-Xmx128m", @@ -356,7 +337,6 @@ public class CompressedClassPointers { smallHeapTestWith1GNoCoop(); largeHeapTestNoCoop(); largePagesTestNoCoop(); - heapBaseMinAddressTestNoCoop(); sharingTestNoCoop(); } } diff --git a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassSpaceSize.java b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassSpaceSize.java index 7bbb9edb7b4..0898e6b96eb 100644 --- a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassSpaceSize.java +++ b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassSpaceSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -107,13 +107,6 @@ public class CompressedClassSpaceSize { output = new OutputAnalyzer(pb.start()); output.shouldContain("outside the allowed range") .shouldHaveExitValue(1); - - pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:-UseCompressedClassPointers", - "-XX:CompressedClassSpaceSize=" + minAllowedClassSpaceSize, - "-version"); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Setting CompressedClassSpaceSize has no effect when compressed class pointers are not used") - .shouldHaveExitValue(0); } break; case "valid_small": { diff --git a/test/hotspot/jtreg/runtime/CompressedOops/CompressedKlassPointerAndOops.java b/test/hotspot/jtreg/runtime/CompressedOops/CompressedKlassPointerAndOops.java index 403654d1cc8..32bb432308b 100644 --- a/test/hotspot/jtreg/runtime/CompressedOops/CompressedKlassPointerAndOops.java +++ b/test/hotspot/jtreg/runtime/CompressedOops/CompressedKlassPointerAndOops.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,7 @@ /* * @test - * @bug 8000968 - * @summary NPG: UseCompressedClassPointers asserts with ObjectAlignmentInBytes=32 + * @summary Test Compressed Oops with different object alignments * @library /test/lib * @modules java.base/jdk.internal.misc * java.management @@ -51,7 +50,6 @@ public class CompressedKlassPointerAndOops { OutputAnalyzer output; pb = ProcessTools.createLimitedTestJavaProcessBuilder( - "-XX:+UseCompressedClassPointers", "-XX:+UseCompressedOops", "-XX:ObjectAlignmentInBytes=" + alignment, "-version"); diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/TestVMConfigInHsErrFile.java b/test/hotspot/jtreg/runtime/ErrorHandling/TestVMConfigInHsErrFile.java index 1e168221726..8f41121fbc1 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/TestVMConfigInHsErrFile.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/TestVMConfigInHsErrFile.java @@ -56,7 +56,7 @@ public class TestVMConfigInHsErrFile { public static void main(String[] args) throws Exception { switch (args[0]) { case "coh-on" -> testCompactObjectHeaders(); - case "coh-off" -> testCompressedClassPointers(); + case "coh-off" -> testNotCompactObjectHeaders(); } } @@ -79,20 +79,16 @@ public class TestVMConfigInHsErrFile { Pattern[] expectedPatterns = new Pattern[] { Pattern.compile("# Java VM: .*compact obj headers.*") }; - Pattern[] notExpectedPatterns = new Pattern[] { - Pattern.compile("# Java VM: .*compressed class ptrs.*") - }; - HsErrFileUtils.checkHsErrFileContent(f, expectedPatterns, notExpectedPatterns, true, true); + HsErrFileUtils.checkHsErrFileContent(f, expectedPatterns, null, true, true); } - private static void testCompressedClassPointers() throws Exception { + private static void testNotCompactObjectHeaders() throws Exception { ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( "-XX:+UnlockDiagnosticVMOptions", "-XX:+UnlockExperimentalVMOptions", "-XX:-UseCompactObjectHeaders", - "-XX:+UseCompressedClassPointers", "-Xmx100M", "-XX:-CreateCoredumpOnCrash", "-XX:ErrorHandlerTest=14", @@ -104,14 +100,11 @@ public class TestVMConfigInHsErrFile { // extract hs-err file File f = HsErrFileUtils.openHsErrFileFromOutput(output); - Pattern[] expectedPatterns = new Pattern[] { - Pattern.compile("# Java VM: .*compressed class ptrs.*") - }; Pattern[] notExpectedPatterns = new Pattern[] { Pattern.compile("# Java VM: .*compact obj headers.*") }; - HsErrFileUtils.checkHsErrFileContent(f, expectedPatterns, notExpectedPatterns, true, true); + HsErrFileUtils.checkHsErrFileContent(f, null, notExpectedPatterns, true, true); } } diff --git a/test/hotspot/jtreg/runtime/FieldLayout/BaseOffsets.java b/test/hotspot/jtreg/runtime/FieldLayout/BaseOffsets.java index 73f5a1c6137..00b09fec6ed 100644 --- a/test/hotspot/jtreg/runtime/FieldLayout/BaseOffsets.java +++ b/test/hotspot/jtreg/runtime/FieldLayout/BaseOffsets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * 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,47 +22,29 @@ */ /* - * @test id=with-coops-with-ccp + * @test id=with-coops * @library /test/lib / * @requires vm.bits == "64" * @modules java.base/jdk.internal.misc * java.management * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseCompressedOops -XX:+UseCompressedClassPointers -XX:-UseCompactObjectHeaders BaseOffsets + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseCompressedOops -XX:-UseCompactObjectHeaders BaseOffsets */ + /* - * @test id=no-coops-with-ccp + * @test id=no-coops * @library /test/lib / * @requires vm.bits == "64" * @modules java.base/jdk.internal.misc * java.management * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseCompressedOops -XX:+UseCompressedClassPointers -XX:-UseCompactObjectHeaders BaseOffsets + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseCompressedOops -XX:-UseCompactObjectHeaders BaseOffsets */ + /* - * @test id=with-coops-no-ccp - * @library /test/lib / - * @requires vm.bits == "64" - * @modules java.base/jdk.internal.misc - * java.management - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseCompressedOops -XX:-UseCompressedClassPointers -XX:-UseCompactObjectHeaders BaseOffsets - */ -/* - * @test id=no-coops-no-ccp - * @library /test/lib / - * @requires vm.bits == "64" - * @modules java.base/jdk.internal.misc - * java.management - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:-UseCompactObjectHeaders BaseOffsets - */ -/* - * @test id=with-coop--with-coh + * @test id=with-coop-with-coh * @library /test/lib / * @requires vm.bits == "64" * @modules java.base/jdk.internal.misc @@ -71,6 +53,7 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseCompressedOops -XX:+UseCompactObjectHeaders BaseOffsets */ + /* * @test id=no-coops-with-coh * @library /test/lib / @@ -81,6 +64,7 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseCompressedOops -XX:+UseCompactObjectHeaders BaseOffsets */ + /* * @test id=32bit * @library /test/lib / @@ -117,14 +101,10 @@ public class BaseOffsets { INT_OFFSET = 8; INT_ARRAY_OFFSET = 12; LONG_ARRAY_OFFSET = 16; - } else if (WB.getBooleanVMFlag("UseCompressedClassPointers")) { + } else { INT_OFFSET = 12; INT_ARRAY_OFFSET = 16; LONG_ARRAY_OFFSET = 16; - } else { - INT_OFFSET = 16; - INT_ARRAY_OFFSET = 20; - LONG_ARRAY_OFFSET = 24; } } diff --git a/test/hotspot/jtreg/runtime/FieldLayout/FieldDensityTest.java b/test/hotspot/jtreg/runtime/FieldLayout/FieldDensityTest.java index 5f4fb7a79c4..019b428ae84 100644 --- a/test/hotspot/jtreg/runtime/FieldLayout/FieldDensityTest.java +++ b/test/hotspot/jtreg/runtime/FieldLayout/FieldDensityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,8 +37,7 @@ * @library /test/lib * @modules java.base/jdk.internal.misc * java.management - * @run main/othervm -XX:+UseCompressedOops -XX:+UseCompressedClassPointers FieldDensityTest - * @run main/othervm -XX:+UseCompressedOops -XX:-UseCompressedClassPointers FieldDensityTest + * @run main/othervm -XX:+UseCompressedOops FieldDensityTest */ import java.lang.reflect.Field; diff --git a/test/hotspot/jtreg/runtime/FieldLayout/TestOopMapSizeMinimal.java b/test/hotspot/jtreg/runtime/FieldLayout/TestOopMapSizeMinimal.java index 861f1aa3d1b..263bd750ca3 100644 --- a/test/hotspot/jtreg/runtime/FieldLayout/TestOopMapSizeMinimal.java +++ b/test/hotspot/jtreg/runtime/FieldLayout/TestOopMapSizeMinimal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,47 +30,27 @@ import java.util.ArrayList; import java.util.List; /* - * @test id=no_coops_no_ccptr_no_coh + * @test id=no_coops_no_coh * @library /test/lib * @modules java.base/jdk.internal.misc * java.management * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:-UseCompressedOops -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal */ /* - * @test id=coops_no_ccptr_no_coh + * @test id=coops_no_coh * @library /test/lib * @modules java.base/jdk.internal.misc * java.management * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:+UseCompressedOops -XX:-UseCompressedClassPointers -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:+UseCompressedOops -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal */ /* - * @test id=no_coops_ccptr_no_coh - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.management - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:-UseCompressedOops -XX:+UseCompressedClassPointers -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal - */ - -/* - * @test id=coops_ccptr_no_coh - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.management - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:+UseCompressedOops -XX:+UseCompressedClassPointers -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal - */ - -/* - * @test id=no_coops_ccptr_coh + * @test id=no_coops_coh * @library /test/lib * @modules java.base/jdk.internal.misc * java.management @@ -79,6 +59,16 @@ import java.util.List; * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:-UseCompressedOops -XX:+UseCompactObjectHeaders TestOopMapSizeMinimal */ +/* + * @test id=coops_coh + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:+UseCompressedOops -XX:+UseCompactObjectHeaders TestOopMapSizeMinimal + */ + public class TestOopMapSizeMinimal { public static int OOP_SIZE_IN_BYTES = -1; @@ -99,10 +89,8 @@ public class TestOopMapSizeMinimal { if (is_64_bit) { if (WB.getBooleanVMFlag("UseCompactObjectHeaders")) { HEADER_SIZE_IN_BYTES = 8; - } else if (WB.getBooleanVMFlag("UseCompressedClassPointers")) { - HEADER_SIZE_IN_BYTES = 12; } else { - HEADER_SIZE_IN_BYTES = 16; + HEADER_SIZE_IN_BYTES = 12; } } else { HEADER_SIZE_IN_BYTES = 8; @@ -188,10 +176,9 @@ public class TestOopMapSizeMinimal { // DERIVED4 o4 oopmap entry 2 (reversed order) // i4 - // There are two combinations that have gaps: - // -UseCompressedOops + +COH, and -UseCompressedOops + -UseCompressedClassPointers. - // In both cases there is a gap following i1, and i2 will therefore nestle into that gap. - // Otherwise the same logic applies. + // There is one combination that has gaps: + // -UseCompressedOops + +COH: A gap will be following i1, and i2 will therefore nestle into that gap. + // Otherwise, the same logic applies. if (OOP_SIZE_IN_BYTES == 4 || // oop size == int size (OOP_SIZE_IN_BYTES == 8 && HEADER_SIZE_IN_BYTES == 12) diff --git a/test/hotspot/jtreg/runtime/Metaspace/MaxMetaspaceSizeTest.java b/test/hotspot/jtreg/runtime/Metaspace/MaxMetaspaceSizeTest.java index b2ff8ed9bde..a42a6ee3a52 100644 --- a/test/hotspot/jtreg/runtime/Metaspace/MaxMetaspaceSizeTest.java +++ b/test/hotspot/jtreg/runtime/Metaspace/MaxMetaspaceSizeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,6 @@ public class MaxMetaspaceSizeTest { "-Xshare:off", "-Xmx1g", "-XX:MaxMetaspaceSize=4K", - "-XX:+UseCompressedClassPointers", "-XX:CompressedClassSpaceSize=1g", "--version"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); diff --git a/test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java b/test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java index 7ff19326324..f526405900d 100644 --- a/test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java +++ b/test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, SAP and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -22,62 +22,27 @@ * questions. */ +import jdk.test.lib.Platform; import jdk.test.lib.dcmd.PidJcmdExecutor; import jdk.test.lib.process.OutputAnalyzer; -/* - * @test id=test-64bit-ccs - * @summary Test the VM.metaspace command - * @requires vm.bits == "64" - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.management - * @run main/othervm -Dwith-compressed-class-space -XX:MaxMetaspaceSize=201M -Xmx100M -XX:+UseCompressedOops -XX:+UseCompressedClassPointers PrintMetaspaceDcmd - */ +import java.io.IOException; /* - * @test id=test-64bit-noccs + * @test * @summary Test the VM.metaspace command - * @requires vm.bits == "64" * @library /test/lib * @modules java.base/jdk.internal.misc * java.management - * @run main/othervm -Dwithout-compressed-class-space -XX:MaxMetaspaceSize=201M -Xmx100M -XX:-UseCompressedOops -XX:-UseCompressedClassPointers PrintMetaspaceDcmd - */ - - /* - * @test id=test-nospecified - * @summary Test the VM.metaspace command - * @requires vm.bits == "64" - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.management - * @run main/othervm -Dno-specified-flag -Xmx100M -XX:-UseCompressedOops -XX:-UseCompressedClassPointers PrintMetaspaceDcmd - */ - -/* - * @test test-32bit - * @summary Test the VM.metaspace command - * @requires vm.bits == "32" - * @library /test/lib - * @modules java.base/jdk.internal.misc - * java.management - * @run main/othervm -Dwithout-compressed-class-space -XX:MaxMetaspaceSize=201M -Xmx100M PrintMetaspaceDcmd + * @run main/othervm -Dwith-compressed-class-space -XX:MaxMetaspaceSize=201M -Xmx100M PrintMetaspaceDcmd */ public class PrintMetaspaceDcmd { - private static void doTheNoSpecifiedPropTest() throws Exception { - ProcessBuilder pb = new ProcessBuilder(); - OutputAnalyzer output; + public static void main(String [] args) throws IOException { - pb.command(new PidJcmdExecutor().getCommandLine("VM.metaspace", "basic")); - output = new OutputAnalyzer(pb.start()); - output.shouldHaveExitValue(0); - output.shouldMatch("MaxMetaspaceSize: unlimited"); - } + final boolean usesCompressedClassSpace = Platform.is64bit(); - private static void doTheCCSPropTest(boolean usesCompressedClassSpace) throws Exception { ProcessBuilder pb = new ProcessBuilder(); OutputAnalyzer output; @@ -172,15 +137,4 @@ public class PrintMetaspaceDcmd { output.shouldMatch("MaxMetaspaceSize:.*210763776 bytes"); } - public static void main(String args[]) throws Exception { - if (System.getProperty("no-specified-flag") != null) { - doTheNoSpecifiedPropTest(); - } else if (System.getProperty("with-compressed-class-space") != null) { - doTheCCSPropTest(true); - } else if (System.getProperty("without-compressed-class-space") != null) { - doTheCCSPropTest(false); - } else { - throw new IllegalArgumentException("Unrecognized running mode"); - } - } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java b/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java index 6ab9b18c1e9..da994edeb27 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagCombo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,7 +55,6 @@ public class CommandLineFlagCombo { private static final String[] testTable = { "-XX:+UseG1GC", "-XX:+UseSerialGC", "-XX:+UseParallelGC", "-XX:+UseLargePages", // may only take effect on machines with large-pages - "-XX:+UseCompressedClassPointers", "-XX:+UseCompressedOops", "-XX:ObjectAlignmentInBytes=16", "-XX:ObjectAlignmentInBytes=32", @@ -123,7 +122,6 @@ public class CommandLineFlagCombo { if (Platform.is32bit()) { if (testEntry.equals("-XX:+UseCompressedOops") || - testEntry.equals("-XX:+UseCompressedClassPointers") || testEntry.contains("ObjectAlignmentInBytes") ) { System.out.println("Test case not applicable on 32-bit platforms"); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagComboNegative.java b/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagComboNegative.java index 89fe868349b..d5aafb2bcf6 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagComboNegative.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/CommandLineFlagComboNegative.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,9 +66,9 @@ public class CommandLineFlagComboNegative { "An error has occurred while processing the shared archive file", 1) ); } testTable.add( new TestVector("-XX:+UseCompressedOops", "-XX:-UseCompressedOops", - "The saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime, CDS will be disabled.", 1) ); - testTable.add( new TestVector("-XX:+UseCompressedClassPointers", "-XX:-UseCompressedClassPointers", - "The saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime, CDS will be disabled.", 1) ); + "The saved state of UseCompressedOops (1) is different from runtime (0), CDS will be disabled.", 1) ); + testTable.add( new TestVector("-XX:-UseCompressedOops", "-XX:+UseCompressedOops", + "The saved state of UseCompressedOops (0) is different from runtime (1), CDS will be disabled.", 1) ); } private void runTests() throws Exception diff --git a/test/hotspot/jtreg/runtime/cds/appcds/FillerObjectLoadTest.java b/test/hotspot/jtreg/runtime/cds/appcds/FillerObjectLoadTest.java deleted file mode 100644 index 8538155375c..00000000000 --- a/test/hotspot/jtreg/runtime/cds/appcds/FillerObjectLoadTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2022, Tencent. All rights reserved. - * 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 8286066 - * @summary VM crash caused by unloaded FillerObject_klass - * @library /test/lib - * @requires vm.cds - * @requires vm.flagless - * @run driver FillerObjectLoadTest - */ - -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.process.ProcessTools; - -public class FillerObjectLoadTest { - public static void main(String... args) throws Exception { - ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( - "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UseCompressedClassPointers", - "-XX:+UnlockExperimentalVMOptions", "-XX:+UseEpsilonGC", "-Xshare:dump", - "-XX:SharedArchiveFile=" + TestCommon.getNewArchiveName()); - OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); - analyzer.shouldHaveExitValue(0); - - pb = ProcessTools.createLimitedTestJavaProcessBuilder( - "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UseCompressedClassPointers", - "-XX:TLABSize=2048", "-Xshare:dump", - "-XX:SharedArchiveFile=" + TestCommon.getNewArchiveName()); - analyzer = new OutputAnalyzer(pb.start()); - analyzer.shouldHaveExitValue(0); - } -} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/TestCombinedCompressedFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/TestCombinedCompressedFlags.java index 4f3681d30df..f0700b14b55 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/TestCombinedCompressedFlags.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/TestCombinedCompressedFlags.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,12 +46,10 @@ public class TestCombinedCompressedFlags { static class ConfArg { public boolean useCompressedOops; // UseCompressedOops - public boolean useCompressedClassPointers; // UseCompressedClassPointers public String msg; public int code; - public ConfArg(boolean useCompressedOops, boolean useCompressedClassPointers, String msg, int code) { + public ConfArg(boolean useCompressedOops, String msg, int code) { this.useCompressedOops = useCompressedOops; - this.useCompressedClassPointers = useCompressedClassPointers; this.msg = msg; this.code = code; } @@ -65,67 +63,13 @@ public class TestCombinedCompressedFlags { initExecArgs(); } private void initExecArgs() { - /* The combinations have four cases. - * UseCompressedOops UseCompressedClassPointers Result - * 1. - * dump: on on - * test: on on Pass - * on off Fail - * off on Fail - * off off Fail - * 2. - * dump: on off - * test: on off Pass - * on on Fail - * off on Pass - * off off Fail - * 3. - * dump: off on - * test: off on Pass - * on on Fail - * on off Fail - * 4. - * dump: off off - * test: off off Pass - * on on Fail - * on off Fail - **/ + // We fail this test if the UseCompressedOops setting used at dumptime differs from runtime, + // succeed if it is identical execArgs = new ArrayList(); - if (dumpArg.useCompressedOops && dumpArg.useCompressedClassPointers) { - execArgs - .add(new ConfArg(true, true, HELLO_STRING, PASS)); - execArgs - .add(new ConfArg(true, false, EXEC_ABNORMAL_MSG, FAIL)); - execArgs - .add(new ConfArg(false, true, EXEC_ABNORMAL_MSG, FAIL)); - execArgs - .add(new ConfArg(false, false, EXEC_ABNORMAL_MSG, FAIL)); - - } else if(dumpArg.useCompressedOops && !dumpArg.useCompressedClassPointers) { - execArgs - .add(new ConfArg(true, false, HELLO_STRING, PASS)); - execArgs - .add(new ConfArg(true, true, EXEC_ABNORMAL_MSG, FAIL)); - execArgs - .add(new ConfArg(false, true, EXEC_ABNORMAL_MSG, FAIL)); - execArgs - .add(new ConfArg(false, false, EXEC_ABNORMAL_MSG, FAIL)); - - } else if (!dumpArg.useCompressedOops && dumpArg.useCompressedClassPointers) { - execArgs - .add(new ConfArg(false, true, HELLO_STRING, PASS)); - execArgs - .add(new ConfArg(true, true, EXEC_ABNORMAL_MSG, FAIL)); - execArgs - .add(new ConfArg(true, false, EXEC_ABNORMAL_MSG, FAIL)); - } else if (!dumpArg.useCompressedOops && !dumpArg.useCompressedClassPointers) { - execArgs - .add(new ConfArg(false, false, HELLO_STRING, PASS)); - execArgs - .add(new ConfArg(true, true, EXEC_ABNORMAL_MSG, FAIL)); - execArgs - .add(new ConfArg(true, false, EXEC_ABNORMAL_MSG, FAIL)); - } + execArgs + .add(new ConfArg(dumpArg.useCompressedOops, HELLO_STRING, PASS)); + execArgs + .add(new ConfArg(!dumpArg.useCompressedOops, EXEC_ABNORMAL_MSG, FAIL)); } } @@ -134,23 +78,14 @@ public class TestCombinedCompressedFlags { else return "-XX:-UseCompressedOops"; } - public static String getCompressedClassPointersArg(boolean on) { - if (on) return "-XX:+UseCompressedClassPointers"; - else return "-XX:-UseCompressedClassPointers"; - } - public static List runList; public static void configureRunArgs() { runList = new ArrayList(); runList - .add(new RunArg(new ConfArg(true, true, null, PASS))); + .add(new RunArg(new ConfArg(true, null, PASS))); runList - .add(new RunArg(new ConfArg(true, false, null, PASS))); - runList - .add(new RunArg(new ConfArg(false, true, null, PASS))); - runList - .add(new RunArg(new ConfArg(false, false, null, PASS))); + .add(new RunArg(new ConfArg(false, null, PASS))); } public static void main(String[] args) throws Exception { @@ -162,7 +97,6 @@ public class TestCombinedCompressedFlags { .dump(helloJar, new String[] {"Hello"}, getCompressedOopsArg(t.dumpArg.useCompressedOops), - getCompressedClassPointersArg(t.dumpArg.useCompressedClassPointers), "-Xlog:cds", "-XX:NativeMemoryTracking=detail"); out.shouldContain("Dumping shared data to file:"); @@ -175,7 +109,6 @@ public class TestCombinedCompressedFlags { "-Xlog:cds", "-XX:NativeMemoryTracking=detail", getCompressedOopsArg(c.useCompressedOops), - getCompressedClassPointersArg(c.useCompressedClassPointers), "Hello"); out.shouldContain(c.msg); out.shouldHaveExitValue(c.code); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithCDS.java b/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithCDS.java index eb42bb9f93d..bff2bcc727d 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithCDS.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/TestZGCWithCDS.java @@ -36,7 +36,6 @@ * @comment Driver sets compressed oops/class pointers, jtreg overrides will cause problems. Only run the test if the flags are not set via the command line. * @requires vm.opt.UseCompressedOops == null - * @requires vm.opt.UseCompressedClassPointers == null * @run driver TestZGCWithCDS true */ @@ -55,7 +54,6 @@ * @comment Driver sets compressed oops/class pointers, jtreg overrides will cause problems. Only run the test if the flags are not set via the command line. * @requires vm.opt.UseCompressedOops == null - * @requires vm.opt.UseCompressedClassPointers == null * @run driver TestZGCWithCDS false */ @@ -65,7 +63,7 @@ import jdk.test.lib.process.OutputAnalyzer; public class TestZGCWithCDS { public final static String HELLO = "Hello World"; public final static String UNABLE_TO_USE_ARCHIVE = "Unable to use shared archive."; - public final static String ERR_MSG = "The saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime, CDS will be disabled."; + public final static String ERR_MSG = "The saved state of UseCompressedOops (0) is different from runtime (1), CDS will be disabled."; public static void main(String... args) throws Exception { boolean compactHeadersOn = Boolean.valueOf(args[0]); String compactHeaders = "-XX:" + (compactHeadersOn ? "+" : "-") + "UseCompactObjectHeaders"; @@ -80,7 +78,7 @@ public class TestZGCWithCDS { out.shouldContain("Dumping shared data to file:"); out.shouldHaveExitValue(0); - System.out.println("1. Run with same args of dump"); + System.out.println("Run with same args of dump"); out = TestCommon .exec(helloJar, "-XX:+UseZGC", @@ -90,12 +88,11 @@ public class TestZGCWithCDS { out.shouldContain(HELLO); out.shouldHaveExitValue(0); - System.out.println("2. Run with +UseCompressedOops +UseCompressedClassPointers"); + System.out.println("Run with ZGC, +UseCompressedOops"); out = TestCommon .exec(helloJar, "-XX:-UseZGC", "-XX:+UseCompressedOops", // in case turned off by vmoptions - "-XX:+UseCompressedClassPointers", // by jtreg compactHeaders, "-Xlog:cds", "Hello"); @@ -103,37 +100,22 @@ public class TestZGCWithCDS { out.shouldContain(ERR_MSG); out.shouldHaveExitValue(1); - System.out.println("3. Run with -UseCompressedOops -UseCompressedClassPointers"); + System.out.println("Run with SerialGC, -UseCompressedOops"); out = TestCommon .exec(helloJar, "-XX:+UseSerialGC", "-XX:-UseCompressedOops", - "-XX:-UseCompressedClassPointers", - compactHeaders, - "-Xlog:cds", - "Hello"); - out.shouldContain(UNABLE_TO_USE_ARCHIVE); - out.shouldContain(ERR_MSG); - out.shouldHaveExitValue(1); - - System.out.println("4. Run with -UseCompressedOops +UseCompressedClassPointers"); - out = TestCommon - .exec(helloJar, - "-XX:+UseSerialGC", - "-XX:-UseCompressedOops", - "-XX:+UseCompressedClassPointers", compactHeaders, "-Xlog:cds", "Hello"); out.shouldContain(HELLO); out.shouldHaveExitValue(0); - System.out.println("5. Run with +UseCompressedOops -UseCompressedClassPointers"); + System.out.println("Run with SerialGC, +UseCompressedOops"); out = TestCommon .exec(helloJar, "-XX:+UseSerialGC", "-XX:+UseCompressedOops", - "-XX:-UseCompressedClassPointers", compactHeaders, "-Xlog:cds", "Hello"); @@ -141,39 +123,5 @@ public class TestZGCWithCDS { out.shouldContain(ERR_MSG); out.shouldHaveExitValue(1); - System.out.println("6. Run with +UseCompressedOops +UseCompressedClassPointers"); - out = TestCommon - .exec(helloJar, - "-XX:+UseSerialGC", - "-XX:+UseCompressedOops", - "-XX:+UseCompressedClassPointers", - compactHeaders, - "-Xlog:cds", - "Hello"); - out.shouldContain(UNABLE_TO_USE_ARCHIVE); - out.shouldContain(ERR_MSG); - out.shouldHaveExitValue(1); - - System.out.println("7. Dump with -UseCompressedOops -UseCompressedClassPointers"); - out = TestCommon - .dump(helloJar, - new String[] {"Hello"}, - "-XX:+UseSerialGC", - "-XX:-UseCompressedOops", - "-XX:+UseCompressedClassPointers", - compactHeaders, - "-Xlog:cds"); - out.shouldContain("Dumping shared data to file:"); - out.shouldHaveExitValue(0); - - System.out.println("8. Run with ZGC"); - out = TestCommon - .exec(helloJar, - "-XX:+UseZGC", - compactHeaders, - "-Xlog:cds", - "Hello"); - out.shouldContain(HELLO); - out.shouldHaveExitValue(0); } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java index 16a4d258e4f..b0db450e352 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,11 +53,6 @@ public class OldClassSupport2 { // This will disable AOT class linking. Tester tester2 = new Tester("-Djdk.module.showModuleResolution=true"); tester2.run(new String[] {"AOT", "--two-step-training"} ); - - // Heap archiving is disable with -XX:-UseCompressedClassPointers. - // This will disable AOT class linking. - Tester tester3 = new Tester("-XX:-UseCompressedClassPointers"); - tester3.run(new String[] {"AOT", "--two-step-training"} ); } static class Tester extends CDSAppTester { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/DifferentHeapSizes.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/DifferentHeapSizes.java index aeeac7ee0a4..fbf84a2a42d 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/DifferentHeapSizes.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/DifferentHeapSizes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -84,7 +84,7 @@ public class DifferentHeapSizes { } else { result .assertAbnormalExit("Unable to use shared archive.", - "The saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime, CDS will be disabled."); + "The saved state of UseCompressedOops (1) is different from runtime (0), CDS will be disabled."); } } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/CDSStreamTestDriver.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/CDSStreamTestDriver.java index 88af964d836..8d66ff3336f 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/CDSStreamTestDriver.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/CDSStreamTestDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,7 +69,7 @@ public class CDSStreamTestDriver extends DynamicArchiveTestBase { } catch (SkippedException s) { if (GC.Z.isSelected() && s.toString().equals(skippedException)) { System.out.println("Got " + s.toString() + " as expected."); - System.out.println("Because the test was run with ZGC with UseCompressedOops and UseCompressedClassPointers disabled,"); + System.out.println("Because the test was run with ZGC with UseCompressedOops disabled,"); System.out.println("but the base archive was created with the options enabled"); } else { throw new RuntimeException("Archive mapping should always succeed after JDK-8231610 (did the machine run out of memory?)"); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveTestBase.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveTestBase.java index 108002246db..8ee3b405604 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveTestBase.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -306,9 +306,8 @@ class DynamicArchiveTestBase { * the JDK was built via cross-compilation on a different platform; * - the VM under test was started with a different options than the ones * when the default CDS archive was built. E.g. the VM was started with - * -XX:+UseZGC which implicitly disabled the UseCompressedOoops and the - * UseCompressedClassPointers options. Those "compressed" options were - * enabled when the default CDS archive was built. + * -XX:+UseZGC which implicitly disables the UseCompressedOops option. + * UseCompressedOops was enabled when the default CDS archive was built. */ public static boolean isUseSharedSpacesDisabled() { return !WB.isSharingEnabled(); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java index ddbfe2ed862..f2ec33cdefe 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/IncompatibleOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -83,7 +83,9 @@ public class IncompatibleOptions { static final String COMPACT_STRING_MISMATCH = "The shared archive file's CompactStrings setting .* does not equal the current CompactStrings setting"; static final String COMPRESSED_OOPS_NOT_CONSISTENT = - "The saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime, CDS will be disabled."; + "The saved state of UseCompressedOops \\(%d\\) is different from runtime \\(%d\\), CDS will be disabled."; + static final String COMPRESSED_OOPS_NOT_CONSISTENT_10 = COMPRESSED_OOPS_NOT_CONSISTENT.formatted(1, 0); + static final String COMPRESSED_OOPS_NOT_CONSISTENT_01 = COMPRESSED_OOPS_NOT_CONSISTENT.formatted(0, 1); static String appJar; static String[] vmOptionsPrefix = {}; @@ -117,7 +119,7 @@ public class IncompatibleOptions { // Explicitly archive with compressed oops, run without. testDump(3, "-XX:+UseG1GC", "-XX:+UseCompressedOops", null, false); - testExec(3, "-XX:+UseG1GC", "-XX:-UseCompressedOops", COMPRESSED_OOPS_NOT_CONSISTENT, true); + testExec(3, "-XX:+UseG1GC", "-XX:-UseCompressedOops", COMPRESSED_OOPS_NOT_CONSISTENT_10, true); // NOTE: No warning is displayed, by design // Still run, to ensure no crash or exception @@ -130,14 +132,14 @@ public class IncompatibleOptions { testExec(4, "-XX:+UseSerialGC", "", "", false); if (GC.Z.isSupported()) { - testExec(4, "-XX:+UseZGC", "", COMPRESSED_OOPS_NOT_CONSISTENT, true); + testExec(4, "-XX:+UseZGC", "", COMPRESSED_OOPS_NOT_CONSISTENT_10, true); } // Explicitly archive with object streaming and COOPs with one GC, run with other GCs. testDump(4, "-XX:-UseCompressedOops", "-XX:+AOTStreamableObjects", null, false); - testExec(4, "-XX:+UseG1GC", "", COMPRESSED_OOPS_NOT_CONSISTENT, true); - testExec(4, "-XX:+UseParallelGC", "", COMPRESSED_OOPS_NOT_CONSISTENT, true); - testExec(4, "-XX:+UseSerialGC", "", COMPRESSED_OOPS_NOT_CONSISTENT, true); + testExec(4, "-XX:+UseG1GC", "", COMPRESSED_OOPS_NOT_CONSISTENT_01, true); + testExec(4, "-XX:+UseParallelGC", "", COMPRESSED_OOPS_NOT_CONSISTENT_01, true); + testExec(4, "-XX:+UseSerialGC", "", COMPRESSED_OOPS_NOT_CONSISTENT_01, true); testExec(4, "-XX:+UseParallelGC", "-XX:-UseCompressedOops", "", false); testExec(4, "-XX:+UseSerialGC", "-XX:-UseCompressedOops", "", false); diff --git a/test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderStatsTest.java b/test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderStatsTest.java index a89f2508852..aaf54e03f9e 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderStatsTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderStatsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,17 +32,6 @@ * @run testng/othervm --add-exports=java.base/jdk.internal.misc=ALL-UNNAMED --add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED ClassLoaderStatsTest */ -/* - * @test - * @summary Test of diagnostic command VM.classloader_stats (-UseCCP) - * @library /test/lib - * @requires vm.bits != "32" - * @modules java.base/jdk.internal.misc - * java.compiler - * java.management - * jdk.internal.jvmstat/sun.jvmstat.monitor - * @run testng/othervm -XX:-UseCompressedClassPointers --add-exports=java.base/jdk.internal.misc=ALL-UNNAMED --add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED ClassLoaderStatsTest - */ import org.testng.annotations.Test; import org.testng.Assert; @@ -110,7 +99,7 @@ public class ClassLoaderStatsTest { // Minimum expected sizes: initial capacity is governed by the chunk size of the first chunk, which // depends on the arena growth policy. Since this is a normal class loader, we expect as initial chunk - // size at least 4k (if UseCompressedClassPointers is off). + // size at least 4k. // Minimum used size is difficult to guess but should be at least 1k. // Maximum expected sizes: We just assume a reasonable maximum. We only loaded one class, so // we should not see values > 64k. diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java index fc8a92cf9a2..e4111238b59 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java @@ -165,25 +165,6 @@ public class TestIRMatching { BadFailOnConstraint.create(RunTests.class, "bad1", 2, "Load") ); - runCheck(new String[] {"-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UseCompressedClassPointers"}, - BadFailOnConstraint.create(Loads.class, "load", 1, 1, "Load"), - BadFailOnConstraint.create(Loads.class, "load", 1, 3, "LoadI"), - BadCountsConstraint.create(Loads.class, "load", 1, 1, 0), - BadCountsConstraint.create(Loads.class, "load", 1, 2, 1,"Load"), - GoodRuleConstraint.create(Loads.class, "load", 2), - GoodFailOnConstraint.create(Loads.class, "load", 3), - BadCountsConstraint.create(Loads.class, "load", 3, 2, 2,"Store"), - BadFailOnConstraint.create(Loads.class, "load", 4, 2, "Store"), - BadFailOnConstraint.create(Loads.class, "load", 5, "Load"), - BadFailOnConstraint.create(Loads.class, "load", 6, "Load"), - BadFailOnConstraint.create(Loads.class, "load", 7, "Load"), - GoodRuleConstraint.create(Loads.class, "load", 8), - GoodRuleConstraint.create(Loads.class, "load", 9), - GoodRuleConstraint.create(Loads.class, "load", 10), - BadFailOnConstraint.create(Loads.class, "loadKlass", 1), - BadCountsConstraint.create(Loads.class, "loadKlass", 2, 2,"Field") - ); - // Loops runCheck(BadFailOnConstraint.create(Loops.class, "loop", 1, "Loop"), GoodRuleConstraint.create(Loops.class, "loop", 2), diff --git a/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java index 44276a9f7f5..cdaa94e8289 100644 --- a/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java +++ b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java @@ -314,8 +314,9 @@ public class GetObjectSizeIntrinsicsTest extends ASimpleInstrumentationTestCase static final int LARGE_INT_ARRAY_SIZE = 1024*1024*1024 + 1024; static final int LARGE_OBJ_ARRAY_SIZE = (4096/(int)REF_SIZE)*1024*1024 + 1024; - static final boolean CCP = WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompressedClassPointers"); - static final int ARRAY_HEADER_SIZE = CCP ? 16 : (Platform.is64bit() ? 20 : 12); + // 64-bit: 8mw-4ccp-4len + // 32-bit: 4mw-4ccp-4len-4gap + static final int ARRAY_HEADER_SIZE = 16; final String mode; diff --git a/test/jdk/jdk/jfr/event/allocation/TestObjectAllocationInNewTLABEvent.java b/test/jdk/jdk/jfr/event/allocation/TestObjectAllocationInNewTLABEvent.java index 9112cfbc247..9cc3141cd2d 100644 --- a/test/jdk/jdk/jfr/event/allocation/TestObjectAllocationInNewTLABEvent.java +++ b/test/jdk/jdk/jfr/event/allocation/TestObjectAllocationInNewTLABEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -64,9 +64,10 @@ import jdk.test.whitebox.WhiteBox; public class TestObjectAllocationInNewTLABEvent { private final static String EVENT_NAME = EventNames.ObjectAllocationInNewTLAB; - private static final Boolean COMPRESSED_CLASS_PTRS = WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompressedClassPointers"); - - private static final int BYTE_ARRAY_OVERHEAD = (Platform.is64bit() && !COMPRESSED_CLASS_PTRS) ? 24 : 16; + // 64-bit COH: MW8 + L4 + End Alignment = 16 + // 64-bit -COH: MW8 + K4 + L4 = 16 + // 32-bit : MW4 + K4 + L4 + End Alignment = 16 + private static final int BYTE_ARRAY_OVERHEAD = 16; private static final int OBJECT_SIZE = 128 * 1024; private static final int OBJECTS_TO_ALLOCATE = 100; diff --git a/test/jdk/jdk/jfr/event/allocation/TestObjectAllocationOutsideTLABEvent.java b/test/jdk/jdk/jfr/event/allocation/TestObjectAllocationOutsideTLABEvent.java index a7695b76849..9a4f0f28974 100644 --- a/test/jdk/jdk/jfr/event/allocation/TestObjectAllocationOutsideTLABEvent.java +++ b/test/jdk/jdk/jfr/event/allocation/TestObjectAllocationOutsideTLABEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -64,9 +64,10 @@ import jdk.test.whitebox.WhiteBox; public class TestObjectAllocationOutsideTLABEvent { private static final String EVENT_NAME = EventNames.ObjectAllocationOutsideTLAB; - private static final Boolean COMPRESSED_CLASS_PTRS = WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompressedClassPointers"); - - private static final int BYTE_ARRAY_OVERHEAD = (Platform.is64bit() && !COMPRESSED_CLASS_PTRS) ? 24 : 16; + // 64-bit COH: MW8 + L4 + End Alignment = 16 + // 64-bit -COH: MW8 + K4 + L4 = 16 + // 32-bit : MW4 + K4 + L4 + End Alignment = 16 + private static final int BYTE_ARRAY_OVERHEAD = 16; private static final int OBJECT_SIZE = 128 * 1024; private static final int OBJECTS_TO_ALLOCATE = 100; diff --git a/test/jdk/jdk/jfr/event/allocation/TestObjectAllocationSampleEventThrottling.java b/test/jdk/jdk/jfr/event/allocation/TestObjectAllocationSampleEventThrottling.java index 47f5b31f881..214f7336a73 100644 --- a/test/jdk/jdk/jfr/event/allocation/TestObjectAllocationSampleEventThrottling.java +++ b/test/jdk/jdk/jfr/event/allocation/TestObjectAllocationSampleEventThrottling.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * 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,9 +52,10 @@ import jdk.test.whitebox.WhiteBox; public class TestObjectAllocationSampleEventThrottling { private static final String EVENT_NAME = EventNames.ObjectAllocationSample; - private static final Boolean COMPRESSED_CLASS_PTRS = WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompressedClassPointers"); - - private static final int BYTE_ARRAY_OVERHEAD = (Platform.is64bit() && !COMPRESSED_CLASS_PTRS) ? 24 : 16; + // 64-bit COH: MW8 + L4 + End Alignment = 16 + // 64-bit -COH: MW8 + K4 + L4 = 16 + // 32-bit : MW4 + K4 + L4 + End Alignment = 16 + private static final int BYTE_ARRAY_OVERHEAD = 16; private static final int OBJECT_SIZE = 128 * 1024; private static final int OBJECTS_TO_ALLOCATE = 100; diff --git a/test/jdk/jdk/jfr/event/gc/heapsummary/TestHeapSummaryEventDefNewSerial.java b/test/jdk/jdk/jfr/event/gc/heapsummary/TestHeapSummaryEventDefNewSerial.java index f70d7a85f16..b880bd93bc5 100644 --- a/test/jdk/jdk/jfr/event/gc/heapsummary/TestHeapSummaryEventDefNewSerial.java +++ b/test/jdk/jdk/jfr/event/gc/heapsummary/TestHeapSummaryEventDefNewSerial.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,16 +33,6 @@ import jdk.test.lib.jfr.GCHelper; * @run main/othervm -XX:+UseSerialGC jdk.jfr.event.gc.heapsummary.TestHeapSummaryEventDefNewSerial */ -/** - * @test - * @bug 8264008 - * @requires vm.flagless - * @requires vm.hasJFR & vm.bits == 64 - * @requires vm.gc == "Serial" | vm.gc == null - * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseSerialGC -XX:-UseCompressedClassPointers - * jdk.jfr.event.gc.heapsummary.TestHeapSummaryEventDefNewSerial - */ public class TestHeapSummaryEventDefNewSerial { public static void main(String[] args) throws Exception { HeapSummaryEventAllGcs.test(GCHelper.gcDefNew, GCHelper.gcSerialOld); diff --git a/test/jdk/jdk/jfr/event/gc/objectcount/ObjectCountEventVerifier.java b/test/jdk/jdk/jfr/event/gc/objectcount/ObjectCountEventVerifier.java index 0ab663f130a..d7e6b3209d3 100644 --- a/test/jdk/jdk/jfr/event/gc/objectcount/ObjectCountEventVerifier.java +++ b/test/jdk/jdk/jfr/event/gc/objectcount/ObjectCountEventVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -70,7 +70,7 @@ public class ObjectCountEventVerifier { private static long expectedFooArraySize(long count) { boolean runsOn32Bit = System.getProperty("sun.arch.data.model").equals("32"); int bytesPerWord = runsOn32Bit ? 4 : 8; - int objectHeaderSize = bytesPerWord * 3; // length will be aligned on 64 bits + int objectHeaderSize = runsOn32Bit ? 12 : 16; int alignmentInOopArray = runsOn32Bit ? 4 : 0; int ptrSize = bytesPerWord; return objectHeaderSize + alignmentInOopArray + count * ptrSize; diff --git a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithG1ConcurrentMark.java b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithG1ConcurrentMark.java index eb69289aa5e..fbf2d8b543d 100644 --- a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithG1ConcurrentMark.java +++ b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithG1ConcurrentMark.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,7 @@ import jdk.test.lib.jfr.GCHelper; * @requires (vm.gc == "G1" | vm.gc == null) * & vm.opt.ExplicitGCInvokesConcurrent != false * @library /test/lib /test/jdk - * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:+ExplicitGCInvokesConcurrent -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithG1ConcurrentMark + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:+ExplicitGCInvokesConcurrent -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithG1ConcurrentMark */ public class TestObjectCountAfterGCEventWithG1ConcurrentMark { public static void main(String[] args) throws Exception { diff --git a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithG1FullCollection.java b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithG1FullCollection.java index 6e91cf4c644..758ab6988b5 100644 --- a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithG1FullCollection.java +++ b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithG1FullCollection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,7 @@ import jdk.test.lib.jfr.GCHelper; * @requires (vm.gc == "G1" | vm.gc == null) * & vm.opt.ExplicitGCInvokesConcurrent != true * @library /test/lib /test/jdk - * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseG1GC -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithG1FullCollection + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseG1GC -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithG1FullCollection */ public class TestObjectCountAfterGCEventWithG1FullCollection { public static void main(String[] args) throws Exception { diff --git a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithParallelOld.java b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithParallelOld.java index 2a17e540be8..ac43d0d24e4 100644 --- a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithParallelOld.java +++ b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithParallelOld.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ import jdk.test.lib.jfr.GCHelper; * @requires vm.hasJFR * @requires vm.gc == "Parallel" | vm.gc == null * @library /test/lib /test/jdk - * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithParallelOld + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithParallelOld */ public class TestObjectCountAfterGCEventWithParallelOld { public static void main(String[] args) throws Exception { diff --git a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithSerial.java b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithSerial.java index 888eb089027..eac0e935fea 100644 --- a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithSerial.java +++ b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithSerial.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ import jdk.test.lib.jfr.GCHelper; * @requires vm.hasJFR * @requires vm.gc == "Serial" | vm.gc == null * @library /test/lib /test/jdk - * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseSerialGC -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithSerial + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseSerialGC -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithSerial */ public class TestObjectCountAfterGCEventWithSerial { public static void main(String[] args) throws Exception { diff --git a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountEvent.java b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountEvent.java index a9034ba0eaa..0a5d48e83c8 100644 --- a/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountEvent.java +++ b/test/jdk/jdk/jfr/event/gc/objectcount/TestObjectCountEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,7 @@ import jdk.test.lib.jfr.Events; * @requires vm.hasJFR * @requires vm.gc == "Serial" | vm.gc == null * @library /test/lib /test/jdk - * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseSerialGC -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:MarkSweepDeadRatio=0 -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountEvent + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseSerialGC -XX:-UseCompressedOops -XX:MarkSweepDeadRatio=0 -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountEvent */ public class TestObjectCountEvent { private static final String objectCountEventPath = EventNames.ObjectCount; diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 2d604a92b75..16fd32e626b 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -474,8 +474,7 @@ public class VMProps implements Callable> { /** * @return true if it's possible for "java -Xshare:dump" to write Java heap objects - * with the current set of jtreg VM options. For example, false will be returned - * if -XX:-UseCompressedClassPointers is specified. + * with the current set of jtreg VM options. */ protected String vmCDSCanWriteArchivedJavaHeap() { return "" + ("true".equals(vmCDS()) && WB.canWriteJavaHeapArchive()); @@ -483,8 +482,7 @@ public class VMProps implements Callable> { /** * @return true if it's possible for "java -Xshare:dump" to write Java heap objects - * with the current set of jtreg VM options. For example, false will be returned - * if -XX:-UseCompressedClassPointers is specified. + * with the current set of jtreg VM options. */ protected String vmCDSCanWriteMappedArchivedJavaHeap() { return "" + ("true".equals(vmCDS()) && WB.canWriteMappedJavaHeapArchive()); @@ -492,8 +490,7 @@ public class VMProps implements Callable> { /** * @return true if it's possible for "java -Xshare:dump" to write Java heap objects - * with the current set of jtreg VM options. For example, false will be returned - * if -XX:-UseCompressedClassPointers is specified. + * with the current set of jtreg VM options. */ protected String vmCDSCanWriteStreamedArchivedJavaHeap() { return "" + ("true".equals(vmCDS()) && WB.canWriteStreamedJavaHeapArchive()); From e07ab7fb9b5e49093f84b05fb6cecfa00f1b0f52 Mon Sep 17 00:00:00 2001 From: Arno Zeller Date: Thu, 26 Mar 2026 11:17:18 +0000 Subject: [PATCH 130/160] 8380288: [BACKOUT] Incorrect Interpretation of POSIX TZ Environment Variable on AIX Reviewed-by: mbaesken, mdoerr --- .../unix/native/libjava/TimeZone_md.c | 66 +++++++------------ 1 file changed, 22 insertions(+), 44 deletions(-) diff --git a/src/java.base/unix/native/libjava/TimeZone_md.c b/src/java.base/unix/native/libjava/TimeZone_md.c index 39e7b726220..cd253edde60 100644 --- a/src/java.base/unix/native/libjava/TimeZone_md.c +++ b/src/java.base/unix/native/libjava/TimeZone_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -352,15 +352,33 @@ getPlatformTimeZoneID() } static char * -getJavaTimezoneFromPlatform(const char *tz_buf, size_t tz_len, const char *mapfilename) { +mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) { FILE *tzmapf; + char mapfilename[PATH_MAX + 1]; char line[256]; int linecount = 0; + char *tz_buf = NULL; + char *temp_tz = NULL; char *javatz = NULL; + size_t tz_len = 0; + /* On AIX, the TZ environment variable may end with a comma + * followed by modifier fields until early AIX6.1. + * This restriction has been removed from AIX7. */ + + tz_buf = strdup(tz); + tz_len = strlen(tz_buf); + + /* Open tzmappings file, with buffer overrun check */ + if ((strlen(java_home_dir) + 15) > PATH_MAX) { + jio_fprintf(stderr, "Path %s/lib/tzmappings exceeds maximum path length\n", java_home_dir); + goto tzerr; + } + strcpy(mapfilename, java_home_dir); + strcat(mapfilename, "/lib/tzmappings"); if ((tzmapf = fopen(mapfilename, "r")) == NULL) { jio_fprintf(stderr, "can't open %s\n", mapfilename); - return NULL; + goto tzerr; } while (fgets(line, sizeof(line), tzmapf) != NULL) { @@ -413,50 +431,10 @@ getJavaTimezoneFromPlatform(const char *tz_buf, size_t tz_len, const char *mapfi break; } } - (void) fclose(tzmapf); - return javatz; -} - -static char * -mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) { - char mapfilename[PATH_MAX + 1]; - char *tz_buf = NULL; - char *javatz = NULL; - char *temp_tz = NULL; - size_t tz_len = 0; - - /* On AIX, the TZ environment variable may end with a comma - * followed by modifier fields until early AIX6.1. - * This restriction has been removed from AIX7. */ - - tz_buf = strdup(tz); - tz_len = strlen(tz_buf); - - /* Open tzmappings file, with buffer overrun check */ - if ((strlen(java_home_dir) + 15) > PATH_MAX) { - jio_fprintf(stderr, "Path %s/lib/tzmappings exceeds maximum path length\n", java_home_dir); - goto tzerr; - } - strcpy(mapfilename, java_home_dir); - strcat(mapfilename, "/lib/tzmappings"); - - // First attempt to find the Java timezone for the full tz string - javatz = getJavaTimezoneFromPlatform(tz_buf, tz_len, mapfilename); - - // If no match was found, check for timezone with truncated value - if (javatz == NULL) { - temp_tz = strchr(tz, ','); - tz_len = (temp_tz == NULL) ? strlen(tz) : temp_tz - tz; - free((void *) tz_buf); - tz_buf = (char *)malloc(tz_len + 1); - memcpy(tz_buf, tz, tz_len); - tz_buf[tz_len] = '\0'; - javatz = getJavaTimezoneFromPlatform(tz_buf, tz_len, mapfilename); - } tzerr: - if (tz_buf != NULL) { + if (tz_buf != NULL ) { free((void *) tz_buf); } From 407b677d5fe20f1958f530207e62234d510daca4 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Thu, 26 Mar 2026 11:41:10 +0000 Subject: [PATCH 131/160] 8380942: Fix whitespace and conditional logic in interpreter code Reviewed-by: dholmes, aartemov --- src/hotspot/share/interpreter/interpreterRuntime.cpp | 8 +++----- src/hotspot/share/interpreter/linkResolver.cpp | 10 +++++----- src/hotspot/share/interpreter/oopMapCache.cpp | 10 +++++----- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp index 59d5f29023c..e7b35b121a2 100644 --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp @@ -243,9 +243,9 @@ JRT_ENTRY(void, InterpreterRuntime::multianewarray(JavaThread* current, jint* fi // We may want to pass in more arguments - could make this slightly faster LastFrameAccessor last_frame(current); ConstantPool* constants = last_frame.method()->constants(); - int i = last_frame.get_index_u2(Bytecodes::_multianewarray); - Klass* klass = constants->klass_at(i, CHECK); - int nof_dims = last_frame.number_of_dimensions(); + int i = last_frame.get_index_u2(Bytecodes::_multianewarray); + Klass* klass = constants->klass_at(i, CHECK); + int nof_dims = last_frame.number_of_dimensions(); assert(klass->is_klass(), "not a class"); assert(nof_dims >= 1, "multianewarray rank must be nonzero"); @@ -756,12 +756,10 @@ JRT_LEAF(void, InterpreterRuntime::monitorexit(BasicObjectLock* elem)) elem->set_obj(nullptr); JRT_END - JRT_ENTRY(void, InterpreterRuntime::throw_illegal_monitor_state_exception(JavaThread* current)) THROW(vmSymbols::java_lang_IllegalMonitorStateException()); JRT_END - JRT_ENTRY(void, InterpreterRuntime::new_illegal_monitor_state_exception(JavaThread* current)) // Returns an illegal exception to install into the current thread. The // pending_exception flag is cleared so normal exception handling does not diff --git a/src/hotspot/share/interpreter/linkResolver.cpp b/src/hotspot/share/interpreter/linkResolver.cpp index c82398b654c..25fff580c9d 100644 --- a/src/hotspot/share/interpreter/linkResolver.cpp +++ b/src/hotspot/share/interpreter/linkResolver.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1045,7 +1045,7 @@ void LinkResolver::resolve_field(fieldDescriptor& fd, stringStream ss; ss.print("Update to %s final field %s.%s attempted from a different class (%s) than the field's declaring class", is_static ? "static" : "non-static", resolved_klass->external_name(), fd.name()->as_C_string(), - current_klass->external_name()); + current_klass->external_name()); THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), ss.as_string()); } @@ -1260,7 +1260,7 @@ void LinkResolver::runtime_resolve_special_method(CallInfo& result, methodHandle sel_method(THREAD, resolved_method()); if (link_info.check_access() && - // check if the method is not + // check if the method is not , which is never inherited resolved_method->name() != vmSymbols::object_initializer_name()) { Klass* current_klass = link_info.current_klass(); @@ -1724,8 +1724,8 @@ void LinkResolver::resolve_invoke(CallInfo& result, Handle recv, const constantP } void LinkResolver::resolve_invoke(CallInfo& result, Handle& recv, - const methodHandle& attached_method, - Bytecodes::Code byte, TRAPS) { + const methodHandle& attached_method, + Bytecodes::Code byte, TRAPS) { Klass* defc = attached_method->method_holder(); Symbol* name = attached_method->name(); Symbol* type = attached_method->signature(); diff --git a/src/hotspot/share/interpreter/oopMapCache.cpp b/src/hotspot/share/interpreter/oopMapCache.cpp index d7c02296f33..5a1ad7b7883 100644 --- a/src/hotspot/share/interpreter/oopMapCache.cpp +++ b/src/hotspot/share/interpreter/oopMapCache.cpp @@ -256,8 +256,8 @@ bool OopMapCacheEntry::verify_mask(CellTypeState* vars, CellTypeState* stack, in if (log) st.print("Locals (%d): ", max_locals); for(int i = 0; i < max_locals; i++) { - bool v1 = is_oop(i) ? true : false; - bool v2 = vars[i].is_reference() ? true : false; + bool v1 = is_oop(i); + bool v2 = vars[i].is_reference(); assert(v1 == v2, "locals oop mask generation error"); if (log) st.print("%d", v1 ? 1 : 0); } @@ -265,8 +265,8 @@ bool OopMapCacheEntry::verify_mask(CellTypeState* vars, CellTypeState* stack, in if (log) st.print("Stack (%d): ", stack_top); for(int j = 0; j < stack_top; j++) { - bool v1 = is_oop(max_locals + j) ? true : false; - bool v2 = stack[j].is_reference() ? true : false; + bool v1 = is_oop(max_locals + j); + bool v2 = stack[j].is_reference(); assert(v1 == v2, "stack oop mask generation error"); if (log) st.print("%d", v1 ? 1 : 0); } @@ -350,7 +350,7 @@ void OopMapCacheEntry::set_mask(CellTypeState *vars, CellTypeState *stack, int s } // set oop bit - if ( cell->is_reference()) { + if (cell->is_reference()) { value |= (mask << oop_bit_number ); _num_oops++; } From 60ce4857f406a3fe68be008ab1ca206b032412dc Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Thu, 26 Mar 2026 11:48:34 +0000 Subject: [PATCH 132/160] 8380931: Refactor remaining java/nio/channels TestNG tests to use JUnit Reviewed-by: alanb --- .../SocketChannel/AdaptorStreams.java | 81 +++-- .../SocketChannel/ConnectionReset.java | 22 +- .../SocketChannel/ReadWriteAfterClose.java | 54 +-- .../channels/etc/LocalSocketAddressType.java | 63 ++-- .../java/nio/channels/etc/OpenAndConnect.java | 199 ++++++----- .../nio/channels/etc/ProtocolFamilies.java | 239 ++++++------- .../unixdomain/EmptySunPathForSocketFile.java | 16 +- .../channels/unixdomain/FileAttributes.java | 27 +- .../nio/channels/unixdomain/IOExchanges.java | 328 ++++++++++-------- .../nio/channels/unixdomain/NullTest.java | 13 +- 10 files changed, 561 insertions(+), 481 deletions(-) diff --git a/test/jdk/java/nio/channels/SocketChannel/AdaptorStreams.java b/test/jdk/java/nio/channels/SocketChannel/AdaptorStreams.java index 3487ae45de1..240d9e3baf7 100644 --- a/test/jdk/java/nio/channels/SocketChannel/AdaptorStreams.java +++ b/test/jdk/java/nio/channels/SocketChannel/AdaptorStreams.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* @test * @bug 8222774 4430139 - * @run testng AdaptorStreams + * @run junit AdaptorStreams * @summary Exercise socket adaptor input/output streams */ @@ -44,77 +44,82 @@ import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; -@Test public class AdaptorStreams { /** * Test read when bytes are available */ + @Test public void testRead1() throws Exception { withConnection((sc, peer) -> { peer.getOutputStream().write(99); int n = sc.socket().getInputStream().read(); - assertEquals(n, 99); + assertEquals(99, n); }); } /** * Test read blocking before bytes are available */ + @Test public void testRead2() throws Exception { withConnection((sc, peer) -> { scheduleWrite(peer.getOutputStream(), 99, 1000); int n = sc.socket().getInputStream().read(); - assertEquals(n, 99); + assertEquals(99, n); }); } /** * Test read when peer has closed connection */ + @Test public void testRead3() throws Exception { withConnection((sc, peer) -> { peer.close(); int n = sc.socket().getInputStream().read(); - assertEquals(n, -1); + assertEquals(-1, n); }); } /** * Test read blocking before peer closes connection */ + @Test public void testRead4() throws Exception { withConnection((sc, peer) -> { scheduleClose(peer, 1000); int n = sc.socket().getInputStream().read(); - assertEquals(n, -1); + assertEquals(-1, n); }); } /** * Test async close of socket when thread blocked in read */ + @Test public void testRead5() throws Exception { withConnection((sc, peer) -> { scheduleClose(sc, 2000); InputStream in = sc.socket().getInputStream(); - expectThrows(IOException.class, () -> in.read()); + assertThrows(IOException.class, () -> in.read()); }); } /** * Test interrupted status set before read */ + @Test public void testRead6() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); Thread.currentThread().interrupt(); try { InputStream in = s.getInputStream(); - expectThrows(IOException.class, () -> in.read()); + assertThrows(IOException.class, () -> in.read()); } finally { Thread.interrupted(); // clear interrupt } @@ -125,13 +130,14 @@ public class AdaptorStreams { /** * Test interrupt of thread blocked in read */ + @Test public void testRead7() throws Exception { withConnection((sc, peer) -> { Future interrupter = scheduleInterrupt(Thread.currentThread(), 2000); Socket s = sc.socket(); try { InputStream in = s.getInputStream(); - expectThrows(IOException.class, () -> in.read()); + assertThrows(IOException.class, () -> in.read()); } finally { interrupter.cancel(true); Thread.interrupted(); // clear interrupt @@ -143,68 +149,74 @@ public class AdaptorStreams { /** * Test read when channel is configured non-blocking */ + @Test public void testRead8() throws Exception { withConnection((sc, peer) -> { sc.configureBlocking(false); InputStream in = sc.socket().getInputStream(); - expectThrows(IllegalBlockingModeException.class, () -> in.read()); + assertThrows(IllegalBlockingModeException.class, () -> in.read()); }); } /** * Test timed read when bytes are available */ + @Test public void testTimedRead1() throws Exception { withConnection((sc, peer) -> { peer.getOutputStream().write(99); Socket s = sc.socket(); s.setSoTimeout(60_000); int n = s.getInputStream().read(); - assertEquals(n, 99); + assertEquals(99, n); }); } /** * Test timed read blocking before bytes are available */ + @Test public void testTimedRead2() throws Exception { withConnection((sc, peer) -> { scheduleWrite(peer.getOutputStream(), 99, 1000); Socket s = sc.socket(); s.setSoTimeout(60_000); int n = s.getInputStream().read(); - assertEquals(n, 99); + assertEquals(99, n); }); } /** * Test timed read when the read times out */ + @Test public void testTimedRead3() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); s.setSoTimeout(500); InputStream in = s.getInputStream(); - expectThrows(SocketTimeoutException.class, () -> in.read()); + assertThrows(SocketTimeoutException.class, () -> in.read()); }); } /** * Test async close of socket when thread blocked in timed read */ + @Test public void testTimedRead4() throws Exception { withConnection((sc, peer) -> { scheduleClose(sc, 2000); Socket s = sc.socket(); s.setSoTimeout(60_000); InputStream in = s.getInputStream(); - expectThrows(IOException.class, () -> in.read()); + assertThrows(IOException.class, () -> in.read()); }); } /** * Test interrupted status set before timed read */ + @Test public void testTimedRead5() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); @@ -212,7 +224,7 @@ public class AdaptorStreams { try { s.setSoTimeout(60_000); InputStream in = s.getInputStream(); - expectThrows(IOException.class, () -> in.read()); + assertThrows(IOException.class, () -> in.read()); } finally { Thread.interrupted(); // clear interrupt } @@ -223,6 +235,7 @@ public class AdaptorStreams { /** * Test interrupt of thread blocked in timed read */ + @Test public void testTimedRead6() throws Exception { withConnection((sc, peer) -> { Future interrupter = scheduleInterrupt(Thread.currentThread(), 2000); @@ -230,7 +243,7 @@ public class AdaptorStreams { try { s.setSoTimeout(60_000); InputStream in = s.getInputStream(); - expectThrows(IOException.class, () -> in.read()); + assertThrows(IOException.class, () -> in.read()); assertTrue(s.isClosed()); } finally { interrupter.cancel(true); @@ -243,10 +256,11 @@ public class AdaptorStreams { /** * Test async close of socket when thread blocked in write */ + @Test public void testWrite1() throws Exception { withConnection((sc, peer) -> { scheduleClose(sc, 2000); - expectThrows(IOException.class, () -> { + assertThrows(IOException.class, () -> { OutputStream out = sc.socket().getOutputStream(); byte[] data = new byte[64*1000]; while (true) { @@ -259,13 +273,14 @@ public class AdaptorStreams { /** * Test interrupted status set before write */ + @Test public void testWrite2() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); Thread.currentThread().interrupt(); try { OutputStream out = s.getOutputStream(); - expectThrows(IOException.class, () -> out.write(99)); + assertThrows(IOException.class, () -> out.write(99)); } finally { Thread.interrupted(); // clear interrupt } @@ -276,12 +291,13 @@ public class AdaptorStreams { /** * Test interrupt of thread blocked in write */ + @Test public void testWrite3() throws Exception { withConnection((sc, peer) -> { Future interrupter = scheduleInterrupt(Thread.currentThread(), 2000); Socket s = sc.socket(); try { - expectThrows(IOException.class, () -> { + assertThrows(IOException.class, () -> { OutputStream out = sc.socket().getOutputStream(); byte[] data = new byte[64*1000]; while (true) { @@ -299,11 +315,12 @@ public class AdaptorStreams { /** * Test write when channel is configured non-blocking */ + @Test public void testWrite4() throws Exception { withConnection((sc, peer) -> { sc.configureBlocking(false); OutputStream out = sc.socket().getOutputStream(); - expectThrows(IllegalBlockingModeException.class, () -> out.write(99)); + assertThrows(IllegalBlockingModeException.class, () -> out.write(99)); }); } @@ -311,6 +328,7 @@ public class AdaptorStreams { * Test read when there are bytes available and another thread is blocked * in write */ + @Test public void testConcurrentReadWrite1() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); @@ -328,13 +346,14 @@ public class AdaptorStreams { // test read when bytes are available peer.getOutputStream().write(99); int n = s.getInputStream().read(); - assertEquals(n, 99); + assertEquals(99, n); }); } /** * Test read blocking when another thread is blocked in write */ + @Test public void testConcurrentReadWrite2() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); @@ -352,13 +371,14 @@ public class AdaptorStreams { // test read blocking until bytes are available scheduleWrite(peer.getOutputStream(), 99, 500); int n = s.getInputStream().read(); - assertEquals(n, 99); + assertEquals(99, n); }); } /** * Test writing when another thread is blocked in read */ + @Test public void testConcurrentReadWrite3() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); @@ -372,7 +392,7 @@ public class AdaptorStreams { // test write s.getOutputStream().write(99); int n = peer.getInputStream().read(); - assertEquals(n, 99); + assertEquals(99, n); }); } @@ -380,6 +400,7 @@ public class AdaptorStreams { * Test timed read when there are bytes available and another thread is * blocked in write */ + @Test public void testConcurrentTimedReadWrite1() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); @@ -398,13 +419,14 @@ public class AdaptorStreams { peer.getOutputStream().write(99); s.setSoTimeout(60_000); int n = s.getInputStream().read(); - assertEquals(n, 99); + assertEquals(99, n); }); } /** * Test timed read blocking when another thread is blocked in write */ + @Test public void testConcurrentTimedReadWrite2() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); @@ -423,13 +445,14 @@ public class AdaptorStreams { scheduleWrite(peer.getOutputStream(), 99, 500); s.setSoTimeout(60_000); int n = s.getInputStream().read(); - assertEquals(n, 99); + assertEquals(99, n); }); } /** * Test writing when another thread is blocked in read */ + @Test public void testConcurrentTimedReadWrite3() throws Exception { withConnection((sc, peer) -> { Socket s = sc.socket(); @@ -444,7 +467,7 @@ public class AdaptorStreams { // test write s.getOutputStream().write(99); int n = peer.getInputStream().read(); - assertEquals(n, 99); + assertEquals(99, n); }); } diff --git a/test/jdk/java/nio/channels/SocketChannel/ConnectionReset.java b/test/jdk/java/nio/channels/SocketChannel/ConnectionReset.java index 759d819fa4f..2d89d708110 100644 --- a/test/jdk/java/nio/channels/SocketChannel/ConnectionReset.java +++ b/test/jdk/java/nio/channels/SocketChannel/ConnectionReset.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,9 +21,9 @@ * questions. */ -/** +/* * @test - * @run testng ConnectionReset + * @run junit ConnectionReset * @summary Test behavior of SocketChannel.read and the Socket adaptor read * and available methods when a connection is reset */ @@ -36,12 +36,10 @@ import java.net.ServerSocket; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; -import java.lang.reflect.Method; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; -@Test public class ConnectionReset { static final int REPEAT_COUNT = 5; @@ -50,6 +48,7 @@ public class ConnectionReset { * Tests SocketChannel.read when the connection is reset and there are no * bytes to read. */ + @Test public void testSocketChannelReadNoData() throws IOException { System.out.println("testSocketChannelReadNoData"); withResetConnection(null, sc -> { @@ -69,6 +68,7 @@ public class ConnectionReset { * Tests SocketChannel.read when the connection is reset and there are bytes * to read. */ + @Test public void testSocketChannelReadData() throws IOException { System.out.println("testSocketChannelReadData"); byte[] data = { 1, 2, 3 }; @@ -99,6 +99,7 @@ public class ConnectionReset { * Tests available before Socket read when the connection is reset and there * are no bytes to read. */ + @Test public void testAvailableBeforeSocketReadNoData() throws IOException { System.out.println("testAvailableBeforeSocketReadNoData"); withResetConnection(null, sc -> { @@ -107,7 +108,7 @@ public class ConnectionReset { for (int i=0; i %d%n", bytesAvailable); - assertTrue(bytesAvailable == 0); + assertEquals(0, bytesAvailable); try { int bytesRead = in.read(); if (bytesRead == -1) { @@ -127,6 +128,7 @@ public class ConnectionReset { * Tests available before Socket read when the connection is reset and there * are bytes to read. */ + @Test public void testAvailableBeforeSocketReadData() throws IOException { System.out.println("testAvailableBeforeSocketReadData"); byte[] data = { 1, 2, 3 }; @@ -160,6 +162,7 @@ public class ConnectionReset { * Tests Socket read before available when the connection is reset and there * are no bytes to read. */ + @Test public void testSocketReadNoDataBeforeAvailable() throws IOException { System.out.println("testSocketReadNoDataBeforeAvailable"); withResetConnection(null, sc -> { @@ -179,7 +182,7 @@ public class ConnectionReset { } int bytesAvailable = in.available(); System.out.format("available => %d%n", bytesAvailable); - assertTrue(bytesAvailable == 0); + assertEquals(0, bytesAvailable); } }); } @@ -188,6 +191,7 @@ public class ConnectionReset { * Tests Socket read before available when the connection is reset and there * are bytes to read. */ + @Test public void testSocketReadDataBeforeAvailable() throws IOException { System.out.println("testSocketReadDataBeforeAvailable"); byte[] data = { 1, 2, 3 }; diff --git a/test/jdk/java/nio/channels/SocketChannel/ReadWriteAfterClose.java b/test/jdk/java/nio/channels/SocketChannel/ReadWriteAfterClose.java index 037e750a552..0af09df4949 100644 --- a/test/jdk/java/nio/channels/SocketChannel/ReadWriteAfterClose.java +++ b/test/jdk/java/nio/channels/SocketChannel/ReadWriteAfterClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,9 +21,6 @@ * questions. */ -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; import java.io.IOException; import java.net.InetAddress; @@ -34,38 +31,45 @@ import java.nio.channels.ClosedChannelException; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; -import static org.testng.Assert.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; /* * @test * @bug 8246707 - * @library /test/lib * @summary Reading or Writing to a closed SocketChannel should throw a ClosedChannelException - * @run testng/othervm ReadWriteAfterClose + * @run junit/othervm ReadWriteAfterClose */ public class ReadWriteAfterClose { - private ServerSocketChannel listener; - private SocketAddress saddr; + private static ServerSocketChannel listener; + private static SocketAddress saddr; private static final int bufCapacity = 4; private static final int bufArraySize = 4; private static final Class CCE = ClosedChannelException.class; - @BeforeTest - public void setUp() throws IOException { + @BeforeAll + public static void setUp() throws IOException { listener = ServerSocketChannel.open(); listener.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); saddr = listener.getLocalAddress(); } + @AfterAll + public static void tearDown() throws IOException { + if (listener != null) listener.close(); + } + @Test public void testWriteAfterClose1() throws IOException { SocketChannel sc = SocketChannel.open(saddr); sc.close(); ByteBuffer bufWrite = ByteBuffer.allocate(bufCapacity); - Throwable ex = expectThrows(CCE, () -> sc.write(bufWrite)); - assertEquals(ex.getClass(), CCE); + Throwable ex = assertThrows(CCE, () -> sc.write(bufWrite)); + assertSame(CCE, ex.getClass()); } @Test @@ -73,8 +77,8 @@ public class ReadWriteAfterClose { SocketChannel sc = SocketChannel.open(saddr); sc.close(); ByteBuffer[] bufArrayWrite = allocateBufArray(); - Throwable ex = expectThrows(CCE, () -> sc.write(bufArrayWrite)); - assertEquals(ex.getClass(), CCE); + Throwable ex = assertThrows(CCE, () -> sc.write(bufArrayWrite)); + assertSame(CCE, ex.getClass()); } @Test @@ -82,8 +86,8 @@ public class ReadWriteAfterClose { SocketChannel sc = SocketChannel.open(saddr); sc.close(); ByteBuffer[] bufArrayWrite = allocateBufArray(); - Throwable ex = expectThrows(CCE, () -> sc.write(bufArrayWrite, 0, bufArraySize)); - assertEquals(ex.getClass(), CCE); + Throwable ex = assertThrows(CCE, () -> sc.write(bufArrayWrite, 0, bufArraySize)); + assertSame(CCE, ex.getClass()); } @Test @@ -91,8 +95,8 @@ public class ReadWriteAfterClose { SocketChannel sc = SocketChannel.open(saddr); sc.close(); ByteBuffer dst = ByteBuffer.allocate(bufCapacity); - Throwable ex = expectThrows(CCE, () -> sc.read(dst)); - assertEquals(ex.getClass(), CCE); + Throwable ex = assertThrows(CCE, () -> sc.read(dst)); + assertSame(CCE, ex.getClass()); } @Test @@ -100,8 +104,8 @@ public class ReadWriteAfterClose { SocketChannel sc = SocketChannel.open(saddr); sc.close(); ByteBuffer[] dstArray = allocateBufArray(); - Throwable ex = expectThrows(CCE, () -> sc.read(dstArray)); - assertEquals(ex.getClass(), CCE); + Throwable ex = assertThrows(CCE, () -> sc.read(dstArray)); + assertSame(CCE, ex.getClass()); } @Test @@ -109,8 +113,8 @@ public class ReadWriteAfterClose { SocketChannel sc = SocketChannel.open(saddr); sc.close(); ByteBuffer[] dstArray = allocateBufArray(); - Throwable ex = expectThrows(CCE, () -> sc.read(dstArray, 0, bufArraySize)); - assertEquals(ex.getClass(), CCE); + Throwable ex = assertThrows(CCE, () -> sc.read(dstArray, 0, bufArraySize)); + assertSame(CCE, ex.getClass()); } public ByteBuffer[] allocateBufArray() { @@ -120,8 +124,4 @@ public class ReadWriteAfterClose { return bufArr; } - @AfterTest - public void tearDown() throws IOException { - listener.close(); - } } \ No newline at end of file diff --git a/test/jdk/java/nio/channels/etc/LocalSocketAddressType.java b/test/jdk/java/nio/channels/etc/LocalSocketAddressType.java index 63be790c478..60e41af6288 100644 --- a/test/jdk/java/nio/channels/etc/LocalSocketAddressType.java +++ b/test/jdk/java/nio/channels/etc/LocalSocketAddressType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,40 +26,44 @@ * @summary Test local address type * @library /test/lib * @build jdk.test.lib.NetworkConfiguration - * @run testng/othervm LocalSocketAddressType - * @run testng/othervm -Djava.net.preferIPv4Stack=true LocalSocketAddressType + * @run junit/othervm LocalSocketAddressType + * @run junit/othervm -Djava.net.preferIPv4Stack=true LocalSocketAddressType */ import jdk.test.lib.NetworkConfiguration; import jdk.test.lib.net.IPSupport; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import java.net.*; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.nio.channels.DatagramChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; -import java.util.stream.Collectors; import java.util.stream.Stream; -import static java.lang.Boolean.parseBoolean; -import static java.lang.System.getProperty; import static java.lang.System.out; -import static jdk.test.lib.Asserts.assertEquals; -import static jdk.test.lib.Asserts.assertTrue; import static jdk.test.lib.net.IPSupport.*; +import jtreg.SkippedException; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class LocalSocketAddressType { - @BeforeTest() - public void setup() { + @BeforeAll() + public static void setup() { IPSupport.printPlatformSupport(out); - throwSkippedExceptionIfNonOperational(); + try { + throwSkippedExceptionIfNonOperational(); + } catch (SkippedException skippedException) { + // jtreg.SkippedException would cause a JUnit test to fail + Assumptions.assumeTrue(false, skippedException.getMessage()); + } } - @DataProvider(name = "addresses") public static Iterator addresses() throws Exception { NetworkConfiguration nc = NetworkConfiguration.probe(); return Stream.concat(nc.ip4Addresses(), nc.ip6Addresses()) @@ -67,36 +71,39 @@ public class LocalSocketAddressType { .iterator(); } - @Test(dataProvider = "addresses") - public static void testSocketChannel(InetSocketAddress addr) throws Exception { + @ParameterizedTest + @MethodSource("addresses") + public void testSocketChannel(InetSocketAddress addr) throws Exception { try (var c = SocketChannel.open()) { Class cls = addr.getAddress().getClass(); InetAddress ia = ((InetSocketAddress)c.bind(addr).getLocalAddress()).getAddress(); - assertEquals(ia.getClass(), cls); + assertEquals(cls, ia.getClass()); ia = c.socket().getLocalAddress(); - assertEquals(ia.getClass(), cls); + assertEquals(cls, ia.getClass()); } } - @Test(dataProvider = "addresses") - public static void testServerSocketChannel(InetSocketAddress addr) throws Exception { + @ParameterizedTest + @MethodSource("addresses") + public void testServerSocketChannel(InetSocketAddress addr) throws Exception { try (var c = ServerSocketChannel.open()) { Class cls = addr.getAddress().getClass(); InetAddress ia = ((InetSocketAddress)c.bind(addr).getLocalAddress()).getAddress(); - assertEquals(ia.getClass(), cls); + assertEquals(cls, ia.getClass()); ia = c.socket().getInetAddress(); - assertEquals(ia.getClass(), cls); + assertEquals(cls, ia.getClass()); } } - @Test(dataProvider = "addresses") - public static void testDatagramChannel(InetSocketAddress addr) throws Exception { + @ParameterizedTest + @MethodSource("addresses") + public void testDatagramChannel(InetSocketAddress addr) throws Exception { try (var c = DatagramChannel.open()) { Class cls = addr.getAddress().getClass(); InetAddress ia = ((InetSocketAddress)c.bind(addr).getLocalAddress()).getAddress(); - assertEquals(ia.getClass(), cls); + assertEquals(cls, ia.getClass()); ia = c.socket().getLocalAddress(); - assertEquals(ia.getClass(), cls); + assertEquals(cls, ia.getClass()); } } } diff --git a/test/jdk/java/nio/channels/etc/OpenAndConnect.java b/test/jdk/java/nio/channels/etc/OpenAndConnect.java index b183076e49d..0e0347ebb57 100644 --- a/test/jdk/java/nio/channels/etc/OpenAndConnect.java +++ b/test/jdk/java/nio/channels/etc/OpenAndConnect.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,23 +23,32 @@ import jdk.test.lib.NetworkConfiguration; import jdk.test.lib.net.IPSupport; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.IOException; -import java.net.*; -import java.nio.channels.*; -import java.util.Arrays; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ProtocolFamily; +import java.net.SocketAddress; +import java.nio.channels.DatagramChannel; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; import java.util.List; import java.util.LinkedList; -import static java.lang.System.getProperty; import static java.lang.System.out; import static java.net.StandardProtocolFamily.INET; import static java.net.StandardProtocolFamily.INET6; import static jdk.test.lib.net.IPSupport.*; +import jtreg.SkippedException; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + /* * @test * @summary Test SocketChannel, ServerSocketChannel and DatagramChannel @@ -48,10 +57,9 @@ import static jdk.test.lib.net.IPSupport.*; * addresses (Inet4Address, Inet6Address). * @library /test/lib * @build jdk.test.lib.NetworkConfiguration - * @run testng/othervm OpenAndConnect + * @run junit/othervm OpenAndConnect */ - public class OpenAndConnect { static final Inet4Address IA4ANYLOCAL; static final Inet6Address IA6ANYLOCAL; @@ -77,11 +85,16 @@ public class OpenAndConnect { } } - @BeforeTest() - public void setup() { + @BeforeAll() + public static void setup() { NetworkConfiguration.printSystemConfiguration(out); IPSupport.printPlatformSupport(out); - throwSkippedExceptionIfNonOperational(); + try { + throwSkippedExceptionIfNonOperational(); + } catch (SkippedException skippedException) { + // jtreg.SkippedException would cause a JUnit test to fail + Assumptions.assumeTrue(false, skippedException.getMessage()); + } out.println("IA4LOCAL: " + IA4LOCAL); out.println("IA6LOCAL: " + IA6LOCAL); @@ -91,9 +104,8 @@ public class OpenAndConnect { out.println("IA6LOOPBACK: " + IA6LOOPBACK); } - @DataProvider(name = "openConnect") - public Object[][] openConnect() { - LinkedList l = new LinkedList<>(); + public static List openConnect() { + LinkedList l = new LinkedList<>(); if (IPSupport.hasIPv4()) { l.addAll(openConnectV4Tests); if (IA4LOCAL != null) { @@ -112,7 +124,7 @@ public class OpenAndConnect { l.addAll(openConnectV4LocalAndV6Tests); } } - return l.toArray(new Object[][]{}); + return l; } // +----- sfam is server/first socket family @@ -127,92 +139,91 @@ public class OpenAndConnect { // | | | | this is address used for connect // | | | | also. // | | | | - // | | | | - // | | | | - // | | | | - // + + + + - // { sfam, saddr, cfam, caddr, } + // | | | +---------------+ + // | | | | + // | | +---------------+ | + // | | | | + // | +---------------+ | | + // | | | | + // +---------------+ | | | + // | | | | + // + + + + + // Arguments.of( sfam, saddr, cfam, caddr, ) // Basic tests for when an IPv4 is available - public static List openConnectV4Tests = - Arrays.asList(new Object[][] { - { INET, IA4LOOPBACK, INET, IA4LOOPBACK }, - { INET, IA4LOOPBACK, null, IA4LOOPBACK }, - { INET, IA4ANYLOCAL, null, IA4LOOPBACK }, - { INET, IA4ANYLOCAL, INET, IA4LOOPBACK }, - { null, IA4LOOPBACK, INET, IA4ANYLOCAL }, - { null, IA4LOOPBACK, INET, IA4LOOPBACK }, - { null, IA4LOOPBACK, INET, null }, - { null, IA4LOOPBACK, null, null } - }); + public static List openConnectV4Tests = + List.of(Arguments.of( INET, IA4LOOPBACK, INET, IA4LOOPBACK ), + Arguments.of( INET, IA4LOOPBACK, null, IA4LOOPBACK ), + Arguments.of( INET, IA4ANYLOCAL, null, IA4LOOPBACK ), + Arguments.of( INET, IA4ANYLOCAL, INET, IA4LOOPBACK ), + Arguments.of( null, IA4LOOPBACK, INET, IA4ANYLOCAL ), + Arguments.of( null, IA4LOOPBACK, INET, IA4LOOPBACK ), + Arguments.of( null, IA4LOOPBACK, INET, null ), + Arguments.of( null, IA4LOOPBACK, null, null ) + ); // Additional tests for when an IPv4 local address is available - public List openConnectV4LocalTests = - Arrays.asList(new Object[][] { - { INET, IA4LOCAL, INET, IA4LOCAL }, - { INET, IA4LOCAL, null, IA4LOCAL }, - { INET, IA4LOCAL, null, DONT_BIND }, - { INET, IA4ANYLOCAL, INET, IA4LOCAL }, - { INET, IA4ANYLOCAL, null, IA4LOCAL }, - { null, IA4LOCAL, INET, IA4ANYLOCAL }, - { null, IA4LOCAL, INET, IA4LOCAL }, - { null, IA4LOCAL, INET, null }, - { null, IA4LOCAL, null, null } - }); + public static List openConnectV4LocalTests = + List.of(Arguments.of( INET, IA4LOCAL, INET, IA4LOCAL ), + Arguments.of( INET, IA4LOCAL, null, IA4LOCAL ), + Arguments.of( INET, IA4LOCAL, null, DONT_BIND ), + Arguments.of( INET, IA4ANYLOCAL, INET, IA4LOCAL ), + Arguments.of( INET, IA4ANYLOCAL, null, IA4LOCAL ), + Arguments.of( null, IA4LOCAL, INET, IA4ANYLOCAL ), + Arguments.of( null, IA4LOCAL, INET, IA4LOCAL ), + Arguments.of( null, IA4LOCAL, INET, null ), + Arguments.of( null, IA4LOCAL, null, null ) + ); // Basic tests for when an IPv6 is available - public List openConnectV6Tests = - Arrays.asList(new Object[][] { - { INET6, IA6ANYLOCAL, null, IA6LOOPBACK }, - { INET6, IA6ANYLOCAL, INET6, IA6LOOPBACK }, - { INET6, IA6LOOPBACK, INET6, IA6LOOPBACK }, - { INET6, IA6LOOPBACK, INET6, IA6LOOPBACK }, - { null, IA6ANYLOCAL, null, IA6LOOPBACK }, - { null, IA6ANYLOCAL, INET6, IA6LOOPBACK }, - { null, IA6LOOPBACK, INET6, IA6LOOPBACK }, - { null, IA6LOOPBACK, INET6, DONT_BIND }, - { null, IA6LOOPBACK, INET6, null }, - { null, IA6LOOPBACK, null, IA6LOOPBACK }, - { null, IA6LOOPBACK, null, null }, - { null, IA6LOOPBACK, INET6, IA6ANYLOCAL }, - { null, IA6LOOPBACK, null, IA6ANYLOCAL } - }); + public static List openConnectV6Tests = + List.of(Arguments.of( INET6, IA6ANYLOCAL, null, IA6LOOPBACK ), + Arguments.of( INET6, IA6ANYLOCAL, INET6, IA6LOOPBACK ), + Arguments.of( INET6, IA6LOOPBACK, INET6, IA6LOOPBACK ), + Arguments.of( INET6, IA6LOOPBACK, INET6, IA6LOOPBACK ), + Arguments.of( null, IA6ANYLOCAL, null, IA6LOOPBACK ), + Arguments.of( null, IA6ANYLOCAL, INET6, IA6LOOPBACK ), + Arguments.of( null, IA6LOOPBACK, INET6, IA6LOOPBACK ), + Arguments.of( null, IA6LOOPBACK, INET6, DONT_BIND ), + Arguments.of( null, IA6LOOPBACK, INET6, null ), + Arguments.of( null, IA6LOOPBACK, null, IA6LOOPBACK ), + Arguments.of( null, IA6LOOPBACK, null, null ), + Arguments.of( null, IA6LOOPBACK, INET6, IA6ANYLOCAL ), + Arguments.of( null, IA6LOOPBACK, null, IA6ANYLOCAL ) + ); // Additional tests for when an IPv6 local address is available - public List openConnectV6LocalTests = - Arrays.asList(new Object[][] { - { INET6, IA6ANYLOCAL, null, IA6LOCAL }, - { INET6, IA6ANYLOCAL, INET6, IA6LOCAL }, - { INET6, IA6LOCAL, INET6, IA6LOCAL }, - { INET6, IA6LOCAL, null, IA6LOCAL }, - { INET6, IA6LOCAL, null, DONT_BIND }, - { INET6, IA6LOCAL, INET6, IA6LOCAL }, - { null, IA6ANYLOCAL, null, IA6LOCAL }, - { null, IA6ANYLOCAL, INET6, IA6LOCAL }, - { null, IA6LOCAL, INET6, IA6LOCAL }, - { null, IA6LOCAL, INET6, IA6ANYLOCAL }, - { null, IA6LOCAL, null, IA6ANYLOCAL }, - { null, IA6LOCAL, null, IA6LOCAL }, - { null, IA6LOCAL, INET6, null }, - { null, IA6LOCAL, null, null } - }); + public static List openConnectV6LocalTests = + List.of(Arguments.of( INET6, IA6ANYLOCAL, null, IA6LOCAL ), + Arguments.of( INET6, IA6ANYLOCAL, INET6, IA6LOCAL ), + Arguments.of( INET6, IA6LOCAL, INET6, IA6LOCAL ), + Arguments.of( INET6, IA6LOCAL, null, IA6LOCAL ), + Arguments.of( INET6, IA6LOCAL, null, DONT_BIND ), + Arguments.of( INET6, IA6LOCAL, INET6, IA6LOCAL ), + Arguments.of( null, IA6ANYLOCAL, null, IA6LOCAL ), + Arguments.of( null, IA6ANYLOCAL, INET6, IA6LOCAL ), + Arguments.of( null, IA6LOCAL, INET6, IA6LOCAL ), + Arguments.of( null, IA6LOCAL, INET6, IA6ANYLOCAL ), + Arguments.of( null, IA6LOCAL, null, IA6ANYLOCAL ), + Arguments.of( null, IA6LOCAL, null, IA6LOCAL ), + Arguments.of( null, IA6LOCAL, INET6, null ), + Arguments.of( null, IA6LOCAL, null, null ) + ); // Additional tests for when IPv4 and IPv6 are available - public List openConnectV4AndV6Tests = - Arrays.asList(new Object[][] { - { null, IA4LOOPBACK, INET6, IA6ANYLOCAL }, - { null, IA4LOOPBACK, null, IA6ANYLOCAL }, - { null, IA4LOOPBACK, INET6, DONT_BIND }, - { null, IA4LOOPBACK, INET6, null } - }); + public static List openConnectV4AndV6Tests = + List.of(Arguments.of( null, IA4LOOPBACK, INET6, IA6ANYLOCAL ), + Arguments.of( null, IA4LOOPBACK, null, IA6ANYLOCAL ), + Arguments.of( null, IA4LOOPBACK, INET6, DONT_BIND ), + Arguments.of( null, IA4LOOPBACK, INET6, null ) + ); // Additional tests for when IPv4 local address and IPv6 are available - public List openConnectV4LocalAndV6Tests = - Arrays.asList(new Object[][] { - { null, IA4LOCAL, INET6, IA6ANYLOCAL }, - { null, IA4LOCAL, INET6, null }, - { null, IA4LOCAL, null, IA6ANYLOCAL } - }); + public static List openConnectV4LocalAndV6Tests = + List.of(Arguments.of( null, IA4LOCAL, INET6, IA6ANYLOCAL ), + Arguments.of( null, IA4LOCAL, INET6, null ), + Arguments.of( null, IA4LOCAL, null, IA6ANYLOCAL ) + ); /** * If the destination address is the wildcard, it is replaced by the alternate @@ -227,7 +238,8 @@ public class OpenAndConnect { return isa; } - @Test(dataProvider = "openConnect") + @ParameterizedTest + @MethodSource("openConnect") public void scOpenAndConnect(ProtocolFamily sfam, InetAddress saddr, ProtocolFamily cfam, @@ -248,7 +260,8 @@ public class OpenAndConnect { } } - @Test(dataProvider = "openConnect") + @ParameterizedTest + @MethodSource("openConnect") public void dcOpenAndConnect(ProtocolFamily sfam, InetAddress saddr, ProtocolFamily cfam, diff --git a/test/jdk/java/nio/channels/etc/ProtocolFamilies.java b/test/jdk/java/nio/channels/etc/ProtocolFamilies.java index 45226e9a97e..7f823ff35a8 100644 --- a/test/jdk/java/nio/channels/etc/ProtocolFamilies.java +++ b/test/jdk/java/nio/channels/etc/ProtocolFamilies.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,20 +23,38 @@ import jdk.test.lib.NetworkConfiguration; import jdk.test.lib.net.IPSupport; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.testng.Assert.ThrowingRunnable; + import java.io.IOException; -import java.net.*; -import java.nio.channels.*; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ProtocolFamily; +import java.net.SocketAddress; +import java.net.StandardProtocolFamily; +import java.net.UnknownHostException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.UnsupportedAddressTypeException; import java.nio.channels.spi.SelectorProvider; +import java.util.List; + import static java.lang.System.out; import static java.net.StandardProtocolFamily.INET; import static java.net.StandardProtocolFamily.INET6; import static jdk.test.lib.net.IPSupport.*; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; + +import jtreg.SkippedException; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertThrows; /* * @test @@ -44,8 +62,8 @@ import static org.testng.Assert.assertThrows; * with various ProtocolFamily combinations * @library /test/lib * @build jdk.test.lib.NetworkConfiguration - * @run testng ProtocolFamilies - * @run testng/othervm -Djava.net.preferIPv4Stack=true ProtocolFamilies + * @run junit ProtocolFamilies + * @run junit/othervm -Djava.net.preferIPv4Stack=true ProtocolFamilies */ @@ -55,11 +73,16 @@ public class ProtocolFamilies { static Inet4Address ia4; static Inet6Address ia6; - @BeforeTest() - public void setup() throws Exception { + @BeforeAll() + public static void setup() throws Exception { NetworkConfiguration.printSystemConfiguration(out); IPSupport.printPlatformSupport(out); - throwSkippedExceptionIfNonOperational(); + try { + throwSkippedExceptionIfNonOperational(); + } catch (SkippedException skippedException) { + // jtreg.SkippedException would cause a JUnit test to fail + Assumptions.assumeTrue(false, skippedException.getMessage()); + } ia4 = getLocalIPv4Address(); ia6 = getLocalIPv6Address(); @@ -70,111 +93,89 @@ public class ProtocolFamilies { static final Class UATE = UnsupportedAddressTypeException.class; static final Class UOE = UnsupportedOperationException.class; - @DataProvider(name = "open") - public Object[][] open() { + public static List open() { if (hasIPv6 && !preferIPv4) { - return new Object[][]{ - { INET, null }, - { INET6, null } - }; + return List.of( + Arguments.of( INET, null ), + Arguments.of( INET6, null ) + ); } else { - return new Object[][]{ - { INET, null }, - { INET6, UOE } - }; + return List.of( + Arguments.of( INET, null ), + Arguments.of( INET6, UOE ) + ); } } - @Test(dataProvider = "open") + @ParameterizedTest + @MethodSource("open") public void scOpen(StandardProtocolFamily family, Class expectedException) - throws Throwable + throws IOException { - SocketChannel sc = null; - try { - if (expectedException == UOE) { - try { - sc = openSC(family); - } catch (UnsupportedOperationException e) {} - } else { - sc = openSC(family); - } - } finally { - if (sc != null) - sc.close(); + if (expectedException != null) { + assertThrows(expectedException, () -> openSC(family)); + } else { + try (var _ = openSC(family)) { } } } - @Test(dataProvider = "open") + @ParameterizedTest + @MethodSource("open") public void sscOpen(StandardProtocolFamily family, Class expectedException) - throws Throwable + throws IOException { - ServerSocketChannel ssc = null; - try { - if (expectedException == UOE) { - try { - ssc = openSSC(family); - } catch (UnsupportedOperationException e) {} - } else { - openSSC(family); - } - } finally { - if (ssc != null) - ssc.close(); + if (expectedException != null) { + assertThrows(expectedException, () -> openSSC(family)); + } else { + try (var _ = openSSC(family)) { } } } - @Test(dataProvider = "open") + @ParameterizedTest + @MethodSource("open") public void dcOpen(StandardProtocolFamily family, Class expectedException) - throws Throwable + throws IOException { - DatagramChannel dc = null; - try { - if (expectedException == UOE) { - try { - dc = openDC(family); - } catch (UnsupportedOperationException e) {} - } else { - openDC(family); - } - } finally { - if (dc != null) - dc.close(); + if (expectedException != null) { + assertThrows(expectedException, () -> openDC(family)); + } else { + try (var _ = openDC(family)) { } } } - @DataProvider(name = "openBind") - public Object[][] openBind() { + public static List openBind() { if (hasIPv6 && !preferIPv4) { - return new Object[][]{ - { INET, INET, null }, - { INET, INET6, UATE }, - { INET, null, null }, - { INET6, INET, null }, - { INET6, INET6, null }, - { INET6, null, null }, - { null, INET, null }, - { null, INET6, null }, - { null, null, null } - }; + return List.of( + Arguments.of( INET, INET, null ), + Arguments.of( INET, INET6, UATE ), + Arguments.of( INET, null, null ), + Arguments.of( INET6, INET, null ), + Arguments.of( INET6, INET6, null ), + Arguments.of( INET6, null, null ), + Arguments.of( null, INET, null ), + Arguments.of( null, INET6, null ), + Arguments.of( null, null, null ) + ); } else { - return new Object[][]{ - { INET, INET, null }, - { INET, INET6, UATE }, - { INET, null, null }, - { null, INET, null }, - { null, INET6, UATE }, - { null, null, null } - }; + return List.of( + Arguments.of( INET, INET, null ), + Arguments.of( INET, INET6, UATE ), + Arguments.of( INET, null, null ), + Arguments.of( null, INET, null ), + Arguments.of( null, INET6, UATE ), + Arguments.of( null, null, null ) + ); } } // SocketChannel open - INET, INET6, default // SocketChannel bind - INET, INET6, null - @Test(dataProvider = "openBind") + @ParameterizedTest + @MethodSource("openBind") public void scOpenBind(StandardProtocolFamily ofamily, StandardProtocolFamily bfamily, Class expectedException) @@ -182,9 +183,9 @@ public class ProtocolFamilies { { try (SocketChannel sc = openSC(ofamily)) { SocketAddress addr = getSocketAddress(bfamily); - ThrowingRunnable bindOp = () -> sc.bind(addr); - if (expectedException == null) - bindOp.run(); + Executable bindOp = () -> sc.bind(addr); + if (expectedException == null) + bindOp.execute(); else assertThrows(expectedException, bindOp); } @@ -193,7 +194,8 @@ public class ProtocolFamilies { // ServerSocketChannel open - INET, INET6, default // ServerSocketChannel bind - INET, INET6, null - @Test(dataProvider = "openBind") + @ParameterizedTest + @MethodSource("openBind") public void sscOpenBind(StandardProtocolFamily ofamily, StandardProtocolFamily bfamily, Class expectedException) @@ -201,9 +203,9 @@ public class ProtocolFamilies { { try (ServerSocketChannel ssc = openSSC(ofamily)) { SocketAddress addr = getSocketAddress(bfamily); - ThrowingRunnable bindOp = () -> ssc.bind(addr); + Executable bindOp = () -> ssc.bind(addr); if (expectedException == null) - bindOp.run(); + bindOp.execute(); else assertThrows(expectedException, bindOp); } @@ -212,7 +214,8 @@ public class ProtocolFamilies { // DatagramChannel open - INET, INET6, default // DatagramChannel bind - INET, INET6, null - @Test(dataProvider = "openBind") + @ParameterizedTest + @MethodSource("openBind") public void dcOpenBind(StandardProtocolFamily ofamily, StandardProtocolFamily bfamily, Class expectedException) @@ -220,9 +223,9 @@ public class ProtocolFamilies { { try (DatagramChannel dc = openDC(ofamily)) { SocketAddress addr = getSocketAddress(bfamily); - ThrowingRunnable bindOp = () -> dc.bind(addr); + Executable bindOp = () -> dc.bind(addr); if (expectedException == null) - bindOp.run(); + bindOp.execute(); else assertThrows(expectedException, bindOp); } @@ -231,32 +234,32 @@ public class ProtocolFamilies { // SocketChannel open - INET, INET6, default // SocketChannel connect - INET, INET6, default - @DataProvider(name = "openConnect") - public Object[][] openConnect() { + public static List openConnect() { if (hasIPv6 && !preferIPv4) { - return new Object[][]{ - { INET, INET, null }, - { INET, INET6, null }, - { INET, null, null }, - { INET6, INET, UATE }, - { INET6, INET6, null }, - { INET6, null, null }, - { null, INET, UATE }, - { null, INET6, null }, - { null, null, null } - }; + return List.of( + Arguments.of( INET, INET, null ), + Arguments.of( INET, INET6, null ), + Arguments.of( INET, null, null ), + Arguments.of( INET6, INET, UATE ), + Arguments.of( INET6, INET6, null ), + Arguments.of( INET6, null, null ), + Arguments.of( null, INET, UATE ), + Arguments.of( null, INET6, null ), + Arguments.of( null, null, null ) + ); } else { // INET6 channels cannot be created - UOE - tested elsewhere - return new Object[][]{ - { INET, INET, null }, - { INET, null, null }, - { null, INET, null }, - { null, null, null } - }; + return List.of( + Arguments.of( INET, INET, null ), + Arguments.of( INET, null, null ), + Arguments.of( null, INET, null ), + Arguments.of( null, null, null ) + ); } } - @Test(dataProvider = "openConnect") + @ParameterizedTest + @MethodSource("openConnect") public void scOpenConnect(StandardProtocolFamily sfamily, StandardProtocolFamily cfamily, Class expectedException) @@ -279,7 +282,7 @@ public class ProtocolFamilies { // Tests null handling @Test public void testNulls() { - assertThrows(NPE, () -> SocketChannel.open((ProtocolFamily)null)); + assertThrows(NPE, () -> SocketChannel.open((ProtocolFamily) null)); assertThrows(NPE, () -> ServerSocketChannel.open(null)); assertThrows(NPE, () -> DatagramChannel.open(null)); diff --git a/test/jdk/java/nio/channels/unixdomain/EmptySunPathForSocketFile.java b/test/jdk/java/nio/channels/unixdomain/EmptySunPathForSocketFile.java index 79f8ead7aa2..ec47babdff3 100644 --- a/test/jdk/java/nio/channels/unixdomain/EmptySunPathForSocketFile.java +++ b/test/jdk/java/nio/channels/unixdomain/EmptySunPathForSocketFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,11 +21,11 @@ * questions. */ -/** +/* * @test * @bug 8256461 * @modules java.base/sun.nio.fs - * @run testng EmptySunPathForSocketFile + * @run junit EmptySunPathForSocketFile */ import java.nio.file.FileSystems; @@ -33,7 +33,8 @@ import java.nio.file.Path; import java.nio.file.spi.FileSystemProvider; import sun.nio.fs.AbstractFileSystemProvider; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; /** * Check that AbstractFileSystemProvider.getSunPathForSocketFile with @@ -41,9 +42,14 @@ import static org.testng.Assert.assertEquals; */ public class EmptySunPathForSocketFile { public static void main(String[] args) throws Exception { + new EmptySunPathForSocketFile().test(); + } + + @Test + public void test() { Path path = Path.of(""); FileSystemProvider provider = FileSystems.getDefault().provider(); byte[] bb = ((AbstractFileSystemProvider) provider).getSunPathForSocketFile(path); - assertEquals(bb, new byte[0]); + assertArrayEquals(new byte[0], bb); } } diff --git a/test/jdk/java/nio/channels/unixdomain/FileAttributes.java b/test/jdk/java/nio/channels/unixdomain/FileAttributes.java index 3ff92a6bc05..5e9b65f90b2 100644 --- a/test/jdk/java/nio/channels/unixdomain/FileAttributes.java +++ b/test/jdk/java/nio/channels/unixdomain/FileAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,35 +21,34 @@ * questions. */ -/** +/* * @test * @bug 8252971 - * @library /test/lib - * @run testng FileAttributes + * @run junit FileAttributes */ import java.io.IOException; import java.io.File; -import java.net.*; -import java.nio.channels.*; +import java.net.UnixDomainSocketAddress; +import java.nio.channels.SocketChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; -import java.util.Arrays; -import org.testng.annotations.Test; -import org.testng.SkipException; import static java.net.StandardProtocolFamily.UNIX; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** */ public class FileAttributes { @Test - public static void test() throws Exception { + public void test() throws Exception { checkSupported(); Path path = null; try (var chan = SocketChannel.open(UNIX)) { @@ -89,7 +88,7 @@ public class FileAttributes { try { SocketChannel.open(UNIX).close(); } catch (UnsupportedOperationException e) { - throw new SkipException("Unix domain channels not supported"); + Assumptions.assumeTrue(false, "Unix domain channels not supported"); } catch (Exception e) { // continue test to see what problem is } diff --git a/test/jdk/java/nio/channels/unixdomain/IOExchanges.java b/test/jdk/java/nio/channels/unixdomain/IOExchanges.java index 33a7b5ef3c4..9924d02504a 100644 --- a/test/jdk/java/nio/channels/unixdomain/IOExchanges.java +++ b/test/jdk/java/nio/channels/unixdomain/IOExchanges.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,38 +21,45 @@ * questions. */ -/** +/* * @test * @bug 8245194 - * @run testng/othervm/timeout=480 IOExchanges + * @run junit/othervm/timeout=480 IOExchanges */ import java.io.IOException; -import java.net.*; -import java.nio.channels.*; +import java.net.ProtocolFamily; +import java.net.SocketAddress; +import java.net.UnixDomainSocketAddress; import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; import java.nio.file.Files; +import java.util.List; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.StandardProtocolFamily.*; import static java.nio.channels.SelectionKey.OP_ACCEPT; import static java.nio.channels.SelectionKey.OP_READ; import static java.nio.channels.SelectionKey.OP_WRITE; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class IOExchanges { static boolean unixDomainSupported = true; - @BeforeTest() - public void setup() { + @BeforeAll() + public static void setup() { try { - SocketChannel.open(UNIX); + SocketChannel.open(UNIX).close(); } catch (IOException | UnsupportedOperationException e) { unixDomainSupported = false; out.println("Unix domain channels not supported"); @@ -72,8 +79,8 @@ public class IOExchanges { } public static void deleteFile(SocketAddress addr) throws Exception { - if (addr instanceof UnixDomainSocketAddress) { - Files.deleteIfExists(((UnixDomainSocketAddress) addr).getPath()); + if (addr instanceof UnixDomainSocketAddress uaddr) { + Files.deleteIfExists(uaddr.getPath()); } } @@ -118,18 +125,12 @@ public class IOExchanges { SPINBAccep_NBConn_NBIO_RW_12a */ - @DataProvider(name = "family") - public Object[][] family() { - return unixDomainSupported ? - new Object[][] { - { UNIX }, - { INET }} - : new Object[][] { - { INET } - }; + public static List family() { + return unixDomainSupported ? List.of(INET, UNIX) : List.of(INET); } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void BAccep_BConn_BIO_WR_1(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -140,25 +141,26 @@ public class IOExchanges { try (SocketChannel sc = openSocketChannel(family)) { assertTrue(sc.connect(addr)); ByteBuffer bb = ByteBuffer.allocate(10).put((byte) 0x01).flip(); - assertEquals(sc.write(bb), 1); + assertEquals(1, sc.write(bb)); out.printf("wrote: 0x%x%n", bb.get(0)); - assertEquals(sc.read(bb.clear()), -1); + assertEquals(-1, sc.read(bb.clear())); } }); t.start(); try (SocketChannel sc = ssc.accept()) { ByteBuffer bb = ByteBuffer.allocate(10); - assertEquals(sc.read(bb), 1); + assertEquals(1, sc.read(bb)); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x01); + assertEquals(0x01, bb.get(0)); } t.awaitCompletion(); deleteFile(addr); } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void BAccep_BConn_BIO_RW_2(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -169,25 +171,26 @@ public class IOExchanges { try (SocketChannel sc = openSocketChannel(family)) { assertTrue(sc.connect(addr)); ByteBuffer bb = ByteBuffer.allocate(10); - assertEquals(sc.read(bb), 1); + assertEquals(1, sc.read(bb)); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x02); + assertEquals(0x02, bb.get(0)); } }); t.start(); try (SocketChannel sc = ssc.accept()) { ByteBuffer bb = ByteBuffer.allocate(10).put((byte) 0x02).flip(); - assertEquals(sc.write(bb), 1); + assertEquals(1, sc.write(bb)); out.printf("wrote: 0x%x%n", bb.get(0)); - assertEquals(sc.read(bb.clear()), -1); + assertEquals(-1, sc.read(bb.clear())); } t.awaitCompletion(); deleteFile(addr); } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SELNBAccep_BConn_BIO_WR_3(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family); @@ -199,28 +202,29 @@ public class IOExchanges { try (SocketChannel sc = openSocketChannel(family)) { assertTrue(sc.connect(addr)); ByteBuffer bb = ByteBuffer.allocate(10).put((byte) 0x03).flip(); - assertEquals(sc.write(bb), 1); + assertEquals(1, sc.write(bb)); out.printf("wrote: 0x%x%n", bb.get(0)); - assertEquals(sc.read(bb.clear()), -1); + assertEquals(-1, sc.read(bb.clear())); } }); t.start(); ssc.configureBlocking(false).register(selector, OP_ACCEPT); - assertEquals(selector.select(), 1); + assertEquals(1, selector.select()); try (SocketChannel sc = ssc.accept()) { ByteBuffer bb = ByteBuffer.allocate(10); - assertEquals(sc.read(bb), 1); + assertEquals(1, sc.read(bb)); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x03); + assertEquals(0x03, bb.get(0)); } t.awaitCompletion(); deleteFile(addr); } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SELNBAccep_BConn_BIO_RW_4(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family); @@ -232,21 +236,21 @@ public class IOExchanges { try (SocketChannel sc = openSocketChannel(family)) { assertTrue(sc.connect(addr)); ByteBuffer bb = ByteBuffer.allocate(10); - assertEquals(sc.read(bb), 1); + assertEquals(1, sc.read(bb)); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x04); + assertEquals(0x04, bb.get(0)); } }); t.start(); ssc.configureBlocking(false).register(selector, OP_ACCEPT); - assertEquals(selector.select(), 1); + assertEquals(1, selector.select()); try (SocketChannel sc = ssc.accept()) { ByteBuffer bb = ByteBuffer.allocate(10).put((byte) 0x04).flip(); - assertEquals(sc.write(bb), 1); + assertEquals(1, sc.write(bb)); out.printf("wrote: 0x%x%n", bb.get(0)); - assertEquals(sc.read(bb.clear()), -1); + assertEquals(-1, sc.read(bb.clear())); } t.awaitCompletion(); @@ -254,7 +258,8 @@ public class IOExchanges { } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SPINNBAccep_BConn_BIO_WR_5(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -265,9 +270,9 @@ public class IOExchanges { try (SocketChannel sc = openSocketChannel(family)) { assertTrue(sc.connect(addr)); ByteBuffer bb = ByteBuffer.allocate(10).put((byte) 0x05).flip(); - assertEquals(sc.write(bb), 1); + assertEquals(1, sc.write(bb)); out.printf("wrote: 0x%x%n", bb.get(0)); - assertEquals(sc.read(bb.clear()), -1); + assertEquals(-1, sc.read(bb.clear())); } }); t.start(); @@ -284,16 +289,17 @@ public class IOExchanges { try (SocketChannel sc = accepted) { ByteBuffer bb = ByteBuffer.allocate(10); - assertEquals(sc.read(bb), 1); + assertEquals(1, sc.read(bb)); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x05); + assertEquals(0x05, bb.get(0)); } t.awaitCompletion(); deleteFile(addr); } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SPINNBAccep_BConn_BIO_RW_6(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -304,9 +310,9 @@ public class IOExchanges { try (SocketChannel sc = openSocketChannel(family)) { assertTrue(sc.connect(addr)); ByteBuffer bb = ByteBuffer.allocate(10); - assertEquals(sc.read(bb), 1); + assertEquals(1, sc.read(bb)); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x06); + assertEquals(0x06, bb.get(0)); } }); t.start(); @@ -323,9 +329,9 @@ public class IOExchanges { try (SocketChannel sc = accepted) { ByteBuffer bb = ByteBuffer.allocate(10).put((byte) 0x06).flip(); - assertEquals(sc.write(bb), 1); + assertEquals(1, sc.write(bb)); out.printf("wrote: 0x%x%n", bb.get(0)); - assertEquals(sc.read(bb.clear()), -1); + assertEquals(-1, sc.read(bb.clear())); } t.awaitCompletion(); @@ -336,7 +342,8 @@ public class IOExchanges { // Similar to the previous six scenarios, but with same-thread // non-blocking connect. - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void BAccep_NBConn_BIO_WR_7(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -352,16 +359,16 @@ public class IOExchanges { sc.configureBlocking(true); TestThread t = TestThread.of("t7", () -> { ByteBuffer bb = ByteBuffer.allocate(10).put((byte) 0x07).flip(); - assertEquals(sc.write(bb), 1); + assertEquals(1, sc.write(bb)); out.printf("wrote: 0x%x%n", bb.get(0)); - assertEquals(sc.read(bb.clear()), -1); + assertEquals(-1, sc.read(bb.clear())); }); t.start(); ByteBuffer bb = ByteBuffer.allocate(10); - assertEquals(sc2.read(bb), 1); + assertEquals(1, sc2.read(bb)); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x07); + assertEquals(0x07, bb.get(0)); sc2.shutdownOutput(); t.awaitCompletion(); } @@ -370,7 +377,8 @@ public class IOExchanges { } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void BAccep_NBConn_BIO_RW_8(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -386,17 +394,17 @@ public class IOExchanges { sc.configureBlocking(true); TestThread t = TestThread.of("t8", () -> { ByteBuffer bb = ByteBuffer.allocate(10); - assertEquals(sc.read(bb), 1); + assertEquals(1, sc.read(bb)); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x08); + assertEquals(0x08, bb.get(0)); sc.shutdownOutput(); }); t.start(); ByteBuffer bb = ByteBuffer.allocate(10).put((byte) 0x08).flip(); - assertEquals(sc2.write(bb), 1); + assertEquals(1, sc2.write(bb)); out.printf("wrote: 0x%x%n", bb.get(0)); - assertEquals(sc2.read(bb.clear()), -1); + assertEquals(-1, sc2.read(bb.clear())); t.awaitCompletion(); } } @@ -404,7 +412,8 @@ public class IOExchanges { } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SELNBAccep_NBConn_BIO_WR_9(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -417,23 +426,23 @@ public class IOExchanges { sc.connect(addr); ssc.configureBlocking(false).register(selector, OP_ACCEPT); - assertEquals(selector.select(), 1); + assertEquals(1, selector.select()); try (SocketChannel sc2 = ssc.accept()) { assertTrue(sc.finishConnect()); sc.configureBlocking(true); TestThread t = TestThread.of("t9", () -> { ByteBuffer bb = ByteBuffer.allocate(10).put((byte) 0x09).flip(); - assertEquals(sc.write(bb), 1); + assertEquals(1, sc.write(bb)); out.printf("wrote: 0x%x%n", bb.get(0)); - assertEquals(sc.read(bb.clear()), -1); + assertEquals(-1, sc.read(bb.clear())); }); t.start(); ByteBuffer bb = ByteBuffer.allocate(10); - assertEquals(sc2.read(bb), 1); + assertEquals(1, sc2.read(bb)); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x09); + assertEquals(0x09, bb.get(0)); sc2.shutdownOutput(); t.awaitCompletion(); } @@ -442,7 +451,8 @@ public class IOExchanges { } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SELNBAccep_NBConn_BIO_RW_10(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -455,24 +465,24 @@ public class IOExchanges { sc.connect(addr); ssc.configureBlocking(false).register(selector, OP_ACCEPT); - assertEquals(selector.select(), 1); + assertEquals(1, selector.select()); try (SocketChannel sc2 = ssc.accept()) { assertTrue(sc.finishConnect()); sc.configureBlocking(true); TestThread t = TestThread.of("t10", () -> { ByteBuffer bb = ByteBuffer.allocate(10); - assertEquals(sc.read(bb), 1); + assertEquals(1, sc.read(bb)); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x10); + assertEquals(0x10, bb.get(0)); sc.shutdownOutput(); }); t.start(); ByteBuffer bb = ByteBuffer.allocate(10).put((byte) 0x10).flip(); - assertEquals(sc2.write(bb), 1); + assertEquals(1, sc2.write(bb)); out.printf("wrote: 0x%x%n", bb.get(0)); - assertEquals(sc2.read(bb.clear()), -1); + assertEquals(-1, sc2.read(bb.clear())); t.awaitCompletion(); } } @@ -480,7 +490,8 @@ public class IOExchanges { } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SPINNBAccep_NBConn_BIO_WR_11(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -506,16 +517,16 @@ public class IOExchanges { sc.configureBlocking(true); TestThread t = TestThread.of("t11", () -> { ByteBuffer bb = ByteBuffer.allocate(10).put((byte) 0x11).flip(); - assertEquals(sc.write(bb), 1); + assertEquals(1, sc.write(bb)); out.printf("wrote: 0x%x%n", bb.get(0)); - assertEquals(sc.read(bb.clear()), -1); + assertEquals(-1, sc.read(bb.clear())); }); t.start(); ByteBuffer bb = ByteBuffer.allocate(10); - assertEquals(sc2.read(bb), 1); + assertEquals(1, sc2.read(bb)); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x11); + assertEquals(0x11, bb.get(0)); sc2.shutdownOutput(); t.awaitCompletion(); } @@ -524,7 +535,8 @@ public class IOExchanges { } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SPINNBAccep_NBConn_BIO_RW_12(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -550,17 +562,17 @@ public class IOExchanges { sc.configureBlocking(true); TestThread t = TestThread.of("t12", () -> { ByteBuffer bb = ByteBuffer.allocate(10); - assertEquals(sc.read(bb), 1); + assertEquals(1, sc.read(bb)); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x12); + assertEquals(0x12, bb.get(0)); sc.shutdownOutput(); }); t.start(); ByteBuffer bb = ByteBuffer.allocate(10).put((byte) 0x12).flip(); - assertEquals(sc2.write(bb), 1); + assertEquals(1, sc2.write(bb)); out.printf("wrote: 0x%x%n", bb.get(0)); - assertEquals(sc2.read(bb.clear()), -1); + assertEquals(-1, sc2.read(bb.clear())); t.awaitCompletion(); } } @@ -572,7 +584,8 @@ public class IOExchanges { // Similar to the previous twelve scenarios but with non-blocking IO // --- - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void BAccep_BConn_NBIO_WR_1a(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -589,13 +602,13 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.write(bb)) < 1) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("wrote: 0x%x%n", bb.get(0)); k.interestOps(OP_READ); selector.select(); bb.clear(); while ((c = sc.read(bb)) == 0) ; - assertEquals(c, -1); + assertEquals(-1, c); } }); t.start(); @@ -608,16 +621,17 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.read(bb)) == 0) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x1A); + assertEquals(0x1A, bb.get(0)); } t.awaitCompletion(); deleteFile(addr); } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void BAccep_BConn_NBIO_RW_2a(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -634,9 +648,9 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.read(bb)) == 0) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x2A); + assertEquals(0x2A, bb.get(0)); } }); t.start(); @@ -649,20 +663,21 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.write(bb)) < 1) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("wrote: 0x%x%n", bb.get(0)); k.interestOps(OP_READ); selector.select(); bb.clear(); while ((c = sc.read(bb)) == 0) ; - assertEquals(c, -1); + assertEquals(-1, c); } t.awaitCompletion(); deleteFile(addr); } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SELNBAccep_BConn_NBIO_WR_3a(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family); @@ -680,19 +695,19 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.write(bb)) < 1) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("wrote: 0x%x%n", bb.get(0)); k.interestOps(OP_READ); selector.select(); bb.clear(); while ((c = sc.read(bb)) == 0) ; - assertEquals(c, -1); + assertEquals(-1, c); } }); t.start(); ssc.configureBlocking(false).register(aselector, OP_ACCEPT); - assertEquals(aselector.select(), 1); + assertEquals(1, aselector.select()); try (SocketChannel sc = ssc.accept(); Selector selector = Selector.open()) { @@ -702,16 +717,17 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.read(bb)) == 0) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x3A); + assertEquals(0x3A, bb.get(0)); } t.awaitCompletion(); deleteFile(addr); } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SELNBAccep_BConn_NBIO_RW_4a(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family); @@ -729,15 +745,15 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.read(bb)) == 0) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x4A); + assertEquals(0x4A, bb.get(0)); } }); t.start(); ssc.configureBlocking(false).register(aselector, OP_ACCEPT); - assertEquals(aselector.select(), 1); + assertEquals(1, aselector.select()); try (SocketChannel sc = ssc.accept(); Selector selector = Selector.open()) { @@ -747,20 +763,21 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.write(bb)) < 1) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("wrote: 0x%x%n", bb.get(0)); k.interestOps(OP_READ); selector.select(); bb.clear(); while ((c = sc.read(bb)) == 0) ; - assertEquals(c, -1); + assertEquals(-1, c); } t.awaitCompletion(); deleteFile(addr); } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SPINNBAccep_BConn_NBIO_WR_5a(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -777,13 +794,13 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.write(bb)) < 1) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("wrote: 0x%x%n", bb.get(0)); k.interestOps(OP_READ); selector.select(); bb.clear(); while ((c = sc.read(bb)) == 0) ; - assertEquals(c, -1); + assertEquals(-1, c); } }); t.start(); @@ -806,16 +823,17 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.read(bb)) == 0) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x5A); + assertEquals(0x5A, bb.get(0)); } t.awaitCompletion(); deleteFile(addr); } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SPINNBAccep_BConn_NBIO_RW_6a(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -832,9 +850,9 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.read(bb)) == 0) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x6A); + assertEquals(0x6A, bb.get(0)); } }); t.start(); @@ -857,13 +875,13 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.write(bb)) < 1) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("wrote: 0x%x%n", bb.get(0)); k.interestOps(OP_READ); selector.select(); bb.clear(); while ((c = sc.read(bb)) == 0) ; - assertEquals(c, -1); + assertEquals(-1, c); } t.awaitCompletion(); @@ -874,7 +892,8 @@ public class IOExchanges { // Similar to the previous six scenarios but with same-thread // non-blocking connect. - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void BAccep_NBConn_NBIO_WR_7a(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -895,13 +914,13 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.write(bb)) < 1) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("wrote: 0x%x%n", bb.get(0)); k.interestOps(OP_READ); selector.select(); bb.clear(); while ((c = sc.read(bb)) == 0) ; - assertEquals(c, -1); + assertEquals(-1, c); } }); t.start(); @@ -913,9 +932,9 @@ public class IOExchanges { selector.select(); int c; while ((c = sc2.read(bb)) == 0) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), 0x7A); + assertEquals(0x7A, bb.get(0)); sc2.shutdownOutput(); } t.awaitCompletion(); @@ -925,7 +944,8 @@ public class IOExchanges { } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void BAccep_NBConn_NBIO_RW_8a(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -945,9 +965,9 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.read(bb)) == 0) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), (byte) 0x8A); + assertEquals((byte) 0x8A, bb.get(0)); sc.shutdownOutput(); } }); @@ -960,13 +980,13 @@ public class IOExchanges { selector.select(); int c; while ((c = sc2.write(bb)) < 1) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("wrote: 0x%x%n", bb.get(0)); k.interestOps(OP_READ); selector.select(); bb.clear(); while ((c = sc2.read(bb)) == 0) ; - assertEquals(c, -1); + assertEquals(-1, c); } t.awaitCompletion(); } @@ -975,7 +995,8 @@ public class IOExchanges { } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SELNBAccep_NBConn_NBIO_WR_9a(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -988,7 +1009,7 @@ public class IOExchanges { Selector aselector = Selector.open(); ssc.configureBlocking(false).register(aselector, OP_ACCEPT); - assertEquals(aselector.select(), 1); + assertEquals(1, aselector.select()); try (SocketChannel sc2 = ssc.accept()) { assertTrue(sc.finishConnect()); @@ -1000,13 +1021,13 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.write(bb)) < 1) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("wrote: 0x%x%n", bb.get(0)); k.interestOps(OP_READ); selector.select(); bb.clear(); while ((c = sc.read(bb)) == 0) ; - assertEquals(c, -1); + assertEquals(-1, c); } }); t.start(); @@ -1018,9 +1039,9 @@ public class IOExchanges { selector.select(); int c; while ((c = sc2.read(bb)) == 0) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), (byte) 0x9A); + assertEquals((byte) 0x9A, bb.get(0)); sc2.shutdownOutput(); } t.awaitCompletion(); @@ -1030,7 +1051,8 @@ public class IOExchanges { } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SELNBAccep_NBConn_NBIO_RW_10a(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -1043,7 +1065,7 @@ public class IOExchanges { Selector aselector = Selector.open(); ssc.configureBlocking(false).register(aselector, OP_ACCEPT); - assertEquals(aselector.select(), 1); + assertEquals(1, aselector.select()); try (SocketChannel sc2 = ssc.accept()) { assertTrue(sc.finishConnect()); @@ -1054,9 +1076,9 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.read(bb)) == 0) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), (byte) 0xAA); + assertEquals((byte) 0xAA, bb.get(0)); sc.shutdownOutput(); } }); @@ -1069,13 +1091,13 @@ public class IOExchanges { selector.select(); int c; while ((c = sc2.write(bb)) < 1) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("wrote: 0x%x%n", bb.get(0)); k.interestOps(OP_READ); selector.select(); bb.clear(); while ((c = sc2.read(bb)) == 0) ; - assertEquals(c, -1); + assertEquals(-1, c); } t.awaitCompletion(); } @@ -1084,7 +1106,8 @@ public class IOExchanges { } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SPINBAccep_NBConn_NBIO_WR_11a(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -1115,13 +1138,13 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.write(bb)) < 1) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("wrote: 0x%x%n", bb.get(0)); k.interestOps(OP_READ); selector.select(); bb.clear(); while ((c = sc.read(bb)) == 0) ; - assertEquals(c, -1); + assertEquals(-1, c); } }); t.start(); @@ -1133,9 +1156,9 @@ public class IOExchanges { selector.select(); int c; while ((c = sc2.read(bb)) == 0) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), (byte) 0xBA); + assertEquals((byte) 0xBA, bb.get(0)); sc2.shutdownOutput(); } t.awaitCompletion(); @@ -1145,7 +1168,8 @@ public class IOExchanges { } } - @Test(dataProvider = "family") + @ParameterizedTest + @MethodSource("family") public void SPINBAccep_NBConn_NBIO_RW_12a(ProtocolFamily family) throws Throwable { try (ServerSocketChannel ssc = openServerSocketChannel(family)) { @@ -1175,9 +1199,9 @@ public class IOExchanges { selector.select(); int c; while ((c = sc.read(bb)) == 0) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("read: 0x%x%n", bb.get(0)); - assertEquals(bb.get(0), (byte) 0xCA); + assertEquals((byte) 0xCA, bb.get(0)); sc.shutdownOutput(); } }); @@ -1190,13 +1214,13 @@ public class IOExchanges { selector.select(); int c; while ((c = sc2.write(bb)) < 1) ; - assertEquals(c, 1); + assertEquals(1, c); out.printf("wrote: 0x%x%n", bb.get(0)); k.interestOps(OP_READ); selector.select(); bb.clear(); while ((c = sc2.read(bb)) == 0) ; - assertEquals(c, -1); + assertEquals(-1, c); } t.awaitCompletion(); } diff --git a/test/jdk/java/nio/channels/unixdomain/NullTest.java b/test/jdk/java/nio/channels/unixdomain/NullTest.java index ed96cfdecc9..97a5b1d1b8e 100644 --- a/test/jdk/java/nio/channels/unixdomain/NullTest.java +++ b/test/jdk/java/nio/channels/unixdomain/NullTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,10 +21,10 @@ * questions. */ -/** +/* * @test * @bug 8245194 - * @run testng NullTest + * @run junit NullTest */ import java.net.ProtocolFamily; @@ -32,8 +32,9 @@ import java.net.SocketAddress; import java.net.UnixDomainSocketAddress; import java.nio.channels.*; import java.nio.file.Path; -import org.testng.annotations.Test; -import static org.testng.Assert.assertThrows; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Check for NPE @@ -45,7 +46,7 @@ public class NullTest { NullPointerException.class; @Test - public static void runTest() throws Exception { + public void runTest() throws Exception { assertThrows(NPE, () -> SocketChannel.open((ProtocolFamily)null)); assertThrows(NPE, () -> SocketChannel.open((SocketAddress)null)); assertThrows(NPE, () -> ServerSocketChannel.open((ProtocolFamily)null)); From 1e3fb444c5aa0038d123c0f43858c1c4c684d958 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Thu, 26 Mar 2026 13:54:02 +0000 Subject: [PATCH 133/160] 8380409: JVM crashes when -XX:AOTMode=create uses app.aotconf generated with JVMTI agent Reviewed-by: kvn, asmehra --- src/hotspot/share/cds/aotArtifactFinder.cpp | 27 +++++- src/hotspot/share/cds/aotArtifactFinder.hpp | 4 +- src/hotspot/share/cds/heapShared.cpp | 1 + .../aotCache/RedefineCriticalClasses.java | 83 +++++++++++++++++++ .../aotCache/TransformCriticalClasses.java | 72 ++++++++++++++++ 5 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/RedefineCriticalClasses.java create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/TransformCriticalClasses.java diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index f85f1e46520..bd69b18a1aa 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ #include "cds/lambdaProxyClassDictionary.hpp" #include "cds/regeneratedClasses.hpp" #include "classfile/systemDictionaryShared.hpp" +#include "classfile/vmClasses.hpp" #include "logging/log.hpp" #include "memory/metaspaceClosure.hpp" #include "oops/instanceKlass.hpp" @@ -169,6 +170,7 @@ void AOTArtifactFinder::find_artifacts() { end_scanning_for_oops(); TrainingData::cleanup_training_data(); + check_critical_classes(); } void AOTArtifactFinder::start_scanning_for_oops() { @@ -233,6 +235,7 @@ void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) { bool created; _seen_classes->put_if_absent(ik, &created); if (created) { + check_critical_class(ik); append_to_all_cached_classes(ik); // All super types must be added. @@ -310,3 +313,25 @@ void AOTArtifactFinder::all_cached_classes_do(MetaspaceClosure* it) { it->push(_all_cached_classes->adr_at(i)); } } + +void AOTArtifactFinder::check_critical_classes() { + if (CDSConfig::is_dumping_static_archive()) { + // vmClasses are store in the AOT cache (or AOT config file, or static archive). + // If any of the vmClasses is excluded, (usually due to incompatible JVMTI agent), + // the resulting cache/config/archive is unusable. + for (auto id : EnumRange{}) { + check_critical_class(vmClasses::klass_at(id)); + } + } +} + +void AOTArtifactFinder::check_critical_class(InstanceKlass* ik) { + if (SystemDictionaryShared::is_excluded_class(ik)) { + ResourceMark rm; + const char* msg = err_msg("Critical class %s has been excluded. %s cannot be written.", + ik->external_name(), + CDSConfig::type_of_archive_being_written()); + AOTMetaspace::unrecoverable_writing_error(msg); + } +} + diff --git a/src/hotspot/share/cds/aotArtifactFinder.hpp b/src/hotspot/share/cds/aotArtifactFinder.hpp index 05bcde6b0ac..50057b6caee 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.hpp +++ b/src/hotspot/share/cds/aotArtifactFinder.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -81,12 +81,14 @@ class AOTArtifactFinder : AllStatic { static void add_cached_type_array_class(TypeArrayKlass* tak); static void add_cached_instance_class(InstanceKlass* ik); static void append_to_all_cached_classes(Klass* k); + static void check_critical_class(InstanceKlass* ik); public: static void initialize(); static void find_artifacts(); static void add_cached_class(Klass* k); static void add_aot_inited_class(InstanceKlass* ik); static void all_cached_classes_do(MetaspaceClosure* it); + static void check_critical_classes(); static void dispose(); }; diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 708a19e9a7d..3d10e7e1f88 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -564,6 +564,7 @@ bool HeapShared::archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgra return false; } + AOTArtifactFinder::add_cached_class(obj->klass()); AOTOopChecker::check(obj); // Make sure contents of this oop are safe. count_allocation(obj->size()); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/RedefineCriticalClasses.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/RedefineCriticalClasses.java new file mode 100644 index 00000000000..c4753ee3878 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/RedefineCriticalClasses.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 AOT training run should fail if critical classes have been redefined by JVMTI + * @bug 8380409 + * @requires vm.cds.supports.aot.class.linking + * @library /test/lib + * @run driver RedefineClassHelper + * @build RedefineCriticalClasses + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar RedefineCriticalClassesApp + * @run driver RedefineCriticalClasses + */ + +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.ProcessTools; + +public class RedefineCriticalClasses { + public static void main(String... args) throws Exception { + ArrayList processArgs = new ArrayList<>(); + + // redefineagent.jar is created by "@run driver RedefineClassHelper" + processArgs.add("-javaagent:redefineagent.jar"); + + processArgs.add("-XX:AOTMode=record"); + processArgs.add("-XX:AOTConfiguration=app.aotconfig"); + processArgs.add("-Xlog:aot,cds"); + processArgs.add("-cp"); + processArgs.add("app.jar"); + processArgs.add("RedefineCriticalClassesApp"); + + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(processArgs); + CDSTestUtils.executeAndLog(pb, "train") + .shouldContain("Redefined: class java.lang.Class") + .shouldContain("Skipping java/lang/Class: Has been redefined") + .shouldContain("Critical class java.lang.Class has been excluded. AOT configuration file cannot be written") + .shouldHaveExitValue(1); + } +} + +class RedefineCriticalClassesApp { + public static void main(String[] args) throws Exception { + // Use RedefineClassHelper (loaded from redefineagent.jar into the boot class loader) + // to redefine java/lang/Class, using the exact same bytecodes as from the JDK. + // The JVM will mark it as having been redefined by JVMTI and will exclude it from the + // AOT configuration file. + + URL url = new URL("jrt:/java.base/java/lang/Class.class"); + try (InputStream in = url.openConnection().getInputStream()) { + byte[] b = in.readAllBytes(); + System.out.println("Length = " + b.length); + RedefineClassHelper.redefineClass(Class.class, b); + System.out.println("Redefined: " + Class.class); + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/TransformCriticalClasses.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/TransformCriticalClasses.java new file mode 100644 index 00000000000..9f25d58e01f --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/TransformCriticalClasses.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 AOT training run should fail if critical classes have been transformed by JVMTI + * with ClassFileLoadHook + * @bug 8380409 + * @requires vm.cds.supports.aot.class.linking + * @library /test/lib + * @build TransformCriticalClasses + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar TransformCriticalClassesApp + * @run main/othervm/native TransformCriticalClasses + */ + +import java.util.ArrayList; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.ProcessTools; + +public class TransformCriticalClasses { + public static void main(String... args) throws Exception { + ArrayList processArgs = new ArrayList<>(); + + // Tell the native agent SimpleClassFileLoadHook to do an dummy transformation + // of java/lang/Class. This class will be defined using the exact same bytecodes + // as from the JDK, but the JVM will mark it as having been transformed by JVMTI + // and will exclude it from the AOT configuration file. + processArgs.add("-agentlib:SimpleClassFileLoadHook=-early,java/lang/Class,xxxxxx,xxxxxx"); + + processArgs.add("-XX:AOTMode=record"); + processArgs.add("-XX:AOTConfiguration=app.aotconfig"); + processArgs.add("-Xlog:aot,cds"); + processArgs.add("-cp"); + processArgs.add("app.jar"); + processArgs.add("TransformCriticalClassesApp"); + + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(processArgs); + CDSTestUtils.executeAndLog(pb, "train") + .shouldContain("Skipping java/lang/Class: From ClassFileLoadHook") + .shouldContain("Critical class java.lang.Class has been excluded. AOT configuration file cannot be written") + .shouldHaveExitValue(1); + } +} + +class TransformCriticalClassesApp { + public static void main(String[] args) { + System.out.println("HelloWorld"); + } +} From b3048bbed0ab050f139d117e11657a2eef081861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20H=C3=BCbner?= Date: Thu, 26 Mar 2026 14:09:17 +0000 Subject: [PATCH 134/160] 8370504: InterpreterMacroAssembler::profile_virtual_call parameter 'receiver_can_be_null' is unused Reviewed-by: aseoane, azafari, mchevalier, fbredberg --- src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp | 15 ++------------- src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp | 5 ++--- src/hotspot/cpu/arm/interp_masm_arm.cpp | 15 ++------------- src/hotspot/cpu/arm/interp_masm_arm.hpp | 5 ++--- src/hotspot/cpu/ppc/interp_masm_ppc.hpp | 4 ++-- src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp | 17 ++--------------- src/hotspot/cpu/ppc/templateTable_ppc_64.cpp | 6 +++--- src/hotspot/cpu/riscv/interp_masm_riscv.cpp | 15 ++------------- src/hotspot/cpu/riscv/interp_masm_riscv.hpp | 5 ++--- src/hotspot/cpu/s390/interp_masm_s390.cpp | 16 ++-------------- src/hotspot/cpu/s390/interp_masm_s390.hpp | 5 ++--- src/hotspot/cpu/x86/interp_masm_x86.cpp | 17 ++--------------- src/hotspot/cpu/x86/interp_masm_x86.hpp | 5 ++--- 13 files changed, 27 insertions(+), 103 deletions(-) diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index 2b506b241e0..980fedb406d 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -989,26 +989,15 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) { void InterpreterMacroAssembler::profile_virtual_call(Register receiver, - Register mdp, - bool receiver_can_be_null) { + Register mdp) { if (ProfileInterpreter) { Label profile_continue; // If no method data exists, go to profile_continue. test_method_data_pointer(mdp, profile_continue); - Label skip_receiver_profile; - if (receiver_can_be_null) { - Label not_null; - // We are making a call. Increment the count for null receiver. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - b(skip_receiver_profile); - bind(not_null); - } - // Record the receiver type. profile_receiver_type(receiver, mdp, 0); - bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(mdp, in_bytes(VirtualCallData::virtual_call_data_size())); diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp index 74d4430000d..9a074f1ce69 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2015, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -285,8 +285,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_not_taken_branch(Register mdp); void profile_call(Register mdp); void profile_final_call(Register mdp); - void profile_virtual_call(Register receiver, Register mdp, - bool receiver_can_be_null = false); + void profile_virtual_call(Register receiver, Register mdp); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); void profile_typecheck(Register mdp, Register klass); diff --git a/src/hotspot/cpu/arm/interp_masm_arm.cpp b/src/hotspot/cpu/arm/interp_masm_arm.cpp index 23ecea24eb2..aee407864ee 100644 --- a/src/hotspot/cpu/arm/interp_masm_arm.cpp +++ b/src/hotspot/cpu/arm/interp_masm_arm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1210,7 +1210,7 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) { // Sets mdp, blows Rtemp. -void InterpreterMacroAssembler::profile_virtual_call(Register mdp, Register receiver, bool receiver_can_be_null) { +void InterpreterMacroAssembler::profile_virtual_call(Register mdp, Register receiver) { assert_different_registers(mdp, receiver, Rtemp); if (ProfileInterpreter) { @@ -1219,19 +1219,8 @@ void InterpreterMacroAssembler::profile_virtual_call(Register mdp, Register rece // If no method data exists, go to profile_continue. test_method_data_pointer(mdp, profile_continue); - Label skip_receiver_profile; - if (receiver_can_be_null) { - Label not_null; - cbnz(receiver, not_null); - // We are making a call. Increment the count for null receiver. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()), Rtemp); - b(skip_receiver_profile); - bind(not_null); - } - // Record the receiver type. record_klass_in_profile(receiver, mdp, Rtemp, true); - bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(mdp, in_bytes(VirtualCallData::virtual_call_data_size())); diff --git a/src/hotspot/cpu/arm/interp_masm_arm.hpp b/src/hotspot/cpu/arm/interp_masm_arm.hpp index 530be1c577e..147cd252b2c 100644 --- a/src/hotspot/cpu/arm/interp_masm_arm.hpp +++ b/src/hotspot/cpu/arm/interp_masm_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -239,8 +239,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_call(Register mdp); // Sets mdp, blows Rtemp. void profile_final_call(Register mdp); // Sets mdp, blows Rtemp. - void profile_virtual_call(Register mdp, Register receiver, // Sets mdp, blows Rtemp. - bool receiver_can_be_null = false); + void profile_virtual_call(Register mdp, Register receiver); // Sets mdp, blows Rtemp. void profile_ret(Register mdp, Register return_bci); // Sets mdp, blows R0-R3/R0-R18, Rtemp, LR void profile_null_seen(Register mdp); // Sets mdp. void profile_typecheck(Register mdp, Register klass); // Sets mdp, blows Rtemp. diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp index 4ea33ebaf63..275ff92c699 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -258,7 +258,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_not_taken_branch(Register scratch1, Register scratch2); void profile_call(Register scratch1, Register scratch2); void profile_final_call(Register scratch1, Register scratch2); - void profile_virtual_call(Register Rreceiver, Register Rscratch1, Register Rscratch2, bool receiver_can_be_null); + void profile_virtual_call(Register Rreceiver, Register Rscratch1, Register Rscratch2); void profile_typecheck(Register Rklass, Register Rscratch1, Register Rscratch2); void profile_ret(TosState state, Register return_bci, Register scratch1, Register scratch2); void profile_switch_default(Register scratch1, Register scratch2); diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index f7bf457f72c..56eade8e533 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1340,28 +1340,15 @@ void InterpreterMacroAssembler::profile_final_call(Register scratch1, Register s // Count a virtual call in the bytecodes. void InterpreterMacroAssembler::profile_virtual_call(Register Rreceiver, Register Rscratch1, - Register Rscratch2, - bool receiver_can_be_null) { + Register Rscratch2) { if (!ProfileInterpreter) { return; } Label profile_continue; // If no method data exists, go to profile_continue. test_method_data_pointer(profile_continue); - Label skip_receiver_profile; - if (receiver_can_be_null) { - Label not_null; - cmpdi(CR0, Rreceiver, 0); - bne(CR0, not_null); - // We are making a call. Increment the count for null receiver. - increment_mdp_data_at(in_bytes(CounterData::count_offset()), Rscratch1, Rscratch2); - b(skip_receiver_profile); - bind(not_null); - } - // Record the receiver type. record_klass_in_profile(Rreceiver, Rscratch1, Rscratch2); - bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(in_bytes(VirtualCallData::virtual_call_data_size())); diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index 8a3af748fa1..37f780535b4 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -3489,7 +3489,7 @@ void TemplateTable::invokevirtual(int byte_no) { // Get receiver klass. __ load_klass_check_null_throw(Rrecv_klass, Rrecv, R11_scratch1); __ verify_klass_ptr(Rrecv_klass); - __ profile_virtual_call(Rrecv_klass, R11_scratch1, R12_scratch2, false); + __ profile_virtual_call(Rrecv_klass, R11_scratch1, R12_scratch2); generate_vtable_call(Rrecv_klass, Rvtableindex_or_method, Rret_addr, R11_scratch1); } @@ -3596,7 +3596,7 @@ void TemplateTable::invokeinterface_object_method(Register Rrecv_klass, // Non-final callc case. __ bind(LnotFinal); __ lhz(Rindex, in_bytes(ResolvedMethodEntry::table_index_offset()), Rcache); - __ profile_virtual_call(Rrecv_klass, Rtemp1, Rscratch, false); + __ profile_virtual_call(Rrecv_klass, Rtemp1, Rscratch); generate_vtable_call(Rrecv_klass, Rindex, Rret, Rscratch); } @@ -3664,7 +3664,7 @@ void TemplateTable::invokeinterface(int byte_no) { __ lookup_interface_method(Rrecv_klass, Rinterface_klass, noreg, noreg, Rscratch1, Rscratch2, L_no_such_interface, /*return_method=*/false); - __ profile_virtual_call(Rrecv_klass, Rscratch1, Rscratch2, false); + __ profile_virtual_call(Rrecv_klass, Rscratch1, Rscratch2); // Find entry point to call. diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp index 744590bec2b..804c2072ba5 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -1040,26 +1040,15 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) { void InterpreterMacroAssembler::profile_virtual_call(Register receiver, - Register mdp, - bool receiver_can_be_null) { + Register mdp) { if (ProfileInterpreter) { Label profile_continue; // If no method data exists, go to profile_continue. test_method_data_pointer(mdp, profile_continue); - Label skip_receiver_profile; - if (receiver_can_be_null) { - Label not_null; - // We are making a call. Increment the count for null receiver. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - j(skip_receiver_profile); - bind(not_null); - } - // Record the receiver type. profile_receiver_type(receiver, mdp, 0); - bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp index 59cc76b022f..df86f0dc532 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2015, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -274,8 +274,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_not_taken_branch(Register mdp); void profile_call(Register mdp); void profile_final_call(Register mdp); - void profile_virtual_call(Register receiver, Register mdp, - bool receiver_can_be_null = false); + void profile_virtual_call(Register receiver, Register mdp); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); void profile_typecheck(Register mdp, Register klass); diff --git a/src/hotspot/cpu/s390/interp_masm_s390.cpp b/src/hotspot/cpu/s390/interp_masm_s390.cpp index a80ca26239b..d5239898dd7 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.cpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1259,27 +1259,15 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) { void InterpreterMacroAssembler::profile_virtual_call(Register receiver, Register mdp, - Register reg2, - bool receiver_can_be_null) { + Register reg2) { if (ProfileInterpreter) { NearLabel profile_continue; // If no method data exists, go to profile_continue. test_method_data_pointer(mdp, profile_continue); - NearLabel skip_receiver_profile; - if (receiver_can_be_null) { - NearLabel not_null; - compareU64_and_branch(receiver, (intptr_t)0L, bcondNotEqual, not_null); - // We are making a call. Increment the count for null receiver. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - z_bru(skip_receiver_profile); - bind(not_null); - } - // Record the receiver type. record_klass_in_profile(receiver, mdp, reg2); - bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(mdp, in_bytes(VirtualCallData::virtual_call_data_size())); diff --git a/src/hotspot/cpu/s390/interp_masm_s390.hpp b/src/hotspot/cpu/s390/interp_masm_s390.hpp index d981f9ea01e..b816185b065 100644 --- a/src/hotspot/cpu/s390/interp_masm_s390.hpp +++ b/src/hotspot/cpu/s390/interp_masm_s390.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -296,8 +296,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_call(Register mdp); void profile_final_call(Register mdp); void profile_virtual_call(Register receiver, Register mdp, - Register scratch2, - bool receiver_can_be_null = false); + Register scratch2); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); void profile_typecheck(Register mdp, Register klass, Register scratch); diff --git a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp index b2ea4143ac4..a38971c86fb 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.cpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1392,28 +1392,15 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) { void InterpreterMacroAssembler::profile_virtual_call(Register receiver, - Register mdp, - bool receiver_can_be_null) { + Register mdp) { if (ProfileInterpreter) { Label profile_continue; // If no method data exists, go to profile_continue. test_method_data_pointer(mdp, profile_continue); - Label skip_receiver_profile; - if (receiver_can_be_null) { - Label not_null; - testptr(receiver, receiver); - jccb(Assembler::notZero, not_null); - // We are making a call. Increment the count for null receiver. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - jmp(skip_receiver_profile); - bind(not_null); - } - // Record the receiver type. profile_receiver_type(receiver, mdp, 0); - bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(mdp, in_bytes(VirtualCallData::virtual_call_data_size())); diff --git a/src/hotspot/cpu/x86/interp_masm_x86.hpp b/src/hotspot/cpu/x86/interp_masm_x86.hpp index 4114028f78e..dfbd7ab64e0 100644 --- a/src/hotspot/cpu/x86/interp_masm_x86.hpp +++ b/src/hotspot/cpu/x86/interp_masm_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -243,8 +243,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_not_taken_branch(Register mdp); void profile_call(Register mdp); void profile_final_call(Register mdp); - void profile_virtual_call(Register receiver, Register mdp, - bool receiver_can_be_null = false); + void profile_virtual_call(Register receiver, Register mdp); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); void profile_typecheck(Register mdp, Register klass); From 22831cb0d8f828327b49f45e04f2a62ae33754ee Mon Sep 17 00:00:00 2001 From: Kerem Kat Date: Thu, 26 Mar 2026 14:15:11 +0000 Subject: [PATCH 135/160] 8378005: Exclude boxing calls from guaranteed safepoint check in loop opts Reviewed-by: mhaessig, chagedorn --- src/hotspot/share/opto/loopnode.cpp | 16 +++- .../TestBoxingInfiniteLoopBrokenIf.java | 88 +++++++++++++++++++ ...TestStressLongCountedLoopInfiniteLoop.java | 75 ++++++++++++++++ 3 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestBoxingInfiniteLoopBrokenIf.java create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopInfiniteLoop.java diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 686a0a05f66..23186a25320 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -4345,8 +4345,13 @@ void IdealLoopTree::allpaths_check_safepts(VectorSet &visited, Node_List &stack) visited.set(_head->_idx); while (stack.size() > 0) { Node* n = stack.pop(); - if (n->is_Call() && n->as_Call()->guaranteed_safepoint()) { - // Terminate this path + if (n->is_Call() && n->as_Call()->guaranteed_safepoint() + && !(n->is_CallStaticJava() && n->as_CallStaticJava()->is_boxing_method())) { + // Terminate this path: guaranteed safepoint found. + // Boxing CallStaticJava calls are excluded as they may lack a safepoint on the fast path. This is + // not done via CallStaticJavaNode::guaranteed_safepoint() as that also controls PcDesc emission. + // In the future, guaranteed_safepoint() should be reworked to correctly handle boxing methods + // to avoid this additional check. } else if (n->Opcode() == Op_SafePoint) { if (_phase->get_loop(n) != this) { if (_required_safept == nullptr) _required_safept = new Node_List(); @@ -4444,7 +4449,12 @@ void IdealLoopTree::check_safepts(VectorSet &visited, Node_List &stack) { if (!_irreducible) { // Scan the dom-path nodes from tail to head for (Node* n = tail(); n != _head; n = _phase->idom(n)) { - if (n->is_Call() && n->as_Call()->guaranteed_safepoint()) { + // Boxing CallStaticJava calls are excluded as they may lack a safepoint on the fast path. This is + // not done via CallStaticJavaNode::guaranteed_safepoint() as that also controls PcDesc emission. + // In the future, guaranteed_safepoint() should be reworked to correctly handle boxing methods + // to avoid this additional check. + if (n->is_Call() && n->as_Call()->guaranteed_safepoint() + && !(n->is_CallStaticJava() && n->as_CallStaticJava()->is_boxing_method())) { has_call = true; _has_sfpt = 1; // Then no need for a safept! break; diff --git a/test/hotspot/jtreg/compiler/loopopts/TestBoxingInfiniteLoopBrokenIf.java b/test/hotspot/jtreg/compiler/loopopts/TestBoxingInfiniteLoopBrokenIf.java new file mode 100644 index 00000000000..18b4816445b --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestBoxingInfiniteLoopBrokenIf.java @@ -0,0 +1,88 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 8378005 + * @summary Verify that an infinite loop with boxing does not crash when the containing If is broken + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.loopopts; +import compiler.lib.ir_framework.*; +import jdk.test.lib.Utils; + +public class TestBoxingInfiniteLoopBrokenIf { + static boolean flag; + static volatile boolean testReturned; + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + framework.addScenarios( + new Scenario(0), + new Scenario(1, "-XX:PerMethodTrapLimit=0", "-XX:CompileCommand=dontinline,*::valueOf", "-XX:CompileCommand=dontinline,*::throwWrongPath"), + new Scenario(2, "-XX:PerMethodTrapLimit=0", "-XX:CompileCommand=dontinline,*::valueOf", "-XX:CompileCommand=dontinline,*::throwWrongPath", + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:StressLongCountedLoop=1") + ); + framework.start(); + } + + @Test + static void test() { + new Integer(0); // Enable EA + Loop opts + + if (flag) { // Always false + throwWrongPath(); // Not inlined to simplify graph + } else { + // Infinite loop: j is always 0, Integer.valueOf(0) < 1 is always true. + for (int j = 0; Integer.valueOf(j) < 1;) { + j = 0; + } + } + } + + @Run(test = "test") + @Warmup(0) + static void runTest(RunInfo info) { + testReturned = false; + Thread thread = new Thread(() -> { + test(); + testReturned = true; + }); + thread.setDaemon(true); + thread.start(); + try { + Thread.sleep(Utils.adjustTimeout(500)); + } catch (InterruptedException e) { + } + if (testReturned) { + throw new RuntimeException("test() should not return: infinite loop was incorrectly optimized away"); + } + } + + static void throwWrongPath() { + throw new RuntimeException("Wrong path"); + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopInfiniteLoop.java b/test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopInfiniteLoop.java new file mode 100644 index 00000000000..81996018a2b --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestStressLongCountedLoopInfiniteLoop.java @@ -0,0 +1,75 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * 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 8378005 + * @summary assert in proj_out when StressLongCountedLoop converts infinite loop to long counted loop nest + * @run main ${test.main.class} + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:StressLongCountedLoop=1 + * -XX:CompileCommand=compileonly,${test.main.class}::test ${test.main.class} + */ + +package compiler.loopopts; + +public class TestStressLongCountedLoopInfiniteLoop { + static int RANGE; + + public static void main(String[] args) { + int[] a = new int[RANGE]; + for (int i = 0; i < 10000; i++) { + try { + test(a, 0); + } catch (ArrayIndexOutOfBoundsException e) { + } + } + } + + static void test(int[] a, int invar) { + // new Integer(0) works too, using MyInteger to avoid deprecation warnings. + a[new MyInteger(0).v()] = 0; + + Object o; + for (int i = 0; i < 1; i++) { + o = 1; + } + + // Infinite loop: j is always 0, 0 < 1 is always true. + for (int j = 0; Integer.valueOf(j) < 1;) { + j = 0; + } + } + + static class MyInteger { + int v; + + MyInteger(int v) { + this.v = v; + } + + int v() { + return Integer.valueOf(v); + } + } +} From 8ecb14cecf1f05ea7c65fcfb5bb05e1e3a895d72 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Thu, 26 Mar 2026 14:50:56 +0000 Subject: [PATCH 136/160] 8379454: SA still calculates end address with incorrect alignment Reviewed-by: kevinw, cjplummer --- src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c index e2681be73fe..029aac1f107 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.c @@ -182,13 +182,14 @@ static bool fill_addr_info(lib_info* lib) { return false; } + long page_size = sysconf(_SC_PAGE_SIZE); lib->end = (uintptr_t)-1L; lib->exec_start = (uintptr_t)-1L; lib->exec_end = (uintptr_t)-1L; for (ph = phbuf, cnt = 0; cnt < ehdr.e_phnum; cnt++, ph++) { if (ph->p_type == PT_LOAD) { uintptr_t aligned_start = lib->base + align_down(ph->p_vaddr, ph->p_align); - uintptr_t aligned_end = aligned_start + align_up(ph->p_memsz, ph->p_align); + uintptr_t aligned_end = aligned_start + align_up(ph->p_memsz, page_size); if ((lib->end == (uintptr_t)-1L) || (lib->end < aligned_end)) { lib->end = aligned_end; } From 541557ad1d9dfacc63490b5bd30e20455b0332c4 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Thu, 26 Mar 2026 17:19:50 +0000 Subject: [PATCH 137/160] 8380671: Refactor BasicFloat16ArithTests.java Reviewed-by: bpb --- .../vector/BasicFloat16ArithTests.java | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/test/jdk/jdk/incubator/vector/BasicFloat16ArithTests.java b/test/jdk/jdk/incubator/vector/BasicFloat16ArithTests.java index 755745394fc..cdc7ce566f7 100644 --- a/test/jdk/jdk/incubator/vector/BasicFloat16ArithTests.java +++ b/test/jdk/jdk/incubator/vector/BasicFloat16ArithTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,8 @@ public class BasicFloat16ArithTests { private static float NaNf = Float.NaN; private static final float MAX_VAL_FP16 = 0x1.ffcp15f; + private static final float MIN_NRM_FP16 = 0x1.0p-14f; + private static final float MIN_VAL_FP16 = 0x1.0p-24f; public static void main(String... args) { checkBitWise(); @@ -145,9 +147,9 @@ public class BasicFloat16ArithTests { checkInt(PRECISION, 11, "Float16.PRECISION"); checkInt(SIZE, 16, "Float16.SIZE"); - checkFloat16(MIN_VALUE, 0x1.0p-24f, "Float16.MIN_VALUE"); - checkFloat16(MIN_NORMAL, 0x1.0p-14f, "Float16.MIN_NORMAL"); - checkFloat16(MAX_VALUE, 65504.0f, "Float16.MAX_VALUE"); + checkFloat16(MIN_VALUE, MIN_VAL_FP16, "Float16.MIN_VALUE"); + checkFloat16(MIN_NORMAL, MIN_NRM_FP16, "Float16.MIN_NORMAL"); + checkFloat16(MAX_VALUE, 65504.0f, "Float16.MAX_VALUE"); checkFloat16(POSITIVE_INFINITY, InfinityF, "+infinity"); checkFloat16(NEGATIVE_INFINITY, -InfinityF, "-infinity"); @@ -389,12 +391,12 @@ public class BasicFloat16ArithTests { { NaNf, MAX_EXPONENT + 1}, // Subnormal and almost subnormal values - {-0.0f, MIN_EXPONENT - 1}, - {+0.0f, MIN_EXPONENT - 1}, - { 0x1.0p-24f, MIN_EXPONENT - 1}, // Float16.MIN_VALUE - {-0x1.0p-24f, MIN_EXPONENT - 1}, // Float16.MIN_VALUE - { 0x1.0p-14f, MIN_EXPONENT}, // Float16.MIN_NORMAL - {-0x1.0p-14f, MIN_EXPONENT}, // Float16.MIN_NORMAL + {-0.0f, MIN_EXPONENT - 1}, + {+0.0f, MIN_EXPONENT - 1}, + { MIN_VAL_FP16, MIN_EXPONENT - 1}, + {-MIN_VAL_FP16, MIN_EXPONENT - 1}, + { MIN_NRM_FP16, MIN_EXPONENT}, + {-MIN_NRM_FP16, MIN_EXPONENT}, // Normal values { 1.0f, 0}, @@ -424,17 +426,17 @@ public class BasicFloat16ArithTests { { NaNf, NaNf}, // Zeros, subnormals, and MIN_VALUE all have MIN_VALUE as an ulp. - {-0.0f, 0x1.0p-24f}, - {+0.0f, 0x1.0p-24f}, - { 0x1.0p-24f, 0x1.0p-24f}, - {-0x1.0p-24f, 0x1.0p-24f}, - { 0x1.0p-14f, 0x1.0p-24f}, - {-0x1.0p-14f, 0x1.0p-24f}, + {-0.0f, MIN_VAL_FP16}, + {+0.0f, MIN_VAL_FP16}, + { MIN_VAL_FP16, MIN_VAL_FP16}, + {-MIN_VAL_FP16, MIN_VAL_FP16}, + { MIN_NRM_FP16, MIN_VAL_FP16}, + {-MIN_NRM_FP16, MIN_VAL_FP16}, - // ulp is 10 bits away - {0x1.0p0f, 0x0.004p0f}, // 1.0f - {0x1.0p1f, 0x0.004p1f}, // 2.0f - {0x1.0p2f, 0x0.004p2f}, // 4.0f + // ulp is (PRECISION - 1) = 10 bits away + {0x1.0p0f, 0x1.0p-10f}, // 1.0f + {0x1.0p1f, 0x1.0p-9f}, // 2.0f + {0x1.0p2f, 0x1.0p-8f}, // 4.0f {MAX_VAL_FP16*0.5f, 0x0.004p14f}, {MAX_VAL_FP16, 0x0.004p15f}, @@ -507,12 +509,12 @@ public class BasicFloat16ArithTests { } private static void checkValueOfLong() { - checkFloat16(valueOf(-65_521), Float.NEGATIVE_INFINITY, "-infinity"); - checkFloat16(valueOf(-65_520), Float.NEGATIVE_INFINITY, "-infinity"); - checkFloat16(valueOf(-65_519), -MAX_VALUE.floatValue(), "-MAX_VALUE"); - checkFloat16(valueOf(65_519), MAX_VALUE.floatValue(), "MAX_VALUE"); - checkFloat16(valueOf(65_520), Float.POSITIVE_INFINITY, "+infinity"); - checkFloat16(valueOf(65_521), Float.POSITIVE_INFINITY, "+infinity"); + checkFloat16(valueOf(-65_521), Float.NEGATIVE_INFINITY, "-65_521"); + checkFloat16(valueOf(-65_520), Float.NEGATIVE_INFINITY, "-65_520"); + checkFloat16(valueOf(-65_519), -MAX_VALUE.floatValue(), "-65_519"); + checkFloat16(valueOf( 65_519), MAX_VALUE.floatValue(), "65_519"); + checkFloat16(valueOf( 65_520), Float.POSITIVE_INFINITY, "65_520"); + checkFloat16(valueOf( 65_521), Float.POSITIVE_INFINITY, "65_521"); } private static void checkValueOfString() { @@ -792,7 +794,7 @@ public class BasicFloat16ArithTests { // threshold; subtracting a non-zero finite value will // result in MAX_VALUE, adding zero or a positive // value will overflow. - {0x1.2p10f, 0x1.c7p5f, -0x1.0p-14f, + {0x1.2p10f, 0x1.c7p5f, -MIN_NRM_FP16, MAX_VAL_FP16}, {0x1.2p10f, 0x1.c7p5f, -0.0f, @@ -801,7 +803,7 @@ public class BasicFloat16ArithTests { {0x1.2p10f, 0x1.c7p5f, +0.0f, InfinityF}, - {0x1.2p10f, 0x1.c7p5f, +0x1.0p-14f, + {0x1.2p10f, 0x1.c7p5f, +MIN_NRM_FP16, InfinityF}, {0x1.2p10f, 0x1.c7p5f, InfinityF, From aecc16358f5f1f6fa5cb78522d6db70430b79987 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Thu, 26 Mar 2026 17:53:53 +0000 Subject: [PATCH 138/160] 8230421: Java cannot handle Euro sign at the Simplified Chinese Windows Command Prompt Reviewed-by: sherman --- .../windows/native/libjava/java_props_md.c | 67 +++++++++---------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/src/java.base/windows/native/libjava/java_props_md.c b/src/java.base/windows/native/libjava/java_props_md.c index e152dbe9bef..6504891af34 100644 --- a/src/java.base/windows/native/libjava/java_props_md.c +++ b/src/java.base/windows/native/libjava/java_props_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,43 +74,32 @@ getEncodingInternal(LCID lcid) case 65001: strcpy(ret, "UTF-8"); break; - case 874: /* 9:Thai */ - case 932: /* 10:Japanese */ - case 949: /* 12:Korean Extended Wansung */ - case 950: /* 13:Chinese (Taiwan, Hongkong, Macau) */ - case 1361: /* 15:Korean Johab */ + case 874: /* Thai */ + case 932: /* Japanese */ + case 936: /* Chinese (Simplified) */ + case 949: /* Korean Extended Wansung */ + case 950: /* Chinese (Taiwan, Hongkong, Macau) */ + case 1361: /* Korean Johab */ ret[0] = 'M'; ret[1] = 'S'; - break; - case 936: - strcpy(ret, "GBK"); - break; - case 54936: - strcpy(ret, "GB18030"); - break; - default: - ret[0] = 'C'; - ret[1] = 'p'; - break; - } - //Traditional Chinese Windows should use MS950_HKSCS_XP as the - //default encoding, if HKSCS patch has been installed. - // "old" MS950 0xfa41 -> u+e001 - // "new" MS950 0xfa41 -> u+92db - if (strcmp(ret, "MS950") == 0) { - TCHAR mbChar[2] = {(char)0xfa, (char)0x41}; - WCHAR unicodeChar; - MultiByteToWideChar(CP_ACP, 0, mbChar, 2, &unicodeChar, 1); - if (unicodeChar == 0x92db) { - strcpy(ret, "MS950_HKSCS_XP"); - } - } else { - //SimpChinese Windows should use GB18030 as the default - //encoding, if gb18030 patch has been installed (on windows - //2000/XP, (1)Codepage 54936 will be available - //(2)simsun18030.ttc will exist under system fonts dir ) - if (strcmp(ret, "GBK") == 0 && IsValidCodePage(54936)) { + // Special handling for Chinese + if (codepage == 950) { + //Traditional Chinese Windows should use MS950_HKSCS_XP as the + //default encoding, if HKSCS patch has been installed. + // "old" MS950 0xfa41 -> u+e001 + // "new" MS950 0xfa41 -> u+92db + TCHAR mbChar[2] = {(char)0xfa, (char)0x41}; + WCHAR unicodeChar; + MultiByteToWideChar(CP_ACP, 0, mbChar, 2, &unicodeChar, 1); + if (unicodeChar == 0x92db) { + strcpy(ret, "MS950_HKSCS_XP"); + } + } else if (codepage == 936 && IsValidCodePage(54936)) { + //SimpChinese Windows should use GB18030 as the default + //encoding, if gb18030 patch has been installed (on windows + //2000/XP, (1)Codepage 54936 will be available + //(2)simsun18030.ttc will exist under system fonts dir ) char systemPath[MAX_PATH + 1]; char* gb18030Font = "\\FONTS\\SimSun18030.ttc"; FILE *f = NULL; @@ -123,6 +112,14 @@ getEncodingInternal(LCID lcid) } } } + break; + case 54936: + strcpy(ret, "GB18030"); + break; + default: + ret[0] = 'C'; + ret[1] = 'p'; + break; } return ret; From 72176305ae24e4e0c8f6541b229c16cae81e4775 Mon Sep 17 00:00:00 2001 From: Leo Korinth Date: Thu, 26 Mar 2026 18:16:17 +0000 Subject: [PATCH 139/160] 8379346: Cleanup casts and implicit conversions to boolean Reviewed-by: stefank, dholmes, ayang --- src/hotspot/os/linux/hugepages.cpp | 4 ++-- src/hotspot/os/linux/os_linux.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hotspot/os/linux/hugepages.cpp b/src/hotspot/os/linux/hugepages.cpp index 1340c470dff..213845c7134 100644 --- a/src/hotspot/os/linux/hugepages.cpp +++ b/src/hotspot/os/linux/hugepages.cpp @@ -68,7 +68,7 @@ static size_t scan_default_hugepagesize() { // format has been changed), we'll set largest page size to 0 FILE *fp = os::fopen("/proc/meminfo", "r"); - if (fp) { + if (fp != nullptr) { while (!feof(fp)) { int x = 0; char buf[16]; @@ -81,7 +81,7 @@ static size_t scan_default_hugepagesize() { // skip to next line for (;;) { int ch = fgetc(fp); - if (ch == EOF || ch == (int)'\n') break; + if (ch == EOF || ch == '\n') break; } } } diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index c79b0ab9fb5..bf096897aa7 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -1313,7 +1313,7 @@ bool os::is_primordial_thread(void) { // Find the virtual memory area that contains addr static bool find_vma(address addr, address* vma_low, address* vma_high) { FILE *fp = os::fopen("/proc/self/maps", "r"); - if (fp) { + if (fp != nullptr) { address low, high; while (!feof(fp)) { if (fscanf(fp, "%p-%p", &low, &high) == 2) { @@ -1326,7 +1326,7 @@ static bool find_vma(address addr, address* vma_low, address* vma_high) { } for (;;) { int ch = fgetc(fp); - if (ch == EOF || ch == (int)'\n') break; + if (ch == EOF || ch == '\n') break; } } fclose(fp); @@ -4384,7 +4384,7 @@ int os::Linux::get_namespace_pid(int vmid) { os::snprintf_checked(fname, sizeof(fname), "/proc/%d/status", vmid); FILE *fp = os::fopen(fname, "r"); - if (fp) { + if (fp != nullptr) { int pid, nspid; int ret; while (!feof(fp) && !ferror(fp)) { @@ -4398,7 +4398,7 @@ int os::Linux::get_namespace_pid(int vmid) { } for (;;) { int ch = fgetc(fp); - if (ch == EOF || ch == (int)'\n') break; + if (ch == EOF || ch == '\n') break; } } fclose(fp); From c5c5340b521151b9dac44cc72899ef31c2d812cb Mon Sep 17 00:00:00 2001 From: Leo Korinth Date: Thu, 26 Mar 2026 18:37:45 +0000 Subject: [PATCH 140/160] 8379224: Always use zero as invalid page size Reviewed-by: stefank, ayang --- src/hotspot/os/linux/hugepages.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hotspot/os/linux/hugepages.cpp b/src/hotspot/os/linux/hugepages.cpp index 213845c7134..b065f7b1496 100644 --- a/src/hotspot/os/linux/hugepages.cpp +++ b/src/hotspot/os/linux/hugepages.cpp @@ -35,7 +35,7 @@ #include ExplicitHugePageSupport::ExplicitHugePageSupport() : - _initialized{false}, _os_supported{}, _pre_allocated{}, _default_hugepage_size{SIZE_MAX}, _inconsistent{false} {} + _initialized{false}, _os_supported{}, _pre_allocated{}, _default_hugepage_size{0}, _inconsistent{false} {} os::PageSizes ExplicitHugePageSupport::os_supported() const { assert(_initialized, "Not initialized"); @@ -187,7 +187,7 @@ void ExplicitHugePageSupport::scan_os() { } THPSupport::THPSupport() : - _initialized(false), _mode(THPMode::never), _pagesize(SIZE_MAX) {} + _initialized{false}, _mode{THPMode::never}, _pagesize{0} {} THPMode THPSupport::mode() const { @@ -221,7 +221,6 @@ void THPSupport::scan_os() { } // Scan large page size for THP from hpage_pmd_size - _pagesize = 0; if (read_number_file("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size", &_pagesize)) { assert(_pagesize > 0, "Expected"); } From 40d65f1063a27b081b4c074b04192eb2fcf5dd6a Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 26 Mar 2026 18:38:04 +0000 Subject: [PATCH 141/160] 8379583: (fs) Files.copy use of posix_fadvise is problematic on Linux Reviewed-by: alanb --- .../classes/sun/nio/fs/LinuxFileSystem.java | 12 +--- .../sun/nio/fs/UnixConstants.java.template | 4 +- .../bench/java/nio/file/FilesCopy.java | 68 +++++++++++++++++++ 3 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/nio/file/FilesCopy.java diff --git a/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java b/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java index 44e957f54fb..ec3e135b8b1 100644 --- a/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java +++ b/src/java.base/linux/classes/sun/nio/fs/LinuxFileSystem.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -139,10 +139,7 @@ class LinuxFileSystem extends UnixFileSystem { int size, long addressToPollForCancel) throws UnixException { - int advice = POSIX_FADV_SEQUENTIAL | // sequential data access - POSIX_FADV_NOREUSE | // will access only once - POSIX_FADV_WILLNEED; // will access in near future - posix_fadvise(src, 0, 0, advice); + posix_fadvise(src, 0, 0, POSIX_FADV_SEQUENTIAL); super.bufferedCopy(dst, src, address, size, addressToPollForCancel); } @@ -151,10 +148,7 @@ class LinuxFileSystem extends UnixFileSystem { int directCopy(int dst, int src, long addressToPollForCancel) throws UnixException { - int advice = POSIX_FADV_SEQUENTIAL | // sequential data access - POSIX_FADV_NOREUSE | // will access only once - POSIX_FADV_WILLNEED; // will access in near future - posix_fadvise(src, 0, 0, advice); + posix_fadvise(src, 0, 0, POSIX_FADV_SEQUENTIAL); return directCopy0(dst, src, addressToPollForCancel); } diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template b/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template index 6823833582f..7a9a22ac40d 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template +++ b/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -173,7 +173,5 @@ class UnixConstants { #ifdef __linux__ // advice flags used with posix_fadvise(2) static final int PREFIX_POSIX_FADV_SEQUENTIAL = POSIX_FADV_SEQUENTIAL; - static final int PREFIX_POSIX_FADV_NOREUSE = POSIX_FADV_NOREUSE; - static final int PREFIX_POSIX_FADV_WILLNEED = POSIX_FADV_WILLNEED; #endif } diff --git a/test/micro/org/openjdk/bench/java/nio/file/FilesCopy.java b/test/micro/org/openjdk/bench/java/nio/file/FilesCopy.java new file mode 100644 index 00000000000..9472920f071 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/nio/file/FilesCopy.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.java.nio.file; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import static java.nio.file.StandardOpenOption.*; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; + +@State(Scope.Benchmark) +public class FilesCopy { + + private static final int SIZE = Integer.MAX_VALUE; + private static final Path FILE = Path.of("file.dat"); + private static final Path COPY = Path.of("copy.dat"); + + @Setup + public void init() throws IOException { + Files.deleteIfExists(FILE); + Files.deleteIfExists(COPY); + try (FileChannel fc = FileChannel.open(FILE, CREATE_NEW, READ, WRITE)) { + fc.position(SIZE); + fc.write(ByteBuffer.wrap(new byte[] {(byte)27})); + } + } + + @TearDown + public void cleanup() throws IOException { + Files.deleteIfExists(FILE); + Files.deleteIfExists(COPY); + } + + @Benchmark + public void copyFile() throws IOException { + Files.copy(FILE, COPY); + Files.delete(COPY); + } + +} From 68c48290d63675d82f2e9e60241031de023c280f Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Thu, 26 Mar 2026 20:04:13 +0000 Subject: [PATCH 142/160] 8380459: Shenandoah: Do not reset bytes-allocated-since-gc-start when degenerating Reviewed-by: wkemper --- src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp | 5 ++++- .../gc/shenandoah/shenandoahGenerationalControlThread.cpp | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 48183507124..4c6e82c86a5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -138,7 +138,10 @@ void ShenandoahControlThread::run_service() { heuristics->cancel_trigger_request(); - heap->reset_bytes_allocated_since_gc_start(); + if (mode != stw_degenerated) { + // If mode is stw_degenerated, count bytes allocated from the start of the conc GC that experienced alloc failure. + heap->reset_bytes_allocated_since_gc_start(); + } MetaspaceCombinedStats meta_sizes = MetaspaceUtils::get_combined_statistics(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 67cc4d2f703..ec33e671053 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -255,7 +255,8 @@ void ShenandoahGenerationalControlThread::run_gc_cycle(const ShenandoahGCRequest GCIdMark gc_id_mark; - if (gc_mode() != servicing_old) { + if ((gc_mode() != servicing_old) && (gc_mode() != stw_degenerated)) { + // If mode is stw_degenerated, count bytes allocated from the start of the conc GC that experienced alloc failure. _heap->reset_bytes_allocated_since_gc_start(); } From 91892948ee0ac3c62bbddbbf9e6f99e746f3d72f Mon Sep 17 00:00:00 2001 From: Damon Nguyen Date: Thu, 26 Mar 2026 20:21:19 +0000 Subject: [PATCH 143/160] 8380229: 2 Impossible or redundant condition defect groups in 2 files Reviewed-by: honkar, prr --- .../share/native/libharfbuzz/hb-aat-layout-common.hh | 3 +-- .../share/native/libharfbuzz/hb-ot-layout-common.hh | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh index d2ce32616be..4bb41fd0189 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh @@ -1266,8 +1266,7 @@ struct StateTableDriver next_state == StateTableT::STATE_START_OF_TEXT && start_state_safe_to_break_eot && is_not_actionable && - is_not_epsilon_transition && - !last_range; + is_not_epsilon_transition; if (is_null_transition) { diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh index dcacc9cb86c..6b62732bf54 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh @@ -2480,7 +2480,7 @@ struct VarRegionAxis /* TODO Move these to sanitize(). */ if (unlikely (start > peak || peak > end)) return 1.f; - if (unlikely (start < 0 && end > 0 && peak != 0)) + if (unlikely (start < 0 && end > 0)) return 1.f; if (coord <= start || end <= coord) From 4d2623757fbe8c9fa8c22817a993ca85a2403873 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Thu, 26 Mar 2026 20:59:45 +0000 Subject: [PATCH 144/160] 8359433: The final modifier on Windows L&F internal UI classes prevents extending them in apps Reviewed-by: prr, aivanov --- .../java/swing/plaf/windows/WindowsBorders.java | 8 ++++---- .../java/swing/plaf/windows/WindowsButtonUI.java | 2 +- .../plaf/windows/WindowsCheckBoxMenuItemUI.java | 4 ++-- .../java/swing/plaf/windows/WindowsCheckBoxUI.java | 2 +- .../plaf/windows/WindowsClassicLookAndFeel.java | 4 ++-- .../java/swing/plaf/windows/WindowsComboBoxUI.java | 10 +++++----- .../swing/plaf/windows/WindowsDesktopIconUI.java | 4 ++-- .../swing/plaf/windows/WindowsDesktopManager.java | 4 ++-- .../swing/plaf/windows/WindowsDesktopPaneUI.java | 4 ++-- .../swing/plaf/windows/WindowsEditorPaneUI.java | 4 ++-- .../swing/plaf/windows/WindowsFileChooserUI.java | 14 +++++++------- .../windows/WindowsInternalFrameTitlePane.java | 8 ++++---- .../swing/plaf/windows/WindowsInternalFrameUI.java | 4 ++-- .../java/swing/plaf/windows/WindowsLabelUI.java | 2 +- .../java/swing/plaf/windows/WindowsMenuBarUI.java | 4 ++-- .../java/swing/plaf/windows/WindowsMenuItemUI.java | 2 +- .../sun/java/swing/plaf/windows/WindowsMenuUI.java | 6 +++--- .../swing/plaf/windows/WindowsOptionPaneUI.java | 4 ++-- .../swing/plaf/windows/WindowsPasswordFieldUI.java | 4 ++-- .../plaf/windows/WindowsPopupMenuSeparatorUI.java | 4 ++-- .../swing/plaf/windows/WindowsPopupMenuUI.java | 4 ++-- .../swing/plaf/windows/WindowsProgressBarUI.java | 4 ++-- .../plaf/windows/WindowsRadioButtonMenuItemUI.java | 4 ++-- .../java/swing/plaf/windows/WindowsRootPaneUI.java | 4 ++-- .../swing/plaf/windows/WindowsScrollBarUI.java | 4 ++-- .../swing/plaf/windows/WindowsScrollPaneUI.java | 4 ++-- .../swing/plaf/windows/WindowsSeparatorUI.java | 4 ++-- .../java/swing/plaf/windows/WindowsSliderUI.java | 4 ++-- .../java/swing/plaf/windows/WindowsSpinnerUI.java | 4 ++-- .../plaf/windows/WindowsSplitPaneDivider.java | 4 ++-- .../swing/plaf/windows/WindowsSplitPaneUI.java | 4 ++-- .../swing/plaf/windows/WindowsTabbedPaneUI.java | 4 ++-- .../swing/plaf/windows/WindowsTableHeaderUI.java | 4 ++-- .../java/swing/plaf/windows/WindowsTextAreaUI.java | 4 ++-- .../swing/plaf/windows/WindowsTextFieldUI.java | 4 ++-- .../java/swing/plaf/windows/WindowsTextPaneUI.java | 4 ++-- .../swing/plaf/windows/WindowsToggleButtonUI.java | 2 +- .../plaf/windows/WindowsToolBarSeparatorUI.java | 4 ++-- .../java/swing/plaf/windows/WindowsToolBarUI.java | 4 ++-- .../sun/java/swing/plaf/windows/WindowsTreeUI.java | 6 +++--- test/jdk/javax/swing/plaf/windows/bug4991587.java | 4 ++-- 41 files changed, 91 insertions(+), 91 deletions(-) diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java index 81766db8116..572e9e8c117 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -115,7 +115,7 @@ public final class WindowsBorders { } @SuppressWarnings("serial") // Superclass is not serializable across versions - public static final class ProgressBarBorder extends AbstractBorder implements UIResource { + public static class ProgressBarBorder extends AbstractBorder implements UIResource { protected Color shadow; protected Color highlight; @@ -148,7 +148,7 @@ public final class WindowsBorders { * @since 1.4 */ @SuppressWarnings("serial") // Superclass is not serializable across versions - public static final class ToolBarBorder extends AbstractBorder implements UIResource, SwingConstants { + public static class ToolBarBorder extends AbstractBorder implements UIResource, SwingConstants { protected Color shadow; protected Color highlight; @@ -308,7 +308,7 @@ public final class WindowsBorders { * @since 1.4 */ @SuppressWarnings("serial") // Superclass is not serializable across versions - public static final class InternalFrameLineBorder extends LineBorder implements + public static class InternalFrameLineBorder extends LineBorder implements UIResource { protected Color activeColor; protected Color inactiveColor; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java index ee07276aa30..63e99e0804a 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java @@ -56,7 +56,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; * * @author Jeff Dinkins */ -public final class WindowsButtonUI extends BasicButtonUI +public class WindowsButtonUI extends BasicButtonUI { protected int dashedRectGapX; protected int dashedRectGapY; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java index 3a2578b3e0b..47e311486ba 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,7 @@ import com.sun.java.swing.plaf.windows.TMSchema.State; /** * Windows check box menu item. */ -public final class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI { +public class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI { final WindowsMenuItemUIAccessor accessor = new WindowsMenuItemUIAccessor() { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java index 7cb2490fd76..d264393f4d8 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java @@ -35,7 +35,7 @@ import javax.swing.plaf.ComponentUI; * * @author Jeff Dinkins */ -public final class WindowsCheckBoxUI extends WindowsRadioButtonUI +public class WindowsCheckBoxUI extends WindowsRadioButtonUI { // NOTE: WindowsCheckBoxUI inherits from WindowsRadioButtonUI instead // of BasicCheckBoxUI because we want to pick up all the diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java index 5716a875cd7..802b5f66888 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,7 @@ package com.sun.java.swing.plaf.windows; * @since 1.5 */ @SuppressWarnings("serial") // Superclass is not serializable across versions -public final class WindowsClassicLookAndFeel extends WindowsLookAndFeel { +public class WindowsClassicLookAndFeel extends WindowsLookAndFeel { @Override public String getName() { return "Windows Classic"; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java index fdc9b03ae7d..8717fd715ea 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -75,7 +75,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; * @author Tom Santos * @author Igor Kushnirskiy */ -public final class WindowsComboBoxUI extends BasicComboBoxUI { +public class WindowsComboBoxUI extends BasicComboBoxUI { private static final MouseListener rolloverListener = new MouseAdapter() { @@ -532,7 +532,7 @@ public final class WindowsComboBoxUI extends BasicComboBoxUI { } @SuppressWarnings("serial") // Same-version serialization only - protected final class WinComboPopUp extends BasicComboPopup { + protected class WinComboPopUp extends BasicComboPopup { private Skin listBoxBorder = null; private XPStyle xp; @@ -550,7 +550,7 @@ public final class WindowsComboBoxUI extends BasicComboBoxUI { return new InvocationKeyHandler(); } - protected final class InvocationKeyHandler extends BasicComboPopup.InvocationKeyHandler { + protected class InvocationKeyHandler extends BasicComboPopup.InvocationKeyHandler { protected InvocationKeyHandler() { WinComboPopUp.this.super(); } @@ -570,7 +570,7 @@ public final class WindowsComboBoxUI extends BasicComboBoxUI { /** * Subclassed to highlight selected item in an editable combo box. */ - public static final class WindowsComboBoxEditor + public static class WindowsComboBoxEditor extends BasicComboBoxEditor.UIResource { /** diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java index 47ecdf5747b..2cebb050396 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * 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 javax.swing.plaf.basic.BasicDesktopIconUI; /** * Windows icon for a minimized window on the desktop. */ -public final class WindowsDesktopIconUI extends BasicDesktopIconUI { +public class WindowsDesktopIconUI extends BasicDesktopIconUI { private int width; public static ComponentUI createUI(JComponent c) { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java index 79d81bad089..ae081a7690c 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,7 +52,7 @@ import java.lang.ref.WeakReference; * @author Thomas Ball */ @SuppressWarnings("serial") // JDK-implementation class -public final class WindowsDesktopManager extends DefaultDesktopManager +public class WindowsDesktopManager extends DefaultDesktopManager implements java.io.Serializable, javax.swing.plaf.UIResource { /* The frame which is currently selected/activated. diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java index dabbe3fb992..4a3f0ec38b1 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,7 @@ import javax.swing.plaf.basic.BasicDesktopPaneUI; * * @author David Kloba */ -public final class WindowsDesktopPaneUI extends BasicDesktopPaneUI +public class WindowsDesktopPaneUI extends BasicDesktopPaneUI { public static ComponentUI createUI(JComponent c) { return new WindowsDesktopPaneUI(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java index 44cb0e9634c..ea21b41c619 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,7 @@ import javax.swing.text.Caret; /** * Windows rendition of the component. */ -public final class WindowsEditorPaneUI extends BasicEditorPaneUI +public class WindowsEditorPaneUI extends BasicEditorPaneUI { /** diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java index 08c01760be9..86c40ea70d6 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsFileChooserUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -101,7 +101,7 @@ import sun.swing.WindowsPlacesBar; * * @author Jeff Dinkins */ -public final class WindowsFileChooserUI extends BasicFileChooserUI { +public class WindowsFileChooserUI extends BasicFileChooserUI { // The following are private because the implementation of the // Windows FileChooser L&F is not complete yet. @@ -1122,7 +1122,7 @@ public final class WindowsFileChooserUI extends BasicFileChooserUI { * Data model for a type-face selection combo-box. */ @SuppressWarnings("serial") // Superclass is not serializable across versions - protected final class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel { + protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel { Vector directories = new Vector(); int[] depths = null; File selectedDirectory = null; @@ -1252,7 +1252,7 @@ public final class WindowsFileChooserUI extends BasicFileChooserUI { * Render different type sizes and styles. */ @SuppressWarnings("serial") // Superclass is not serializable across versions - public final class FilterComboBoxRenderer extends DefaultListCellRenderer { + public class FilterComboBoxRenderer extends DefaultListCellRenderer { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, @@ -1279,7 +1279,7 @@ public final class WindowsFileChooserUI extends BasicFileChooserUI { * Data model for a type-face selection combo-box. */ @SuppressWarnings("serial") // Superclass is not serializable across versions - protected final class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, + protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener { protected FileFilter[] filters; protected FilterComboBoxModel() { @@ -1362,7 +1362,7 @@ public final class WindowsFileChooserUI extends BasicFileChooserUI { /** * Acts when DirectoryComboBox has changed the selected item. */ - protected final class DirectoryComboBoxAction implements ActionListener { + protected class DirectoryComboBoxAction implements ActionListener { @@ -1387,7 +1387,7 @@ public final class WindowsFileChooserUI extends BasicFileChooserUI { // *********************** // * FileView operations * // *********************** - protected final class WindowsFileView extends BasicFileView { + protected class WindowsFileView extends BasicFileView { /* FileView type descriptions */ @Override diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java index ba4bde12122..029e139fe8f 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -418,7 +418,7 @@ public class WindowsInternalFrameTitlePane extends BasicInternalFrameTitlePane { return new WindowsTitlePaneLayout(); } - public final class WindowsTitlePaneLayout extends BasicInternalFrameTitlePane.TitlePaneLayout { + public class WindowsTitlePaneLayout extends BasicInternalFrameTitlePane.TitlePaneLayout { private Insets captionMargin = null; private Insets contentMargin = null; private XPStyle xp = XPStyle.getXP(); @@ -506,7 +506,7 @@ public class WindowsInternalFrameTitlePane extends BasicInternalFrameTitlePane { } } // end WindowsTitlePaneLayout - public final class WindowsPropertyChangeHandler extends PropertyChangeHandler { + public class WindowsPropertyChangeHandler extends PropertyChangeHandler { @Override public void propertyChange(PropertyChangeEvent evt) { String prop = evt.getPropertyName(); @@ -530,7 +530,7 @@ public class WindowsInternalFrameTitlePane extends BasicInternalFrameTitlePane { *

* Note: We assume here that icons are square. */ - public static final class ScalableIconUIResource implements Icon, UIResource { + public static class ScalableIconUIResource implements Icon, UIResource { // We can use an arbitrary size here because we scale to it in paintIcon() private static final int SIZE = 16; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java index 9db31ba38f9..6e76ac6a5b4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,7 +45,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. */ -public final class WindowsInternalFrameUI extends BasicInternalFrameUI +public class WindowsInternalFrameUI extends BasicInternalFrameUI { XPStyle xp = XPStyle.getXP(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java index 4283f743b97..c910b635491 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java @@ -40,7 +40,7 @@ import sun.swing.SwingUtilities2; /** * Windows rendition of the component. */ -public final class WindowsLabelUI extends BasicLabelUI { +public class WindowsLabelUI extends BasicLabelUI { private static final ComponentUI UI = new WindowsLabelUI(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java index 3d3cf5feee7..ac26dcbf425 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,7 +57,7 @@ import sun.swing.MnemonicHandler; /** * Windows rendition of the component. */ -public final class WindowsMenuBarUI extends BasicMenuBarUI +public class WindowsMenuBarUI extends BasicMenuBarUI { /* to be accessed on the EDT only */ private WindowListener windowListener = null; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java index d15bc93a628..d50540588fb 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java @@ -58,7 +58,7 @@ import sun.swing.SwingUtilities2; * * @author Igor Kushnirskiy */ -public final class WindowsMenuItemUI extends BasicMenuItemUI { +public class WindowsMenuItemUI extends BasicMenuItemUI { /** * The instance of {@code PropertyChangeListener}. */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java index 259c32c74f4..78028db7c00 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,7 +50,7 @@ import com.sun.java.swing.plaf.windows.TMSchema.State; /** * Windows rendition of the component. */ -public final class WindowsMenuUI extends BasicMenuUI { +public class WindowsMenuUI extends BasicMenuUI { protected Integer menuBarHeight; protected boolean hotTrackingOn; @@ -283,7 +283,7 @@ public final class WindowsMenuUI extends BasicMenuUI { * true when the mouse enters the menu and false when it exits. * @since 1.4 */ - protected final class WindowsMouseInputHandler extends BasicMenuUI.MouseInputHandler { + protected class WindowsMouseInputHandler extends BasicMenuUI.MouseInputHandler { @Override public void mouseEntered(MouseEvent evt) { super.mouseEntered(evt); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java index 05c1b177705..3bed1856a55 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,5 +30,5 @@ import javax.swing.plaf.basic.BasicOptionPaneUI; /** * Windows rendition of the component. */ -public final class WindowsOptionPaneUI extends BasicOptionPaneUI { +public class WindowsOptionPaneUI extends BasicOptionPaneUI { } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java index 6adf6e402ec..0c30b291648 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,7 @@ import javax.swing.text.Caret; /** * Windows rendition of the component. */ -public final class WindowsPasswordFieldUI extends BasicPasswordFieldUI { +public class WindowsPasswordFieldUI extends BasicPasswordFieldUI { /** * Creates a UI for a JPasswordField diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java index 576549ae482..f236c6b14fc 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuSeparatorUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,7 @@ import com.sun.java.swing.plaf.windows.XPStyle.Skin; * @author Igor Kushnirskiy */ -public final class WindowsPopupMenuSeparatorUI extends BasicPopupMenuSeparatorUI { +public class WindowsPopupMenuSeparatorUI extends BasicPopupMenuSeparatorUI { public static ComponentUI createUI(JComponent c) { return new WindowsPopupMenuSeparatorUI(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java index 1361286df4a..1c85cfebd94 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,7 +57,7 @@ import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET; * * @author Igor Kushnirskiy */ -public final class WindowsPopupMenuUI extends BasicPopupMenuUI { +public class WindowsPopupMenuUI extends BasicPopupMenuUI { static MnemonicListener mnemonicListener = null; static final Object GUTTER_OFFSET_KEY = diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java index 9cc7d277ff1..5440b98cd1b 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,7 +51,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; * * @author Michael C. Albers */ -public final class WindowsProgressBarUI extends BasicProgressBarUI +public class WindowsProgressBarUI extends BasicProgressBarUI { private Rectangle previousFullBox; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java index 78768c29ab3..2ec78341c2a 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,7 @@ import com.sun.java.swing.plaf.windows.TMSchema.State; /** * Windows rendition of the component. */ -public final class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI { +public class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI { final WindowsMenuItemUIAccessor accessor = new WindowsMenuItemUIAccessor() { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java index 5e08dcf5605..d41fd9421e4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRootPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -70,7 +70,7 @@ import sun.swing.MnemonicHandler; * @author Mark Davidson * @since 1.4 */ -public final class WindowsRootPaneUI extends BasicRootPaneUI { +public class WindowsRootPaneUI extends BasicRootPaneUI { private static final WindowsRootPaneUI windowsRootPaneUI = new WindowsRootPaneUI(); static final AltProcessor altProcessor = new AltProcessor(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java index 04a9f2e97cf..2755f3543f1 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,7 +55,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. */ -public final class WindowsScrollBarUI extends BasicScrollBarUI { +public class WindowsScrollBarUI extends BasicScrollBarUI { private Grid thumbGrid; private Grid highlightGrid; private Dimension horizontalThumbSize; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java index 48e7a8c02fb..56b8eb1004e 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,5 +30,5 @@ import javax.swing.plaf.basic.BasicScrollPaneUI; /** * Windows rendition of the component. */ -public final class WindowsScrollPaneUI extends BasicScrollPaneUI +public class WindowsScrollPaneUI extends BasicScrollPaneUI {} diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java index 2a2caef60d2..12eaa33872c 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSeparatorUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,4 +30,4 @@ import javax.swing.plaf.basic.*; /** * Windows Separator. */ -public final class WindowsSeparatorUI extends BasicSeparatorUI { } +public class WindowsSeparatorUI extends BasicSeparatorUI { } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java index 731775a2575..cfc509babf4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. */ -public final class WindowsSliderUI extends BasicSliderUI +public class WindowsSliderUI extends BasicSliderUI { private boolean rollover = false; private boolean pressed = false; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java index a8e2a2ddcf1..8934bf9ff21 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSpinnerUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; -public final class WindowsSpinnerUI extends BasicSpinnerUI { +public class WindowsSpinnerUI extends BasicSpinnerUI { public static ComponentUI createUI(JComponent c) { return new WindowsSpinnerUI(); } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java index a132756bbee..26cd1bd8c2d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,7 @@ import javax.swing.plaf.basic.BasicSplitPaneUI; * @author Jeff Dinkins */ @SuppressWarnings("serial") // Superclass is not serializable across versions -public final class WindowsSplitPaneDivider extends BasicSplitPaneDivider +public class WindowsSplitPaneDivider extends BasicSplitPaneDivider { /** diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java index 481fa466a5b..b67ab22f48f 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,7 @@ import javax.swing.plaf.basic.BasicSplitPaneUI; /** * Windows rendition of the component. */ -public final class WindowsSplitPaneUI extends BasicSplitPaneUI +public class WindowsSplitPaneUI extends BasicSplitPaneUI { public WindowsSplitPaneUI() { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java index 874b5c65c6e..da8e8b9d385 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,7 +48,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. */ -public final class WindowsTabbedPaneUI extends BasicTabbedPaneUI { +public class WindowsTabbedPaneUI extends BasicTabbedPaneUI { /** * Keys to use for forward focus traversal when the JComponent is * managing focus. diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java index de8f18b4ea1..1db0050f162 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,7 +50,7 @@ import static com.sun.java.swing.plaf.windows.TMSchema.Part; import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; -public final class WindowsTableHeaderUI extends BasicTableHeaderUI { +public class WindowsTableHeaderUI extends BasicTableHeaderUI { private TableCellRenderer originalHeaderRenderer; public static ComponentUI createUI(JComponent h) { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java index 78cceff2a0c..7c9abb12e05 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,7 @@ import javax.swing.text.Caret; /** * Windows rendition of the component. */ -public final class WindowsTextAreaUI extends BasicTextAreaUI { +public class WindowsTextAreaUI extends BasicTextAreaUI { /** * Creates the object to use for a caret. By default an * instance of WindowsCaret is created. This method diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java index 5846dcb9f09..9920ed371d8 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * 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 @@ import javax.swing.text.Position; * * @author Timothy Prinzing */ -public final class WindowsTextFieldUI extends BasicTextFieldUI +public class WindowsTextFieldUI extends BasicTextFieldUI { /** * Creates a UI for a JTextField. diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java index 2c645903e51..d1418205385 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,7 @@ import javax.swing.text.Caret; /** * Windows rendition of the component. */ -public final class WindowsTextPaneUI extends BasicTextPaneUI +public class WindowsTextPaneUI extends BasicTextPaneUI { /** * Creates a UI for a JTextPane. diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java index 67eb5c1d6a0..a612b3f392e 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java @@ -43,7 +43,7 @@ import javax.swing.plaf.basic.BasicToggleButtonUI; * * @author Jeff Dinkins */ -public final class WindowsToggleButtonUI extends BasicToggleButtonUI +public class WindowsToggleButtonUI extends BasicToggleButtonUI { protected int dashedRectGapX; protected int dashedRectGapY; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java index 47175b83d30..1707ce5a80c 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarSeparatorUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,7 +40,7 @@ import static com.sun.java.swing.plaf.windows.XPStyle.Skin; * * @author Mark Davidson */ -public final class WindowsToolBarSeparatorUI extends BasicToolBarSeparatorUI { +public class WindowsToolBarSeparatorUI extends BasicToolBarSeparatorUI { public static ComponentUI createUI( JComponent c ) { return new WindowsToolBarSeparatorUI(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java index 025c30c5c96..4e2cf42bf5d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToolBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,7 +45,7 @@ import javax.swing.plaf.basic.BasicToolBarUI; import static com.sun.java.swing.plaf.windows.TMSchema.Part; -public final class WindowsToolBarUI extends BasicToolBarUI { +public class WindowsToolBarUI extends BasicToolBarUI { public static ComponentUI createUI(JComponent c) { return new WindowsToolBarUI(); diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java index 78384bbd18a..26edfb978bd 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -167,7 +167,7 @@ public class WindowsTreeUI extends BasicTreeUI { * The plus sign button icon */ @SuppressWarnings("serial") // Superclass is not serializable across versions - public static final class CollapsedIcon extends ExpandedIcon { + public static class CollapsedIcon extends ExpandedIcon { public static Icon createCollapsedIcon() { return new CollapsedIcon(); } @@ -185,7 +185,7 @@ public class WindowsTreeUI extends BasicTreeUI { } @SuppressWarnings("serial") // Superclass is not serializable across versions - public final class WindowsTreeCellRenderer extends DefaultTreeCellRenderer { + public class WindowsTreeCellRenderer extends DefaultTreeCellRenderer { /** * Configures the renderer based on the passed in components. diff --git a/test/jdk/javax/swing/plaf/windows/bug4991587.java b/test/jdk/javax/swing/plaf/windows/bug4991587.java index e4e4fde2b86..439bdd4cc61 100644 --- a/test/jdk/javax/swing/plaf/windows/bug4991587.java +++ b/test/jdk/javax/swing/plaf/windows/bug4991587.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4991587 + * @bug 4991587 8359433 * @requires (os.family == "windows") * @summary Tests that disabled JButton text is positioned properly in Windows L&F * @modules java.desktop/com.sun.java.swing.plaf.windows From 062d89bff59e09850d335e8de682c9711f28966b Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 26 Mar 2026 21:23:04 +0000 Subject: [PATCH 145/160] 8380804: Remove remaining AppContext usage from the Swing implementation Reviewed-by: dnguyen, serb --- .../classes/javax/swing/RepaintManager.java | 22 +++---- .../classes/javax/swing/SwingUtilities.java | 21 ------- .../classes/javax/swing/SwingWorker.java | 63 +++++++------------ .../share/classes/javax/swing/Timer.java | 5 +- .../share/classes/javax/swing/UIManager.java | 34 ++-------- 5 files changed, 36 insertions(+), 109 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/RepaintManager.java b/src/java.desktop/share/classes/javax/swing/RepaintManager.java index db8b13afa22..b587cf2da84 100644 --- a/src/java.desktop/share/classes/javax/swing/RepaintManager.java +++ b/src/java.desktop/share/classes/javax/swing/RepaintManager.java @@ -32,7 +32,6 @@ import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import sun.awt.AWTAccessor; -import sun.awt.AppContext; import sun.awt.DisplayChangedListener; import sun.awt.SunToolkit; import sun.java2d.SunGraphicsEnvironment; @@ -1733,21 +1732,14 @@ public class RepaintManager } private static void scheduleDisplayChanges() { - // To avoid threading problems, we notify each RepaintManager + // To avoid threading problems, we notify the RepaintManager // on the thread it was created on. - for (AppContext context : AppContext.getAppContexts()) { - synchronized(context) { - if (!context.isDisposed()) { - EventQueue eventQueue = (EventQueue)context.get( - AppContext.EVENT_QUEUE_KEY); - if (eventQueue != null) { - eventQueue.postEvent(new InvocationEvent( - Toolkit.getDefaultToolkit(), - new DisplayChangedRunnable())); - } - } - } - } + EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); + eventQueue.postEvent( + new InvocationEvent( + Toolkit.getDefaultToolkit(), + new DisplayChangedRunnable()) + ); } } diff --git a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java index ae5faf64f3b..3603292b0ca 100644 --- a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java +++ b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java @@ -38,7 +38,6 @@ import javax.swing.event.MenuDragMouseEvent; import javax.swing.plaf.UIResource; import javax.swing.text.View; -import sun.awt.AppContext; import sun.awt.AWTAccessor; import sun.awt.AWTAccessor.MouseEventAccessor; @@ -1986,26 +1985,6 @@ public class SwingUtilities implements SwingConstants return (WindowListener)sharedOwnerFrame; } - /* Don't make these AppContext accessors public or protected -- - * since AppContext is in sun.awt in 1.2, we shouldn't expose it - * even indirectly with a public API. - */ - // REMIND(aim): phase out use of 4 methods below since they - // are just private covers for AWT methods (?) - - static Object appContextGet(Object key) { - return AppContext.getAppContext().get(key); - } - - static void appContextPut(Object key, Object value) { - AppContext.getAppContext().put(key, value); - } - - static void appContextRemove(Object key) { - AppContext.getAppContext().remove(key); - } - - static Class loadSystemClass(String className) throws ClassNotFoundException { return Class.forName(className, true, Thread.currentThread(). getContextClassLoader()); diff --git a/src/java.desktop/share/classes/javax/swing/SwingWorker.java b/src/java.desktop/share/classes/javax/swing/SwingWorker.java index 75f1700bded..dae695b4868 100644 --- a/src/java.desktop/share/classes/javax/swing/SwingWorker.java +++ b/src/java.desktop/share/classes/javax/swing/SwingWorker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,7 +45,8 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import sun.awt.AppContext; +import sun.awt.util.ThreadGroupUtils; + import sun.swing.AccumulativeRunnable; /** @@ -266,7 +267,7 @@ public abstract class SwingWorker implements RunnableFuture { */ private AccumulativeRunnable doNotifyProgressChange; - private final AccumulativeRunnable doSubmit = getDoSubmit(); + private final AccumulativeRunnable doSubmit = new DoSubmitAccumulativeRunnable(); /** * Values for the {@code state} bound property. @@ -755,18 +756,16 @@ public abstract class SwingWorker implements RunnableFuture { } + private static ExecutorService executorService; + /** * returns workersExecutorService. * - * returns the service stored in the appContext or creates it if - * necessary. + * returns the service and creates it if necessary. * * @return ExecutorService for the {@code SwingWorkers} */ private static synchronized ExecutorService getWorkersExecutorService() { - final AppContext appContext = AppContext.getAppContext(); - ExecutorService executorService = - (ExecutorService) appContext.get(SwingWorker.class); if (executorService == null) { //this creates daemon threads. ThreadFactory threadFactory = @@ -788,46 +787,26 @@ public abstract class SwingWorker implements RunnableFuture { 10L, TimeUnit.MINUTES, new LinkedBlockingQueue(), threadFactory); - appContext.put(SwingWorker.class, executorService); - // Don't use ShutdownHook here as it's not enough. We should track - // AppContext disposal instead of JVM shutdown, see 6799345 for details - final ExecutorService es = executorService; - appContext.addPropertyChangeListener(AppContext.DISPOSED_PROPERTY_NAME, - new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent pce) { - boolean disposed = (Boolean)pce.getNewValue(); - if (disposed) { - final WeakReference executorServiceRef = - new WeakReference(es); - final ExecutorService executorService = - executorServiceRef.get(); - if (executorService != null) { - executorService.shutdown(); - } - } - } + final Runnable shutdownHook = new Runnable() { + final WeakReference executorServiceRef = + new WeakReference(executorService); + public void run() { + final ExecutorService executorService = executorServiceRef.get(); + if (executorService != null) { + executorService.shutdown(); + } } - ); + }; + ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup(); + Thread t = new Thread(rootTG, shutdownHook, + "SwingWorker ES", 0, false); + t.setContextClassLoader(null); + Runtime.getRuntime().addShutdownHook(t); } return executorService; } - private static final Object DO_SUBMIT_KEY = new StringBuilder("doSubmit"); - private static AccumulativeRunnable getDoSubmit() { - synchronized (DO_SUBMIT_KEY) { - final AppContext appContext = AppContext.getAppContext(); - Object doSubmit = appContext.get(DO_SUBMIT_KEY); - if (doSubmit == null) { - doSubmit = new DoSubmitAccumulativeRunnable(); - appContext.put(DO_SUBMIT_KEY, doSubmit); - } - @SuppressWarnings("unchecked") - AccumulativeRunnable tmp = (AccumulativeRunnable) doSubmit; - return tmp; - } - } private static class DoSubmitAccumulativeRunnable extends AccumulativeRunnable implements ActionListener { private static final int DELAY = 1000 / 30; diff --git a/src/java.desktop/share/classes/javax/swing/Timer.java b/src/java.desktop/share/classes/javax/swing/Timer.java index 2cb8381d7d3..1063532715c 100644 --- a/src/java.desktop/share/classes/javax/swing/Timer.java +++ b/src/java.desktop/share/classes/javax/swing/Timer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -173,8 +173,7 @@ public class Timer implements Serializable private final transient Lock lock = new ReentrantLock(); // This field is maintained by TimerQueue. - // eventQueued can also be reset by the TimerQueue, but will only ever - // happen in an AppContext case when TimerQueues thread is destroyed. + // eventQueued can also be reset by the TimerQueue // access to this field is synchronized on getLock() lock. transient TimerQueue.DelayedTimer delayedTimer = null; diff --git a/src/java.desktop/share/classes/javax/swing/UIManager.java b/src/java.desktop/share/classes/javax/swing/UIManager.java index 69063c562e6..f323842ae49 100644 --- a/src/java.desktop/share/classes/javax/swing/UIManager.java +++ b/src/java.desktop/share/classes/javax/swing/UIManager.java @@ -56,7 +56,6 @@ import sun.awt.OSInfo; import sun.swing.SwingUtilities2; import java.util.HashMap; import java.util.Objects; -import sun.awt.AppContext; import sun.awt.AWTAccessor; import sun.swing.SwingAccessor; @@ -179,10 +178,7 @@ public class UIManager implements Serializable /** * This class defines the state managed by the UIManager. For * Swing applications the fields in this class could just as well - * be static members of UIManager however we give them - * "AppContext" - * scope instead so that potentially multiple lightweight - * applications running in a single VM have their own state. + * be static members of UIManager. */ private static class LAFState { @@ -206,8 +202,8 @@ public class UIManager implements Serializable void setSystemDefaults(UIDefaults x) { tables[1] = x; } /** - * Returns the SwingPropertyChangeSupport for the current - * AppContext. If create is a true, a non-null + * Returns the SwingPropertyChangeSupport instance. + * If create is a true, a non-null * SwingPropertyChangeSupport will be returned, if * create is false and this has not been invoked * with true, null will be returned. @@ -1366,18 +1362,7 @@ public class UIManager implements Serializable return; } - // Try to get default LAF from system property, then from AppContext - // (6653395), then use cross-platform one by default. - String lafName = null; - @SuppressWarnings("unchecked") - HashMap lafData = - (HashMap) AppContext.getAppContext().remove("swing.lafdata"); - if (lafData != null) { - lafName = lafData.remove("defaultlaf"); - } - if (lafName == null) { - lafName = getCrossPlatformLookAndFeelClassName(); - } + String lafName = getCrossPlatformLookAndFeelClassName(); lafName = swingProps.getProperty(defaultLAFKey, lafName); try { @@ -1385,13 +1370,6 @@ public class UIManager implements Serializable } catch (Exception e) { throw new Error("Cannot load " + lafName); } - - // Set any properties passed through AppContext (6653395). - if (lafData != null) { - for (Object key: lafData.keySet()) { - UIManager.put(key, lafData.get(key)); - } - } } @@ -1451,8 +1429,8 @@ public class UIManager implements Serializable /* * This method is called before any code that depends on the - * AppContext specific LAFState object runs. - * In some AppContext cases, it's possible for this method + * LAFState object runs. + * In some cases, it's possible for this method * to be re-entered, which is why we grab a lock before calling * initialize(). */ From 6a92c5314464f62889a4042a98b7a14c247ac889 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Thu, 26 Mar 2026 22:52:44 +0000 Subject: [PATCH 146/160] 8380915: Cleanup some Java declarations of the Vector API Reviewed-by: psandoz --- .../jdk/incubator/vector/AbstractMask.java | 14 ++-- .../jdk/incubator/vector/AbstractShuffle.java | 9 ++- .../jdk/incubator/vector/AbstractSpecies.java | 26 +++--- .../jdk/incubator/vector/AbstractVector.java | 13 +-- .../jdk/incubator/vector/ByteVector.java | 3 +- .../jdk/incubator/vector/ByteVector128.java | 28 +++---- .../jdk/incubator/vector/ByteVector256.java | 28 +++---- .../jdk/incubator/vector/ByteVector512.java | 28 +++---- .../jdk/incubator/vector/ByteVector64.java | 28 +++---- .../jdk/incubator/vector/ByteVectorMax.java | 28 +++---- .../jdk/incubator/vector/CPUFeatures.java | 4 +- .../jdk/incubator/vector/DoubleVector.java | 3 +- .../jdk/incubator/vector/DoubleVector128.java | 29 ++++--- .../jdk/incubator/vector/DoubleVector256.java | 29 ++++--- .../jdk/incubator/vector/DoubleVector512.java | 29 ++++--- .../jdk/incubator/vector/DoubleVector64.java | 29 ++++--- .../jdk/incubator/vector/DoubleVectorMax.java | 29 ++++--- .../jdk/incubator/vector/Float16Consts.java | 4 +- .../jdk/incubator/vector/FloatVector.java | 3 +- .../jdk/incubator/vector/FloatVector128.java | 30 ++++--- .../jdk/incubator/vector/FloatVector256.java | 30 ++++--- .../jdk/incubator/vector/FloatVector512.java | 30 ++++--- .../jdk/incubator/vector/FloatVector64.java | 30 ++++--- .../jdk/incubator/vector/FloatVectorMax.java | 30 ++++--- .../jdk/incubator/vector/IntVector.java | 3 +- .../jdk/incubator/vector/IntVector128.java | 28 +++---- .../jdk/incubator/vector/IntVector256.java | 28 +++---- .../jdk/incubator/vector/IntVector512.java | 28 +++---- .../jdk/incubator/vector/IntVector64.java | 28 +++---- .../jdk/incubator/vector/IntVectorMax.java | 28 +++---- .../jdk/incubator/vector/LongVector.java | 3 +- .../jdk/incubator/vector/LongVector128.java | 27 +++---- .../jdk/incubator/vector/LongVector256.java | 27 +++---- .../jdk/incubator/vector/LongVector512.java | 27 +++---- .../jdk/incubator/vector/LongVector64.java | 27 +++---- .../jdk/incubator/vector/LongVectorMax.java | 27 +++---- .../jdk/incubator/vector/ShortVector.java | 3 +- .../jdk/incubator/vector/ShortVector128.java | 28 +++---- .../jdk/incubator/vector/ShortVector256.java | 28 +++---- .../jdk/incubator/vector/ShortVector512.java | 28 +++---- .../jdk/incubator/vector/ShortVector64.java | 28 +++---- .../jdk/incubator/vector/ShortVectorMax.java | 28 +++---- .../classes/jdk/incubator/vector/Util.java | 4 +- .../classes/jdk/incubator/vector/Vector.java | 5 +- .../incubator/vector/VectorIntrinsics.java | 4 +- .../jdk/incubator/vector/VectorMask.java | 2 +- .../incubator/vector/VectorMathLibrary.java | 2 +- .../jdk/incubator/vector/VectorOperators.java | 79 +++++-------------- .../jdk/incubator/vector/VectorShuffle.java | 4 +- .../jdk/incubator/vector/VectorSpecies.java | 9 +-- .../incubator/vector/X-Vector.java.template | 3 +- .../vector/X-VectorBits.java.template | 33 ++++---- 52 files changed, 498 insertions(+), 585 deletions(-) diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractMask.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractMask.java index 5b762edfd3b..9ac90c08c27 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractMask.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractMask.java @@ -24,17 +24,19 @@ */ package jdk.incubator.vector; -import java.util.Objects; - -import jdk.internal.vm.annotation.ForceInline; - import jdk.internal.misc.Unsafe; - +import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; import static jdk.incubator.vector.VectorOperators.*; -abstract class AbstractMask extends VectorMask { +abstract sealed class AbstractMask extends VectorMask + permits ByteVector64.ByteMask64, ByteVector128.ByteMask128, ByteVector256.ByteMask256, ByteVector512.ByteMask512, ByteVectorMax.ByteMaskMax, + DoubleVector64.DoubleMask64, DoubleVector128.DoubleMask128, DoubleVector256.DoubleMask256, DoubleVector512.DoubleMask512, DoubleVectorMax.DoubleMaskMax, + FloatVector64.FloatMask64, FloatVector128.FloatMask128, FloatVector256.FloatMask256, FloatVector512.FloatMask512, FloatVectorMax.FloatMaskMax, + IntVector64.IntMask64, IntVector128.IntMask128, IntVector256.IntMask256, IntVector512.IntMask512, IntVectorMax.IntMaskMax, + LongVector64.LongMask64, LongVector128.LongMask128, LongVector256.LongMask256, LongVector512.LongMask512, LongVectorMax.LongMaskMax, + ShortVector64.ShortMask64, ShortVector128.ShortMask128, ShortVector256.ShortMask256, ShortVector512.ShortMask512, ShortVectorMax.ShortMaskMax { AbstractMask(boolean[] bits) { super(bits); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java index 075400a0d4a..bea495f74fc 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java @@ -25,10 +25,17 @@ package jdk.incubator.vector; import java.util.function.IntUnaryOperator; + import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -abstract class AbstractShuffle extends VectorShuffle { +abstract sealed class AbstractShuffle extends VectorShuffle + permits ByteVector64.ByteShuffle64, ByteVector128.ByteShuffle128, ByteVector256.ByteShuffle256, ByteVector512.ByteShuffle512, ByteVectorMax.ByteShuffleMax, + DoubleVector64.DoubleShuffle64, DoubleVector128.DoubleShuffle128, DoubleVector256.DoubleShuffle256, DoubleVector512.DoubleShuffle512, DoubleVectorMax.DoubleShuffleMax, + FloatVector64.FloatShuffle64, FloatVector128.FloatShuffle128, FloatVector256.FloatShuffle256, FloatVector512.FloatShuffle512, FloatVectorMax.FloatShuffleMax, + IntVector64.IntShuffle64, IntVector128.IntShuffle128, IntVector256.IntShuffle256, IntVector512.IntShuffle512, IntVectorMax.IntShuffleMax, + LongVector64.LongShuffle64, LongVector128.LongShuffle128, LongVector256.LongShuffle256, LongVector512.LongShuffle512, LongVectorMax.LongShuffleMax, + ShortVector64.ShortShuffle64, ShortVector128.ShortShuffle128, ShortVector256.ShortShuffle256, ShortVector512.ShortShuffle512, ShortVectorMax.ShortShuffleMax { static final IntUnaryOperator IDENTITY = i -> i; // Internal representation allows for a maximum index of E.MAX_VALUE - 1 diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java index 6c834077387..3fd2be34346 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java @@ -24,39 +24,31 @@ */ package jdk.incubator.vector; -import java.lang.foreign.MemorySegment; -import jdk.internal.vm.annotation.ForceInline; -import jdk.internal.vm.annotation.Stable; import java.lang.reflect.Array; -import java.nio.ByteOrder; import java.util.Arrays; import java.util.function.Function; import java.util.function.IntUnaryOperator; -abstract class AbstractSpecies extends jdk.internal.vm.vector.VectorSupport.VectorSpecies - implements VectorSpecies { - @Stable +import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Stable; +import jdk.internal.vm.annotation.TrustFinalFields; + +@TrustFinalFields +abstract sealed class AbstractSpecies extends jdk.internal.vm.vector.VectorSupport.VectorSpecies + implements VectorSpecies + permits ByteVector.ByteSpecies, DoubleVector.DoubleSpecies, FloatVector.FloatSpecies, + IntVector.IntSpecies, LongVector.LongSpecies, ShortVector.ShortSpecies { final VectorShape vectorShape; - @Stable final LaneType laneType; - @Stable final int laneCount; - @Stable final int laneCountLog2P1; - @Stable final Class> vectorType; - @Stable final Class> maskType; - @Stable final Class> shuffleType; - @Stable final Function> vectorFactory; - @Stable final VectorShape indexShape; - @Stable final int maxScale, minScale; - @Stable final int vectorBitSize, vectorByteSize; AbstractSpecies(VectorShape vectorShape, diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java index 80260c2bd30..ea8112cc2ae 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java @@ -25,22 +25,17 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; +import java.nio.ByteOrder; +import java.util.function.IntUnaryOperator; -import jdk.internal.foreign.AbstractMemorySegmentImpl; -import jdk.internal.foreign.Utils; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import java.lang.foreign.ValueLayout; -import java.lang.reflect.Array; -import java.nio.ByteOrder; -import java.util.Objects; -import java.util.function.IntUnaryOperator; - import static jdk.incubator.vector.VectorOperators.*; @SuppressWarnings("cast") -abstract class AbstractVector extends Vector { +abstract sealed class AbstractVector extends Vector + permits ByteVector, DoubleVector, FloatVector, IntVector, LongVector, ShortVector { /** * The order of vector bytes when stored in natural, * array elements of the same lane type. diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java index 846032cb5c6..7231ada3273 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code byte} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class ByteVector extends AbstractVector { +public abstract sealed class ByteVector extends AbstractVector + permits ByteVector64, ByteVector128, ByteVector256, ByteVector512, ByteVectorMax { ByteVector(byte[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java index 360afedbbbb..36ea8d081a8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector128.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class ByteVector128 extends ByteVector { @Override @ForceInline public final ByteShuffle128 toShuffle() { - return (ByteShuffle128) toShuffle(vspecies(), false); + return (ByteShuffle128) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -648,7 +646,7 @@ final class ByteVector128 extends ByteVector { @Override ByteMask128 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -658,7 +656,7 @@ final class ByteVector128 extends ByteVector { @Override ByteMask128 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ByteMask128)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -808,16 +806,16 @@ final class ByteVector128 extends ByteVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ByteMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ByteMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ByteMask128)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ByteMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ByteMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ByteMask128)m).getBits())); } @ForceInline @@ -825,7 +823,7 @@ final class ByteVector128 extends ByteVector { static ByteMask128 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ByteMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ByteMask128 TRUE_MASK = new ByteMask128(true); private static final ByteMask128 FALSE_MASK = new ByteMask128(false); @@ -885,7 +883,7 @@ final class ByteVector128 extends ByteVector { @Override ByteVector128 toBitsVector0() { - return ((ByteVector128) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ByteVector128) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -936,7 +934,7 @@ final class ByteVector128 extends ByteVector { @ForceInline public final ByteMask128 laneIsValid() { return (ByteMask128) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -944,7 +942,7 @@ final class ByteVector128 extends ByteVector { public final ByteShuffle128 rearrange(VectorShuffle shuffle) { ByteShuffle128 concreteShuffle = (ByteShuffle128) shuffle; return (ByteShuffle128) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -957,7 +955,7 @@ final class ByteVector128 extends ByteVector { v = (ByteVector128) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ByteShuffle128) v.toShuffle(vspecies(), false); + return (ByteShuffle128) v.toShuffle(VSPECIES, false); } private static byte[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java index ca0c59dd49e..a11268ea40d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector256.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class ByteVector256 extends ByteVector { @Override @ForceInline public final ByteShuffle256 toShuffle() { - return (ByteShuffle256) toShuffle(vspecies(), false); + return (ByteShuffle256) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -680,7 +678,7 @@ final class ByteVector256 extends ByteVector { @Override ByteMask256 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -690,7 +688,7 @@ final class ByteVector256 extends ByteVector { @Override ByteMask256 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ByteMask256)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -840,16 +838,16 @@ final class ByteVector256 extends ByteVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ByteMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ByteMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ByteMask256)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ByteMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ByteMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ByteMask256)m).getBits())); } @ForceInline @@ -857,7 +855,7 @@ final class ByteVector256 extends ByteVector { static ByteMask256 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ByteMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ByteMask256 TRUE_MASK = new ByteMask256(true); private static final ByteMask256 FALSE_MASK = new ByteMask256(false); @@ -917,7 +915,7 @@ final class ByteVector256 extends ByteVector { @Override ByteVector256 toBitsVector0() { - return ((ByteVector256) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ByteVector256) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -968,7 +966,7 @@ final class ByteVector256 extends ByteVector { @ForceInline public final ByteMask256 laneIsValid() { return (ByteMask256) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -976,7 +974,7 @@ final class ByteVector256 extends ByteVector { public final ByteShuffle256 rearrange(VectorShuffle shuffle) { ByteShuffle256 concreteShuffle = (ByteShuffle256) shuffle; return (ByteShuffle256) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -989,7 +987,7 @@ final class ByteVector256 extends ByteVector { v = (ByteVector256) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ByteShuffle256) v.toShuffle(vspecies(), false); + return (ByteShuffle256) v.toShuffle(VSPECIES, false); } private static byte[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java index 1a0c69153bc..707254e034e 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector512.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class ByteVector512 extends ByteVector { @Override @ForceInline public final ByteShuffle512 toShuffle() { - return (ByteShuffle512) toShuffle(vspecies(), false); + return (ByteShuffle512) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -744,7 +742,7 @@ final class ByteVector512 extends ByteVector { @Override ByteMask512 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -754,7 +752,7 @@ final class ByteVector512 extends ByteVector { @Override ByteMask512 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ByteMask512)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -904,16 +902,16 @@ final class ByteVector512 extends ByteVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ByteMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ByteMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ByteMask512)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ByteMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ByteMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ByteMask512)m).getBits())); } @ForceInline @@ -921,7 +919,7 @@ final class ByteVector512 extends ByteVector { static ByteMask512 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ByteMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ByteMask512 TRUE_MASK = new ByteMask512(true); private static final ByteMask512 FALSE_MASK = new ByteMask512(false); @@ -981,7 +979,7 @@ final class ByteVector512 extends ByteVector { @Override ByteVector512 toBitsVector0() { - return ((ByteVector512) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ByteVector512) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -1032,7 +1030,7 @@ final class ByteVector512 extends ByteVector { @ForceInline public final ByteMask512 laneIsValid() { return (ByteMask512) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -1040,7 +1038,7 @@ final class ByteVector512 extends ByteVector { public final ByteShuffle512 rearrange(VectorShuffle shuffle) { ByteShuffle512 concreteShuffle = (ByteShuffle512) shuffle; return (ByteShuffle512) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -1053,7 +1051,7 @@ final class ByteVector512 extends ByteVector { v = (ByteVector512) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ByteShuffle512) v.toShuffle(vspecies(), false); + return (ByteShuffle512) v.toShuffle(VSPECIES, false); } private static byte[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java index 50561eca0f8..d304edfc0c7 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector64.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class ByteVector64 extends ByteVector { @Override @ForceInline public final ByteShuffle64 toShuffle() { - return (ByteShuffle64) toShuffle(vspecies(), false); + return (ByteShuffle64) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -632,7 +630,7 @@ final class ByteVector64 extends ByteVector { @Override ByteMask64 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -642,7 +640,7 @@ final class ByteVector64 extends ByteVector { @Override ByteMask64 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ByteMask64)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -792,16 +790,16 @@ final class ByteVector64 extends ByteVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ByteMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ByteMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ByteMask64)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ByteMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ByteMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ByteMask64)m).getBits())); } @ForceInline @@ -809,7 +807,7 @@ final class ByteVector64 extends ByteVector { static ByteMask64 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ByteMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ByteMask64 TRUE_MASK = new ByteMask64(true); private static final ByteMask64 FALSE_MASK = new ByteMask64(false); @@ -869,7 +867,7 @@ final class ByteVector64 extends ByteVector { @Override ByteVector64 toBitsVector0() { - return ((ByteVector64) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ByteVector64) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -920,7 +918,7 @@ final class ByteVector64 extends ByteVector { @ForceInline public final ByteMask64 laneIsValid() { return (ByteMask64) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -928,7 +926,7 @@ final class ByteVector64 extends ByteVector { public final ByteShuffle64 rearrange(VectorShuffle shuffle) { ByteShuffle64 concreteShuffle = (ByteShuffle64) shuffle; return (ByteShuffle64) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -941,7 +939,7 @@ final class ByteVector64 extends ByteVector { v = (ByteVector64) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ByteShuffle64) v.toShuffle(vspecies(), false); + return (ByteShuffle64) v.toShuffle(VSPECIES, false); } private static byte[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java index ee931bbc077..0084995346b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVectorMax.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class ByteVectorMax extends ByteVector { @Override @ForceInline public final ByteShuffleMax toShuffle() { - return (ByteShuffleMax) toShuffle(vspecies(), false); + return (ByteShuffleMax) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -618,7 +616,7 @@ final class ByteVectorMax extends ByteVector { @Override ByteMaskMax uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -628,7 +626,7 @@ final class ByteVectorMax extends ByteVector { @Override ByteMaskMax bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ByteMaskMax)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -778,16 +776,16 @@ final class ByteVectorMax extends ByteVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ByteMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ByteMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ByteMaskMax)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ByteMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ByteMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ByteMaskMax)m).getBits())); } @ForceInline @@ -795,7 +793,7 @@ final class ByteVectorMax extends ByteVector { static ByteMaskMax maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ByteMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ByteMaskMax TRUE_MASK = new ByteMaskMax(true); private static final ByteMaskMax FALSE_MASK = new ByteMaskMax(false); @@ -855,7 +853,7 @@ final class ByteVectorMax extends ByteVector { @Override ByteVectorMax toBitsVector0() { - return ((ByteVectorMax) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ByteVectorMax) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -906,7 +904,7 @@ final class ByteVectorMax extends ByteVector { @ForceInline public final ByteMaskMax laneIsValid() { return (ByteMaskMax) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -914,7 +912,7 @@ final class ByteVectorMax extends ByteVector { public final ByteShuffleMax rearrange(VectorShuffle shuffle) { ByteShuffleMax concreteShuffle = (ByteShuffleMax) shuffle; return (ByteShuffleMax) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -927,7 +925,7 @@ final class ByteVectorMax extends ByteVector { v = (ByteVectorMax) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ByteShuffleMax) v.toShuffle(vspecies(), false); + return (ByteShuffleMax) v.toShuffle(VSPECIES, false); } private static byte[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/CPUFeatures.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/CPUFeatures.java index c0d8ef03ada..0ad7ba1651d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/CPUFeatures.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/CPUFeatures.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * 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 static jdk.internal.vm.vector.Utils.debug; /** * Enumerates CPU ISA extensions supported by the JVM on the current hardware. */ -/*package-private*/ class CPUFeatures { +/*package-private*/ final class CPUFeatures { private static final Set features = getCPUFeatures(); private static Set getCPUFeatures() { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java index 5e7c97dc56d..6f9b5e53ead 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code double} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class DoubleVector extends AbstractVector { +public abstract sealed class DoubleVector extends AbstractVector + permits DoubleVector64, DoubleVector128, DoubleVector256, DoubleVector512, DoubleVectorMax { DoubleVector(double[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java index 43c7e3f0c46..8d3ec21ec9b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector128.java @@ -35,9 +35,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -360,7 +359,7 @@ final class DoubleVector128 extends DoubleVector { @Override @ForceInline public final DoubleShuffle128 toShuffle() { - return (DoubleShuffle128) toShuffle(vspecies(), false); + return (DoubleShuffle128) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -609,7 +608,7 @@ final class DoubleVector128 extends DoubleVector { @Override DoubleMask128 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -619,7 +618,7 @@ final class DoubleVector128 extends DoubleVector { @Override DoubleMask128 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((DoubleMask128)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -769,16 +768,16 @@ final class DoubleVector128 extends DoubleVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, DoubleMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((DoubleMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((DoubleMask128)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, DoubleMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((DoubleMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((DoubleMask128)m).getBits())); } @ForceInline @@ -786,7 +785,7 @@ final class DoubleVector128 extends DoubleVector { static DoubleMask128 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(DoubleMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final DoubleMask128 TRUE_MASK = new DoubleMask128(true); private static final DoubleMask128 FALSE_MASK = new DoubleMask128(false); @@ -835,7 +834,7 @@ final class DoubleVector128 extends DoubleVector { @Override @ForceInline public DoubleVector128 toVector() { - return (DoubleVector128) toBitsVector().castShape(vspecies(), 0); + return (DoubleVector128) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -846,7 +845,7 @@ final class DoubleVector128 extends DoubleVector { @Override LongVector128 toBitsVector0() { - return ((LongVector128) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector128) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -920,7 +919,7 @@ final class DoubleVector128 extends DoubleVector { @ForceInline public final DoubleMask128 laneIsValid() { return (DoubleMask128) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -928,7 +927,7 @@ final class DoubleVector128 extends DoubleVector { public final DoubleShuffle128 rearrange(VectorShuffle shuffle) { DoubleShuffle128 concreteShuffle = (DoubleShuffle128) shuffle; return (DoubleShuffle128) toBitsVector().rearrange(concreteShuffle.cast(LongVector.SPECIES_128)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -941,7 +940,7 @@ final class DoubleVector128 extends DoubleVector { v = (LongVector128) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (DoubleShuffle128) v.toShuffle(vspecies(), false); + return (DoubleShuffle128) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java index 5f176854dbd..c6bb4b7e3d3 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector256.java @@ -35,9 +35,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -360,7 +359,7 @@ final class DoubleVector256 extends DoubleVector { @Override @ForceInline public final DoubleShuffle256 toShuffle() { - return (DoubleShuffle256) toShuffle(vspecies(), false); + return (DoubleShuffle256) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -613,7 +612,7 @@ final class DoubleVector256 extends DoubleVector { @Override DoubleMask256 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -623,7 +622,7 @@ final class DoubleVector256 extends DoubleVector { @Override DoubleMask256 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((DoubleMask256)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -773,16 +772,16 @@ final class DoubleVector256 extends DoubleVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, DoubleMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((DoubleMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((DoubleMask256)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, DoubleMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((DoubleMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((DoubleMask256)m).getBits())); } @ForceInline @@ -790,7 +789,7 @@ final class DoubleVector256 extends DoubleVector { static DoubleMask256 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(DoubleMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final DoubleMask256 TRUE_MASK = new DoubleMask256(true); private static final DoubleMask256 FALSE_MASK = new DoubleMask256(false); @@ -839,7 +838,7 @@ final class DoubleVector256 extends DoubleVector { @Override @ForceInline public DoubleVector256 toVector() { - return (DoubleVector256) toBitsVector().castShape(vspecies(), 0); + return (DoubleVector256) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -850,7 +849,7 @@ final class DoubleVector256 extends DoubleVector { @Override LongVector256 toBitsVector0() { - return ((LongVector256) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector256) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -924,7 +923,7 @@ final class DoubleVector256 extends DoubleVector { @ForceInline public final DoubleMask256 laneIsValid() { return (DoubleMask256) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -932,7 +931,7 @@ final class DoubleVector256 extends DoubleVector { public final DoubleShuffle256 rearrange(VectorShuffle shuffle) { DoubleShuffle256 concreteShuffle = (DoubleShuffle256) shuffle; return (DoubleShuffle256) toBitsVector().rearrange(concreteShuffle.cast(LongVector.SPECIES_256)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -945,7 +944,7 @@ final class DoubleVector256 extends DoubleVector { v = (LongVector256) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (DoubleShuffle256) v.toShuffle(vspecies(), false); + return (DoubleShuffle256) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java index 0696f48163d..fb1441efc63 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector512.java @@ -35,9 +35,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -360,7 +359,7 @@ final class DoubleVector512 extends DoubleVector { @Override @ForceInline public final DoubleShuffle512 toShuffle() { - return (DoubleShuffle512) toShuffle(vspecies(), false); + return (DoubleShuffle512) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -621,7 +620,7 @@ final class DoubleVector512 extends DoubleVector { @Override DoubleMask512 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -631,7 +630,7 @@ final class DoubleVector512 extends DoubleVector { @Override DoubleMask512 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((DoubleMask512)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -781,16 +780,16 @@ final class DoubleVector512 extends DoubleVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, DoubleMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((DoubleMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((DoubleMask512)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, DoubleMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((DoubleMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((DoubleMask512)m).getBits())); } @ForceInline @@ -798,7 +797,7 @@ final class DoubleVector512 extends DoubleVector { static DoubleMask512 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(DoubleMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final DoubleMask512 TRUE_MASK = new DoubleMask512(true); private static final DoubleMask512 FALSE_MASK = new DoubleMask512(false); @@ -847,7 +846,7 @@ final class DoubleVector512 extends DoubleVector { @Override @ForceInline public DoubleVector512 toVector() { - return (DoubleVector512) toBitsVector().castShape(vspecies(), 0); + return (DoubleVector512) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -858,7 +857,7 @@ final class DoubleVector512 extends DoubleVector { @Override LongVector512 toBitsVector0() { - return ((LongVector512) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector512) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -932,7 +931,7 @@ final class DoubleVector512 extends DoubleVector { @ForceInline public final DoubleMask512 laneIsValid() { return (DoubleMask512) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -940,7 +939,7 @@ final class DoubleVector512 extends DoubleVector { public final DoubleShuffle512 rearrange(VectorShuffle shuffle) { DoubleShuffle512 concreteShuffle = (DoubleShuffle512) shuffle; return (DoubleShuffle512) toBitsVector().rearrange(concreteShuffle.cast(LongVector.SPECIES_512)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -953,7 +952,7 @@ final class DoubleVector512 extends DoubleVector { v = (LongVector512) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (DoubleShuffle512) v.toShuffle(vspecies(), false); + return (DoubleShuffle512) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java index 5b74c2c4619..5583cff80e1 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector64.java @@ -35,9 +35,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -360,7 +359,7 @@ final class DoubleVector64 extends DoubleVector { @Override @ForceInline public final DoubleShuffle64 toShuffle() { - return (DoubleShuffle64) toShuffle(vspecies(), false); + return (DoubleShuffle64) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -607,7 +606,7 @@ final class DoubleVector64 extends DoubleVector { @Override DoubleMask64 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -617,7 +616,7 @@ final class DoubleVector64 extends DoubleVector { @Override DoubleMask64 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((DoubleMask64)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -767,16 +766,16 @@ final class DoubleVector64 extends DoubleVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, DoubleMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((DoubleMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((DoubleMask64)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, DoubleMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((DoubleMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((DoubleMask64)m).getBits())); } @ForceInline @@ -784,7 +783,7 @@ final class DoubleVector64 extends DoubleVector { static DoubleMask64 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(DoubleMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final DoubleMask64 TRUE_MASK = new DoubleMask64(true); private static final DoubleMask64 FALSE_MASK = new DoubleMask64(false); @@ -833,7 +832,7 @@ final class DoubleVector64 extends DoubleVector { @Override @ForceInline public DoubleVector64 toVector() { - return (DoubleVector64) toBitsVector().castShape(vspecies(), 0); + return (DoubleVector64) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -844,7 +843,7 @@ final class DoubleVector64 extends DoubleVector { @Override LongVector64 toBitsVector0() { - return ((LongVector64) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector64) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -918,7 +917,7 @@ final class DoubleVector64 extends DoubleVector { @ForceInline public final DoubleMask64 laneIsValid() { return (DoubleMask64) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -926,7 +925,7 @@ final class DoubleVector64 extends DoubleVector { public final DoubleShuffle64 rearrange(VectorShuffle shuffle) { DoubleShuffle64 concreteShuffle = (DoubleShuffle64) shuffle; return (DoubleShuffle64) toBitsVector().rearrange(concreteShuffle.cast(LongVector.SPECIES_64)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -939,7 +938,7 @@ final class DoubleVector64 extends DoubleVector { v = (LongVector64) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (DoubleShuffle64) v.toShuffle(vspecies(), false); + return (DoubleShuffle64) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java index 07d227d641a..41272a5a5e5 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVectorMax.java @@ -35,9 +35,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -360,7 +359,7 @@ final class DoubleVectorMax extends DoubleVector { @Override @ForceInline public final DoubleShuffleMax toShuffle() { - return (DoubleShuffleMax) toShuffle(vspecies(), false); + return (DoubleShuffleMax) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -606,7 +605,7 @@ final class DoubleVectorMax extends DoubleVector { @Override DoubleMaskMax uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -616,7 +615,7 @@ final class DoubleVectorMax extends DoubleVector { @Override DoubleMaskMax bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((DoubleMaskMax)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -766,16 +765,16 @@ final class DoubleVectorMax extends DoubleVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, DoubleMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((DoubleMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((DoubleMaskMax)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, DoubleMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((DoubleMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((DoubleMaskMax)m).getBits())); } @ForceInline @@ -783,7 +782,7 @@ final class DoubleVectorMax extends DoubleVector { static DoubleMaskMax maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(DoubleMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final DoubleMaskMax TRUE_MASK = new DoubleMaskMax(true); private static final DoubleMaskMax FALSE_MASK = new DoubleMaskMax(false); @@ -832,7 +831,7 @@ final class DoubleVectorMax extends DoubleVector { @Override @ForceInline public DoubleVectorMax toVector() { - return (DoubleVectorMax) toBitsVector().castShape(vspecies(), 0); + return (DoubleVectorMax) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -843,7 +842,7 @@ final class DoubleVectorMax extends DoubleVector { @Override LongVectorMax toBitsVector0() { - return ((LongVectorMax) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVectorMax) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -917,7 +916,7 @@ final class DoubleVectorMax extends DoubleVector { @ForceInline public final DoubleMaskMax laneIsValid() { return (DoubleMaskMax) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -925,7 +924,7 @@ final class DoubleVectorMax extends DoubleVector { public final DoubleShuffleMax rearrange(VectorShuffle shuffle) { DoubleShuffleMax concreteShuffle = (DoubleShuffleMax) shuffle; return (DoubleShuffleMax) toBitsVector().rearrange(concreteShuffle.cast(LongVector.SPECIES_MAX)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -938,7 +937,7 @@ final class DoubleVectorMax extends DoubleVector { v = (LongVectorMax) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (DoubleShuffleMax) v.toShuffle(vspecies(), false); + return (DoubleShuffleMax) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Consts.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Consts.java index 48c4d2199b1..b70b11b0a49 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Consts.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Consts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,7 @@ import static jdk.incubator.vector.Float16.SIZE; * {@code Float16} type. */ -class Float16Consts { +final class Float16Consts { /** * Don't let anyone instantiate this class. */ diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java index 5862a295fa3..cdf2532e4d9 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code float} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class FloatVector extends AbstractVector { +public abstract sealed class FloatVector extends AbstractVector + permits FloatVector64, FloatVector128, FloatVector256, FloatVector512, FloatVectorMax { FloatVector(float[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java index 17c1fdba4fc..24888e966da 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector128.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -360,7 +358,7 @@ final class FloatVector128 extends FloatVector { @Override @ForceInline public final FloatShuffle128 toShuffle() { - return (FloatShuffle128) toShuffle(vspecies(), false); + return (FloatShuffle128) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -613,7 +611,7 @@ final class FloatVector128 extends FloatVector { @Override FloatMask128 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -623,7 +621,7 @@ final class FloatVector128 extends FloatVector { @Override FloatMask128 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((FloatMask128)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -773,16 +771,16 @@ final class FloatVector128 extends FloatVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, FloatMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((FloatMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((FloatMask128)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, FloatMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((FloatMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((FloatMask128)m).getBits())); } @ForceInline @@ -790,7 +788,7 @@ final class FloatVector128 extends FloatVector { static FloatMask128 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(FloatMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final FloatMask128 TRUE_MASK = new FloatMask128(true); private static final FloatMask128 FALSE_MASK = new FloatMask128(false); @@ -839,7 +837,7 @@ final class FloatVector128 extends FloatVector { @Override @ForceInline public FloatVector128 toVector() { - return (FloatVector128) toBitsVector().castShape(vspecies(), 0); + return (FloatVector128) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -850,7 +848,7 @@ final class FloatVector128 extends FloatVector { @Override IntVector128 toBitsVector0() { - return ((IntVector128) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector128) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -875,7 +873,7 @@ final class FloatVector128 extends FloatVector { @ForceInline public final FloatMask128 laneIsValid() { return (FloatMask128) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -883,7 +881,7 @@ final class FloatVector128 extends FloatVector { public final FloatShuffle128 rearrange(VectorShuffle shuffle) { FloatShuffle128 concreteShuffle = (FloatShuffle128) shuffle; return (FloatShuffle128) toBitsVector().rearrange(concreteShuffle.cast(IntVector.SPECIES_128)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -896,7 +894,7 @@ final class FloatVector128 extends FloatVector { v = (IntVector128) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (FloatShuffle128) v.toShuffle(vspecies(), false); + return (FloatShuffle128) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java index 7badb71415e..ecbd80046f4 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector256.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -360,7 +358,7 @@ final class FloatVector256 extends FloatVector { @Override @ForceInline public final FloatShuffle256 toShuffle() { - return (FloatShuffle256) toShuffle(vspecies(), false); + return (FloatShuffle256) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -621,7 +619,7 @@ final class FloatVector256 extends FloatVector { @Override FloatMask256 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -631,7 +629,7 @@ final class FloatVector256 extends FloatVector { @Override FloatMask256 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((FloatMask256)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -781,16 +779,16 @@ final class FloatVector256 extends FloatVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, FloatMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((FloatMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((FloatMask256)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, FloatMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((FloatMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((FloatMask256)m).getBits())); } @ForceInline @@ -798,7 +796,7 @@ final class FloatVector256 extends FloatVector { static FloatMask256 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(FloatMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final FloatMask256 TRUE_MASK = new FloatMask256(true); private static final FloatMask256 FALSE_MASK = new FloatMask256(false); @@ -847,7 +845,7 @@ final class FloatVector256 extends FloatVector { @Override @ForceInline public FloatVector256 toVector() { - return (FloatVector256) toBitsVector().castShape(vspecies(), 0); + return (FloatVector256) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -858,7 +856,7 @@ final class FloatVector256 extends FloatVector { @Override IntVector256 toBitsVector0() { - return ((IntVector256) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector256) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -883,7 +881,7 @@ final class FloatVector256 extends FloatVector { @ForceInline public final FloatMask256 laneIsValid() { return (FloatMask256) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -891,7 +889,7 @@ final class FloatVector256 extends FloatVector { public final FloatShuffle256 rearrange(VectorShuffle shuffle) { FloatShuffle256 concreteShuffle = (FloatShuffle256) shuffle; return (FloatShuffle256) toBitsVector().rearrange(concreteShuffle.cast(IntVector.SPECIES_256)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -904,7 +902,7 @@ final class FloatVector256 extends FloatVector { v = (IntVector256) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (FloatShuffle256) v.toShuffle(vspecies(), false); + return (FloatShuffle256) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java index 7c0786b7fcd..b5a934dd90c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector512.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -360,7 +358,7 @@ final class FloatVector512 extends FloatVector { @Override @ForceInline public final FloatShuffle512 toShuffle() { - return (FloatShuffle512) toShuffle(vspecies(), false); + return (FloatShuffle512) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -637,7 +635,7 @@ final class FloatVector512 extends FloatVector { @Override FloatMask512 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -647,7 +645,7 @@ final class FloatVector512 extends FloatVector { @Override FloatMask512 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((FloatMask512)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -797,16 +795,16 @@ final class FloatVector512 extends FloatVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, FloatMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((FloatMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((FloatMask512)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, FloatMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((FloatMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((FloatMask512)m).getBits())); } @ForceInline @@ -814,7 +812,7 @@ final class FloatVector512 extends FloatVector { static FloatMask512 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(FloatMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final FloatMask512 TRUE_MASK = new FloatMask512(true); private static final FloatMask512 FALSE_MASK = new FloatMask512(false); @@ -863,7 +861,7 @@ final class FloatVector512 extends FloatVector { @Override @ForceInline public FloatVector512 toVector() { - return (FloatVector512) toBitsVector().castShape(vspecies(), 0); + return (FloatVector512) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -874,7 +872,7 @@ final class FloatVector512 extends FloatVector { @Override IntVector512 toBitsVector0() { - return ((IntVector512) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector512) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -899,7 +897,7 @@ final class FloatVector512 extends FloatVector { @ForceInline public final FloatMask512 laneIsValid() { return (FloatMask512) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -907,7 +905,7 @@ final class FloatVector512 extends FloatVector { public final FloatShuffle512 rearrange(VectorShuffle shuffle) { FloatShuffle512 concreteShuffle = (FloatShuffle512) shuffle; return (FloatShuffle512) toBitsVector().rearrange(concreteShuffle.cast(IntVector.SPECIES_512)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -920,7 +918,7 @@ final class FloatVector512 extends FloatVector { v = (IntVector512) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (FloatShuffle512) v.toShuffle(vspecies(), false); + return (FloatShuffle512) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java index fc4877e5ae8..4d3118739ea 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector64.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -360,7 +358,7 @@ final class FloatVector64 extends FloatVector { @Override @ForceInline public final FloatShuffle64 toShuffle() { - return (FloatShuffle64) toShuffle(vspecies(), false); + return (FloatShuffle64) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -609,7 +607,7 @@ final class FloatVector64 extends FloatVector { @Override FloatMask64 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -619,7 +617,7 @@ final class FloatVector64 extends FloatVector { @Override FloatMask64 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((FloatMask64)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -769,16 +767,16 @@ final class FloatVector64 extends FloatVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, FloatMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((FloatMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((FloatMask64)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, FloatMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((FloatMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((FloatMask64)m).getBits())); } @ForceInline @@ -786,7 +784,7 @@ final class FloatVector64 extends FloatVector { static FloatMask64 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(FloatMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final FloatMask64 TRUE_MASK = new FloatMask64(true); private static final FloatMask64 FALSE_MASK = new FloatMask64(false); @@ -835,7 +833,7 @@ final class FloatVector64 extends FloatVector { @Override @ForceInline public FloatVector64 toVector() { - return (FloatVector64) toBitsVector().castShape(vspecies(), 0); + return (FloatVector64) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -846,7 +844,7 @@ final class FloatVector64 extends FloatVector { @Override IntVector64 toBitsVector0() { - return ((IntVector64) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector64) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -871,7 +869,7 @@ final class FloatVector64 extends FloatVector { @ForceInline public final FloatMask64 laneIsValid() { return (FloatMask64) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -879,7 +877,7 @@ final class FloatVector64 extends FloatVector { public final FloatShuffle64 rearrange(VectorShuffle shuffle) { FloatShuffle64 concreteShuffle = (FloatShuffle64) shuffle; return (FloatShuffle64) toBitsVector().rearrange(concreteShuffle.cast(IntVector.SPECIES_64)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -892,7 +890,7 @@ final class FloatVector64 extends FloatVector { v = (IntVector64) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (FloatShuffle64) v.toShuffle(vspecies(), false); + return (FloatShuffle64) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java index 5cfafecdb58..f115a1c79b8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVectorMax.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -360,7 +358,7 @@ final class FloatVectorMax extends FloatVector { @Override @ForceInline public final FloatShuffleMax toShuffle() { - return (FloatShuffleMax) toShuffle(vspecies(), false); + return (FloatShuffleMax) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -606,7 +604,7 @@ final class FloatVectorMax extends FloatVector { @Override FloatMaskMax uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -616,7 +614,7 @@ final class FloatVectorMax extends FloatVector { @Override FloatMaskMax bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((FloatMaskMax)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -766,16 +764,16 @@ final class FloatVectorMax extends FloatVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, FloatMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((FloatMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((FloatMaskMax)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, FloatMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((FloatMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((FloatMaskMax)m).getBits())); } @ForceInline @@ -783,7 +781,7 @@ final class FloatVectorMax extends FloatVector { static FloatMaskMax maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(FloatMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final FloatMaskMax TRUE_MASK = new FloatMaskMax(true); private static final FloatMaskMax FALSE_MASK = new FloatMaskMax(false); @@ -832,7 +830,7 @@ final class FloatVectorMax extends FloatVector { @Override @ForceInline public FloatVectorMax toVector() { - return (FloatVectorMax) toBitsVector().castShape(vspecies(), 0); + return (FloatVectorMax) toBitsVector().castShape(VSPECIES, 0); } @Override @@ -843,7 +841,7 @@ final class FloatVectorMax extends FloatVector { @Override IntVectorMax toBitsVector0() { - return ((IntVectorMax) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVectorMax) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -868,7 +866,7 @@ final class FloatVectorMax extends FloatVector { @ForceInline public final FloatMaskMax laneIsValid() { return (FloatMaskMax) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -876,7 +874,7 @@ final class FloatVectorMax extends FloatVector { public final FloatShuffleMax rearrange(VectorShuffle shuffle) { FloatShuffleMax concreteShuffle = (FloatShuffleMax) shuffle; return (FloatShuffleMax) toBitsVector().rearrange(concreteShuffle.cast(IntVector.SPECIES_MAX)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -889,7 +887,7 @@ final class FloatVectorMax extends FloatVector { v = (IntVectorMax) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (FloatShuffleMax) v.toShuffle(vspecies(), false); + return (FloatShuffleMax) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java index 445c4dfb006..37b7e3eeae4 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code int} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class IntVector extends AbstractVector { +public abstract sealed class IntVector extends AbstractVector + permits IntVector64, IntVector128, IntVector256, IntVector512, IntVectorMax { IntVector(int[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java index 04b10386127..f64328e2a1e 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector128.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class IntVector128 extends IntVector { @Override @ForceInline public final IntShuffle128 toShuffle() { - return (IntShuffle128) toShuffle(vspecies(), false); + return (IntShuffle128) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -624,7 +622,7 @@ final class IntVector128 extends IntVector { @Override IntMask128 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -634,7 +632,7 @@ final class IntVector128 extends IntVector { @Override IntMask128 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((IntMask128)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -784,16 +782,16 @@ final class IntVector128 extends IntVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, IntMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((IntMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((IntMask128)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, IntMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((IntMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((IntMask128)m).getBits())); } @ForceInline @@ -801,7 +799,7 @@ final class IntVector128 extends IntVector { static IntMask128 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(IntMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final IntMask128 TRUE_MASK = new IntMask128(true); private static final IntMask128 FALSE_MASK = new IntMask128(false); @@ -861,7 +859,7 @@ final class IntVector128 extends IntVector { @Override IntVector128 toBitsVector0() { - return ((IntVector128) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector128) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -886,7 +884,7 @@ final class IntVector128 extends IntVector { @ForceInline public final IntMask128 laneIsValid() { return (IntMask128) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -894,7 +892,7 @@ final class IntVector128 extends IntVector { public final IntShuffle128 rearrange(VectorShuffle shuffle) { IntShuffle128 concreteShuffle = (IntShuffle128) shuffle; return (IntShuffle128) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -907,7 +905,7 @@ final class IntVector128 extends IntVector { v = (IntVector128) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (IntShuffle128) v.toShuffle(vspecies(), false); + return (IntShuffle128) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java index 20d9df1cd60..58a1667d2ac 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector256.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class IntVector256 extends IntVector { @Override @ForceInline public final IntShuffle256 toShuffle() { - return (IntShuffle256) toShuffle(vspecies(), false); + return (IntShuffle256) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -632,7 +630,7 @@ final class IntVector256 extends IntVector { @Override IntMask256 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -642,7 +640,7 @@ final class IntVector256 extends IntVector { @Override IntMask256 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((IntMask256)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -792,16 +790,16 @@ final class IntVector256 extends IntVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, IntMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((IntMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((IntMask256)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, IntMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((IntMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((IntMask256)m).getBits())); } @ForceInline @@ -809,7 +807,7 @@ final class IntVector256 extends IntVector { static IntMask256 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(IntMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final IntMask256 TRUE_MASK = new IntMask256(true); private static final IntMask256 FALSE_MASK = new IntMask256(false); @@ -869,7 +867,7 @@ final class IntVector256 extends IntVector { @Override IntVector256 toBitsVector0() { - return ((IntVector256) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector256) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -894,7 +892,7 @@ final class IntVector256 extends IntVector { @ForceInline public final IntMask256 laneIsValid() { return (IntMask256) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -902,7 +900,7 @@ final class IntVector256 extends IntVector { public final IntShuffle256 rearrange(VectorShuffle shuffle) { IntShuffle256 concreteShuffle = (IntShuffle256) shuffle; return (IntShuffle256) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -915,7 +913,7 @@ final class IntVector256 extends IntVector { v = (IntVector256) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (IntShuffle256) v.toShuffle(vspecies(), false); + return (IntShuffle256) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java index 4f3a16e2777..ac48e589a05 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector512.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class IntVector512 extends IntVector { @Override @ForceInline public final IntShuffle512 toShuffle() { - return (IntShuffle512) toShuffle(vspecies(), false); + return (IntShuffle512) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -648,7 +646,7 @@ final class IntVector512 extends IntVector { @Override IntMask512 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -658,7 +656,7 @@ final class IntVector512 extends IntVector { @Override IntMask512 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((IntMask512)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -808,16 +806,16 @@ final class IntVector512 extends IntVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, IntMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((IntMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((IntMask512)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, IntMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((IntMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((IntMask512)m).getBits())); } @ForceInline @@ -825,7 +823,7 @@ final class IntVector512 extends IntVector { static IntMask512 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(IntMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final IntMask512 TRUE_MASK = new IntMask512(true); private static final IntMask512 FALSE_MASK = new IntMask512(false); @@ -885,7 +883,7 @@ final class IntVector512 extends IntVector { @Override IntVector512 toBitsVector0() { - return ((IntVector512) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector512) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -910,7 +908,7 @@ final class IntVector512 extends IntVector { @ForceInline public final IntMask512 laneIsValid() { return (IntMask512) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -918,7 +916,7 @@ final class IntVector512 extends IntVector { public final IntShuffle512 rearrange(VectorShuffle shuffle) { IntShuffle512 concreteShuffle = (IntShuffle512) shuffle; return (IntShuffle512) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -931,7 +929,7 @@ final class IntVector512 extends IntVector { v = (IntVector512) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (IntShuffle512) v.toShuffle(vspecies(), false); + return (IntShuffle512) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java index dd51669943b..25329aa81aa 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector64.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class IntVector64 extends IntVector { @Override @ForceInline public final IntShuffle64 toShuffle() { - return (IntShuffle64) toShuffle(vspecies(), false); + return (IntShuffle64) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -620,7 +618,7 @@ final class IntVector64 extends IntVector { @Override IntMask64 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -630,7 +628,7 @@ final class IntVector64 extends IntVector { @Override IntMask64 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((IntMask64)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -780,16 +778,16 @@ final class IntVector64 extends IntVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, IntMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((IntMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((IntMask64)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, IntMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((IntMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((IntMask64)m).getBits())); } @ForceInline @@ -797,7 +795,7 @@ final class IntVector64 extends IntVector { static IntMask64 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(IntMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final IntMask64 TRUE_MASK = new IntMask64(true); private static final IntMask64 FALSE_MASK = new IntMask64(false); @@ -857,7 +855,7 @@ final class IntVector64 extends IntVector { @Override IntVector64 toBitsVector0() { - return ((IntVector64) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVector64) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -882,7 +880,7 @@ final class IntVector64 extends IntVector { @ForceInline public final IntMask64 laneIsValid() { return (IntMask64) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -890,7 +888,7 @@ final class IntVector64 extends IntVector { public final IntShuffle64 rearrange(VectorShuffle shuffle) { IntShuffle64 concreteShuffle = (IntShuffle64) shuffle; return (IntShuffle64) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -903,7 +901,7 @@ final class IntVector64 extends IntVector { v = (IntVector64) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (IntShuffle64) v.toShuffle(vspecies(), false); + return (IntShuffle64) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java index 0b785b01aec..348fda59381 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVectorMax.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class IntVectorMax extends IntVector { @Override @ForceInline public final IntShuffleMax toShuffle() { - return (IntShuffleMax) toShuffle(vspecies(), false); + return (IntShuffleMax) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -618,7 +616,7 @@ final class IntVectorMax extends IntVector { @Override IntMaskMax uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -628,7 +626,7 @@ final class IntVectorMax extends IntVector { @Override IntMaskMax bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((IntMaskMax)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -778,16 +776,16 @@ final class IntVectorMax extends IntVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, IntMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((IntMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((IntMaskMax)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, IntMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((IntMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((IntMaskMax)m).getBits())); } @ForceInline @@ -795,7 +793,7 @@ final class IntVectorMax extends IntVector { static IntMaskMax maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(IntMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final IntMaskMax TRUE_MASK = new IntMaskMax(true); private static final IntMaskMax FALSE_MASK = new IntMaskMax(false); @@ -866,7 +864,7 @@ final class IntVectorMax extends IntVector { @Override IntVectorMax toBitsVector0() { - return ((IntVectorMax) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((IntVectorMax) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -891,7 +889,7 @@ final class IntVectorMax extends IntVector { @ForceInline public final IntMaskMax laneIsValid() { return (IntMaskMax) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -899,7 +897,7 @@ final class IntVectorMax extends IntVector { public final IntShuffleMax rearrange(VectorShuffle shuffle) { IntShuffleMax concreteShuffle = (IntShuffleMax) shuffle; return (IntShuffleMax) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -912,7 +910,7 @@ final class IntVectorMax extends IntVector { v = (IntVectorMax) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (IntShuffleMax) v.toShuffle(vspecies(), false); + return (IntShuffleMax) v.toShuffle(VSPECIES, false); } private static int[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java index 7ba0af6c139..36300cf892b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code long} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class LongVector extends AbstractVector { +public abstract sealed class LongVector extends AbstractVector + permits LongVector64, LongVector128, LongVector256, LongVector512, LongVectorMax { LongVector(long[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java index 594c82ca1fc..7ce60b2efe0 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector128.java @@ -35,9 +35,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -368,7 +367,7 @@ final class LongVector128 extends LongVector { @Override @ForceInline public final LongShuffle128 toShuffle() { - return (LongShuffle128) toShuffle(vspecies(), false); + return (LongShuffle128) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -610,7 +609,7 @@ final class LongVector128 extends LongVector { @Override LongMask128 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -620,7 +619,7 @@ final class LongVector128 extends LongVector { @Override LongMask128 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((LongMask128)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -770,16 +769,16 @@ final class LongVector128 extends LongVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, LongMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((LongMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((LongMask128)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, LongMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((LongMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((LongMask128)m).getBits())); } @ForceInline @@ -787,7 +786,7 @@ final class LongVector128 extends LongVector { static LongMask128 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(LongMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final LongMask128 TRUE_MASK = new LongMask128(true); private static final LongMask128 FALSE_MASK = new LongMask128(false); @@ -847,7 +846,7 @@ final class LongVector128 extends LongVector { @Override LongVector128 toBitsVector0() { - return ((LongVector128) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector128) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -921,7 +920,7 @@ final class LongVector128 extends LongVector { @ForceInline public final LongMask128 laneIsValid() { return (LongMask128) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -929,7 +928,7 @@ final class LongVector128 extends LongVector { public final LongShuffle128 rearrange(VectorShuffle shuffle) { LongShuffle128 concreteShuffle = (LongShuffle128) shuffle; return (LongShuffle128) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -942,7 +941,7 @@ final class LongVector128 extends LongVector { v = (LongVector128) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (LongShuffle128) v.toShuffle(vspecies(), false); + return (LongShuffle128) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java index c3d1ff4c276..110a54c547f 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector256.java @@ -35,9 +35,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -368,7 +367,7 @@ final class LongVector256 extends LongVector { @Override @ForceInline public final LongShuffle256 toShuffle() { - return (LongShuffle256) toShuffle(vspecies(), false); + return (LongShuffle256) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -614,7 +613,7 @@ final class LongVector256 extends LongVector { @Override LongMask256 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -624,7 +623,7 @@ final class LongVector256 extends LongVector { @Override LongMask256 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((LongMask256)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -774,16 +773,16 @@ final class LongVector256 extends LongVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, LongMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((LongMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((LongMask256)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, LongMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((LongMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((LongMask256)m).getBits())); } @ForceInline @@ -791,7 +790,7 @@ final class LongVector256 extends LongVector { static LongMask256 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(LongMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final LongMask256 TRUE_MASK = new LongMask256(true); private static final LongMask256 FALSE_MASK = new LongMask256(false); @@ -851,7 +850,7 @@ final class LongVector256 extends LongVector { @Override LongVector256 toBitsVector0() { - return ((LongVector256) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector256) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -925,7 +924,7 @@ final class LongVector256 extends LongVector { @ForceInline public final LongMask256 laneIsValid() { return (LongMask256) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -933,7 +932,7 @@ final class LongVector256 extends LongVector { public final LongShuffle256 rearrange(VectorShuffle shuffle) { LongShuffle256 concreteShuffle = (LongShuffle256) shuffle; return (LongShuffle256) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -946,7 +945,7 @@ final class LongVector256 extends LongVector { v = (LongVector256) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (LongShuffle256) v.toShuffle(vspecies(), false); + return (LongShuffle256) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java index b8c95967a99..3502f209c3b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector512.java @@ -35,9 +35,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -368,7 +367,7 @@ final class LongVector512 extends LongVector { @Override @ForceInline public final LongShuffle512 toShuffle() { - return (LongShuffle512) toShuffle(vspecies(), false); + return (LongShuffle512) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -622,7 +621,7 @@ final class LongVector512 extends LongVector { @Override LongMask512 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -632,7 +631,7 @@ final class LongVector512 extends LongVector { @Override LongMask512 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((LongMask512)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -782,16 +781,16 @@ final class LongVector512 extends LongVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, LongMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((LongMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((LongMask512)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, LongMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((LongMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((LongMask512)m).getBits())); } @ForceInline @@ -799,7 +798,7 @@ final class LongVector512 extends LongVector { static LongMask512 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(LongMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final LongMask512 TRUE_MASK = new LongMask512(true); private static final LongMask512 FALSE_MASK = new LongMask512(false); @@ -859,7 +858,7 @@ final class LongVector512 extends LongVector { @Override LongVector512 toBitsVector0() { - return ((LongVector512) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector512) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -933,7 +932,7 @@ final class LongVector512 extends LongVector { @ForceInline public final LongMask512 laneIsValid() { return (LongMask512) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -941,7 +940,7 @@ final class LongVector512 extends LongVector { public final LongShuffle512 rearrange(VectorShuffle shuffle) { LongShuffle512 concreteShuffle = (LongShuffle512) shuffle; return (LongShuffle512) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -954,7 +953,7 @@ final class LongVector512 extends LongVector { v = (LongVector512) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (LongShuffle512) v.toShuffle(vspecies(), false); + return (LongShuffle512) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java index 3c9b525f6d0..2a2fe4329a8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector64.java @@ -35,9 +35,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -368,7 +367,7 @@ final class LongVector64 extends LongVector { @Override @ForceInline public final LongShuffle64 toShuffle() { - return (LongShuffle64) toShuffle(vspecies(), false); + return (LongShuffle64) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -608,7 +607,7 @@ final class LongVector64 extends LongVector { @Override LongMask64 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -618,7 +617,7 @@ final class LongVector64 extends LongVector { @Override LongMask64 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((LongMask64)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -768,16 +767,16 @@ final class LongVector64 extends LongVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, LongMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((LongMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((LongMask64)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, LongMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((LongMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((LongMask64)m).getBits())); } @ForceInline @@ -785,7 +784,7 @@ final class LongVector64 extends LongVector { static LongMask64 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(LongMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final LongMask64 TRUE_MASK = new LongMask64(true); private static final LongMask64 FALSE_MASK = new LongMask64(false); @@ -845,7 +844,7 @@ final class LongVector64 extends LongVector { @Override LongVector64 toBitsVector0() { - return ((LongVector64) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVector64) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -919,7 +918,7 @@ final class LongVector64 extends LongVector { @ForceInline public final LongMask64 laneIsValid() { return (LongMask64) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -927,7 +926,7 @@ final class LongVector64 extends LongVector { public final LongShuffle64 rearrange(VectorShuffle shuffle) { LongShuffle64 concreteShuffle = (LongShuffle64) shuffle; return (LongShuffle64) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -940,7 +939,7 @@ final class LongVector64 extends LongVector { v = (LongVector64) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (LongShuffle64) v.toShuffle(vspecies(), false); + return (LongShuffle64) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java index 4752959f884..157c58e20e8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVectorMax.java @@ -35,9 +35,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -368,7 +367,7 @@ final class LongVectorMax extends LongVector { @Override @ForceInline public final LongShuffleMax toShuffle() { - return (LongShuffleMax) toShuffle(vspecies(), false); + return (LongShuffleMax) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -608,7 +607,7 @@ final class LongVectorMax extends LongVector { @Override LongMaskMax uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -618,7 +617,7 @@ final class LongVectorMax extends LongVector { @Override LongMaskMax bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((LongMaskMax)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -768,16 +767,16 @@ final class LongVectorMax extends LongVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, LongMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((LongMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((LongMaskMax)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, LongMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((LongMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((LongMaskMax)m).getBits())); } @ForceInline @@ -785,7 +784,7 @@ final class LongVectorMax extends LongVector { static LongMaskMax maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(LongMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final LongMaskMax TRUE_MASK = new LongMaskMax(true); private static final LongMaskMax FALSE_MASK = new LongMaskMax(false); @@ -845,7 +844,7 @@ final class LongVectorMax extends LongVector { @Override LongVectorMax toBitsVector0() { - return ((LongVectorMax) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((LongVectorMax) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -919,7 +918,7 @@ final class LongVectorMax extends LongVector { @ForceInline public final LongMaskMax laneIsValid() { return (LongMaskMax) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -927,7 +926,7 @@ final class LongVectorMax extends LongVector { public final LongShuffleMax rearrange(VectorShuffle shuffle) { LongShuffleMax concreteShuffle = (LongShuffleMax) shuffle; return (LongShuffleMax) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -940,7 +939,7 @@ final class LongVectorMax extends LongVector { v = (LongVectorMax) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (LongShuffleMax) v.toShuffle(vspecies(), false); + return (LongShuffleMax) v.toShuffle(VSPECIES, false); } private static long[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java index 7ba465706e8..21bc80a12bc 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code short} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class ShortVector extends AbstractVector { +public abstract sealed class ShortVector extends AbstractVector + permits ShortVector64, ShortVector128, ShortVector256, ShortVector512, ShortVectorMax { ShortVector(short[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java index 89ec97c6be0..22bbfce0928 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector128.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class ShortVector128 extends ShortVector { @Override @ForceInline public final ShortShuffle128 toShuffle() { - return (ShortShuffle128) toShuffle(vspecies(), false); + return (ShortShuffle128) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -632,7 +630,7 @@ final class ShortVector128 extends ShortVector { @Override ShortMask128 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -642,7 +640,7 @@ final class ShortVector128 extends ShortVector { @Override ShortMask128 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ShortMask128)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -792,16 +790,16 @@ final class ShortVector128 extends ShortVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ShortMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ShortMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ShortMask128)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ShortMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ShortMask128)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ShortMask128)m).getBits())); } @ForceInline @@ -809,7 +807,7 @@ final class ShortVector128 extends ShortVector { static ShortMask128 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ShortMask128.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ShortMask128 TRUE_MASK = new ShortMask128(true); private static final ShortMask128 FALSE_MASK = new ShortMask128(false); @@ -869,7 +867,7 @@ final class ShortVector128 extends ShortVector { @Override ShortVector128 toBitsVector0() { - return ((ShortVector128) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ShortVector128) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -908,7 +906,7 @@ final class ShortVector128 extends ShortVector { @ForceInline public final ShortMask128 laneIsValid() { return (ShortMask128) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -916,7 +914,7 @@ final class ShortVector128 extends ShortVector { public final ShortShuffle128 rearrange(VectorShuffle shuffle) { ShortShuffle128 concreteShuffle = (ShortShuffle128) shuffle; return (ShortShuffle128) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -929,7 +927,7 @@ final class ShortVector128 extends ShortVector { v = (ShortVector128) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ShortShuffle128) v.toShuffle(vspecies(), false); + return (ShortShuffle128) v.toShuffle(VSPECIES, false); } private static short[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java index 0f5751c27d8..6011695bf54 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector256.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class ShortVector256 extends ShortVector { @Override @ForceInline public final ShortShuffle256 toShuffle() { - return (ShortShuffle256) toShuffle(vspecies(), false); + return (ShortShuffle256) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -648,7 +646,7 @@ final class ShortVector256 extends ShortVector { @Override ShortMask256 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -658,7 +656,7 @@ final class ShortVector256 extends ShortVector { @Override ShortMask256 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ShortMask256)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -808,16 +806,16 @@ final class ShortVector256 extends ShortVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ShortMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ShortMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ShortMask256)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ShortMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ShortMask256)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ShortMask256)m).getBits())); } @ForceInline @@ -825,7 +823,7 @@ final class ShortVector256 extends ShortVector { static ShortMask256 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ShortMask256.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ShortMask256 TRUE_MASK = new ShortMask256(true); private static final ShortMask256 FALSE_MASK = new ShortMask256(false); @@ -885,7 +883,7 @@ final class ShortVector256 extends ShortVector { @Override ShortVector256 toBitsVector0() { - return ((ShortVector256) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ShortVector256) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -924,7 +922,7 @@ final class ShortVector256 extends ShortVector { @ForceInline public final ShortMask256 laneIsValid() { return (ShortMask256) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -932,7 +930,7 @@ final class ShortVector256 extends ShortVector { public final ShortShuffle256 rearrange(VectorShuffle shuffle) { ShortShuffle256 concreteShuffle = (ShortShuffle256) shuffle; return (ShortShuffle256) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -945,7 +943,7 @@ final class ShortVector256 extends ShortVector { v = (ShortVector256) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ShortShuffle256) v.toShuffle(vspecies(), false); + return (ShortShuffle256) v.toShuffle(VSPECIES, false); } private static short[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java index 3d38dfd88fd..e6101d2e6be 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector512.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class ShortVector512 extends ShortVector { @Override @ForceInline public final ShortShuffle512 toShuffle() { - return (ShortShuffle512) toShuffle(vspecies(), false); + return (ShortShuffle512) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -680,7 +678,7 @@ final class ShortVector512 extends ShortVector { @Override ShortMask512 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -690,7 +688,7 @@ final class ShortVector512 extends ShortVector { @Override ShortMask512 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ShortMask512)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -840,16 +838,16 @@ final class ShortVector512 extends ShortVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ShortMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ShortMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ShortMask512)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ShortMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ShortMask512)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ShortMask512)m).getBits())); } @ForceInline @@ -857,7 +855,7 @@ final class ShortVector512 extends ShortVector { static ShortMask512 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ShortMask512.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ShortMask512 TRUE_MASK = new ShortMask512(true); private static final ShortMask512 FALSE_MASK = new ShortMask512(false); @@ -917,7 +915,7 @@ final class ShortVector512 extends ShortVector { @Override ShortVector512 toBitsVector0() { - return ((ShortVector512) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ShortVector512) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -956,7 +954,7 @@ final class ShortVector512 extends ShortVector { @ForceInline public final ShortMask512 laneIsValid() { return (ShortMask512) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -964,7 +962,7 @@ final class ShortVector512 extends ShortVector { public final ShortShuffle512 rearrange(VectorShuffle shuffle) { ShortShuffle512 concreteShuffle = (ShortShuffle512) shuffle; return (ShortShuffle512) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -977,7 +975,7 @@ final class ShortVector512 extends ShortVector { v = (ShortVector512) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ShortShuffle512) v.toShuffle(vspecies(), false); + return (ShortShuffle512) v.toShuffle(VSPECIES, false); } private static short[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java index b319d98f784..31af959b4a8 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector64.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class ShortVector64 extends ShortVector { @Override @ForceInline public final ShortShuffle64 toShuffle() { - return (ShortShuffle64) toShuffle(vspecies(), false); + return (ShortShuffle64) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -624,7 +622,7 @@ final class ShortVector64 extends ShortVector { @Override ShortMask64 uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -634,7 +632,7 @@ final class ShortVector64 extends ShortVector { @Override ShortMask64 bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ShortMask64)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -784,16 +782,16 @@ final class ShortVector64 extends ShortVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ShortMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ShortMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ShortMask64)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ShortMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ShortMask64)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ShortMask64)m).getBits())); } @ForceInline @@ -801,7 +799,7 @@ final class ShortVector64 extends ShortVector { static ShortMask64 maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ShortMask64.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ShortMask64 TRUE_MASK = new ShortMask64(true); private static final ShortMask64 FALSE_MASK = new ShortMask64(false); @@ -861,7 +859,7 @@ final class ShortVector64 extends ShortVector { @Override ShortVector64 toBitsVector0() { - return ((ShortVector64) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ShortVector64) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -900,7 +898,7 @@ final class ShortVector64 extends ShortVector { @ForceInline public final ShortMask64 laneIsValid() { return (ShortMask64) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -908,7 +906,7 @@ final class ShortVector64 extends ShortVector { public final ShortShuffle64 rearrange(VectorShuffle shuffle) { ShortShuffle64 concreteShuffle = (ShortShuffle64) shuffle; return (ShortShuffle64) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -921,7 +919,7 @@ final class ShortVector64 extends ShortVector { v = (ShortVector64) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ShortShuffle64) v.toShuffle(vspecies(), false); + return (ShortShuffle64) v.toShuffle(VSPECIES, false); } private static short[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java index 69b298857c9..fe0359c4711 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVectorMax.java @@ -25,7 +25,6 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +34,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; // -- This file was mechanically generated: Do not edit! -- // @@ -373,7 +371,7 @@ final class ShortVectorMax extends ShortVector { @Override @ForceInline public final ShortShuffleMax toShuffle() { - return (ShortShuffleMax) toShuffle(vspecies(), false); + return (ShortShuffleMax) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -618,7 +616,7 @@ final class ShortVectorMax extends ShortVector { @Override ShortMaskMax uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -628,7 +626,7 @@ final class ShortVectorMax extends ShortVector { @Override ShortMaskMax bOp(VectorMask m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = ((ShortMaskMax)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -778,16 +776,16 @@ final class ShortVectorMax extends ShortVector { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, ShortMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper(((ShortMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper(((ShortMaskMax)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, ShortMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper(((ShortMaskMax)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper(((ShortMaskMax)m).getBits())); } @ForceInline @@ -795,7 +793,7 @@ final class ShortVectorMax extends ShortVector { static ShortMaskMax maskAll(boolean bit) { return VectorSupport.fromBitsCoerced(ShortMaskMax.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final ShortMaskMax TRUE_MASK = new ShortMaskMax(true); private static final ShortMaskMax FALSE_MASK = new ShortMaskMax(false); @@ -855,7 +853,7 @@ final class ShortVectorMax extends ShortVector { @Override ShortVectorMax toBitsVector0() { - return ((ShortVectorMax) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return ((ShortVectorMax) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -894,7 +892,7 @@ final class ShortVectorMax extends ShortVector { @ForceInline public final ShortMaskMax laneIsValid() { return (ShortMaskMax) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -902,7 +900,7 @@ final class ShortVectorMax extends ShortVector { public final ShortShuffleMax rearrange(VectorShuffle shuffle) { ShortShuffleMax concreteShuffle = (ShortShuffleMax) shuffle; return (ShortShuffleMax) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); } @ForceInline @@ -915,7 +913,7 @@ final class ShortVectorMax extends ShortVector { v = (ShortVectorMax) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return (ShortShuffleMax) v.toShuffle(vspecies(), false); + return (ShortShuffleMax) v.toShuffle(VSPECIES, false); } private static short[] prepare(int[] indices, int offset) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Util.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Util.java index 8562d4b5d7a..133195fa54d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Util.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Util.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ */ package jdk.incubator.vector; -/*package-private*/ class Util { +/*package-private*/ final class Util { public static void requires(boolean cond, String message) { if (!cond) { throw new InternalError(message); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java index 68b4a35067c..85b3bbca269 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Vector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1170,9 +1170,10 @@ import java.util.Arrays; * @param the boxed version of {@code ETYPE}, * the element type of a vector * + * @sealedGraph */ @SuppressWarnings("exports") -public abstract class Vector extends jdk.internal.vm.vector.VectorSupport.Vector { +public abstract sealed class Vector extends jdk.internal.vm.vector.VectorSupport.Vector permits AbstractVector { // This type is sealed within its package. // Users cannot roll their own vector types. diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorIntrinsics.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorIntrinsics.java index 266a843083a..f0115371d48 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorIntrinsics.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorIntrinsics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ import jdk.internal.vm.annotation.ForceInline; import java.util.Objects; -/*non-public*/ class VectorIntrinsics { +/*non-public*/ final class VectorIntrinsics { static final int VECTOR_ACCESS_OOB_CHECK = Integer.getInteger("jdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK", 2); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java index 13ee9e27e0d..607b194946b 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java @@ -131,7 +131,7 @@ import java.util.Objects; * the element type of a vector */ @SuppressWarnings("exports") -public abstract class VectorMask extends jdk.internal.vm.vector.VectorSupport.VectorMask { +public abstract sealed class VectorMask extends jdk.internal.vm.vector.VectorSupport.VectorMask permits AbstractMask { VectorMask(boolean[] bits) { super(bits); } /** diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java index 1c1cfcc78c7..823cebd85a9 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMathLibrary.java @@ -42,7 +42,7 @@ import static jdk.internal.vm.vector.Utils.debug; * A wrapper for native vector math libraries bundled with the JDK (SVML and SLEEF). * Binds vector operations to native implementations provided by the libraries. */ -/*package-private*/ class VectorMathLibrary { +/*package-private*/ final class VectorMathLibrary { private static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup(); interface Library { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java index 84009c55ac9..2f2d33ab130 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java @@ -24,13 +24,12 @@ */ package jdk.incubator.vector; -import java.util.function.IntFunction; -import java.util.HashMap; import java.util.ArrayList; +import java.util.HashMap; +import java.util.function.IntFunction; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; - import jdk.internal.vm.vector.VectorSupport; import static jdk.internal.vm.vector.Utils.isNonCapturingLambda; @@ -115,7 +114,7 @@ import static jdk.internal.vm.vector.Utils.isNonCapturingLambda; * operations on individual lane values. * */ -public abstract class VectorOperators { +public final class VectorOperators { private VectorOperators() { } /** @@ -131,12 +130,9 @@ public abstract class VectorOperators { * @see VectorOperators.Test Test * @see VectorOperators.Conversion Conversion * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. + * @sealedGraph */ - public interface Operator { + public sealed interface Operator { /** * Returns the symbolic name of this operator, * as a constant in {@link VectorOperators}. @@ -235,13 +231,8 @@ public abstract class VectorOperators { * usable in expressions like {@code w = v0.}{@link * Vector#lanewise(VectorOperators.Unary) * lanewise}{@code (NEG)}. - * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. */ - public interface Unary extends Operator { + public sealed interface Unary extends Operator { } /** @@ -252,12 +243,9 @@ public abstract class VectorOperators { * Vector#lanewise(VectorOperators.Binary,Vector) * lanewise}{@code (ADD, v1)}. * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. + * @sealedGraph */ - public interface Binary extends Operator { + public sealed interface Binary extends Operator { } /** @@ -267,13 +255,8 @@ public abstract class VectorOperators { * usable in expressions like {@code w = v0.}{@link * Vector#lanewise(VectorOperators.Ternary,Vector,Vector) * lanewise}{@code (FMA, v1, v2)}. - * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. */ - public interface Ternary extends Operator { + public sealed interface Ternary extends Operator { } /** @@ -283,13 +266,8 @@ public abstract class VectorOperators { * usable in expressions like {@code e = v0.}{@link * IntVector#reduceLanes(VectorOperators.Associative) * reduceLanes}{@code (ADD)}. - * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. */ - public interface Associative extends Binary { + public sealed interface Associative extends Binary { } /** @@ -299,13 +277,8 @@ public abstract class VectorOperators { * usable in expressions like {@code m = v0.}{@link * FloatVector#test(VectorOperators.Test) * test}{@code (IS_FINITE)}. - * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. */ - public interface Test extends Operator { + public sealed interface Test extends Operator { } /** @@ -315,13 +288,8 @@ public abstract class VectorOperators { * usable in expressions like {@code m = v0.}{@link * Vector#compare(VectorOperators.Comparison,Vector) * compare}{@code (LT, v1)}. - * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. */ - public interface Comparison extends Operator { + public sealed interface Comparison extends Operator { } /** @@ -336,13 +304,8 @@ public abstract class VectorOperators { * domain type (the input lane type) * @param the boxed element type for the conversion * range type (the output lane type) - * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. */ - public interface Conversion extends Operator { + public sealed interface Conversion extends Operator { /** * The domain of this conversion, a primitive type. * @return the domain of this conversion @@ -831,7 +794,7 @@ public abstract class VectorOperators { kind, dom, ran); } - private abstract static class OperatorImpl implements Operator { + private abstract static sealed class OperatorImpl implements Operator { private final String symName; private final String opName; private final int opInfo; @@ -956,35 +919,35 @@ public abstract class VectorOperators { } } - private static class UnaryImpl extends OperatorImpl implements Unary { + private static final class UnaryImpl extends OperatorImpl implements Unary { private UnaryImpl(String symName, String opName, int opInfo) { super(symName, opName, opInfo); assert((opInfo & VO_ARITY_MASK) == VO_UNARY); } } - private static class BinaryImpl extends OperatorImpl implements Binary { + private static sealed class BinaryImpl extends OperatorImpl implements Binary permits AssociativeImpl { private BinaryImpl(String symName, String opName, int opInfo) { super(symName, opName, opInfo); assert((opInfo & VO_ARITY_MASK) == VO_BINARY); } } - private static class TernaryImpl extends OperatorImpl implements Ternary { + private static final class TernaryImpl extends OperatorImpl implements Ternary { private TernaryImpl(String symName, String opName, int opInfo) { super(symName, opName, opInfo); assert((opInfo & VO_ARITY_MASK) == VO_TERNARY); } } - private static class AssociativeImpl extends BinaryImpl implements Associative { + private static final class AssociativeImpl extends BinaryImpl implements Associative { private AssociativeImpl(String symName, String opName, int opInfo) { super(symName, opName, opInfo); } } /*package-private*/ - static + static final class ConversionImpl extends OperatorImpl implements Conversion { private ConversionImpl(String symName, String opName, int opInfo, @@ -1260,7 +1223,7 @@ public abstract class VectorOperators { } } - private static class TestImpl extends OperatorImpl implements Test { + private static final class TestImpl extends OperatorImpl implements Test { private TestImpl(String symName, String opName, int opInfo) { super(symName, opName, opInfo); assert((opInfo & VO_ARITY_MASK) == VO_UNARY); @@ -1272,7 +1235,7 @@ public abstract class VectorOperators { } } - private static class ComparisonImpl extends OperatorImpl implements Comparison { + private static final class ComparisonImpl extends OperatorImpl implements Comparison { private ComparisonImpl(String symName, String opName, int opInfo) { super(symName, opName, opInfo); assert((opInfo & VO_ARITY_MASK) == VO_BINARY); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorShuffle.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorShuffle.java index 9cde9d2315c..5da38a25e16 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorShuffle.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorShuffle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -136,7 +136,7 @@ import java.util.function.IntUnaryOperator; * the element type of a vector */ @SuppressWarnings("exports") -public abstract class VectorShuffle extends jdk.internal.vm.vector.VectorSupport.VectorShuffle { +public abstract sealed class VectorShuffle extends jdk.internal.vm.vector.VectorSupport.VectorShuffle permits AbstractShuffle { VectorShuffle(Object indices) { super(indices); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorSpecies.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorSpecies.java index e80bbf231ea..4c3ef3f471d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorSpecies.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorSpecies.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * 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,11 +36,6 @@ import java.util.function.IntUnaryOperator; * of element type ({@code ETYPE}) * and {@link VectorShape shape}. * - * @apiNote - * User code should not implement this interface. A future release of - * this type may restrict implementations to be members of the same - * package. - * * @implNote * The string representation of an instance of this interface will * be of the form "Species[ETYPE, VLENGTH, SHAPE]", where {@code @@ -57,7 +52,7 @@ import java.util.function.IntUnaryOperator; * @param the boxed version of {@code ETYPE}, * the element type of a vector */ -public interface VectorSpecies { +public sealed interface VectorSpecies permits AbstractSpecies { /** * Returns the primitive element type of vectors of this * species. diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template index 95fe8ca35db..b3c5bfac302 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template @@ -49,7 +49,8 @@ import static jdk.incubator.vector.VectorOperators.*; * {@code $type$} values. */ @SuppressWarnings("cast") // warning: redundant cast -public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { +public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtype$> + permits $Type$Vector64, $Type$Vector128, $Type$Vector256, $Type$Vector512, $Type$VectorMax { $abstractvectortype$($type$[] vec) { super(vec); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template index 35041a0b70f..d66d22cab19 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template @@ -25,7 +25,9 @@ package jdk.incubator.vector; import java.lang.foreign.MemorySegment; +#if[longOrDouble] import java.lang.foreign.ValueLayout; +#end[longOrDouble] import java.nio.ByteOrder; import java.util.Arrays; import java.util.Objects; @@ -35,9 +37,8 @@ import jdk.internal.ValueBased; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.vector.VectorSupport; -import static jdk.internal.vm.vector.VectorSupport.*; - import static jdk.incubator.vector.VectorOperators.*; +import static jdk.internal.vm.vector.VectorSupport.*; #warn This file is preprocessed before being compiled @@ -389,7 +390,7 @@ final class $vectortype$ extends $abstractvectortype$ { @Override @ForceInline public final $shuffletype$ toShuffle() { - return ($shuffletype$) toShuffle(vspecies(), false); + return ($shuffletype$) toShuffle(VSPECIES, false); } // Specialized unary testing @@ -905,7 +906,7 @@ final class $vectortype$ extends $abstractvectortype$ { @Override $masktype$ uOp(MUnOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); for (int i = 0; i < res.length; i++) { res[i] = f.apply(i, bits[i]); @@ -915,7 +916,7 @@ final class $vectortype$ extends $abstractvectortype$ { @Override $masktype$ bOp(VectorMask<$Boxtype$> m, MBinOp f) { - boolean[] res = new boolean[vspecies().laneCount()]; + boolean[] res = new boolean[VSPECIES.laneCount()]; boolean[] bits = getBits(); boolean[] mbits = (($masktype$)m).getBits(); for (int i = 0; i < res.length; i++) { @@ -1065,16 +1066,16 @@ final class $vectortype$ extends $abstractvectortype$ { @ForceInline public boolean anyTrue() { return VectorSupport.test(BT_ne, $masktype$.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> anyTrueHelper((($masktype$)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> anyTrueHelper((($masktype$)m).getBits())); } @Override @ForceInline public boolean allTrue() { return VectorSupport.test(BT_overflow, $masktype$.class, LANEBITS_TYPE_ORDINAL, VLENGTH, - this, vspecies().maskAll(true), - (m, __) -> allTrueHelper((($masktype$)m).getBits())); + this, VSPECIES.maskAll(true), + (m, _) -> allTrueHelper((($masktype$)m).getBits())); } @ForceInline @@ -1082,7 +1083,7 @@ final class $vectortype$ extends $abstractvectortype$ { static $masktype$ maskAll(boolean bit) { return VectorSupport.fromBitsCoerced($masktype$.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, - (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); + (v, _) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } private static final $masktype$ TRUE_MASK = new $masktype$(true); private static final $masktype$ FALSE_MASK = new $masktype$(false); @@ -1147,7 +1148,7 @@ final class $vectortype$ extends $abstractvectortype$ { @Override @ForceInline public $vectortype$ toVector() { - return ($vectortype$) toBitsVector().castShape(vspecies(), 0); + return ($vectortype$) toBitsVector().castShape(VSPECIES, 0); } #else[FP] @Override @@ -1165,7 +1166,7 @@ final class $vectortype$ extends $abstractvectortype$ { @Override $bitsvectortype$ toBitsVector0() { - return (($bitsvectortype$) vspecies().asIntegral().dummyVector()).vectorFactory(indices()); + return (($bitsvectortype$) VSPECIES.asIntegral().dummyVector()).vectorFactory(indices()); } @Override @@ -1301,7 +1302,7 @@ final class $vectortype$ extends $abstractvectortype$ { @ForceInline public final $masktype$ laneIsValid() { return ($masktype$) toBitsVector().compare(VectorOperators.GE, 0) - .cast(vspecies()); + .cast(VSPECIES); } @ForceInline @@ -1310,10 +1311,10 @@ final class $vectortype$ extends $abstractvectortype$ { $shuffletype$ concreteShuffle = ($shuffletype$) shuffle; #if[FP] return ($shuffletype$) toBitsVector().rearrange(concreteShuffle.cast($Bitstype$Vector.SPECIES_$BITS$)) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); #else[FP] return ($shuffletype$) toBitsVector().rearrange(concreteShuffle) - .toShuffle(vspecies(), false); + .toShuffle(VSPECIES, false); #end[FP] } @@ -1327,7 +1328,7 @@ final class $vectortype$ extends $abstractvectortype$ { v = ($bitsvectortype$) v.blend(v.lanewise(VectorOperators.ADD, length()), v.compare(VectorOperators.LT, 0)); } - return ($shuffletype$) v.toShuffle(vspecies(), false); + return ($shuffletype$) v.toShuffle(VSPECIES, false); } private static $bitstype$[] prepare(int[] indices, int offset) { From 5164fbc9f841bec1ad4fb33b11a3ad11489d49d8 Mon Sep 17 00:00:00 2001 From: Rui Li Date: Thu, 26 Mar 2026 23:47:12 +0000 Subject: [PATCH 147/160] 8374191: Shenandoah: Consider an assert in get_object_age Reviewed-by: wkemper --- src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index 02f2beaf4e0..6d77cccaa6a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -344,6 +344,8 @@ uint ShenandoahHeap::get_object_age(oop obj) { } if (w.has_monitor()) { w = w.monitor()->header(); + } else { + assert(!w.has_displaced_mark_helper(), "Mark word should not be displaced"); } assert(w.age() <= markWord::max_age, "Impossible!"); return w.age(); From 1a9965555477ee2a6cb65e91ef54ed608e4bee66 Mon Sep 17 00:00:00 2001 From: Mohamed Issa Date: Fri, 27 Mar 2026 04:56:30 +0000 Subject: [PATCH 148/160] 8378295: Update scalar AVX10 floating point min/max definitions Reviewed-by: sviswanathan, mhaessig, jbhateja, sparasa --- src/hotspot/cpu/x86/assembler_x86.cpp | 160 ++++---- src/hotspot/cpu/x86/assembler_x86.hpp | 34 +- src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp | 77 +++- src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp | 26 +- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 46 ++- src/hotspot/cpu/x86/macroAssembler_x86.hpp | 22 +- src/hotspot/cpu/x86/x86.ad | 353 +++++++++--------- .../math/TestFpMinMaxReductions.java | 32 +- .../compiler/lib/ir_framework/IRNode.java | 36 +- .../vector/Float16OperationsBenchmark.java | 38 +- .../bench/vm/compiler/FpMinMaxIntrinsics.java | 171 ++++++++- 11 files changed, 650 insertions(+), 345 deletions(-) diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index 38a28a6ec49..a4f2968f0d1 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -3472,7 +3472,7 @@ void Assembler::vmovdqu(XMMRegister dst, XMMRegister src) { emit_int16(0x6F, (0xC0 | encode)); } -void Assembler::vmovw(XMMRegister dst, Register src) { +void Assembler::evmovw(XMMRegister dst, Register src) { assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16"); InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_is_evex_instruction(); @@ -3480,7 +3480,7 @@ void Assembler::vmovw(XMMRegister dst, Register src) { emit_int16(0x6E, (0xC0 | encode)); } -void Assembler::vmovw(Register dst, XMMRegister src) { +void Assembler::evmovw(Register dst, XMMRegister src) { assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16"); InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_is_evex_instruction(); @@ -3488,6 +3488,36 @@ void Assembler::vmovw(Register dst, XMMRegister src) { emit_int16(0x7E, (0xC0 | encode)); } +void Assembler::evmovw(XMMRegister dst, Address src) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_16bit); + attributes.set_is_evex_instruction(); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); + emit_int8(0x6E); + emit_operand(dst, src, 0); +} + +void Assembler::evmovw(Address dst, XMMRegister src) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_16bit); + attributes.set_is_evex_instruction(); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); + emit_int8(0x7E); + emit_operand(src, dst, 0); +} + +void Assembler::evmovw(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); + emit_int16(0x6E, (0xC0 | encode)); +} + void Assembler::vmovdqu(XMMRegister dst, Address src) { assert(UseAVX > 0, ""); InstructionMark im(this); @@ -7310,6 +7340,42 @@ void Assembler::etzcntq(Register dst, Address src, bool no_flags) { emit_operand(dst, src, 0); } +void Assembler::evucomish(XMMRegister dst, Address src) { + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_16bit); + attributes.set_is_evex_instruction(); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_MAP5, &attributes); + emit_int8(0x2E); + emit_operand(dst, src, 0); +} + +void Assembler::evucomish(XMMRegister dst, XMMRegister src) { + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_MAP5, &attributes); + emit_int16(0x2E, (0xC0 | encode)); +} + +void Assembler::evucomxsh(XMMRegister dst, Address src) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_16bit); + attributes.set_is_evex_instruction(); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); + emit_int8(0x2E); + emit_operand(dst, src, 0); +} + +void Assembler::evucomxsh(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); + emit_int16(0x2E, (0xC0 | encode)); +} + void Assembler::ucomisd(XMMRegister dst, Address src) { InstructionMark im(this); InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); @@ -7327,7 +7393,7 @@ void Assembler::ucomisd(XMMRegister dst, XMMRegister src) { emit_int16(0x2E, (0xC0 | encode)); } -void Assembler::vucomxsd(XMMRegister dst, Address src) { +void Assembler::evucomxsd(XMMRegister dst, Address src) { assert(VM_Version::supports_avx10_2(), ""); InstructionMark im(this); InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); @@ -7338,7 +7404,7 @@ void Assembler::vucomxsd(XMMRegister dst, Address src) { emit_operand(dst, src, 0); } -void Assembler::vucomxsd(XMMRegister dst, XMMRegister src) { +void Assembler::evucomxsd(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_avx10_2(), ""); InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_is_evex_instruction(); @@ -7361,7 +7427,7 @@ void Assembler::ucomiss(XMMRegister dst, XMMRegister src) { emit_int16(0x2E, (0xC0 | encode)); } -void Assembler::vucomxss(XMMRegister dst, Address src) { +void Assembler::evucomxss(XMMRegister dst, Address src) { assert(VM_Version::supports_avx10_2(), ""); InstructionMark im(this); InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); @@ -7372,7 +7438,7 @@ void Assembler::vucomxss(XMMRegister dst, Address src) { emit_operand(dst, src, 0); } -void Assembler::vucomxss(XMMRegister dst, XMMRegister src) { +void Assembler::evucomxss(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_avx10_2(), ""); InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); attributes.set_is_evex_instruction(); @@ -8411,30 +8477,6 @@ void Assembler::vmulsh(XMMRegister dst, XMMRegister nds, XMMRegister src) { emit_int16(0x59, (0xC0 | encode)); } -void Assembler::vmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src) { - assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - attributes.set_is_evex_instruction(); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); - emit_int16(0x5F, (0xC0 | encode)); -} - -void Assembler::eminmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) { - assert(VM_Version::supports_avx10_2(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - attributes.set_is_evex_instruction(); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3A, &attributes); - emit_int24(0x53, (0xC0 | encode), imm8); -} - -void Assembler::vminsh(XMMRegister dst, XMMRegister nds, XMMRegister src) { - assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16"); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - attributes.set_is_evex_instruction(); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_MAP5, &attributes); - emit_int16(0x5D, (0xC0 | encode)); -} - void Assembler::vsqrtsh(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_avx512_fp16(), "requires AVX512-FP16"); InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); @@ -13369,48 +13411,38 @@ bool Assembler::is_demotable(bool no_flags, int dst_enc, int nds_enc) { return (!no_flags && dst_enc == nds_enc); } -void Assembler::vmaxss(XMMRegister dst, XMMRegister nds, XMMRegister src) { - assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); - emit_int16(0x5F, (0xC0 | encode)); -} - -void Assembler::vmaxsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { - assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - attributes.set_rex_vex_w_reverted(); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); - emit_int16(0x5F, (0xC0 | encode)); -} - -void Assembler::vminss(XMMRegister dst, XMMRegister nds, XMMRegister src) { - assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); - emit_int16(0x5D, (0xC0 | encode)); -} - -void Assembler::eminmaxss(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) { +void Assembler::evminmaxsh(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8) { assert(VM_Version::supports_avx10_2(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x53, (0xC0 | encode), imm8); +} + +void Assembler::evminmaxss(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8) { + assert(VM_Version::supports_avx10_2(), ""); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int24(0x53, (0xC0 | encode), imm8); } -void Assembler::vminsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { - assert(VM_Version::supports_avx(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); - attributes.set_rex_vex_w_reverted(); - int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); - emit_int16(0x5D, (0xC0 | encode)); -} - -void Assembler::eminmaxsd(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8) { +void Assembler::evminmaxsd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8) { assert(VM_Version::supports_avx10_2(), ""); - InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int24(0x53, (0xC0 | encode), imm8); } diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index 57a5e25d7a6..98684752b0c 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1694,8 +1694,11 @@ private: void movsbl(Register dst, Address src); void movsbl(Register dst, Register src); - void vmovw(XMMRegister dst, Register src); - void vmovw(Register dst, XMMRegister src); + void evmovw(XMMRegister dst, Register src); + void evmovw(Register dst, XMMRegister src); + void evmovw(XMMRegister dst, Address src); + void evmovw(Address dst, XMMRegister src); + void evmovw(XMMRegister dst, XMMRegister src); void movsbq(Register dst, Address src); void movsbq(Register dst, Register src); @@ -2329,17 +2332,23 @@ private: void tzcntq(Register dst, Address src); void etzcntq(Register dst, Address src, bool no_flags); + // Unordered Compare Scalar Half-Precision Floating-Point Values and set EFLAGS + void evucomish(XMMRegister dst, Address src); + void evucomish(XMMRegister dst, XMMRegister src); + void evucomxsh(XMMRegister dst, Address src); + void evucomxsh(XMMRegister dst, XMMRegister src); + // Unordered Compare Scalar Double-Precision Floating-Point Values and set EFLAGS void ucomisd(XMMRegister dst, Address src); void ucomisd(XMMRegister dst, XMMRegister src); - void vucomxsd(XMMRegister dst, Address src); - void vucomxsd(XMMRegister dst, XMMRegister src); + void evucomxsd(XMMRegister dst, Address src); + void evucomxsd(XMMRegister dst, XMMRegister src); // Unordered Compare Scalar Single-Precision Floating-Point Values and set EFLAGS void ucomiss(XMMRegister dst, Address src); void ucomiss(XMMRegister dst, XMMRegister src); - void vucomxss(XMMRegister dst, Address src); - void vucomxss(XMMRegister dst, XMMRegister src); + void evucomxss(XMMRegister dst, Address src); + void evucomxss(XMMRegister dst, XMMRegister src); void xabort(int8_t imm8); @@ -2417,11 +2426,6 @@ private: void vsubss(XMMRegister dst, XMMRegister nds, Address src); void vsubss(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vmaxss(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vmaxsd(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vminss(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vminsd(XMMRegister dst, XMMRegister nds, XMMRegister src); - void sarxl(Register dst, Register src1, Register src2); void sarxl(Register dst, Address src1, Register src2); void sarxq(Register dst, Register src1, Register src2); @@ -2552,8 +2556,6 @@ private: void vsubsh(XMMRegister dst, XMMRegister nds, XMMRegister src); void vmulsh(XMMRegister dst, XMMRegister nds, XMMRegister src); void vdivsh(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vminsh(XMMRegister dst, XMMRegister nds, XMMRegister src); void vsqrtsh(XMMRegister dst, XMMRegister src); void vfmadd132sh(XMMRegister dst, XMMRegister src1, XMMRegister src2); @@ -2790,9 +2792,9 @@ private: void vminpd(XMMRegister dst, XMMRegister src1, XMMRegister src2, int vector_len); // AVX10.2 floating point minmax instructions - void eminmaxsh(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8); - void eminmaxss(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8); - void eminmaxsd(XMMRegister dst, XMMRegister nds, XMMRegister src, int imm8); + void evminmaxsh(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8); + void evminmaxss(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8); + void evminmaxsd(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8); void evminmaxph(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len); void evminmaxph(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int imm8, int vector_len); void evminmaxps(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int imm8, int vector_len); diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 5b5fb02967c..c1df726b5ba 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -1037,8 +1037,8 @@ void C2_MacroAssembler::evminmax_fp(int opcode, BasicType elem_bt, } } -void C2_MacroAssembler::vminmax_fp(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, - XMMRegister src1, XMMRegister src2, int vlen_enc) { +void C2_MacroAssembler::vminmax_fp_avx10_2(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, + XMMRegister src1, XMMRegister src2, int vlen_enc) { assert(opc == Op_MinV || opc == Op_MinReductionV || opc == Op_MaxV || opc == Op_MaxReductionV, "sanity"); @@ -1052,6 +1052,21 @@ void C2_MacroAssembler::vminmax_fp(int opc, BasicType elem_bt, XMMRegister dst, } } +void C2_MacroAssembler::sminmax_fp_avx10_2(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, + XMMRegister src1, XMMRegister src2) { + assert(opc == Op_MinF || opc == Op_MaxF || + opc == Op_MinD || opc == Op_MaxD, "sanity"); + + int imm8 = (opc == Op_MinF || opc == Op_MinD) ? AVX10_2_MINMAX_MIN_COMPARE_SIGN + : AVX10_2_MINMAX_MAX_COMPARE_SIGN; + if (elem_bt == T_FLOAT) { + evminmaxss(dst, mask, src1, src2, true, imm8); + } else { + assert(elem_bt == T_DOUBLE, ""); + evminmaxsd(dst, mask, src1, src2, true, imm8); + } +} + // Float/Double signum void C2_MacroAssembler::signum_fp(int opcode, XMMRegister dst, XMMRegister zero, XMMRegister one) { assert(opcode == Op_SignumF || opcode == Op_SignumD, "sanity"); @@ -1063,7 +1078,7 @@ void C2_MacroAssembler::signum_fp(int opcode, XMMRegister dst, XMMRegister zero, // If other floating point comparison instructions used, ZF=1 for equal and unordered cases if (opcode == Op_SignumF) { if (VM_Version::supports_avx10_2()) { - vucomxss(dst, zero); + evucomxss(dst, zero); jcc(Assembler::negative, DONE_LABEL); } else { ucomiss(dst, zero); @@ -1074,7 +1089,7 @@ void C2_MacroAssembler::signum_fp(int opcode, XMMRegister dst, XMMRegister zero, xorps(dst, ExternalAddress(StubRoutines::x86::vector_float_sign_flip()), noreg); } else if (opcode == Op_SignumD) { if (VM_Version::supports_avx10_2()) { - vucomxsd(dst, zero); + evucomxsd(dst, zero); jcc(Assembler::negative, DONE_LABEL); } else { ucomisd(dst, zero); @@ -2400,7 +2415,7 @@ void C2_MacroAssembler::reduceFloatMinMax(int opcode, int vlen, bool is_dst_vali } if (VM_Version::supports_avx10_2()) { - vminmax_fp(opcode, T_FLOAT, wdst, k0, wtmp, wsrc, vlen_enc); + vminmax_fp_avx10_2(opcode, T_FLOAT, wdst, k0, wtmp, wsrc, vlen_enc); } else { vminmax_fp(opcode, T_FLOAT, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc); } @@ -2409,7 +2424,7 @@ void C2_MacroAssembler::reduceFloatMinMax(int opcode, int vlen, bool is_dst_vali } if (is_dst_valid) { if (VM_Version::supports_avx10_2()) { - vminmax_fp(opcode, T_FLOAT, dst, k0, wdst, dst, Assembler::AVX_128bit); + vminmax_fp_avx10_2(opcode, T_FLOAT, dst, k0, wdst, dst, Assembler::AVX_128bit); } else { vminmax_fp(opcode, T_FLOAT, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit); } @@ -2440,7 +2455,7 @@ void C2_MacroAssembler::reduceDoubleMinMax(int opcode, int vlen, bool is_dst_val } if (VM_Version::supports_avx10_2()) { - vminmax_fp(opcode, T_DOUBLE, wdst, k0, wtmp, wsrc, vlen_enc); + vminmax_fp_avx10_2(opcode, T_DOUBLE, wdst, k0, wtmp, wsrc, vlen_enc); } else { vminmax_fp(opcode, T_DOUBLE, wdst, wtmp, wsrc, tmp, atmp, btmp, vlen_enc); } @@ -2451,7 +2466,7 @@ void C2_MacroAssembler::reduceDoubleMinMax(int opcode, int vlen, bool is_dst_val if (is_dst_valid) { if (VM_Version::supports_avx10_2()) { - vminmax_fp(opcode, T_DOUBLE, dst, k0, wdst, dst, Assembler::AVX_128bit); + vminmax_fp_avx10_2(opcode, T_DOUBLE, dst, k0, wdst, dst, Assembler::AVX_128bit); } else { vminmax_fp(opcode, T_DOUBLE, dst, wdst, dst, tmp, atmp, btmp, Assembler::AVX_128bit); } @@ -7061,13 +7076,25 @@ void C2_MacroAssembler::evfp16ph(int opcode, XMMRegister dst, XMMRegister src1, } } -void C2_MacroAssembler::scalar_max_min_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, - KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2) { - vector_max_min_fp16(opcode, dst, src1, src2, ktmp, xtmp1, xtmp2, Assembler::AVX_128bit); +void C2_MacroAssembler::sminmax_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2) { + vminmax_fp16(opcode, dst, src1, src2, ktmp, xtmp1, xtmp2, Assembler::AVX_128bit); } -void C2_MacroAssembler::vector_max_min_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, - KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2, int vlen_enc) { +void C2_MacroAssembler::sminmax_fp16_avx10_2(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp) { + if (opcode == Op_MaxHF) { + // dst = max(src1, src2) + evminmaxsh(dst, ktmp, src1, src2, true, AVX10_2_MINMAX_MAX_COMPARE_SIGN); + } else { + assert(opcode == Op_MinHF, ""); + // dst = min(src1, src2) + evminmaxsh(dst, ktmp, src1, src2, true, AVX10_2_MINMAX_MIN_COMPARE_SIGN); + } +} + +void C2_MacroAssembler::vminmax_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2, int vlen_enc) { if (opcode == Op_MaxVHF || opcode == Op_MaxHF) { // Move sign bits of src2 to mask register. evpmovw2m(ktmp, src2, vlen_enc); @@ -7110,3 +7137,27 @@ void C2_MacroAssembler::vector_max_min_fp16(int opcode, XMMRegister dst, XMMRegi Assembler::evmovdquw(dst, ktmp, xtmp1, true, vlen_enc); } } + +void C2_MacroAssembler::vminmax_fp16_avx10_2(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp, int vlen_enc) { + if (opcode == Op_MaxVHF) { + // dst = max(src1, src2) + evminmaxph(dst, ktmp, src1, src2, true, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vlen_enc); + } else { + assert(opcode == Op_MinVHF, ""); + // dst = min(src1, src2) + evminmaxph(dst, ktmp, src1, src2, true, AVX10_2_MINMAX_MIN_COMPARE_SIGN, vlen_enc); + } +} + +void C2_MacroAssembler::vminmax_fp16_avx10_2(int opcode, XMMRegister dst, XMMRegister src1, Address src2, + KRegister ktmp, int vlen_enc) { + if (opcode == Op_MaxVHF) { + // dst = max(src1, src2) + evminmaxph(dst, ktmp, src1, src2, true, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vlen_enc); + } else { + assert(opcode == Op_MinVHF, ""); + // dst = min(src1, src2) + evminmaxph(dst, ktmp, src1, src2, true, AVX10_2_MINMAX_MIN_COMPARE_SIGN, vlen_enc); + } +} diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index 6d8b0ceaebe..4e77f8a5f6f 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,8 +67,11 @@ public: XMMRegister tmp, XMMRegister atmp, XMMRegister btmp, int vlen_enc); - void vminmax_fp(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, - XMMRegister src1, XMMRegister src2, int vlen_enc); + void vminmax_fp_avx10_2(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, + XMMRegister src1, XMMRegister src2, int vlen_enc); + + void sminmax_fp_avx10_2(int opc, BasicType elem_bt, XMMRegister dst, KRegister mask, + XMMRegister src1, XMMRegister src2); void vpuminmaxq(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, XMMRegister xtmp1, XMMRegister xtmp2, int vlen_enc); @@ -576,11 +579,20 @@ public: void evfp16ph(int opcode, XMMRegister dst, XMMRegister src1, Address src2, int vlen_enc); - void vector_max_min_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, - KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2, int vlen_enc); + void vminmax_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2, int vlen_enc); - void scalar_max_min_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, - KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2); + void vminmax_fp16_avx10_2(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp, int vlen_enc); + + void vminmax_fp16_avx10_2(int opcode, XMMRegister dst, XMMRegister src1, Address src2, + KRegister ktmp, int vlen_enc); + + void sminmax_fp16(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp, XMMRegister xtmp1, XMMRegister xtmp2); + + void sminmax_fp16_avx10_2(int opcode, XMMRegister dst, XMMRegister src1, XMMRegister src2, + KRegister ktmp); void reconstruct_frame_pointer(Register rtmp); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index a0f08145d55..356bf8af5c0 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -1958,6 +1958,16 @@ void MacroAssembler::movflt(XMMRegister dst, AddressLiteral src, Register rscrat } } +void MacroAssembler::movhlf(XMMRegister dst, XMMRegister src, Register rscratch) { + if (VM_Version::supports_avx10_2()) { + evmovw(dst, src); + } else { + assert(rscratch != noreg, "missing"); + evmovw(rscratch, src); + evmovw(dst, rscratch); + } +} + void MacroAssembler::mov64(Register dst, int64_t imm64) { if (is_uimm32(imm64)) { movl(dst, checked_cast(imm64)); @@ -2661,14 +2671,14 @@ void MacroAssembler::ucomisd(XMMRegister dst, AddressLiteral src, Register rscra } } -void MacroAssembler::vucomxsd(XMMRegister dst, AddressLiteral src, Register rscratch) { +void MacroAssembler::evucomxsd(XMMRegister dst, AddressLiteral src, Register rscratch) { assert(rscratch != noreg || always_reachable(src), "missing"); if (reachable(src)) { - Assembler::vucomxsd(dst, as_Address(src)); + Assembler::evucomxsd(dst, as_Address(src)); } else { lea(rscratch, src); - Assembler::vucomxsd(dst, Address(rscratch, 0)); + Assembler::evucomxsd(dst, Address(rscratch, 0)); } } @@ -2683,14 +2693,36 @@ void MacroAssembler::ucomiss(XMMRegister dst, AddressLiteral src, Register rscra } } -void MacroAssembler::vucomxss(XMMRegister dst, AddressLiteral src, Register rscratch) { +void MacroAssembler::evucomxss(XMMRegister dst, AddressLiteral src, Register rscratch) { assert(rscratch != noreg || always_reachable(src), "missing"); if (reachable(src)) { - Assembler::vucomxss(dst, as_Address(src)); + Assembler::evucomxss(dst, as_Address(src)); } else { lea(rscratch, src); - Assembler::vucomxss(dst, Address(rscratch, 0)); + Assembler::evucomxss(dst, Address(rscratch, 0)); + } +} + +void MacroAssembler::evucomish(XMMRegister dst, AddressLiteral src, Register rscratch) { + assert(rscratch != noreg || always_reachable(src), "missing"); + + if (reachable(src)) { + Assembler::evucomish(dst, as_Address(src)); + } else { + lea(rscratch, src); + Assembler::evucomish(dst, Address(rscratch, 0)); + } +} + +void MacroAssembler::evucomxsh(XMMRegister dst, AddressLiteral src, Register rscratch) { + assert(rscratch != noreg || always_reachable(src), "missing"); + + if (reachable(src)) { + Assembler::evucomxsh(dst, as_Address(src)); + } else { + lea(rscratch, src); + Assembler::evucomxsh(dst, Address(rscratch, 0)); } } @@ -9163,7 +9195,7 @@ void MacroAssembler::evpmaxs(BasicType type, XMMRegister dst, KRegister mask, XM case T_FLOAT: evminmaxps(dst, mask, nds, src, merge, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vector_len); break; case T_DOUBLE: - evminmaxps(dst, mask, nds, src, merge, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vector_len); break; + evminmaxpd(dst, mask, nds, src, merge, AVX10_2_MINMAX_MAX_COMPARE_SIGN, vector_len); break; default: fatal("Unexpected type argument %s", type2name(type)); break; } diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index 3bdd1e4477a..021d2943ee8 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -162,6 +162,8 @@ class MacroAssembler: public Assembler { void incrementq(AddressLiteral dst, Register rscratch = noreg); + void movhlf(XMMRegister dst, XMMRegister src, Register rscratch = noreg); + // Support optimal SSE move instructions. void movflt(XMMRegister dst, XMMRegister src) { if (dst-> encoding() == src->encoding()) return; @@ -1308,21 +1310,29 @@ public: void subss(XMMRegister dst, Address src) { Assembler::subss(dst, src); } void subss(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); + void evucomish(XMMRegister dst, XMMRegister src) { Assembler::evucomish(dst, src); } + void evucomish(XMMRegister dst, Address src) { Assembler::evucomish(dst, src); } + void evucomish(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); + + void evucomxsh(XMMRegister dst, XMMRegister src) { Assembler::evucomxsh(dst, src); } + void evucomxsh(XMMRegister dst, Address src) { Assembler::evucomxsh(dst, src); } + void evucomxsh(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); + void ucomiss(XMMRegister dst, XMMRegister src) { Assembler::ucomiss(dst, src); } void ucomiss(XMMRegister dst, Address src) { Assembler::ucomiss(dst, src); } void ucomiss(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); - void vucomxss(XMMRegister dst, XMMRegister src) { Assembler::vucomxss(dst, src); } - void vucomxss(XMMRegister dst, Address src) { Assembler::vucomxss(dst, src); } - void vucomxss(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); + void evucomxss(XMMRegister dst, XMMRegister src) { Assembler::evucomxss(dst, src); } + void evucomxss(XMMRegister dst, Address src) { Assembler::evucomxss(dst, src); } + void evucomxss(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); void ucomisd(XMMRegister dst, XMMRegister src) { Assembler::ucomisd(dst, src); } void ucomisd(XMMRegister dst, Address src) { Assembler::ucomisd(dst, src); } void ucomisd(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); - void vucomxsd(XMMRegister dst, XMMRegister src) { Assembler::vucomxsd(dst, src); } - void vucomxsd(XMMRegister dst, Address src) { Assembler::vucomxsd(dst, src); } - void vucomxsd(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); + void evucomxsd(XMMRegister dst, XMMRegister src) { Assembler::evucomxsd(dst, src); } + void evucomxsd(XMMRegister dst, Address src) { Assembler::evucomxsd(dst, src); } + void evucomxsd(XMMRegister dst, AddressLiteral src, Register rscratch = noreg); // Bitwise Logical XOR of Packed Double-Precision Floating-Point Values void xorpd(XMMRegister dst, XMMRegister src); diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index f31d64f3d7e..d7014141234 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -1708,84 +1708,99 @@ static void emit_cmpfp3(MacroAssembler* masm, Register dst) { __ bind(done); } -// Math.min() # Math.max() -// -------------------------- -// ucomis[s/d] # -// ja -> b # a -// jp -> NaN # NaN -// jb -> a # b -// je # -// |-jz -> a | b # a & b -// | -> a # +enum FP_PREC { + fp_prec_hlf, + fp_prec_flt, + fp_prec_dbl +}; + +static inline void emit_fp_ucom(MacroAssembler* masm, enum FP_PREC pt, + XMMRegister p, XMMRegister q) { + if (pt == fp_prec_hlf) { + __ evucomish(p, q); + } else if (pt == fp_prec_flt) { + __ ucomiss(p, q); + } else { + __ ucomisd(p, q); + } +} + +static inline void movfp(MacroAssembler* masm, enum FP_PREC pt, + XMMRegister dst, XMMRegister src, Register scratch) { + if (pt == fp_prec_hlf) { + __ movhlf(dst, src, scratch); + } else if (pt == fp_prec_flt) { + __ movflt(dst, src); + } else { + __ movdbl(dst, src); + } +} + +// Math.min() # Math.max() +// ----------------------------- +// (v)ucomis[h/s/d] # +// ja -> b # a +// jp -> NaN # NaN +// jb -> a # b +// je # +// |-jz -> a | b # a & b +// | -> a # static void emit_fp_min_max(MacroAssembler* masm, XMMRegister dst, XMMRegister a, XMMRegister b, XMMRegister xmmt, Register rt, - bool min, bool single) { + bool min, enum FP_PREC pt) { Label nan, zero, below, above, done; - if (single) - __ ucomiss(a, b); - else - __ ucomisd(a, b); + emit_fp_ucom(masm, pt, a, b); - if (dst->encoding() != (min ? b : a)->encoding()) + if (dst->encoding() != (min ? b : a)->encoding()) { __ jccb(Assembler::above, above); // CF=0 & ZF=0 - else + } else { __ jccb(Assembler::above, done); + } __ jccb(Assembler::parity, nan); // PF=1 __ jccb(Assembler::below, below); // CF=1 // equal __ vpxor(xmmt, xmmt, xmmt, Assembler::AVX_128bit); - if (single) { - __ ucomiss(a, xmmt); - __ jccb(Assembler::equal, zero); + emit_fp_ucom(masm, pt, a, xmmt); - __ movflt(dst, a); - __ jmp(done); - } - else { - __ ucomisd(a, xmmt); - __ jccb(Assembler::equal, zero); + __ jccb(Assembler::equal, zero); + movfp(masm, pt, dst, a, rt); - __ movdbl(dst, a); - __ jmp(done); - } + __ jmp(done); __ bind(zero); - if (min) + if (min) { __ vpor(dst, a, b, Assembler::AVX_128bit); - else + } else { __ vpand(dst, a, b, Assembler::AVX_128bit); + } __ jmp(done); __ bind(above); - if (single) - __ movflt(dst, min ? b : a); - else - __ movdbl(dst, min ? b : a); + movfp(masm, pt, dst, min ? b : a, rt); __ jmp(done); __ bind(nan); - if (single) { + if (pt == fp_prec_hlf) { + __ movl(rt, 0x00007e00); // Float16.NaN + __ evmovw(dst, rt); + } else if (pt == fp_prec_flt) { __ movl(rt, 0x7fc00000); // Float.NaN __ movdl(dst, rt); - } - else { + } else { __ mov64(rt, 0x7ff8000000000000L); // Double.NaN __ movdq(dst, rt); } __ jmp(done); __ bind(below); - if (single) - __ movflt(dst, min ? a : b); - else - __ movdbl(dst, min ? a : b); + movfp(masm, pt, dst, min ? a : b, rt); __ bind(done); } @@ -7345,146 +7360,140 @@ instruct loadAOTRCAddress(rRegP dst, immAOTRuntimeConstantsAddress con) ins_pipe(ialu_reg_fat); %} +// min = java.lang.Math.min(float a, float b) // max = java.lang.Math.max(float a, float b) -instruct maxF_reg_avx10_2(regF dst, regF a, regF b) %{ - predicate(VM_Version::supports_avx10_2()); +instruct minmaxF_reg_avx10_2(regF dst, regF a, regF b) +%{ + predicate(VM_Version::supports_avx10_2() && !VLoopReductions::is_reduction(n)); match(Set dst (MaxF a b)); - format %{ "maxF $dst, $a, $b" %} - ins_encode %{ - __ eminmaxss($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_2_MINMAX_MAX_COMPARE_SIGN); - %} - ins_pipe( pipe_slow ); -%} - -// max = java.lang.Math.max(float a, float b) -instruct maxF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, legRegF btmp) %{ - predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); - match(Set dst (MaxF a b)); - effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); - format %{ "maxF $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} - ins_encode %{ - __ vminmax_fp(Op_MaxV, T_FLOAT, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); - %} - ins_pipe( pipe_slow ); -%} - -instruct maxF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRegI rtmp, rFlagsReg cr) %{ - predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); - match(Set dst (MaxF a b)); - effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); - - format %{ "maxF_reduction $dst, $a, $b \t!using $xtmp and $rtmp as TEMP" %} - ins_encode %{ - emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, - false /*min*/, true /*single*/); - %} - ins_pipe( pipe_slow ); -%} - -// max = java.lang.Math.max(double a, double b) -instruct maxD_reg_avx10_2(regD dst, regD a, regD b) %{ - predicate(VM_Version::supports_avx10_2()); - match(Set dst (MaxD a b)); - format %{ "maxD $dst, $a, $b" %} - ins_encode %{ - __ eminmaxsd($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_2_MINMAX_MAX_COMPARE_SIGN); - %} - ins_pipe( pipe_slow ); -%} - -// max = java.lang.Math.max(double a, double b) -instruct maxD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, legRegD btmp) %{ - predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); - match(Set dst (MaxD a b)); - effect(USE a, USE b, TEMP atmp, TEMP btmp, TEMP tmp); - format %{ "maxD $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} - ins_encode %{ - __ vminmax_fp(Op_MaxV, T_DOUBLE, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); - %} - ins_pipe( pipe_slow ); -%} - -instruct maxD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRegL rtmp, rFlagsReg cr) %{ - predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); - match(Set dst (MaxD a b)); - effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); - - format %{ "maxD_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} - ins_encode %{ - emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, - false /*min*/, false /*single*/); - %} - ins_pipe( pipe_slow ); -%} - -// max = java.lang.Math.min(float a, float b) -instruct minF_reg_avx10_2(regF dst, regF a, regF b) %{ - predicate(VM_Version::supports_avx10_2()); match(Set dst (MinF a b)); - format %{ "minF $dst, $a, $b" %} + + format %{ "minmaxF $dst, $a, $b" %} ins_encode %{ - __ eminmaxss($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_2_MINMAX_MIN_COMPARE_SIGN); + int opcode = this->ideal_Opcode(); + __ sminmax_fp_avx10_2(opcode, T_FLOAT, $dst$$XMMRegister, k0, $a$$XMMRegister, $b$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct minmaxF_reduction_reg_avx10_2(regF dst, regF a, regF b, regF xtmp, rRegI rtmp, rFlagsReg cr) +%{ + predicate(VM_Version::supports_avx10_2() && VLoopReductions::is_reduction(n)); + match(Set dst (MaxF a b)); + match(Set dst (MinF a b)); + effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); + + format %{ "minmaxF_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + bool min = (opcode == Op_MinF) ? true : false; + emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, + min, fp_prec_flt /*pt*/); %} ins_pipe( pipe_slow ); %} // min = java.lang.Math.min(float a, float b) -instruct minF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, legRegF btmp) %{ +// max = java.lang.Math.max(float a, float b) +instruct minmaxF_reg(legRegF dst, legRegF a, legRegF b, legRegF tmp, legRegF atmp, legRegF btmp) +%{ predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); + match(Set dst (MaxF a b)); match(Set dst (MinF a b)); effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); - format %{ "minF $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} + + format %{ "minmaxF $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} ins_encode %{ - __ vminmax_fp(Op_MinV, T_FLOAT, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); + int opcode = this->ideal_Opcode(); + int param_opcode = (opcode == Op_MinF) ? Op_MinV : Op_MaxV; + __ vminmax_fp(param_opcode, T_FLOAT, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, + $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); %} ins_pipe( pipe_slow ); %} -instruct minF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRegI rtmp, rFlagsReg cr) %{ +instruct minmaxF_reduction_reg(legRegF dst, legRegF a, legRegF b, legRegF xtmp, rRegI rtmp, rFlagsReg cr) +%{ predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); + match(Set dst (MaxF a b)); match(Set dst (MinF a b)); effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); - format %{ "minF_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} + format %{ "minmaxF_reduction $dst, $a, $b \t!using $xtmp and $rtmp as TEMP" %} ins_encode %{ + int opcode = this->ideal_Opcode(); + bool min = (opcode == Op_MinF) ? true : false; emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, - true /*min*/, true /*single*/); - %} - ins_pipe( pipe_slow ); -%} - -// max = java.lang.Math.min(double a, double b) -instruct minD_reg_avx10_2(regD dst, regD a, regD b) %{ - predicate(VM_Version::supports_avx10_2()); - match(Set dst (MinD a b)); - format %{ "minD $dst, $a, $b" %} - ins_encode %{ - __ eminmaxsd($dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, AVX10_2_MINMAX_MIN_COMPARE_SIGN); + min, fp_prec_flt /*pt*/); %} ins_pipe( pipe_slow ); %} // min = java.lang.Math.min(double a, double b) -instruct minD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, legRegD btmp) %{ - predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); +// max = java.lang.Math.max(double a, double b) +instruct minmaxD_reg_avx10_2(regD dst, regD a, regD b) +%{ + predicate(VM_Version::supports_avx10_2() && !VLoopReductions::is_reduction(n)); + match(Set dst (MaxD a b)); match(Set dst (MinD a b)); - effect(USE a, USE b, TEMP tmp, TEMP atmp, TEMP btmp); - format %{ "minD $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} + + format %{ "minmaxD $dst, $a, $b" %} ins_encode %{ - __ vminmax_fp(Op_MinV, T_DOUBLE, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); + int opcode = this->ideal_Opcode(); + __ sminmax_fp_avx10_2(opcode, T_DOUBLE, $dst$$XMMRegister, k0, $a$$XMMRegister, $b$$XMMRegister); %} ins_pipe( pipe_slow ); %} -instruct minD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRegL rtmp, rFlagsReg cr) %{ - predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); +instruct minmaxD_reduction_reg_avx10_2(regD dst, regD a, regD b, regD xtmp, rRegI rtmp, rFlagsReg cr) +%{ + predicate(VM_Version::supports_avx10_2() && VLoopReductions::is_reduction(n)); + match(Set dst (MaxD a b)); match(Set dst (MinD a b)); effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); - format %{ "maxD_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} + format %{ "minmaxD_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} ins_encode %{ + int opcode = this->ideal_Opcode(); + bool min = (opcode == Op_MinD) ? true : false; emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, - true /*min*/, false /*single*/); + min, fp_prec_dbl /*pt*/); + %} + ins_pipe( pipe_slow ); +%} + +// min = java.lang.Math.min(double a, double b) +// max = java.lang.Math.max(double a, double b) +instruct minmaxD_reg(legRegD dst, legRegD a, legRegD b, legRegD tmp, legRegD atmp, legRegD btmp) +%{ + predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && !VLoopReductions::is_reduction(n)); + match(Set dst (MaxD a b)); + match(Set dst (MinD a b)); + effect(USE a, USE b, TEMP atmp, TEMP btmp, TEMP tmp); + + format %{ "minmaxD $dst, $a, $b \t! using $tmp, $atmp and $btmp as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + int param_opcode = (opcode == Op_MinD) ? Op_MinV : Op_MaxV; + __ vminmax_fp(param_opcode, T_DOUBLE, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $tmp$$XMMRegister, + $atmp$$XMMRegister, $btmp$$XMMRegister, Assembler::AVX_128bit); + %} + ins_pipe( pipe_slow ); +%} + +instruct minmaxD_reduction_reg(legRegD dst, legRegD a, legRegD b, legRegD xtmp, rRegL rtmp, rFlagsReg cr) +%{ + predicate(!VM_Version::supports_avx10_2() && UseAVX > 0 && VLoopReductions::is_reduction(n)); + match(Set dst (MaxD a b)); + match(Set dst (MinD a b)); + effect(USE a, USE b, TEMP xtmp, TEMP rtmp, KILL cr); + + format %{ "minmaxD_reduction $dst, $a, $b \t! using $xtmp and $rtmp as TEMP" %} + ins_encode %{ + int opcode = this->ideal_Opcode(); + bool min = (opcode == Op_MinD) ? true : false; + emit_fp_min_max(masm, $dst$$XMMRegister, $a$$XMMRegister, $b$$XMMRegister, $xtmp$$XMMRegister, $rtmp$$Register, + min, fp_prec_dbl /*pt*/); %} ins_pipe( pipe_slow ); %} @@ -14394,9 +14403,9 @@ instruct cmpF_cc_regCFE(rFlagsRegUCFE cr, regF src1, regF src2) %{ match(Set cr (CmpF src1 src2)); ins_cost(100); - format %{ "vucomxss $src1, $src2" %} + format %{ "evucomxss $src1, $src2" %} ins_encode %{ - __ vucomxss($src1$$XMMRegister, $src2$$XMMRegister); + __ evucomxss($src1$$XMMRegister, $src2$$XMMRegister); %} ins_pipe(pipe_slow); %} @@ -14416,9 +14425,9 @@ instruct cmpF_cc_memCFE(rFlagsRegUCFE cr, regF src1, memory src2) %{ match(Set cr (CmpF src1 (LoadF src2))); ins_cost(100); - format %{ "vucomxss $src1, $src2" %} + format %{ "evucomxss $src1, $src2" %} ins_encode %{ - __ vucomxss($src1$$XMMRegister, $src2$$Address); + __ evucomxss($src1$$XMMRegister, $src2$$Address); %} ins_pipe(pipe_slow); %} @@ -14438,9 +14447,9 @@ instruct cmpF_cc_immCFE(rFlagsRegUCFE cr, regF src, immF con) %{ match(Set cr (CmpF src con)); ins_cost(100); - format %{ "vucomxss $src, [$constantaddress]\t# load from constant table: float=$con" %} + format %{ "evucomxss $src, [$constantaddress]\t# load from constant table: float=$con" %} ins_encode %{ - __ vucomxss($src$$XMMRegister, $constantaddress($con)); + __ evucomxss($src$$XMMRegister, $constantaddress($con)); %} ins_pipe(pipe_slow); %} @@ -14479,9 +14488,9 @@ instruct cmpD_cc_regCFE(rFlagsRegUCFE cr, regD src1, regD src2) %{ match(Set cr (CmpD src1 src2)); ins_cost(100); - format %{ "vucomxsd $src1, $src2 test" %} + format %{ "evucomxsd $src1, $src2 test" %} ins_encode %{ - __ vucomxsd($src1$$XMMRegister, $src2$$XMMRegister); + __ evucomxsd($src1$$XMMRegister, $src2$$XMMRegister); %} ins_pipe(pipe_slow); %} @@ -14501,9 +14510,9 @@ instruct cmpD_cc_memCFE(rFlagsRegUCFE cr, regD src1, memory src2) %{ match(Set cr (CmpD src1 (LoadD src2))); ins_cost(100); - format %{ "vucomxsd $src1, $src2" %} + format %{ "evucomxsd $src1, $src2" %} ins_encode %{ - __ vucomxsd($src1$$XMMRegister, $src2$$Address); + __ evucomxsd($src1$$XMMRegister, $src2$$Address); %} ins_pipe(pipe_slow); %} @@ -14522,9 +14531,9 @@ instruct cmpD_cc_immCFE(rFlagsRegUCFE cr, regD src, immD con) %{ match(Set cr (CmpD src con)); ins_cost(100); - format %{ "vucomxsd $src, [$constantaddress]\t# load from constant table: double=$con" %} + format %{ "evucomxsd $src, [$constantaddress]\t# load from constant table: double=$con" %} ins_encode %{ - __ vucomxsd($src$$XMMRegister, $constantaddress($con)); + __ evucomxsd($src$$XMMRegister, $constantaddress($con)); %} ins_pipe(pipe_slow); %} @@ -18832,7 +18841,7 @@ instruct ReplHF_reg(vec dst, regF src, rRegI rtmp) %{ format %{ "replicateHF $dst, $src \t! using $rtmp as TEMP" %} ins_encode %{ int vlen_enc = vector_length_encoding(this); - __ vmovw($rtmp$$Register, $src$$XMMRegister); + __ evmovw($rtmp$$Register, $src$$XMMRegister); __ evpbroadcastw($dst$$XMMRegister, $rtmp$$Register, vlen_enc); %} ins_pipe( pipe_slow ); @@ -20947,7 +20956,7 @@ instruct minmaxFP_reg_avx10_2(vec dst, vec a, vec b) %{ int vlen_enc = vector_length_encoding(this); int opcode = this->ideal_Opcode(); BasicType elem_bt = Matcher::vector_element_basic_type(this); - __ vminmax_fp(opcode, elem_bt, $dst$$XMMRegister, k0, $a$$XMMRegister, $b$$XMMRegister, vlen_enc); + __ vminmax_fp_avx10_2(opcode, elem_bt, $dst$$XMMRegister, k0, $a$$XMMRegister, $b$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -25291,9 +25300,9 @@ instruct vector_selectfrom_twovectors_reg_evex(vec index, vec src1, vec src2) instruct reinterpretS2HF(regF dst, rRegI src) %{ match(Set dst (ReinterpretS2HF src)); - format %{ "vmovw $dst, $src" %} + format %{ "evmovw $dst, $src" %} ins_encode %{ - __ vmovw($dst$$XMMRegister, $src$$Register); + __ evmovw($dst$$XMMRegister, $src$$Register); %} ins_pipe(pipe_slow); %} @@ -25301,9 +25310,9 @@ instruct reinterpretS2HF(regF dst, rRegI src) instruct reinterpretHF2S(rRegI dst, regF src) %{ match(Set dst (ReinterpretHF2S src)); - format %{ "vmovw $dst, $src" %} + format %{ "evmovw $dst, $src" %} ins_encode %{ - __ vmovw($dst$$Register, $src$$XMMRegister); + __ evmovw($dst$$Register, $src$$XMMRegister); %} ins_pipe(pipe_slow); %} @@ -25357,10 +25366,11 @@ instruct scalar_minmax_HF_reg_avx10_2(regF dst, regF src1, regF src2) predicate(VM_Version::supports_avx10_2()); match(Set dst (MaxHF src1 src2)); match(Set dst (MinHF src1 src2)); + format %{ "scalar_min_max_fp16 $dst, $src1, $src2" %} ins_encode %{ - int function = this->ideal_Opcode() == Op_MinHF ? AVX10_2_MINMAX_MIN_COMPARE_SIGN : AVX10_2_MINMAX_MAX_COMPARE_SIGN; - __ eminmaxsh($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, function); + int opcode = this->ideal_Opcode(); + __ sminmax_fp16_avx10_2(opcode, $dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, k0); %} ins_pipe( pipe_slow ); %} @@ -25371,11 +25381,12 @@ instruct scalar_minmax_HF_reg(regF dst, regF src1, regF src2, kReg ktmp, regF xt match(Set dst (MaxHF src1 src2)); match(Set dst (MinHF src1 src2)); effect(TEMP_DEF dst, TEMP ktmp, TEMP xtmp1, TEMP xtmp2); + format %{ "scalar_min_max_fp16 $dst, $src1, $src2\t using $ktmp, $xtmp1 and $xtmp2 as TEMP" %} ins_encode %{ int opcode = this->ideal_Opcode(); - __ scalar_max_min_fp16(opcode, $dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, $ktmp$$KRegister, - $xtmp1$$XMMRegister, $xtmp2$$XMMRegister); + __ sminmax_fp16(opcode, $dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, $ktmp$$KRegister, + $xtmp1$$XMMRegister, $xtmp2$$XMMRegister); %} ins_pipe( pipe_slow ); %} @@ -25475,8 +25486,9 @@ instruct vector_minmax_HF_mem_avx10_2(vec dst, vec src1, memory src2) format %{ "vector_min_max_fp16_mem $dst, $src1, $src2" %} ins_encode %{ int vlen_enc = vector_length_encoding(this); - int function = this->ideal_Opcode() == Op_MinVHF ? AVX10_2_MINMAX_MIN_COMPARE_SIGN : AVX10_2_MINMAX_MAX_COMPARE_SIGN; - __ evminmaxph($dst$$XMMRegister, k0, $src1$$XMMRegister, $src2$$Address, true, function, vlen_enc); + int opcode = this->ideal_Opcode(); + __ vminmax_fp16_avx10_2(opcode, $dst$$XMMRegister, $src1$$XMMRegister, $src2$$Address, + k0, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -25489,8 +25501,9 @@ instruct vector_minmax_HF_reg_avx10_2(vec dst, vec src1, vec src2) format %{ "vector_min_max_fp16 $dst, $src1, $src2" %} ins_encode %{ int vlen_enc = vector_length_encoding(this); - int function = this->ideal_Opcode() == Op_MinVHF ? AVX10_2_MINMAX_MIN_COMPARE_SIGN : AVX10_2_MINMAX_MAX_COMPARE_SIGN; - __ evminmaxph($dst$$XMMRegister, k0, $src1$$XMMRegister, $src2$$XMMRegister, true, function, vlen_enc); + int opcode = this->ideal_Opcode(); + __ vminmax_fp16_avx10_2(opcode, $dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, + k0, vlen_enc); %} ins_pipe( pipe_slow ); %} @@ -25505,8 +25518,8 @@ instruct vector_minmax_HF_reg(vec dst, vec src1, vec src2, kReg ktmp, vec xtmp1, ins_encode %{ int vlen_enc = vector_length_encoding(this); int opcode = this->ideal_Opcode(); - __ vector_max_min_fp16(opcode, $dst$$XMMRegister, $src2$$XMMRegister, $src1$$XMMRegister, $ktmp$$KRegister, - $xtmp1$$XMMRegister, $xtmp2$$XMMRegister, vlen_enc); + __ vminmax_fp16(opcode, $dst$$XMMRegister, $src2$$XMMRegister, $src1$$XMMRegister, $ktmp$$KRegister, + $xtmp1$$XMMRegister, $xtmp2$$XMMRegister, vlen_enc); %} ins_pipe( pipe_slow ); %} diff --git a/test/hotspot/jtreg/compiler/intrinsics/math/TestFpMinMaxReductions.java b/test/hotspot/jtreg/compiler/intrinsics/math/TestFpMinMaxReductions.java index ff0277b33f7..5f516890dbe 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/math/TestFpMinMaxReductions.java +++ b/test/hotspot/jtreg/compiler/intrinsics/math/TestFpMinMaxReductions.java @@ -55,21 +55,21 @@ public class TestFpMinMaxReductions { } @Test - @IR(counts = {IRNode.MIN_F_REG, "1"}, - failOn = {IRNode.MIN_F_REDUCTION_REG}) + @IR(counts = {IRNode.MINMAX_F_REG, "1"}, + failOn = {IRNode.MINMAX_F_REDUCTION_REG}) private static float testFloatMin() { return Math.min(floatInput1, floatInput2); } @Test - @IR(counts = {IRNode.MAX_F_REG, "1"}, - failOn = {IRNode.MAX_F_REDUCTION_REG}) + @IR(counts = {IRNode.MINMAX_F_REG, "1"}, + failOn = {IRNode.MINMAX_F_REDUCTION_REG}) private static float testFloatMax() { return Math.max(floatInput1, floatInput2); } @Test - @IR(counts = {IRNode.MIN_F_REDUCTION_REG, ">= 1"}) + @IR(counts = {IRNode.MINMAX_F_REDUCTION_REG, ">= 1"}) private static float testFloatMinReduction() { float fmin = Float.POSITIVE_INFINITY; for (int i = 0; i < floatArray.length; i++) { @@ -79,7 +79,7 @@ public class TestFpMinMaxReductions { } @Test - @IR(counts = {IRNode.MIN_F_REDUCTION_REG, ">= 1"}) + @IR(counts = {IRNode.MINMAX_F_REDUCTION_REG, ">= 1"}) private static float testFloatMinReductionPartiallyUnrolled() { float fmin = Float.POSITIVE_INFINITY; for (int i = 0; i < floatArray.length / 2; i++) { @@ -90,7 +90,7 @@ public class TestFpMinMaxReductions { } @Test - @IR(counts = {IRNode.MIN_F_REDUCTION_REG, ">= 1"}) + @IR(counts = {IRNode.MINMAX_F_REDUCTION_REG, ">= 1"}) private static float testFloatMinReductionNonCounted() { float fmin = Float.POSITIVE_INFINITY; for (int i = 0; i < floatArray.length; i += stride) { @@ -100,7 +100,7 @@ public class TestFpMinMaxReductions { } @Test - @IR(counts = {IRNode.MIN_F_REDUCTION_REG, ">= 1"}) + @IR(counts = {IRNode.MINMAX_F_REDUCTION_REG, ">= 1"}) private static float testFloatMinReductionGlobalAccumulator() { acc = Float.POSITIVE_INFINITY; for (int i = 0; i < floatArray.length; i++) { @@ -110,7 +110,7 @@ public class TestFpMinMaxReductions { } @Test - @IR(counts = {IRNode.MIN_F_REDUCTION_REG, ">= 1"}) + @IR(counts = {IRNode.MINMAX_F_REDUCTION_REG, ">= 1"}) private static float testFloatMinReductionInOuterLoop() { float fmin = Float.POSITIVE_INFINITY; int count = 0; @@ -124,7 +124,7 @@ public class TestFpMinMaxReductions { } @Test - @IR(counts = {IRNode.MAX_F_REDUCTION_REG, ">= 1"}) + @IR(counts = {IRNode.MINMAX_F_REDUCTION_REG, ">= 1"}) private static float testFloatMaxReduction() { float fmax = Float.NEGATIVE_INFINITY; for (int i = 0; i < floatArray.length; i++) { @@ -134,21 +134,21 @@ public class TestFpMinMaxReductions { } @Test - @IR(counts = {IRNode.MIN_D_REG, "1"}, - failOn = {IRNode.MIN_D_REDUCTION_REG}) + @IR(counts = {IRNode.MINMAX_D_REG, "1"}, + failOn = {IRNode.MINMAX_D_REDUCTION_REG}) private static double testDoubleMin() { return Math.min(doubleInput1, doubleInput2); } @Test - @IR(counts = {IRNode.MAX_D_REG, "1"}, - failOn = {IRNode.MAX_D_REDUCTION_REG}) + @IR(counts = {IRNode.MINMAX_D_REG, "1"}, + failOn = {IRNode.MINMAX_D_REDUCTION_REG}) private static double testDoubleMax() { return Math.max(doubleInput1, doubleInput2); } @Test - @IR(counts = {IRNode.MIN_D_REDUCTION_REG, ">= 1"}) + @IR(counts = {IRNode.MINMAX_D_REDUCTION_REG, ">= 1"}) private static double testDoubleMinReduction() { double fmin = Double.POSITIVE_INFINITY; for (int i = 0; i < doubleArray.length; i++) { @@ -158,7 +158,7 @@ public class TestFpMinMaxReductions { } @Test - @IR(counts = {IRNode.MAX_D_REDUCTION_REG, ">= 1"}) + @IR(counts = {IRNode.MINMAX_D_REDUCTION_REG, ">= 1"}) private static double testDoubleMaxReduction() { double fmax = Double.NEGATIVE_INFINITY; for (int i = 0; i < doubleArray.length; i++) { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 8885d1283df..0753a0b04bc 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -1203,31 +1203,11 @@ public class IRNode { beforeMatchingNameRegex(MAX_D, "MaxD"); } - public static final String MAX_D_REDUCTION_REG = PREFIX + "MAX_D_REDUCTION_REG" + POSTFIX; - static { - machOnlyNameRegex(MAX_D_REDUCTION_REG, "maxD_reduction_reg"); - } - - public static final String MAX_D_REG = PREFIX + "MAX_D_REG" + POSTFIX; - static { - machOnlyNameRegex(MAX_D_REG, "maxD_reg"); - } - public static final String MAX_F = PREFIX + "MAX_F" + POSTFIX; static { beforeMatchingNameRegex(MAX_F, "MaxF"); } - public static final String MAX_F_REDUCTION_REG = PREFIX + "MAX_F_REDUCTION_REG" + POSTFIX; - static { - machOnlyNameRegex(MAX_F_REDUCTION_REG, "maxF_reduction_reg"); - } - - public static final String MAX_F_REG = PREFIX + "MAX_F_REG" + POSTFIX; - static { - machOnlyNameRegex(MAX_F_REG, "maxF_reg"); - } - public static final String MAX_I = PREFIX + "MAX_I" + POSTFIX; static { beforeMatchingNameRegex(MAX_I, "MaxI"); @@ -1309,14 +1289,14 @@ public class IRNode { beforeMatchingNameRegex(MIN_D, "MinD"); } - public static final String MIN_D_REDUCTION_REG = PREFIX + "MIN_D_REDUCTION_REG" + POSTFIX; + public static final String MINMAX_D_REDUCTION_REG = PREFIX + "MINMAX_D_REDUCTION_REG" + POSTFIX; static { - machOnlyNameRegex(MIN_D_REDUCTION_REG, "minD_reduction_reg"); + machOnlyNameRegex(MINMAX_D_REDUCTION_REG, "minmaxD_reduction_reg"); } - public static final String MIN_D_REG = PREFIX + "MIN_D_REG" + POSTFIX; + public static final String MINMAX_D_REG = PREFIX + "MINMAX_D_REG" + POSTFIX; static { - machOnlyNameRegex(MIN_D_REG, "minD_reg"); + machOnlyNameRegex(MINMAX_D_REG, "minmaxD_reg"); } public static final String MIN_F = PREFIX + "MIN_F" + POSTFIX; @@ -1324,14 +1304,14 @@ public class IRNode { beforeMatchingNameRegex(MIN_F, "MinF"); } - public static final String MIN_F_REDUCTION_REG = PREFIX + "MIN_F_REDUCTION_REG" + POSTFIX; + public static final String MINMAX_F_REDUCTION_REG = PREFIX + "MINMAX_F_REDUCTION_REG" + POSTFIX; static { - machOnlyNameRegex(MIN_F_REDUCTION_REG, "minF_reduction_reg"); + machOnlyNameRegex(MINMAX_F_REDUCTION_REG, "minmaxF_reduction_reg"); } - public static final String MIN_F_REG = PREFIX + "MIN_F_REG" + POSTFIX; + public static final String MINMAX_F_REG = PREFIX + "MINMAX_F_REG" + POSTFIX; static { - machOnlyNameRegex(MIN_F_REG, "minF_reg"); + machOnlyNameRegex(MINMAX_F_REG, "minmaxF_reg"); } public static final String MIN_I = PREFIX + "MIN_I" + POSTFIX; diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java index cbfe9958924..92c0b58005f 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/Float16OperationsBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ import jdk.incubator.vector.*; import org.openjdk.jmh.annotations.*; import static jdk.incubator.vector.Float16.*; import static java.lang.Float.*; +import java.util.Random; @OutputTimeUnit(TimeUnit.MILLISECONDS) @State(Scope.Thread) @@ -45,11 +46,20 @@ public class Float16OperationsBenchmark { short [] vector5; boolean [] vectorPredicate; + private int c0, c1, c2, s1, s2; + + Random r; + static final short f16_one = Float.floatToFloat16(1.0f); static final short f16_two = Float.floatToFloat16(2.0f); @Setup(Level.Trial) public void BmSetup() { + r = new Random(); + + c1 = s1 = step(); + c2 = vectorDim - (s2 = step()); + rexp = new int[vectorDim]; vectorRes = new short[vectorDim]; vector1 = new short[vectorDim]; @@ -84,6 +94,16 @@ public class Float16OperationsBenchmark { ); } + private int step() { + return (r.nextInt() & 0xf) + 1; + } + + private void inc() { + c1 = c1 + s1 < vectorDim ? c1 + s1 : (s1 = step()); + c2 = c2 - s2 > 0 ? c2 - s2 : vectorDim - (s2 = step()); + c0 = Math.abs(c2 - c1); + } + @Benchmark public void addBenchmark() { for (int i = 0; i < vectorDim; i++) { @@ -200,6 +220,14 @@ public class Float16OperationsBenchmark { } } + @Benchmark + public void maxScalarBenchmark() { + for (int i = 0; i < vectorDim; i++) { + inc(); // Ensures no auto-vectorization + vectorRes[c0] = float16ToRawShortBits(max(shortBitsToFloat16(vector1[c1]), shortBitsToFloat16(vector2[c2]))); + } + } + @Benchmark public void minBenchmark() { for (int i = 0; i < vectorDim; i++) { @@ -207,6 +235,14 @@ public class Float16OperationsBenchmark { } } + @Benchmark + public void minScalarBenchmark() { + for (int i = 0; i < vectorDim; i++) { + inc(); // Ensures no auto-vectorization + vectorRes[c0] = float16ToRawShortBits(min(shortBitsToFloat16(vector1[c1]), shortBitsToFloat16(vector2[c2]))); + } + } + @Benchmark public void sqrtBenchmark() { for (int i = 0; i < vectorDim; i++) { diff --git a/test/micro/org/openjdk/bench/vm/compiler/FpMinMaxIntrinsics.java b/test/micro/org/openjdk/bench/vm/compiler/FpMinMaxIntrinsics.java index 27ae2214157..62c33f5fafe 100644 --- a/test/micro/org/openjdk/bench/vm/compiler/FpMinMaxIntrinsics.java +++ b/test/micro/org/openjdk/bench/vm/compiler/FpMinMaxIntrinsics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,14 +45,15 @@ public class FpMinMaxIntrinsics { private Random r = new Random(); private static int stride = 1; - private static float acc; + private static float f_acc; + private static double d_acc; @Setup public void init() { c1 = s1 = step(); c2 = COUNT - (s2 = step()); - for (int i=0; i Date: Fri, 27 Mar 2026 07:13:56 +0000 Subject: [PATCH 149/160] 8380872: Remove lingering comments about the removed stack locking Reviewed-by: aboldtch, dholmes --- src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp | 2 +- src/hotspot/share/oops/markWord.hpp | 1 - src/hotspot/share/runtime/objectMonitor.hpp | 6 ++---- src/hotspot/share/runtime/synchronizer.cpp | 13 +++++-------- src/hotspot/share/runtime/vmOperations.cpp | 3 --- 5 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index c1df726b5ba..23b9a77844d 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -152,7 +152,7 @@ inline Assembler::AvxVectorLen C2_MacroAssembler::vector_length_encoding(int vle // Because the transitions from emitted code to the runtime // monitorenter/exit helper stubs are so slow it's critical that -// we inline both the stack-locking fast path and the inflated fast path. +// we inline both the lock-stack fast path and the inflated fast path. // // See also: cmpFastLock and cmpFastUnlock. // diff --git a/src/hotspot/share/oops/markWord.hpp b/src/hotspot/share/oops/markWord.hpp index c54a9f1bf5d..4583e6bd3a1 100644 --- a/src/hotspot/share/oops/markWord.hpp +++ b/src/hotspot/share/oops/markWord.hpp @@ -54,7 +54,6 @@ // // - the two lock bits are used to describe three states: locked/unlocked and monitor. // -// [ptr | 00] locked ptr points to real header on stack (stack-locking in use) // [header | 00] locked locked regular object header (fast-locking in use) // [header | 01] unlocked regular object header // [ptr | 10] monitor inflated lock (header is swapped out, UseObjectMonitorTable == false) diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index 842aa1b374e..3ab7b8ea519 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -89,10 +89,8 @@ class ObjectWaiter : public CHeapObj { } }; -// The ObjectMonitor class implements the heavyweight version of a -// JavaMonitor. The lightweight BasicLock/stack lock version has been -// inflated into an ObjectMonitor. This inflation is typically due to -// contention or use of Object.wait(). +// The ObjectMonitor class implements the heavyweight version of a JavaMonitor. +// This inflation is typically due to contention or use of Object.wait(). // // WARNING: This is a very sensitive and fragile class. DO NOT make any // changes unless you are fully aware of the underlying semantics. diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index e3293f94eeb..fa178dcb5a1 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -830,8 +830,7 @@ void ObjectSynchronizer::owned_monitors_iterate_filtered(MonitorClosure* closure }); } -// Iterate ObjectMonitors where the owner == thread; this does NOT include -// ObjectMonitors where owner is set to a stack-lock address in thread. +// Iterate ObjectMonitors where the owner == thread. void ObjectSynchronizer::owned_monitors_iterate(MonitorClosure* closure, JavaThread* thread) { int64_t key = ObjectMonitor::owner_id_from(thread); auto thread_filter = [&](ObjectMonitor* monitor) { return monitor->owner() == key; }; @@ -1964,12 +1963,10 @@ ObjectMonitor* ObjectSynchronizer::inflate_into_object_header(oop object, Object const markWord mark = object->mark_acquire(); // The mark can be in one of the following states: - // * inflated - Just return if using stack-locking. - // If using fast-locking and the ObjectMonitor owner - // is anonymous and the locking_thread owns the - // object lock, then we make the locking_thread - // the ObjectMonitor owner and remove the lock from - // the locking_thread's lock stack. + // * inflated - If the ObjectMonitor owner is anonymous and the + // locking_thread owns the object lock, then we make the + // locking_thread the ObjectMonitor owner and remove the + // lock from the locking_thread's lock stack. // * fast-locked - Coerce it to inflated from fast-locked. // * unlocked - Aggressively inflate the object. diff --git a/src/hotspot/share/runtime/vmOperations.cpp b/src/hotspot/share/runtime/vmOperations.cpp index ef480f04c57..c4a77ce3275 100644 --- a/src/hotspot/share/runtime/vmOperations.cpp +++ b/src/hotspot/share/runtime/vmOperations.cpp @@ -276,9 +276,6 @@ void VM_ThreadDump::doit_epilogue() { } // Hash table of int64_t to a list of ObjectMonitor* owned by the JavaThread. -// The JavaThread's owner key is either a JavaThread* or a stack lock -// address in the JavaThread so we use "int64_t". -// class ObjectMonitorsDump : public MonitorClosure, public ObjectMonitorsView { private: static unsigned int ptr_hash(int64_t const& s1) { From cee1e040b3729309e0f9515c4852670226e5ca88 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Fri, 27 Mar 2026 07:56:55 +0000 Subject: [PATCH 150/160] 8380541: G1: Add g1CollectorState.inline.hpp file Reviewed-by: ayang, stefank --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 2 +- .../share/gc/g1/g1CollectedHeap.inline.hpp | 1 - src/hotspot/share/gc/g1/g1CollectionSet.cpp | 2 +- src/hotspot/share/gc/g1/g1CollectorState.cpp | 36 ++--- src/hotspot/share/gc/g1/g1CollectorState.hpp | 74 +++------- .../share/gc/g1/g1CollectorState.inline.hpp | 128 ++++++++++++++++++ src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 2 +- .../share/gc/g1/g1FullGCCompactTask.cpp | 2 +- src/hotspot/share/gc/g1/g1HeapVerifier.cpp | 1 + src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp | 1 + src/hotspot/share/gc/g1/g1Policy.cpp | 12 ++ src/hotspot/share/gc/g1/g1Policy.hpp | 11 +- src/hotspot/share/gc/g1/g1RemSet.cpp | 1 + src/hotspot/share/gc/g1/g1RootClosures.cpp | 1 + src/hotspot/share/gc/g1/g1RootProcessor.cpp | 3 +- src/hotspot/share/gc/g1/g1Trace.cpp | 1 + src/hotspot/share/gc/g1/g1VMOperations.cpp | 1 + src/hotspot/share/gc/g1/g1YoungCollector.cpp | 2 +- .../g1/g1YoungGCAllocationFailureInjector.cpp | 1 + .../gc/g1/g1YoungGCPostEvacuateTasks.cpp | 2 +- src/hotspot/share/prims/whitebox.cpp | 1 + 21 files changed, 190 insertions(+), 95 deletions(-) create mode 100644 src/hotspot/share/gc/g1/g1CollectorState.inline.hpp diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index be9ecf19123..e6dd91df84b 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -34,7 +34,7 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectionSetCandidates.hpp" -#include "gc/g1/g1CollectorState.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1ConcurrentRefineThread.hpp" diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index 90e87607b87..bad9ac18eec 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -28,7 +28,6 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1BarrierSet.hpp" -#include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentMark.inline.hpp" #include "gc/g1/g1EvacFailureRegions.hpp" #include "gc/g1/g1EvacStats.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index 978925d88cb..b3bcf6094ab 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -26,7 +26,7 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.inline.hpp" #include "gc/g1/g1CollectionSetCandidates.inline.hpp" -#include "gc/g1/g1CollectorState.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" #include "gc/g1/g1HeapRegionSet.hpp" diff --git a/src/hotspot/share/gc/g1/g1CollectorState.cpp b/src/hotspot/share/gc/g1/g1CollectorState.cpp index 2b550f36904..76de9c65cc8 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.cpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.cpp @@ -22,42 +22,32 @@ * */ -#include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectorState.hpp" -#include "gc/g1/g1ConcurrentMarkThread.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "runtime/safepoint.hpp" +#include "utilities/debug.hpp" G1CollectorState::Pause G1CollectorState::gc_pause_type(bool concurrent_operation_is_full_mark) const { assert(SafepointSynchronize::is_at_safepoint(), "must be"); switch (_phase) { case Phase::YoungNormal: return Pause::Normal; - case Phase::YoungPrepareMixed: return Pause::PrepareMixed; case Phase::YoungConcurrentStart: return concurrent_operation_is_full_mark ? Pause::ConcurrentStartFull : Pause::ConcurrentStartUndo; + case Phase::YoungPrepareMixed: return Pause::PrepareMixed; case Phase::Mixed: return Pause::Mixed; case Phase::FullGC: return Pause::Full; default: ShouldNotReachHere(); } } -bool G1CollectorState::is_in_concurrent_cycle() const { - G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return cm->is_in_concurrent_cycle(); +const char* G1CollectorState::to_string(Pause type) { + static const char* pause_strings[] = { "Normal", + "Concurrent Start", // Do not distinguish between the different + "Concurrent Start", // Concurrent Start pauses. + "Prepare Mixed", + "Cleanup", + "Remark", + "Mixed", + "Full" }; + return pause_strings[static_cast(type)]; } - -bool G1CollectorState::is_in_marking() const { - G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return cm->is_in_marking(); -} - -bool G1CollectorState::is_in_mark_or_rebuild() const { - G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return is_in_marking() || cm->is_in_rebuild_or_scrub(); -} - -bool G1CollectorState::is_in_reset_for_next_cycle() const { - G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); - return cm->is_in_reset_for_next_cycle(); -} - diff --git a/src/hotspot/share/gc/g1/g1CollectorState.hpp b/src/hotspot/share/gc/g1/g1CollectorState.hpp index 64c848959ae..42aaeab03b2 100644 --- a/src/hotspot/share/gc/g1/g1CollectorState.hpp +++ b/src/hotspot/share/gc/g1/g1CollectorState.hpp @@ -67,26 +67,25 @@ public: _initiate_conc_mark_if_possible(false) { } // Phase setters - void set_in_normal_young_gc() { _phase = Phase::YoungNormal; } - void set_in_space_reclamation_phase() { _phase = Phase::Mixed; } - void set_in_full_gc() { _phase = Phase::FullGC; } + inline void set_in_normal_young_gc(); + inline void set_in_space_reclamation_phase(); + inline void set_in_full_gc(); - // Pause setters - void set_in_concurrent_start_gc() { _phase = Phase::YoungConcurrentStart; _initiate_conc_mark_if_possible = false; } - void set_in_prepare_mixed_gc() { _phase = Phase::YoungPrepareMixed; } + inline void set_in_concurrent_start_gc(); + inline void set_in_prepare_mixed_gc(); - void set_initiate_conc_mark_if_possible(bool v) { _initiate_conc_mark_if_possible = v; } + inline void set_initiate_conc_mark_if_possible(bool v); // Phase getters - bool is_in_young_only_phase() const { return _phase == Phase::YoungNormal || _phase == Phase::YoungConcurrentStart || _phase == Phase::YoungPrepareMixed; } - bool is_in_mixed_phase() const { return _phase == Phase::Mixed; } + inline bool is_in_young_only_phase() const; + inline bool is_in_mixed_phase() const; // Specific pauses - bool is_in_concurrent_start_gc() const { return _phase == Phase::YoungConcurrentStart; } - bool is_in_prepare_mixed_gc() const { return _phase == Phase::YoungPrepareMixed; } - bool is_in_full_gc() const { return _phase == Phase::FullGC; } + inline bool is_in_concurrent_start_gc() const; + inline bool is_in_prepare_mixed_gc() const; + inline bool is_in_full_gc() const; - bool initiate_conc_mark_if_possible() const { return _initiate_conc_mark_if_possible; } + inline bool initiate_conc_mark_if_possible() const; bool is_in_concurrent_cycle() const; bool is_in_marking() const; @@ -107,50 +106,17 @@ public: // Calculate GC Pause Type from internal state. Pause gc_pause_type(bool concurrent_operation_is_full_mark) const; - static const char* to_string(Pause type) { - static const char* pause_strings[] = { "Normal", - "Concurrent Start", // Do not distinguish between the different - "Concurrent Start", // Concurrent Start pauses. - "Prepare Mixed", - "Cleanup", - "Remark", - "Mixed", - "Full" }; - return pause_strings[static_cast(type)]; - } + static const char* to_string(Pause type); - static void assert_is_young_pause(Pause type) { - assert(type != Pause::Full, "must be"); - assert(type != Pause::Remark, "must be"); - assert(type != Pause::Cleanup, "must be"); - } + // Pause kind queries + inline static void assert_is_young_pause(Pause type); - static bool is_young_only_pause(Pause type) { - assert_is_young_pause(type); - return type == Pause::ConcurrentStartUndo || - type == Pause::ConcurrentStartFull || - type == Pause::PrepareMixed || - type == Pause::Normal; - } + inline static bool is_young_only_pause(Pause type); + inline static bool is_concurrent_start_pause(Pause type); + inline static bool is_prepare_mixed_pause(Pause type); + inline static bool is_mixed_pause(Pause type); - static bool is_mixed_pause(Pause type) { - assert_is_young_pause(type); - return type == Pause::Mixed; - } - - static bool is_prepare_mixed_pause(Pause type) { - assert_is_young_pause(type); - return type == Pause::PrepareMixed; - } - - static bool is_concurrent_start_pause(Pause type) { - assert_is_young_pause(type); - return type == Pause::ConcurrentStartFull || type == Pause::ConcurrentStartUndo; - } - - static bool is_concurrent_cycle_pause(Pause type) { - return type == Pause::Cleanup || type == Pause::Remark; - } + inline static bool is_concurrent_cycle_pause(Pause type); }; ENUMERATOR_RANGE(G1CollectorState::Pause, G1CollectorState::Pause::Normal, G1CollectorState::Pause::Full) diff --git a/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp b/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp new file mode 100644 index 00000000000..0c6c9c879c3 --- /dev/null +++ b/src/hotspot/share/gc/g1/g1CollectorState.inline.hpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_G1_G1COLLECTORSTATE_INLINE_HPP +#define SHARE_GC_G1_G1COLLECTORSTATE_INLINE_HPP + +#include "gc/g1/g1CollectorState.hpp" + +#include "gc/g1/g1CollectedHeap.hpp" +#include "gc/g1/g1ConcurrentMark.inline.hpp" + +inline void G1CollectorState::set_in_normal_young_gc() { + _phase = Phase::YoungNormal; +} +inline void G1CollectorState::set_in_space_reclamation_phase() { + _phase = Phase::Mixed; +} +inline void G1CollectorState::set_in_full_gc() { + _phase = Phase::FullGC; +} + +inline void G1CollectorState::set_in_concurrent_start_gc() { + _phase = Phase::YoungConcurrentStart; + _initiate_conc_mark_if_possible = false; +} +inline void G1CollectorState::set_in_prepare_mixed_gc() { + _phase = Phase::YoungPrepareMixed; +} + +inline void G1CollectorState::set_initiate_conc_mark_if_possible(bool v) { + _initiate_conc_mark_if_possible = v; +} + +inline bool G1CollectorState::is_in_young_only_phase() const { + return _phase == Phase::YoungNormal || + _phase == Phase::YoungConcurrentStart || + _phase == Phase::YoungPrepareMixed; +} +inline bool G1CollectorState::is_in_mixed_phase() const { + return _phase == Phase::Mixed; +} + +inline bool G1CollectorState::is_in_prepare_mixed_gc() const { + return _phase == Phase::YoungPrepareMixed; +} +inline bool G1CollectorState::is_in_full_gc() const { + return _phase == Phase::FullGC; +} +inline bool G1CollectorState::is_in_concurrent_start_gc() const { + return _phase == Phase::YoungConcurrentStart; +} + +inline bool G1CollectorState::initiate_conc_mark_if_possible() const { + return _initiate_conc_mark_if_possible; +} + +inline bool G1CollectorState::is_in_concurrent_cycle() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return cm->is_in_concurrent_cycle(); +} +inline bool G1CollectorState::is_in_marking() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return cm->is_in_marking(); +} +inline bool G1CollectorState::is_in_mark_or_rebuild() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return is_in_marking() || cm->is_in_rebuild_or_scrub(); +} +inline bool G1CollectorState::is_in_reset_for_next_cycle() const { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + return cm->is_in_reset_for_next_cycle(); +} + +inline void G1CollectorState::assert_is_young_pause(Pause type) { + assert(type != Pause::Full, "must be"); + assert(type != Pause::Remark, "must be"); + assert(type != Pause::Cleanup, "must be"); +} + +inline bool G1CollectorState::is_young_only_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::ConcurrentStartUndo || + type == Pause::ConcurrentStartFull || + type == Pause::PrepareMixed || + type == Pause::Normal; +} + +inline bool G1CollectorState::is_mixed_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::Mixed; +} + +inline bool G1CollectorState::is_prepare_mixed_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::PrepareMixed; +} + +inline bool G1CollectorState::is_concurrent_start_pause(Pause type) { + assert_is_young_pause(type); + return type == Pause::ConcurrentStartFull || type == Pause::ConcurrentStartUndo; +} + +inline bool G1CollectorState::is_concurrent_cycle_pause(Pause type) { + return type == Pause::Cleanup || type == Pause::Remark; +} + +#endif // SHARE_GC_G1_G1COLLECTORSTATE_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 8fd355615d0..1caa8dbdd06 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -30,7 +30,7 @@ #include "gc/g1/g1CardSetMemory.hpp" #include "gc/g1/g1CardTableClaimTable.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectorState.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMark.inline.hpp" #include "gc/g1/g1ConcurrentMarkRemarkTasks.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp index 5dbf70f36b3..93d8da0d842 100644 --- a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp @@ -22,7 +22,7 @@ * */ -#include "gc/g1/g1CollectedHeap.hpp" +#include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp" #include "gc/g1/g1FullCollector.inline.hpp" #include "gc/g1/g1FullGCCompactionPoint.hpp" diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index dd7a8aa117d..714a2473a08 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -25,6 +25,7 @@ #include "code/nmethod.hpp" #include "gc/g1/g1Allocator.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMarkThread.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionRemSet.hpp" diff --git a/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp b/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp index ee11bbd961f..b5ff4272764 100644 --- a/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp +++ b/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp @@ -23,6 +23,7 @@ */ #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMark.inline.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1GCCounters.hpp" diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 05caff1257a..0145f1e6e1d 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -28,6 +28,7 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1CollectionSetCandidates.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" @@ -1133,6 +1134,17 @@ double G1Policy::predict_eden_copy_time_ms(uint count, size_t* bytes_to_copy) co return _analytics->predict_object_copy_time_ms(expected_bytes, collector_state()->is_in_young_only_phase()); } +bool G1Policy::should_update_surv_rate_group_predictors() { + return collector_state()->is_in_young_only_phase() && !collector_state()->is_in_mark_or_rebuild(); +} + +void G1Policy::cset_regions_freed() { + bool update = should_update_surv_rate_group_predictors(); + + _eden_surv_rate_group->all_surviving_words_recorded(predictor(), update); + _survivor_surv_rate_group->all_surviving_words_recorded(predictor(), update); +} + double G1Policy::predict_region_copy_time_ms(G1HeapRegion* hr, bool for_young_only_phase) const { size_t const bytes_to_copy = predict_bytes_to_copy(hr); return _analytics->predict_object_copy_time_ms(bytes_to_copy, for_young_only_phase); diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 585e26441d5..bcc3bceda49 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -114,9 +114,7 @@ class G1Policy: public CHeapObj { G1ConcurrentStartToMixedTimeTracker _concurrent_start_to_mixed; - bool should_update_surv_rate_group_predictors() { - return collector_state()->is_in_young_only_phase() && !collector_state()->is_in_mark_or_rebuild(); - } + bool should_update_surv_rate_group_predictors(); double pending_cards_processing_time() const; public: @@ -160,12 +158,7 @@ public: // bytes_to_copy is non-null. double predict_eden_copy_time_ms(uint count, size_t* bytes_to_copy = nullptr) const; - void cset_regions_freed() { - bool update = should_update_surv_rate_group_predictors(); - - _eden_surv_rate_group->all_surviving_words_recorded(predictor(), update); - _survivor_surv_rate_group->all_surviving_words_recorded(predictor(), update); - } + void cset_regions_freed(); G1MMUTracker* mmu_tracker() { return _mmu_tracker; diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index 4b4a8a68c30..9f9f0ecdf3a 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -31,6 +31,7 @@ #include "gc/g1/g1CardTableEntryClosure.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1ConcurrentRefineSweepTask.hpp" #include "gc/g1/g1FromCardCache.hpp" diff --git a/src/hotspot/share/gc/g1/g1RootClosures.cpp b/src/hotspot/share/gc/g1/g1RootClosures.cpp index 2d5150c27aa..16c47cddea1 100644 --- a/src/hotspot/share/gc/g1/g1RootClosures.cpp +++ b/src/hotspot/share/gc/g1/g1RootClosures.cpp @@ -22,6 +22,7 @@ * */ +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1OopClosures.inline.hpp" #include "gc/g1/g1RootClosures.hpp" #include "gc/g1/g1SharedClosures.hpp" diff --git a/src/hotspot/share/gc/g1/g1RootProcessor.cpp b/src/hotspot/share/gc/g1/g1RootProcessor.cpp index dac237cb277..a534eefb428 100644 --- a/src/hotspot/share/gc/g1/g1RootProcessor.cpp +++ b/src/hotspot/share/gc/g1/g1RootProcessor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ #include "code/codeCache.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1GCParPhaseTimesTracker.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1Trace.cpp b/src/hotspot/share/gc/g1/g1Trace.cpp index 242a97ca4e3..d6eadda5d50 100644 --- a/src/hotspot/share/gc/g1/g1Trace.cpp +++ b/src/hotspot/share/gc/g1/g1Trace.cpp @@ -22,6 +22,7 @@ * */ +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1EvacInfo.hpp" #include "gc/g1/g1HeapRegionTraceType.hpp" #include "gc/g1/g1Trace.hpp" diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index f98f0b078f3..b0c6b680b78 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -23,6 +23,7 @@ */ #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1Policy.hpp" #include "gc/g1/g1Trace.hpp" diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 04ccac5ff05..a4938416f25 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -31,7 +31,7 @@ #include "gc/g1/g1CardSetMemory.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSetCandidates.inline.hpp" -#include "gc/g1/g1CollectorState.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1EvacFailureRegions.inline.hpp" #include "gc/g1/g1EvacInfo.hpp" diff --git a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp index 2291a755cd3..2b33a85da29 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCAllocationFailureInjector.cpp @@ -23,6 +23,7 @@ */ #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1YoungGCAllocationFailureInjector.inline.hpp" #include "gc/shared/gc_globals.hpp" diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index 3d49b8f025b..14282383e29 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -29,7 +29,7 @@ #include "gc/g1/g1CardTableEntryClosure.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSetCandidates.inline.hpp" -#include "gc/g1/g1CollectorState.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMark.inline.hpp" #include "gc/g1/g1EvacFailureRegions.inline.hpp" #include "gc/g1/g1EvacInfo.hpp" diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index a13d0ba47c8..f50e6ddb3db 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -107,6 +107,7 @@ #if INCLUDE_G1GC #include "gc/g1/g1Arguments.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1CollectorState.inline.hpp" #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1HeapRegionManager.hpp" From 53c864a881d2183d3664a6a5a56480bd99fffe45 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 27 Mar 2026 08:18:47 +0000 Subject: [PATCH 151/160] 8380960: "Foreign function access" discussion links to wrong downcallHandle overload Reviewed-by: mcimadamore --- .../share/classes/java/lang/foreign/package-info.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/lang/foreign/package-info.java b/src/java.base/share/classes/java/lang/foreign/package-info.java index 438d42ae7d1..2070f0c70a8 100644 --- a/src/java.base/share/classes/java/lang/foreign/package-info.java +++ b/src/java.base/share/classes/java/lang/foreign/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -114,7 +114,7 @@ * and we use it to {@linkplain java.lang.foreign.SymbolLookup#findOrThrow(java.lang.String) look up} * the {@code strlen} function in the standard C library; a downcall method handle * targeting said function is subsequently - * {@linkplain java.lang.foreign.Linker#downcallHandle(FunctionDescriptor, Linker.Option...) obtained}. + * {@linkplain java.lang.foreign.Linker#downcallHandle(MemorySegment, FunctionDescriptor, Linker.Option...) obtained}. * To complete the linking successfully, we must provide a * {@link java.lang.foreign.FunctionDescriptor} instance, describing the signature of the * {@code strlen} function. From this information, the linker will uniquely determine From c0e500ad996dd7174f6f37481f2da48dc6d98f56 Mon Sep 17 00:00:00 2001 From: Anton Seoane Ampudia Date: Fri, 27 Mar 2026 09:44:37 +0000 Subject: [PATCH 152/160] 8379794: C2: UBSAN runtime error: shift exponent 64 is too large for 64-bit type 'long unsigned int' Reviewed-by: rcastanedalo, qamai, dlong --- src/hotspot/cpu/x86/x86.ad | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index d7014141234..eaa88d900c7 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -23972,8 +23972,12 @@ instruct vmask_gen_imm(kReg dst, immL len, rRegL temp) %{ format %{ "vector_mask_gen $len \t! vector mask generator" %} effect(TEMP temp); ins_encode %{ - __ mov64($temp$$Register, (0xFFFFFFFFFFFFFFFFUL >> (64 -$len$$constant))); - __ kmovql($dst$$KRegister, $temp$$Register); + if ($len$$constant > 0) { + __ mov64($temp$$Register, right_n_bits($len$$constant)); + __ kmovql($dst$$KRegister, $temp$$Register); + } else { + __ kxorql($dst$$KRegister, $dst$$KRegister, $dst$$KRegister); + } %} ins_pipe( pipe_slow ); %} From cca8c23871f1669fdd002652a20a6b7935704e30 Mon Sep 17 00:00:00 2001 From: Serhiy Sachkov Date: Fri, 27 Mar 2026 10:36:34 +0000 Subject: [PATCH 153/160] 8380999: Update IPSupport by adding diagnoseConfigurationIssue() method Reviewed-by: dfuchs --- test/lib/jdk/test/lib/net/IPSupport.java | 25 ++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/test/lib/jdk/test/lib/net/IPSupport.java b/test/lib/jdk/test/lib/net/IPSupport.java index 31255e20c6a..4a77c3a9bae 100644 --- a/test/lib/jdk/test/lib/net/IPSupport.java +++ b/test/lib/jdk/test/lib/net/IPSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ import java.net.InetAddress; import java.net.ProtocolFamily; import java.net.StandardProtocolFamily; import java.nio.channels.SocketChannel; +import java.util.Optional; import jtreg.SkippedException; @@ -124,13 +125,33 @@ public class IPSupport { * is non-operational */ public static void throwSkippedExceptionIfNonOperational() throws SkippedException { + Optional configurationIssue = diagnoseConfigurationIssue(); + configurationIssue.map(SkippedException::new).ifPresent(x -> { + throw x; + }); + } + + /** + * Checks that the platform supports the ability to create a + * minimally-operational socket whose protocol is either one of IPv4 + * or IPv6. + * + *

A minimally-operation socket is one that can be created and + * bound to an IP-specific loopback address. IP support is + * considered non-operational if a socket cannot be bound to either + * one of, an IPv4 loopback address, or the IPv6 loopback address. + * + * @return Optinal with config issue or empty Optinal if no issue found + */ + public static Optional diagnoseConfigurationIssue(){ if (!currentConfigurationIsValid()) { ByteArrayOutputStream os = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(os); ps.println("Invalid networking configuration"); printPlatformSupport(ps); - throw new SkippedException(os.toString()); + return Optional.of(os.toString()); } + return Optional.empty(); } /** From b242eef3123a936f53ea76b28b2f6350e52afa94 Mon Sep 17 00:00:00 2001 From: Ivan Walulya Date: Fri, 27 Mar 2026 11:20:05 +0000 Subject: [PATCH 154/160] 8380656: G1: Refactor G1IHOPControl Co-authored-by: Thomas Schatzl Reviewed-by: ayang, tschatzl --- .../g1ConcurrentStartToMixedTimeTracker.hpp | 8 +- src/hotspot/share/gc/g1/g1IHOPControl.cpp | 115 ++++++++++-------- src/hotspot/share/gc/g1/g1IHOPControl.hpp | 43 ++++--- src/hotspot/share/gc/g1/g1Policy.cpp | 14 +-- .../gtest/gc/g1/test_g1IHOPControl.cpp | 38 +++--- 5 files changed, 121 insertions(+), 97 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentStartToMixedTimeTracker.hpp b/src/hotspot/share/gc/g1/g1ConcurrentStartToMixedTimeTracker.hpp index 57372e695c8..f8bad4bdcd7 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentStartToMixedTimeTracker.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentStartToMixedTimeTracker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ // Used to track time from the end of concurrent start to the first mixed GC. // After calling the concurrent start/mixed gc notifications, the result can be -// obtained in last_marking_time() once, after which the tracking resets. +// obtained in get_and_reset_last_marking_time() once, after which the tracking resets. // Any pauses recorded by add_pause() will be subtracted from that results. class G1ConcurrentStartToMixedTimeTracker { private: @@ -60,7 +60,7 @@ public: } } - double last_marking_time() { + double get_and_reset_last_marking_time() { assert(has_result(), "Do not have all measurements yet."); double result = (_mixed_start_time - _concurrent_start_end_time) - _total_pause_time; reset(); @@ -80,6 +80,8 @@ public: } } + bool is_active() const { return _active; } + // Returns whether we have a result that can be retrieved. bool has_result() const { return _mixed_start_time > 0.0 && _concurrent_start_end_time > 0.0; } }; diff --git a/src/hotspot/share/gc/g1/g1IHOPControl.cpp b/src/hotspot/share/gc/g1/g1IHOPControl.cpp index 43698e9f12b..1e1c52477f9 100644 --- a/src/hotspot/share/gc/g1/g1IHOPControl.cpp +++ b/src/hotspot/share/gc/g1/g1IHOPControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,18 +38,18 @@ double G1IHOPControl::predict(const TruncatedSeq* seq) const { bool G1IHOPControl::have_enough_data_for_prediction() const { assert(_is_adaptive, "precondition"); - return ((size_t)_marking_times_s.num() >= G1AdaptiveIHOPNumInitialSamples) && - ((size_t)_allocation_rate_s.num() >= G1AdaptiveIHOPNumInitialSamples); + return ((size_t)_marking_start_to_mixed_time_s.num() >= G1AdaptiveIHOPNumInitialSamples) && + ((size_t)_old_gen_alloc_rate.num() >= G1AdaptiveIHOPNumInitialSamples); } -double G1IHOPControl::last_marking_length_s() const { - return _marking_times_s.last(); +double G1IHOPControl::last_marking_start_to_mixed_time_s() const { + return _marking_start_to_mixed_time_s.last(); } -size_t G1IHOPControl::actual_target_threshold() const { +size_t G1IHOPControl::effective_target_occupancy() const { assert(_is_adaptive, "precondition"); - // The actual target threshold takes the heap reserve and the expected waste in + // The effective target occupancy takes the heap reserve and the expected waste in // free space into account. // _heap_reserve is that part of the total heap capacity that is reserved for // eventual promotion failure. @@ -79,9 +79,9 @@ G1IHOPControl::G1IHOPControl(double ihop_percent, _last_allocation_time_s(0.0), _old_gen_alloc_tracker(old_gen_alloc_tracker), _predictor(predictor), - _marking_times_s(10, 0.05), - _allocation_rate_s(10, 0.05), - _last_unrestrained_young_size(0) { + _marking_start_to_mixed_time_s(10, 0.05), + _old_gen_alloc_rate(10, 0.05), + _expected_young_gen_at_first_mixed_gc(0) { assert(_initial_ihop_percent >= 0.0 && _initial_ihop_percent <= 100.0, "IHOP percent out of range: %.3f", ihop_percent); assert(!_is_adaptive || _predictor != nullptr, "precondition"); @@ -98,85 +98,104 @@ void G1IHOPControl::report_statistics(G1NewTracer* new_tracer, size_t non_young_ send_trace_event(new_tracer, non_young_occupancy); } -void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t additional_buffer_size) { +void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t expected_young_gen_size) { assert(allocation_time_s > 0, "Invalid allocation time: %.3f", allocation_time_s); _last_allocation_time_s = allocation_time_s; double alloc_rate = _old_gen_alloc_tracker->last_period_old_gen_growth() / allocation_time_s; - _allocation_rate_s.add(alloc_rate); - _last_unrestrained_young_size = additional_buffer_size; + _old_gen_alloc_rate.add(alloc_rate); + _expected_young_gen_at_first_mixed_gc = expected_young_gen_size; } -void G1IHOPControl::update_marking_length(double marking_length_s) { - assert(marking_length_s >= 0.0, "Invalid marking length: %.3f", marking_length_s); - _marking_times_s.add(marking_length_s); +void G1IHOPControl::add_marking_start_to_mixed_length(double length_s) { + assert(length_s >= 0.0, "Invalid marking length: %.3f", length_s); + _marking_start_to_mixed_time_s.add(length_s); } -size_t G1IHOPControl::get_conc_mark_start_threshold() { +// Determine the old generation occupancy threshold at which to start +// concurrent marking such that reclamation (first Mixed GC) begins +// before the heap reaches a critical occupancy level. +size_t G1IHOPControl::old_gen_threshold_for_conc_mark_start() { guarantee(_target_occupancy > 0, "Target occupancy must be initialized"); if (!_is_adaptive || !have_enough_data_for_prediction()) { return (size_t)(_initial_ihop_percent * _target_occupancy / 100.0); } - double pred_marking_time = predict(&_marking_times_s); - double pred_rate = predict(&_allocation_rate_s); - size_t pred_bytes = (size_t)(pred_marking_time * pred_rate); - size_t predicted_needed = pred_bytes + _last_unrestrained_young_size; - size_t internal_threshold = actual_target_threshold(); + // During the time between marking start and the first Mixed GC, + // additional memory will be consumed: + // - Old gen grows due to allocations: + // old_gen_alloc_bytes = old_gen_alloc_rate * marking_start_to_mixed_time + // - Young gen will occupy a certain size at the first Mixed GC: + // expected_young_gen_at_first_mixed_gc + double marking_start_to_mixed_time = predict(&_marking_start_to_mixed_time_s); + double old_gen_alloc_rate = predict(&_old_gen_alloc_rate); + size_t old_gen_alloc_bytes = (size_t)(marking_start_to_mixed_time * old_gen_alloc_rate); - return predicted_needed < internal_threshold - ? internal_threshold - predicted_needed + // Therefore, the total heap occupancy at the first Mixed GC is: + // current_old_gen + old_gen_growth + expected_young_gen_at_first_mixed_gc + // + // To ensure this does not exceed the target_heap_occupancy, we work + // backwards to compute the old gen occupancy at which marking must start: + // mark_start_threshold = target_heap_occupancy - + // (old_gen_growth + expected_young_gen_at_first_mixed_gc) + + size_t predicted_needed = old_gen_alloc_bytes + _expected_young_gen_at_first_mixed_gc; + size_t target_heap_occupancy = effective_target_occupancy(); + + return predicted_needed < target_heap_occupancy + ? target_heap_occupancy - predicted_needed : 0; } void G1IHOPControl::print_log(size_t non_young_occupancy) { assert(_target_occupancy > 0, "Target occupancy still not updated yet."); - size_t cur_conc_mark_start_threshold = get_conc_mark_start_threshold(); - log_debug(gc, ihop)("Basic information (value update), threshold: %zuB (%1.2f), target occupancy: %zuB, non-young occupancy: %zuB, " - "recent allocation size: %zuB, recent allocation duration: %1.2fms, recent old gen allocation rate: %1.2fB/s, recent marking phase length: %1.2fms", - cur_conc_mark_start_threshold, - percent_of(cur_conc_mark_start_threshold, _target_occupancy), + size_t old_gen_mark_start_threshold = old_gen_threshold_for_conc_mark_start(); + log_debug(gc, ihop)("Basic information (value update), old-gen threshold: %zuB (%1.2f%%), target occupancy: %zuB, old-gen occupancy: %zuB (%1.2f%%), " + "recent old-gen allocation size: %zuB, recent allocation duration: %1.2fms, recent old-gen allocation rate: %1.2fB/s, recent marking phase length: %1.2fms", + old_gen_mark_start_threshold, + percent_of(old_gen_mark_start_threshold, _target_occupancy), _target_occupancy, non_young_occupancy, + percent_of(non_young_occupancy, _target_occupancy), _old_gen_alloc_tracker->last_period_old_gen_bytes(), _last_allocation_time_s * 1000.0, _last_allocation_time_s > 0.0 ? _old_gen_alloc_tracker->last_period_old_gen_bytes() / _last_allocation_time_s : 0.0, - last_marking_length_s() * 1000.0); + last_marking_start_to_mixed_time_s() * 1000.0); if (!_is_adaptive) { return; } - size_t actual_threshold = actual_target_threshold(); - log_debug(gc, ihop)("Adaptive IHOP information (value update), threshold: %zuB (%1.2f), internal target threshold: %zuB, " - "non-young occupancy: %zuB, additional buffer size: %zuB, predicted old gen allocation rate: %1.2fB/s, " - "predicted marking phase length: %1.2fms, prediction active: %s", - cur_conc_mark_start_threshold, - percent_of(cur_conc_mark_start_threshold, actual_threshold), - actual_threshold, + size_t effective_target = effective_target_occupancy(); + log_debug(gc, ihop)("Adaptive IHOP information (value update), prediction active: %s, old-gen threshold: %zuB (%1.2f%%), internal target occupancy: %zuB, " + "old-gen occupancy: %zuB, additional buffer size: %zuB, predicted old-gen allocation rate: %1.2fB/s, " + "predicted marking phase length: %1.2fms", + BOOL_TO_STR(have_enough_data_for_prediction()), + old_gen_mark_start_threshold, + percent_of(old_gen_mark_start_threshold, effective_target), + effective_target, non_young_occupancy, - _last_unrestrained_young_size, - predict(&_allocation_rate_s), - predict(&_marking_times_s) * 1000.0, - have_enough_data_for_prediction() ? "true" : "false"); + _expected_young_gen_at_first_mixed_gc, + predict(&_old_gen_alloc_rate), + predict(&_marking_start_to_mixed_time_s) * 1000.0); } void G1IHOPControl::send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy) { assert(_target_occupancy > 0, "Target occupancy still not updated yet."); - tracer->report_basic_ihop_statistics(get_conc_mark_start_threshold(), + tracer->report_basic_ihop_statistics(old_gen_threshold_for_conc_mark_start(), _target_occupancy, non_young_occupancy, _old_gen_alloc_tracker->last_period_old_gen_bytes(), _last_allocation_time_s, - last_marking_length_s()); + last_marking_start_to_mixed_time_s()); if (_is_adaptive) { - tracer->report_adaptive_ihop_statistics(get_conc_mark_start_threshold(), - actual_target_threshold(), + tracer->report_adaptive_ihop_statistics(old_gen_threshold_for_conc_mark_start(), + effective_target_occupancy(), non_young_occupancy, - _last_unrestrained_young_size, - predict(&_allocation_rate_s), - predict(&_marking_times_s), + _expected_young_gen_at_first_mixed_gc, + predict(&_old_gen_alloc_rate), + predict(&_marking_start_to_mixed_time_s), have_enough_data_for_prediction()); } } diff --git a/src/hotspot/share/gc/g1/g1IHOPControl.hpp b/src/hotspot/share/gc/g1/g1IHOPControl.hpp index 24061c026d1..ff209012f02 100644 --- a/src/hotspot/share/gc/g1/g1IHOPControl.hpp +++ b/src/hotspot/share/gc/g1/g1IHOPControl.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,8 +58,11 @@ class G1IHOPControl : public CHeapObj { const G1OldGenAllocationTracker* _old_gen_alloc_tracker; const G1Predictions* _predictor; - TruncatedSeq _marking_times_s; - TruncatedSeq _allocation_rate_s; + // Wall-clock time in seconds from marking start to the first mixed GC, + // excluding GC Pause time. + TruncatedSeq _marking_start_to_mixed_time_s; + // Old generation allocation rate in bytes per second. + TruncatedSeq _old_gen_alloc_rate; // The most recent unrestrained size of the young gen. This is used as an additional // factor in the calculation of the threshold, as the threshold is based on @@ -68,18 +71,18 @@ class G1IHOPControl : public CHeapObj { // Since we cannot know what young gen sizes are used in the future, we will just // use the current one. We expect that this one will be one with a fairly large size, // as there is no marking or mixed gc that could impact its size too much. - size_t _last_unrestrained_young_size; + size_t _expected_young_gen_at_first_mixed_gc; // Get a new prediction bounded below by zero from the given sequence. double predict(const TruncatedSeq* seq) const; bool have_enough_data_for_prediction() const; - double last_marking_length_s() const; + double last_marking_start_to_mixed_time_s() const; - // The "actual" target threshold the algorithm wants to keep during and at the - // end of marking. This is typically lower than the requested threshold, as the + // The "effective" target occupancy the algorithm wants to keep until the start + // of Mixed GCs. This is typically lower than the target occupancy, as the // algorithm needs to consider restrictions by the environment. - size_t actual_target_threshold() const; + size_t effective_target_occupancy() const; void print_log(size_t non_young_occupancy); void send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy); @@ -95,22 +98,24 @@ class G1IHOPControl : public CHeapObj { // Adjust target occupancy. void update_target_occupancy(size_t new_target_occupancy); - // Update information about time during which allocations in the Java heap occurred, - // how large these allocations were in bytes, and an additional buffer. - // The allocations should contain any amount of space made unusable for further - // allocation, e.g. any waste caused by TLAB allocation, space at the end of - // humongous objects that can not be used for allocation, etc. - // Together with the target occupancy, this additional buffer should contain the - // difference between old gen size and total heap size at the start of reclamation, - // and space required for that reclamation. - void update_allocation_info(double allocation_time_s, size_t additional_buffer_size); + void update_target_after_marking_phase(); + + // Update allocation rate information and current expected young gen size for the + // first mixed gc needed for the predictor. Allocation rate is given as the + // separately passed in allocation increment and the time passed (mutator time) + // for the latest allocation increment here. Allocation size is the memory needed + // during the mutator before and the first mixed gc pause itself. + // Contents include young gen at that point, and the memory required for evacuating + // the collection set in that first mixed gc (including waste caused by PLAB + // allocation etc.). + void update_allocation_info(double allocation_time_s, size_t expected_young_gen_size); // Update the time spent in the mutator beginning from the end of concurrent start to // the first mixed gc. - void update_marking_length(double marking_length_s); + void add_marking_start_to_mixed_length(double length_s); // Get the current non-young occupancy at which concurrent marking should start. - size_t get_conc_mark_start_threshold(); + size_t old_gen_threshold_for_conc_mark_start(); void report_statistics(G1NewTracer* tracer, size_t non_young_occupancy); }; diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 0145f1e6e1d..24a82113d20 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -738,15 +738,15 @@ bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_wor return false; } - size_t marking_initiating_used_threshold = _ihop_control->get_conc_mark_start_threshold(); + size_t marking_initiating_old_gen_threshold = _ihop_control->old_gen_threshold_for_conc_mark_start(); size_t non_young_occupancy = _g1h->non_young_occupancy_after_allocation(allocation_word_size); bool result = false; - if (non_young_occupancy > marking_initiating_used_threshold) { + if (non_young_occupancy > marking_initiating_old_gen_threshold) { result = collector_state()->is_in_young_only_phase(); log_debug(gc, ergo, ihop)("%s non-young occupancy: %zuB allocation request: %zuB threshold: %zuB (%1.2f) source: %s", result ? "Request concurrent cycle initiation (occupancy higher than threshold)" : "Do not request concurrent cycle initiation (still doing mixed collections)", - non_young_occupancy, allocation_word_size * HeapWordSize, marking_initiating_used_threshold, (double) marking_initiating_used_threshold / _g1h->capacity() * 100, source); + non_young_occupancy, allocation_word_size * HeapWordSize, marking_initiating_old_gen_threshold, (double) marking_initiating_old_gen_threshold / _g1h->capacity() * 100, source); } return result; } @@ -972,8 +972,7 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar update_young_length_bounds(); _old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * G1HeapRegion::GrainBytes); - if (update_ihop_prediction(app_time_ms / 1000.0, - G1CollectorState::is_young_only_pause(this_pause))) { + if (update_ihop_prediction(app_time_ms / 1000.0, is_young_only_pause)) { _ihop_control->report_statistics(_g1h->gc_tracer_stw(), _g1h->non_young_occupancy_after_allocation(allocation_word_size)); } } else { @@ -1030,14 +1029,13 @@ bool G1Policy::update_ihop_prediction(double mutator_time_s, bool report = false; - double marking_to_mixed_time = -1.0; if (!this_gc_was_young_only && _concurrent_start_to_mixed.has_result()) { - marking_to_mixed_time = _concurrent_start_to_mixed.last_marking_time(); + double marking_to_mixed_time = _concurrent_start_to_mixed.get_and_reset_last_marking_time(); assert(marking_to_mixed_time > 0.0, "Concurrent start to mixed time must be larger than zero but is %.3f", marking_to_mixed_time); if (marking_to_mixed_time > min_valid_time) { - _ihop_control->update_marking_length(marking_to_mixed_time); + _ihop_control->add_marking_start_to_mixed_length(marking_to_mixed_time); report = true; } } diff --git a/test/hotspot/gtest/gc/g1/test_g1IHOPControl.cpp b/test/hotspot/gtest/gc/g1/test_g1IHOPControl.cpp index ff661fde00d..0ee0889b6e5 100644 --- a/test/hotspot/gtest/gc/g1/test_g1IHOPControl.cpp +++ b/test/hotspot/gtest/gc/g1/test_g1IHOPControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,7 +40,7 @@ static void test_update(G1IHOPControl* ctrl, test_update_allocation_tracker(alloc_tracker, alloc_amount); for (int i = 0; i < 100; i++) { ctrl->update_allocation_info(alloc_time, young_size); - ctrl->update_marking_length(mark_time); + ctrl->add_marking_start_to_mixed_length(mark_time); } } @@ -57,7 +57,7 @@ static void test_update_humongous(G1IHOPControl* ctrl, alloc_tracker->reset_after_gc(humongous_bytes_after_last_gc); for (int i = 0; i < 100; i++) { ctrl->update_allocation_info(alloc_time, young_size); - ctrl->update_marking_length(mark_time); + ctrl->add_marking_start_to_mixed_length(mark_time); } } @@ -74,26 +74,26 @@ TEST_VM(G1IHOPControl, static_simple) { G1IHOPControl ctrl(initial_ihop, &alloc_tracker, is_adaptive, nullptr, 0, 0); ctrl.update_target_occupancy(100); - size_t threshold = ctrl.get_conc_mark_start_threshold(); + size_t threshold = ctrl.old_gen_threshold_for_conc_mark_start(); EXPECT_EQ(initial_ihop, threshold); test_update_allocation_tracker(&alloc_tracker, 100); ctrl.update_allocation_info(100.0, 100); - threshold = ctrl.get_conc_mark_start_threshold(); + threshold = ctrl.old_gen_threshold_for_conc_mark_start(); EXPECT_EQ(initial_ihop, threshold); - ctrl.update_marking_length(1000.0); - threshold = ctrl.get_conc_mark_start_threshold(); + ctrl.add_marking_start_to_mixed_length(1000.0); + threshold = ctrl.old_gen_threshold_for_conc_mark_start(); EXPECT_EQ(initial_ihop, threshold); // Whatever we pass, the IHOP value must stay the same. test_update(&ctrl, &alloc_tracker, 2, 10, 10, 3); - threshold = ctrl.get_conc_mark_start_threshold(); + threshold = ctrl.old_gen_threshold_for_conc_mark_start(); EXPECT_EQ(initial_ihop, threshold); test_update(&ctrl, &alloc_tracker, 12, 10, 10, 3); - threshold = ctrl.get_conc_mark_start_threshold(); + threshold = ctrl.old_gen_threshold_for_conc_mark_start(); EXPECT_EQ(initial_ihop, threshold); } @@ -126,23 +126,23 @@ TEST_VM(G1IHOPControl, adaptive_simple) { - (young_size + alloc_amount1 / alloc_time1 * marking_time1); size_t threshold; - threshold = ctrl.get_conc_mark_start_threshold(); + threshold = ctrl.old_gen_threshold_for_conc_mark_start(); EXPECT_EQ(initial_threshold, threshold); for (size_t i = 0; i < G1AdaptiveIHOPNumInitialSamples - 1; i++) { test_update_allocation_tracker(&alloc_tracker, alloc_amount1); ctrl.update_allocation_info(alloc_time1, young_size); - ctrl.update_marking_length(marking_time1); + ctrl.add_marking_start_to_mixed_length(marking_time1); // Not enough data yet. - threshold = ctrl.get_conc_mark_start_threshold(); + threshold = ctrl.old_gen_threshold_for_conc_mark_start(); ASSERT_EQ(initial_threshold, threshold) << "on step " << i; } test_update(&ctrl, &alloc_tracker, alloc_time1, alloc_amount1, young_size, marking_time1); - threshold = ctrl.get_conc_mark_start_threshold(); + threshold = ctrl.old_gen_threshold_for_conc_mark_start(); EXPECT_EQ(settled_ihop1, threshold); @@ -155,7 +155,7 @@ TEST_VM(G1IHOPControl, adaptive_simple) { test_update(&ctrl, &alloc_tracker, alloc_time2, alloc_amount2, young_size, marking_time2); - threshold = ctrl.get_conc_mark_start_threshold(); + threshold = ctrl.old_gen_threshold_for_conc_mark_start(); EXPECT_LT(threshold, settled_ihop1); @@ -166,14 +166,14 @@ TEST_VM(G1IHOPControl, adaptive_simple) { const size_t settled_ihop3 = 0; test_update(&ctrl, &alloc_tracker, alloc_time3, alloc_amount3, young_size, marking_time3); - threshold = ctrl.get_conc_mark_start_threshold(); + threshold = ctrl.old_gen_threshold_for_conc_mark_start(); EXPECT_EQ(settled_ihop3, threshold); // And back to some arbitrary value. test_update(&ctrl, &alloc_tracker, alloc_time2, alloc_amount2, young_size, marking_time2); - threshold = ctrl.get_conc_mark_start_threshold(); + threshold = ctrl.old_gen_threshold_for_conc_mark_start(); EXPECT_GT(threshold, settled_ihop3); } @@ -205,7 +205,7 @@ TEST_VM(G1IHOPControl, adaptive_humongous) { humongous_bytes_after_last_gc, young_size, marking_time); // Test threshold size_t threshold; - threshold = ctrl.get_conc_mark_start_threshold(); + threshold = ctrl.old_gen_threshold_for_conc_mark_start(); // Adjusted allocated bytes: // Total bytes: humongous_bytes // Freed hum bytes: humongous_bytes - humongous_bytes_after_last_gc @@ -219,7 +219,7 @@ TEST_VM(G1IHOPControl, adaptive_humongous) { ctrl2.update_target_occupancy(target_size); test_update_humongous(&ctrl2, &alloc_tracker, duration, old_bytes, humongous_bytes, humongous_bytes_after_gc, young_size, marking_time); - threshold = ctrl2.get_conc_mark_start_threshold(); + threshold = ctrl2.old_gen_threshold_for_conc_mark_start(); // Adjusted allocated bytes: // Total bytes: old_bytes + humongous_bytes // Freed hum bytes: humongous_bytes - (humongous_bytes_after_gc - humongous_bytes_after_last_gc) @@ -235,7 +235,7 @@ TEST_VM(G1IHOPControl, adaptive_humongous) { ctrl3.update_target_occupancy(target_size); test_update_humongous(&ctrl3, &alloc_tracker, duration, old_bytes, humongous_bytes, humongous_bytes_after_gc, young_size, marking_time); - threshold = ctrl3.get_conc_mark_start_threshold(); + threshold = ctrl3.old_gen_threshold_for_conc_mark_start(); // Adjusted allocated bytes: // All humongous are cleaned up since humongous_bytes_after_gc < humongous_bytes_after_last_gc // Total bytes: old_bytes + humongous_bytes From 299452402551d5387eb41ad799ce6a05c05237b9 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Fri, 27 Mar 2026 13:26:49 +0000 Subject: [PATCH 155/160] 8380956: HexFormat shoud have @ValueBased Reviewed-by: rriggs, stuefe, liach --- src/java.base/share/classes/java/util/HexFormat.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/util/HexFormat.java b/src/java.base/share/classes/java/util/HexFormat.java index aebb8b9af52..7d9fe08108d 100644 --- a/src/java.base/share/classes/java/util/HexFormat.java +++ b/src/java.base/share/classes/java/util/HexFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2024, Alibaba Group Holding Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,6 +26,7 @@ package java.util; +import jdk.internal.ValueBased; import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.util.HexDigits; @@ -134,7 +135,7 @@ import java.nio.CharBuffer; * @since 17 */ - +@ValueBased public final class HexFormat { // Access to create strings from a byte array. From 426547d2c8df1d6b9b494eb28d0fbbdfe58c8821 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 27 Mar 2026 14:56:54 +0000 Subject: [PATCH 156/160] 8380968: classfile package doc mentions nonexistent UtfEntry Reviewed-by: liach --- .../classes/java/lang/classfile/package-info.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/lang/classfile/package-info.java b/src/java.base/share/classes/java/lang/classfile/package-info.java index 460f6699e7b..8bf5559df0a 100644 --- a/src/java.base/share/classes/java/lang/classfile/package-info.java +++ b/src/java.base/share/classes/java/lang/classfile/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -493,9 +493,9 @@ * * {@snippet lang="text" : * ClassElement = - * FieldModel*(UtfEntry name, Utf8Entry descriptor) - * | MethodModel*(UtfEntry name, Utf8Entry descriptor) - * | ModuleAttribute?(int flags, ModuleEntry moduleName, UtfEntry moduleVersion, + * FieldModel*(Utf8Entry name, Utf8Entry descriptor) + * | MethodModel*(Utf8Entry name, Utf8Entry descriptor) + * | ModuleAttribute?(int flags, ModuleEntry moduleName, Utf8Entry moduleVersion, * List requires, List opens, * List exports, List provides, * List uses) @@ -588,7 +588,7 @@ * | LabelTarget(Label label) * | LineNumber(int line) * | ExceptionCatch(Label tryStart, Label tryEnd, Label handler, ClassEntry exception) - * | LocalVariable(int slot, UtfEntry name, Utf8Entry type, Label startScope, Label endScope) + * | LocalVariable(int slot, Utf8Entry name, Utf8Entry type, Label startScope, Label endScope) * | LocalVariableType(int slot, Utf8Entry name, Utf8Entry type, Label startScope, Label endScope) * | CharacterRange(int rangeStart, int rangeEnd, int flags, Label startScope, Label endScope) * } From 1ed1bb8ccbc7ba0cbb54f4354c64d39b3ea64d90 Mon Sep 17 00:00:00 2001 From: Daisuke Yamazaki Date: Fri, 27 Mar 2026 15:52:13 +0000 Subject: [PATCH 157/160] 8379818: Refactor java/nio/file/Files/StreamLinesTest.java to use JUnit Reviewed-by: bpb --- .../java/nio/file/Files/StreamLinesTest.java | 63 ++++++++----------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/test/jdk/java/nio/file/Files/StreamLinesTest.java b/test/jdk/java/nio/file/Files/StreamLinesTest.java index 402b114ae0b..7812a4ceb89 100644 --- a/test/jdk/java/nio/file/Files/StreamLinesTest.java +++ b/test/jdk/java/nio/file/Files/StreamLinesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,18 +23,15 @@ /* @test * @bug 8072773 - * @library /test/lib /lib/testlibrary/bootlib - * @build java.base/java.util.stream.OpTestCase - * jdk.test.lib.RandomFactory - * @run testng/othervm StreamLinesTest + * @library /test/lib + * @build jdk.test.lib.RandomFactory + * @run junit/othervm StreamLinesTest * @summary Tests streams returned from Files.lines, primarily focused on * testing the file-channel-based stream stream with supported * character sets * @key randomness */ -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -50,13 +47,16 @@ import java.util.EnumSet; import java.util.List; import java.util.Random; import java.util.function.IntFunction; -import java.util.function.Supplier; -import java.util.stream.OpTestCase; import java.util.stream.Stream; -import java.util.stream.TestData; import jdk.test.lib.RandomFactory; -public class StreamLinesTest extends OpTestCase { +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class StreamLinesTest { enum LineSeparator { NONE(""), @@ -120,16 +120,15 @@ public class StreamLinesTest extends OpTestCase { } } - static Object[] of(String description, IntFunction lineGenerator, + static Arguments of(String description, IntFunction lineGenerator, IntFunction separatorGenerator, int n, Charset cs) { - return new Object[]{description, lineGenerator, separatorGenerator, n, cs}; + return Arguments.argumentSet(description, lineGenerator, separatorGenerator, n, cs); } private static final Random random = RandomFactory.getRandom(); - @DataProvider - public static Object[][] lines() { - List l = new ArrayList<>(); + static Stream lines() { + List l = new ArrayList<>(); // Include the three supported optimal-line charsets and one // which does not @@ -175,38 +174,26 @@ public class StreamLinesTest extends OpTestCase { 1024, charset)); } - return l.toArray(new Object[][]{}); + return l.stream(); } - @Test(dataProvider = "lines") - public void test(String description, - IntFunction lineGenerator, IntFunction separatorGenerator, + @ParameterizedTest + @MethodSource("lines") + public void test(IntFunction lineGenerator, IntFunction separatorGenerator, int lines, Charset cs) throws IOException { Path p = generateTempFileWithLines(lineGenerator, separatorGenerator, lines, cs, false); - Supplier> ss = () -> { - try { - return Files.lines(p, cs); - } - catch (IOException e) { - throw new RuntimeException(e); - } - }; - // Test without a separator at the end List expected = readAllLines(p, cs); - withData(TestData.Factory.ofSupplier("Lines with no separator at end", ss)) - .stream(s -> s) - .expectedResult(expected) - .exercise(); + try (Stream s = Files.lines(p, cs)) { + assertEquals(expected, s.toList()); + } // Test with a separator at the end writeLineSeparator(p, separatorGenerator, lines, cs); expected = readAllLines(p, cs); - withData(TestData.Factory.ofSupplier("Lines with separator at end", ss)) - .stream(s -> s) - .expectedResult(expected) - .exercise(); + try (Stream s = Files.lines(p, cs)) { + assertEquals(expected, s.toList()); + } } - } From ba700f105a1b65db8e905faa146884407ee13257 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Fri, 27 Mar 2026 16:16:09 +0000 Subject: [PATCH 158/160] 8381059: Add class name to deoptimization events and -Xlog:deoptimization=debug output Reviewed-by: dlong, aseoane --- src/hotspot/share/runtime/deoptimization.cpp | 53 ++++++++++++++------ 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 5fbe2842a2b..cfdcf52a5eb 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -2021,7 +2021,7 @@ static void post_deoptimization_event(nmethod* nm, #endif // INCLUDE_JFR static void log_deopt(nmethod* nm, Method* tm, intptr_t pc, frame& fr, int trap_bci, - const char* reason_name, const char* reason_action) { + const char* reason_name, const char* reason_action, const char* class_name) { LogTarget(Debug, deoptimization) lt; if (lt.is_enabled()) { LogStream ls(lt); @@ -2035,6 +2035,9 @@ static void log_deopt(nmethod* nm, Method* tm, intptr_t pc, frame& fr, int trap_ } ls.print("%s ", reason_name); ls.print("%s ", reason_action); + if (class_name != nullptr) { + ls.print("%s ", class_name); + } ls.print_cr("pc=" INTPTR_FORMAT " relative_pc=" INTPTR_FORMAT, pc, fr.pc() - nm->code_begin()); } @@ -2135,6 +2138,17 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr MethodData* trap_mdo = get_method_data(current, profiled_method, create_if_missing); + Symbol* class_name = nullptr; + bool unresolved = false; + if (unloaded_class_index >= 0) { + constantPoolHandle constants (current, trap_method->constants()); + if (constants->tag_at(unloaded_class_index).is_unresolved_klass()) { + class_name = constants->klass_name_at(unloaded_class_index); + unresolved = true; + } else if (constants->tag_at(unloaded_class_index).is_symbol()) { + class_name = constants->symbol_at(unloaded_class_index); + } + } { // Log Deoptimization event for JFR, UL and event system Method* tm = trap_method(); const char* reason_name = trap_reason_name(reason); @@ -2142,10 +2156,24 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr intptr_t pc = p2i(fr.pc()); JFR_ONLY(post_deoptimization_event(nm, tm, trap_bci, trap_bc, reason, action);) - log_deopt(nm, tm, pc, fr, trap_bci, reason_name, reason_action); - Events::log_deopt_message(current, "Uncommon trap: reason=%s action=%s pc=" INTPTR_FORMAT " method=%s @ %d %s", + + ResourceMark rm; + + const char* class_name_str = nullptr; + const char* class_name_msg = nullptr; + stringStream st, stm; + if (class_name != nullptr) { + class_name->print_symbol_on(&st); + class_name_str = st.freeze(); + stm.print("class=%s ", class_name_str); + class_name_msg = stm.freeze(); + } else { + class_name_msg = ""; + } + log_deopt(nm, tm, pc, fr, trap_bci, reason_name, reason_action, class_name_str); + Events::log_deopt_message(current, "Uncommon trap: reason=%s action=%s pc=" INTPTR_FORMAT " method=%s @ %d %s%s", reason_name, reason_action, pc, - tm->name_and_sig_as_C_string(), trap_bci, nm->compiler_name()); + tm->name_and_sig_as_C_string(), trap_bci, class_name_msg, nm->compiler_name()); } // Print a bunch of diagnostics, if requested. @@ -2173,20 +2201,13 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr #endif nm->log_identity(xtty); } - Symbol* class_name = nullptr; - bool unresolved = false; - if (unloaded_class_index >= 0) { - constantPoolHandle constants (current, trap_method->constants()); - if (constants->tag_at(unloaded_class_index).is_unresolved_klass()) { - class_name = constants->klass_name_at(unloaded_class_index); - unresolved = true; - if (xtty != nullptr) + if (class_name != nullptr) { + if (xtty != nullptr) { + if (unresolved) { xtty->print(" unresolved='1'"); - } else if (constants->tag_at(unloaded_class_index).is_symbol()) { - class_name = constants->symbol_at(unloaded_class_index); - } - if (xtty != nullptr) + } xtty->name(class_name); + } } if (xtty != nullptr && trap_mdo != nullptr && (int)reason < (int)MethodData::_trap_hist_limit) { // Dump the relevant MDO state. From a4d160e64614fa2f61a57b3eedb333fb665723f5 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Fri, 27 Mar 2026 16:19:22 +0000 Subject: [PATCH 159/160] 8380828: AOTCodeReader::fix_relocations() should be called before ICache::invalidate_range() is called Reviewed-by: adinn, asmehra --- src/hotspot/share/code/aotCodeCache.cpp | 65 +++++++++++++++---------- src/hotspot/share/code/aotCodeCache.hpp | 17 +++++-- src/hotspot/share/code/codeBlob.cpp | 56 +++++++++------------ src/hotspot/share/code/codeBlob.hpp | 19 ++++---- 4 files changed, 84 insertions(+), 73 deletions(-) diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 4ad5e12d808..030e2684bfc 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -539,6 +539,9 @@ AOTCodeReader::AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry) { _load_buffer = cache->cache_buffer(); _read_position = 0; _lookup_failed = false; + _name = nullptr; + _reloc_data = nullptr; + _oop_maps = nullptr; } void AOTCodeReader::set_read_position(uint pos) { @@ -903,16 +906,6 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind has_oop_maps = true; } -#ifndef PRODUCT - // Write asm remarks - if (!cache->write_asm_remarks(blob)) { - return false; - } - if (!cache->write_dbg_strings(blob)) { - return false; - } -#endif /* PRODUCT */ - if (!cache->write_relocations(blob)) { if (!cache->failed()) { // We may miss an address in AOT table - skip this code blob. @@ -921,6 +914,16 @@ bool AOTCodeCache::store_code_blob(CodeBlob& blob, AOTCodeEntry::Kind entry_kind return false; } +#ifndef PRODUCT + // Write asm remarks after relocation info + if (!cache->write_asm_remarks(blob)) { + return false; + } + if (!cache->write_dbg_strings(blob)) { + return false; + } +#endif /* PRODUCT */ + uint entry_size = cache->_write_position - entry_position; AOTCodeEntry* entry = new(cache) AOTCodeEntry(entry_kind, encode_id(entry_kind, id), entry_position, entry_size, name_offset, name_size, @@ -982,39 +985,28 @@ CodeBlob* AOTCodeReader::compile_code_blob(const char* name) { set_lookup_failed(); // Skip this blob return nullptr; } + _name = stored_name; // Read archived code blob uint offset = entry_position + _entry->blob_offset(); CodeBlob* archived_blob = (CodeBlob*)addr(offset); offset += archived_blob->size(); - address reloc_data = (address)addr(offset); + _reloc_data = (address)addr(offset); offset += archived_blob->relocation_size(); set_read_position(offset); - ImmutableOopMapSet* oop_maps = nullptr; if (_entry->has_oop_maps()) { - oop_maps = read_oop_map_set(); + _oop_maps = read_oop_map_set(); } - CodeBlob* code_blob = CodeBlob::create(archived_blob, - stored_name, - reloc_data, - oop_maps - ); + // CodeBlob::restore() calls AOTCodeReader::restore() + CodeBlob* code_blob = CodeBlob::create(archived_blob, this); + if (code_blob == nullptr) { // no space left in CodeCache return nullptr; } -#ifndef PRODUCT - code_blob->asm_remarks().init(); - read_asm_remarks(code_blob->asm_remarks()); - code_blob->dbg_strings().init(); - read_dbg_strings(code_blob->dbg_strings()); -#endif // PRODUCT - - fix_relocations(code_blob); - #ifdef ASSERT LogStreamHandle(Trace, aot, codecache, stubs) log; if (log.is_enabled()) { @@ -1025,6 +1017,25 @@ CodeBlob* AOTCodeReader::compile_code_blob(const char* name) { return code_blob; } +void AOTCodeReader::restore(CodeBlob* code_blob) { + precond(AOTCodeCache::is_on_for_use()); + precond(_name != nullptr); + precond(_reloc_data != nullptr); + + code_blob->set_name(_name); + code_blob->restore_mutable_data(_reloc_data); + code_blob->set_oop_maps(_oop_maps); + + fix_relocations(code_blob); + +#ifndef PRODUCT + code_blob->asm_remarks().init(); + read_asm_remarks(code_blob->asm_remarks()); + code_blob->dbg_strings().init(); + read_dbg_strings(code_blob->dbg_strings()); +#endif // PRODUCT +} + // ------------ process code and data -------------- // Can't use -1. It is valid value for jump to iteself destination diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 7996388faa6..b0d39ff3e08 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -402,11 +402,13 @@ private: void clear_lookup_failed() { _lookup_failed = false; } bool lookup_failed() const { return _lookup_failed; } - AOTCodeEntry* aot_code_entry() { return (AOTCodeEntry*)_entry; } -public: - AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry); + // Values used by restore(code_blob). + // They should be set before calling it. + const char* _name; + address _reloc_data; + ImmutableOopMapSet* _oop_maps; - CodeBlob* compile_code_blob(const char* name); + AOTCodeEntry* aot_code_entry() { return (AOTCodeEntry*)_entry; } ImmutableOopMapSet* read_oop_map_set(); @@ -415,6 +417,13 @@ public: void read_asm_remarks(AsmRemarks& asm_remarks); void read_dbg_strings(DbgStrings& dbg_strings); #endif // PRODUCT + +public: + AOTCodeReader(AOTCodeCache* cache, AOTCodeEntry* entry); + + CodeBlob* compile_code_blob(const char* name); + + void restore(CodeBlob* code_blob); }; // code cache internal runtime constants area used by AOT code diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index fcc0b42a461..5b68ce48c87 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -22,6 +22,7 @@ * */ +#include "code/aotCodeCache.hpp" #include "code/codeBlob.hpp" #include "code/codeCache.hpp" #include "code/relocInfo.hpp" @@ -188,22 +189,6 @@ CodeBlob::CodeBlob(const char* name, CodeBlobKind kind, int size, uint16_t heade assert(_mutable_data == blob_end(), "sanity"); } -void CodeBlob::restore_mutable_data(address reloc_data) { - // Relocation data is now stored as part of the mutable data area; allocate it before copy relocations - if (_mutable_data_size > 0) { - _mutable_data = (address)os::malloc(_mutable_data_size, mtCode); - if (_mutable_data == nullptr) { - vm_exit_out_of_memory(_mutable_data_size, OOM_MALLOC_ERROR, "codebuffer: no space for mutable data"); - } - } else { - _mutable_data = blob_end(); // default value - } - if (_relocation_size > 0) { - assert(_mutable_data_size > 0, "relocation is part of mutable data section"); - memcpy((address)relocation_begin(), reloc_data, relocation_size()); - } -} - void CodeBlob::purge() { assert(_mutable_data != nullptr, "should never be null"); if (_mutable_data != blob_end()) { @@ -240,6 +225,23 @@ void CodeBlob::print_code_on(outputStream* st) { Disassembler::decode(this, st); } +#if INCLUDE_CDS +void CodeBlob::restore_mutable_data(address reloc_data) { + // Relocation data is now stored as part of the mutable data area; allocate it before copy relocations + if (_mutable_data_size > 0) { + _mutable_data = (address)os::malloc(_mutable_data_size, mtCode); + if (_mutable_data == nullptr) { + vm_exit_out_of_memory(_mutable_data_size, OOM_MALLOC_ERROR, "codebuffer: no space for mutable data"); + } + } else { + _mutable_data = blob_end(); // default value + } + if (_relocation_size > 0) { + assert(_mutable_data_size > 0, "relocation is part of mutable data section"); + memcpy((address)relocation_begin(), reloc_data, relocation_size()); + } +} + void CodeBlob::prepare_for_archiving_impl() { set_name(nullptr); _oop_maps = nullptr; @@ -269,24 +271,15 @@ void CodeBlob::post_restore() { vptr(_kind)->post_restore(this); } -CodeBlob* CodeBlob::restore(address code_cache_buffer, - const char* name, - address archived_reloc_data, - ImmutableOopMapSet* archived_oop_maps) +CodeBlob* CodeBlob::restore(address code_cache_buffer, AOTCodeReader* reader) { copy_to(code_cache_buffer); CodeBlob* code_blob = (CodeBlob*)code_cache_buffer; - code_blob->set_name(name); - code_blob->restore_mutable_data(archived_reloc_data); - code_blob->set_oop_maps(archived_oop_maps); + reader->restore(code_blob); return code_blob; } -CodeBlob* CodeBlob::create(CodeBlob* archived_blob, - const char* name, - address archived_reloc_data, - ImmutableOopMapSet* archived_oop_maps - ) +CodeBlob* CodeBlob::create(CodeBlob* archived_blob, AOTCodeReader* reader) { ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock @@ -298,10 +291,7 @@ CodeBlob* CodeBlob::create(CodeBlob* archived_blob, MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); address code_cache_buffer = (address)CodeCache::allocate(size, CodeBlobType::NonNMethod); if (code_cache_buffer != nullptr) { - blob = archived_blob->restore(code_cache_buffer, - name, - archived_reloc_data, - archived_oop_maps); + blob = archived_blob->restore(code_cache_buffer, reader); assert(blob != nullptr, "sanity check"); // Flush the code block @@ -315,6 +305,8 @@ CodeBlob* CodeBlob::create(CodeBlob* archived_blob, return blob; } +#endif // INCLUDE_CDS + //----------------------------------------------------------------------------------------- // Creates a RuntimeBlob from a CodeBuffer and copy code and relocation info. diff --git a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp index 0469b6c71b1..1f9cf0adcc3 100644 --- a/src/hotspot/share/code/codeBlob.hpp +++ b/src/hotspot/share/code/codeBlob.hpp @@ -34,6 +34,7 @@ #include "utilities/align.hpp" #include "utilities/macros.hpp" +class AOTCodeReader; class ImmutableOopMap; class ImmutableOopMapSet; class JNIHandleBlock; @@ -107,9 +108,6 @@ class CodeBlob { friend class VMStructs; friend class JVMCIVMStructs; -private: - void restore_mutable_data(address reloc_data); - protected: // order fields from large to small to minimize padding between fields ImmutableOopMapSet* _oop_maps; // OopMap for this CodeBlob @@ -169,8 +167,8 @@ protected: void operator delete(void* p) { } - void prepare_for_archiving_impl(); - void post_restore_impl(); + void prepare_for_archiving_impl() NOT_CDS_RETURN; + void post_restore_impl() NOT_CDS_RETURN; public: @@ -304,6 +302,9 @@ public: void use_strings(DbgStrings &strings) { _dbg_strings.share(strings); } #endif +#if INCLUDE_CDS + void restore_mutable_data(address reloc_data); + void copy_to(address buffer) { memcpy(buffer, this, this->size()); } @@ -314,11 +315,9 @@ public: // methods to restore a blob from AOT code cache into the CodeCache void post_restore(); - CodeBlob* restore(address code_cache_buffer, const char* name, address archived_reloc_data, ImmutableOopMapSet* archived_oop_maps); - static CodeBlob* create(CodeBlob* archived_blob, - const char* name, - address archived_reloc_data, - ImmutableOopMapSet* archived_oop_maps); + CodeBlob* restore(address code_cache_buffer, AOTCodeReader* reader); + static CodeBlob* create(CodeBlob* archived_blob, AOTCodeReader* reader); +#endif }; //---------------------------------------------------------------------------------------------------- From 28e96d76b0c7a792cc88bb9183e9cb6a83fcaba2 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Fri, 27 Mar 2026 17:57:30 +0000 Subject: [PATCH 160/160] 8377976: GenShen: Explicit GC requests must clear concurrent gc request cancellation Reviewed-by: kdnilsen --- .../shenandoah/shenandoahGenerationalControlThread.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index ec33e671053..064f43ffd6b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -121,10 +121,11 @@ void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest& assert(request.generation != nullptr, "Must know which generation to use for degenerated cycle"); } } else { - if (request.cause == GCCause::_shenandoah_concurrent_gc) { - // This is a regulator request. It is also possible that the regulator "canceled" an old mark, - // so we can clear that here. This clear operation will only clear the cancellation if it is - // a regulator request. + if (request.cause == GCCause::_shenandoah_concurrent_gc || ShenandoahCollectorPolicy::is_explicit_gc(request.cause)) { + // This is a regulator request or an explicit gc request. Note that an explicit gc request is allowed to + // "upgrade" a regulator request. It is possible that the regulator "canceled" an old mark, so we must + // clear that cancellation here or the explicit gc cycle will erroneously detect it as a cancellation. + // This clear operation will only clear the cancellation if it was set by regulator request. _heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc); } request.generation = _requested_generation;