From 2074b975c3d08fec2ecd47dab48132be2ec7c3cf Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Wed, 7 Jan 2026 10:06:29 +0000 Subject: [PATCH 001/204] 8374623: Move DependentAlwaysFalse variable template to its own file Reviewed-by: jsjolen --- .../metaprogramming/dependentAlwaysFalse.hpp | 36 +++++++++++++++++++ src/hotspot/share/runtime/atomic.hpp | 3 +- .../share/utilities/globalDefinitions.hpp | 10 +----- src/hotspot/share/utilities/lockFreeStack.hpp | 3 +- 4 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 src/hotspot/share/metaprogramming/dependentAlwaysFalse.hpp diff --git a/src/hotspot/share/metaprogramming/dependentAlwaysFalse.hpp b/src/hotspot/share/metaprogramming/dependentAlwaysFalse.hpp new file mode 100644 index 00000000000..b1f9d89df11 --- /dev/null +++ b/src/hotspot/share/metaprogramming/dependentAlwaysFalse.hpp @@ -0,0 +1,36 @@ +/* + * 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 + * 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_METAPROGRAMMING_DEPENDENTALWAYSFALSE_HPP +#define SHARE_METAPROGRAMMING_DEPENDENTALWAYSFALSE_HPP + +// This provides a workaround for static_assert(false) in discarded or +// otherwise uninstantiated places. Instead use +// static_assert(DependentAlwaysFalse, "...") +// See http://wg21.link/p2593r1. Some, but not all, compiler versions we're +// using have implemented that change as a DR: +// https://cplusplus.github.io/CWG/issues/2518.html +template inline constexpr bool DependentAlwaysFalse = false; + +#endif // SHARE_METAPROGRAMMING_DEPENDENTALWAYSFALSE_HPP diff --git a/src/hotspot/share/runtime/atomic.hpp b/src/hotspot/share/runtime/atomic.hpp index 0c335913b5c..02e9f82cfb6 100644 --- a/src/hotspot/share/runtime/atomic.hpp +++ b/src/hotspot/share/runtime/atomic.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 @@ -26,6 +26,7 @@ #define SHARE_RUNTIME_ATOMIC_HPP #include "cppstdlib/type_traits.hpp" +#include "metaprogramming/dependentAlwaysFalse.hpp" #include "metaprogramming/primitiveConversions.hpp" #include "runtime/atomicAccess.hpp" #include "utilities/globalDefinitions.hpp" diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 3284fd3bd15..54602297759 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.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 @@ -1374,14 +1374,6 @@ template int primitive_compare(const K& k0, const K& k1) { template std::add_rvalue_reference_t declval() noexcept; -// This provides a workaround for static_assert(false) in discarded or -// otherwise uninstantiated places. Instead use -// static_assert(DependentAlwaysFalse, "...") -// See http://wg21.link/p2593r1. Some, but not all, compiler versions we're -// using have implemented that change as a DR: -// https://cplusplus.github.io/CWG/issues/2518.html -template inline constexpr bool DependentAlwaysFalse = false; - // Quickly test to make sure IEEE-754 subnormal numbers are correctly // handled. bool IEEE_subnormal_handling_OK(); diff --git a/src/hotspot/share/utilities/lockFreeStack.hpp b/src/hotspot/share/utilities/lockFreeStack.hpp index 3f63482a268..871e697f404 100644 --- a/src/hotspot/share/utilities/lockFreeStack.hpp +++ b/src/hotspot/share/utilities/lockFreeStack.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,6 +25,7 @@ #ifndef SHARE_UTILITIES_LOCKFREESTACK_HPP #define SHARE_UTILITIES_LOCKFREESTACK_HPP +#include "metaprogramming/dependentAlwaysFalse.hpp" #include "runtime/atomic.hpp" #include "runtime/atomicAccess.hpp" #include "utilities/debug.hpp" From f83918c692143802f2e94bed72dfe7121d1742f9 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Wed, 7 Jan 2026 10:43:11 +0000 Subject: [PATCH 002/204] 8369227: Virtual thread stuck in PARKED state Reviewed-by: pchilanomate --- .../classes/java/lang/VirtualThread.java | 35 +++-- .../virtual/stress/ParkAfterTimedPark.java | 120 ++++++++++++++++++ .../Thread/virtual/stress/TimedWaitALot.java | 21 +-- 3 files changed, 154 insertions(+), 22 deletions(-) create mode 100644 test/jdk/java/lang/Thread/virtual/stress/ParkAfterTimedPark.java diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index 514bf07e665..93862db9105 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.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 @@ -89,15 +89,19 @@ final class VirtualThread extends BaseVirtualThread { * * RUNNING -> PARKING // Thread parking with LockSupport.park * PARKING -> PARKED // cont.yield successful, parked indefinitely - * PARKING -> PINNED // cont.yield failed, parked indefinitely on carrier * PARKED -> UNPARKED // unparked, may be scheduled to continue - * PINNED -> RUNNING // unparked, continue execution on same carrier * UNPARKED -> RUNNING // continue execution after park * + * PARKING -> RUNNING // cont.yield failed, need to park on carrier + * RUNNING -> PINNED // park on carrier + * PINNED -> RUNNING // unparked, continue execution on same carrier + * * RUNNING -> TIMED_PARKING // Thread parking with LockSupport.parkNanos * TIMED_PARKING -> TIMED_PARKED // cont.yield successful, timed-parked - * TIMED_PARKING -> TIMED_PINNED // cont.yield failed, timed-parked on carrier * TIMED_PARKED -> UNPARKED // unparked, may be scheduled to continue + * + * TIMED_PARKING -> RUNNING // cont.yield failed, need to park on carrier + * RUNNING -> TIMED_PINNED // park on carrier * TIMED_PINNED -> RUNNING // unparked, continue execution on same carrier * * RUNNING -> BLOCKING // blocking on monitor enter @@ -108,7 +112,7 @@ final class VirtualThread extends BaseVirtualThread { * RUNNING -> WAITING // transitional state during wait on monitor * WAITING -> WAIT // waiting on monitor * WAIT -> BLOCKED // notified, waiting to be unblocked by monitor owner - * WAIT -> UNBLOCKED // timed-out/interrupted + * WAIT -> UNBLOCKED // interrupted * * RUNNING -> TIMED_WAITING // transition state during timed-waiting on monitor * TIMED_WAITING -> TIMED_WAIT // timed-waiting on monitor @@ -856,16 +860,20 @@ final class VirtualThread extends BaseVirtualThread { * Re-enables this virtual thread for scheduling. If this virtual thread is parked * then its task is scheduled to continue, otherwise its next call to {@code park} or * {@linkplain #parkNanos(long) parkNanos} is guaranteed not to block. + * @param lazySubmit to use lazySubmit if possible * @throws RejectedExecutionException if the scheduler cannot accept a task */ - @Override - void unpark() { + private void unpark(boolean lazySubmit) { if (!getAndSetParkPermit(true) && currentThread() != this) { int s = state(); // unparked while parked if ((s == PARKED || s == TIMED_PARKED) && compareAndSetState(s, UNPARKED)) { - submitRunContinuation(); + if (lazySubmit) { + lazySubmitRunContinuation(); + } else { + submitRunContinuation(); + } return; } @@ -888,6 +896,11 @@ final class VirtualThread extends BaseVirtualThread { } } + @Override + void unpark() { + unpark(false); + } + /** * Invoked by unblocker thread to unblock this virtual thread. */ @@ -904,11 +917,7 @@ final class VirtualThread extends BaseVirtualThread { */ private void parkTimeoutExpired() { assert !VirtualThread.currentThread().isVirtual(); - if (!getAndSetParkPermit(true) - && (state() == TIMED_PARKED) - && compareAndSetState(TIMED_PARKED, UNPARKED)) { - lazySubmitRunContinuation(); - } + unpark(true); } /** diff --git a/test/jdk/java/lang/Thread/virtual/stress/ParkAfterTimedPark.java b/test/jdk/java/lang/Thread/virtual/stress/ParkAfterTimedPark.java new file mode 100644 index 00000000000..1b173271a79 --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/stress/ParkAfterTimedPark.java @@ -0,0 +1,120 @@ +/* + * 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 + * 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=parked + * @bug 8369227 + * @summary Stress test untimed park after a timed park when a thread is unparked around the + * same time that the timeout expires. + * @library /test/lib + * @run main/othervm --enable-native-access=ALL-UNNAMED ParkAfterTimedPark 200 false + */ + +/* + * @test id=pinned + * @summary Stress test untimed park, while pinned, and after a timed park when a thread is + * unparked around the same time that the timeout expires. + * @library /test/lib + * @run main/othervm --enable-native-access=ALL-UNNAMED ParkAfterTimedPark 200 true + */ + +import java.time.Instant; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.LockSupport; +import jdk.test.lib.thread.VThreadPinner; + +public class ParkAfterTimedPark { + public static void main(String[] args) throws Exception { + int iterations = (args.length > 0) ? Integer.parseInt(args[0]) : 100; + boolean pinned = (args.length > 1) ? Boolean.parseBoolean(args[1]) : false; + + for (int i = 1; i <= iterations; i++) { + System.out.println(Instant.now() + " => " + i + " of " + iterations); + for (int timeout = 1; timeout <= 10; timeout++) { + test(timeout, true); + } + } + } + + /** + * Creates two virtual threads. The first does a timed-park for the given time, + * then parks in CountDownLatch.await. A second virtual thread unparks the first + * around the same time that the timeout for the first expires. + */ + private static void test(int millis, boolean pinned) throws Exception { + long nanos = TimeUnit.MILLISECONDS.toNanos(millis); + + var finish = new CountDownLatch(1); + + Thread thread1 = Thread.startVirtualThread(() -> { + LockSupport.parkNanos(nanos); + boolean done = false; + while (!done) { + try { + if (pinned) { + VThreadPinner.runPinned(() -> { + finish.await(); + }); + } else { + finish.await(); + } + done = true; + } catch (InterruptedException e) { } + } + }); + + Thread thread2 = Thread.startVirtualThread(() -> { + int delta = ThreadLocalRandom.current().nextInt(millis); + boolean done = false; + while (!done) { + try { + Thread.sleep(millis - delta); + done = true; + } catch (InterruptedException e) { } + } + LockSupport.unpark(thread1); + }); + + // wait for first thread to park before count down + await(thread1, Thread.State.WAITING); + finish.countDown(); + + thread1.join(); + thread2.join(); + } + + /** + * Waits for the given thread to reach a given state. + */ + private static void await(Thread thread, Thread.State expectedState) throws Exception { + Thread.State state = thread.getState(); + while (state != expectedState) { + if (state == Thread.State.TERMINATED) + throw new RuntimeException("Thread has terminated"); + Thread.sleep(10); + state = thread.getState(); + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/stress/TimedWaitALot.java b/test/jdk/java/lang/Thread/virtual/stress/TimedWaitALot.java index 6a81a7c5fee..704e299ad8b 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/TimedWaitALot.java +++ b/test/jdk/java/lang/Thread/virtual/stress/TimedWaitALot.java @@ -94,17 +94,20 @@ public class TimedWaitALot { // start thread to Object.notifyAll at around time that the timeout expires if (notify) { - if (ThreadLocalRandom.current().nextBoolean()) { - synchronized (lock) { + executor.submit(() -> { + if (ThreadLocalRandom.current().nextBoolean()) { + synchronized (lock) { + sleepLessThan(timeout); + lock.notifyAll(); + } + } else { sleepLessThan(timeout); - lock.notifyAll(); + synchronized (lock) { + lock.notifyAll(); + } } - } else { - sleepLessThan(timeout); - synchronized (lock) { - lock.notifyAll(); - } - } + return null; + }); } // start thread to interrupt first thread at around time that the timeout expires From 6af27420e3b1980bc093776e3db76072123f7487 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Wed, 7 Jan 2026 10:43:24 +0000 Subject: [PATCH 003/204] 8373427: StructuredTaskScope::join not clear if called with interrupted status set Reviewed-by: jpai --- .../java/util/concurrent/StructuredTaskScope.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java b/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java index 01bf1158bf6..66ba2c29bb3 100644 --- a/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.java +++ b/src/java.base/share/classes/java/util/concurrent/StructuredTaskScope.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 @@ -1081,10 +1081,9 @@ public sealed interface StructuredTaskScope * does not throw then the {@code Joiner}'s {@code result()} method is invoked to * get the result or throw. * - *

This method may only be invoked by the scope owner. Once the result or - * exception outcome is obtained, this method may not be invoked again. The only - * case where the method may be called again is where {@code InterruptedException} - * is thrown while waiting. + *

This method may only be invoked by the scope owner. It may only be invoked once + * to get the result, exception or timeout outcome, unless the previous invocation + * resulted in an {@code InterruptedException} being thrown. * * @return the result * @throws WrongThreadException if the current thread is not the scope owner @@ -1093,8 +1092,11 @@ public sealed interface StructuredTaskScope * exception from {@link Joiner#result() Joiner.result()} as the cause * @throws TimeoutException if a timeout is set, the timeout expires before or while * waiting, and {@link Joiner#onTimeout() Joiner.onTimeout()} throws this exception - * @throws InterruptedException if interrupted while waiting + * @throws InterruptedException if the current thread is interrupted before or + * while waiting. The current thread's interrupted status is cleared when this + * exception is thrown. * @since 25 + * @see Thread##thread-interruption Thread Interruption */ R join() throws InterruptedException; From d7a3df639977ac8442eec1efb41de6dc50384150 Mon Sep 17 00:00:00 2001 From: Tobias Hotz Date: Wed, 7 Jan 2026 11:48:47 +0000 Subject: [PATCH 004/204] 8374436: compiler/igvn/IntegerDivValueTests.java failed with division by zero Reviewed-by: chagedorn, thartmann --- .../jtreg/compiler/igvn/IntegerDivValueTests.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/IntegerDivValueTests.java b/test/hotspot/jtreg/compiler/igvn/IntegerDivValueTests.java index 3585d1347ac..2abeffb0c41 100644 --- a/test/hotspot/jtreg/compiler/igvn/IntegerDivValueTests.java +++ b/test/hotspot/jtreg/compiler/igvn/IntegerDivValueTests.java @@ -204,7 +204,11 @@ public class IntegerDivValueTests { @Run(test = {"testIntConstantFolding", "testIntConstantFoldingSpecialCase"}) public void checkIntConstants(RunInfo info) { - Asserts.assertEquals(INT_CONST_1 / INT_CONST_2, testIntConstantFolding()); + if (INT_CONST_2 == 0) { + Asserts.assertThrows(ArithmeticException.class, () -> testIntConstantFolding()); + } else { + Asserts.assertEquals(INT_CONST_1 / INT_CONST_2, testIntConstantFolding()); + } Asserts.assertEquals(Integer.MIN_VALUE, testIntConstantFoldingSpecialCase()); } @@ -441,7 +445,11 @@ public class IntegerDivValueTests { @Run(test = {"testLongConstantFolding", "testLongConstantFoldingSpecialCase"}) public void checkLongConstants(RunInfo infoLong) { - Asserts.assertEquals(LONG_CONST_1 / LONG_CONST_2, testLongConstantFolding()); + if (LONG_CONST_2 == 0L) { + Asserts.assertThrows(ArithmeticException.class, () -> testLongConstantFolding()); + } else { + Asserts.assertEquals(LONG_CONST_1 / LONG_CONST_2, testLongConstantFolding()); + } Asserts.assertEquals(Long.MIN_VALUE, testLongConstantFoldingSpecialCase()); } From 929864b1a40eb222d3b7b3451fc6d4e5316a7cc8 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Wed, 7 Jan 2026 11:51:28 +0000 Subject: [PATCH 005/204] 8362087: Test containers/docker/ShareTmpDir.java intermittent fails Reviewed-by: sgehwolf, cnorrbin --- .../jtreg/containers/docker/ShareTmpDir.java | 17 +++++++++++++---- .../containers/docker/WaitForFlagFile.java | 4 ++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java b/test/hotspot/jtreg/containers/docker/ShareTmpDir.java index b7f807d76a3..bbf08c0dae7 100644 --- a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java +++ b/test/hotspot/jtreg/containers/docker/ShareTmpDir.java @@ -66,17 +66,23 @@ public class ShareTmpDir { private static void test() throws Exception { File sharedtmpdir = new File("sharedtmpdir"); File flag = new File(sharedtmpdir, "flag"); - File started = new File(sharedtmpdir, "started"); + File started1 = new File(sharedtmpdir, "started-1"); + File started2 = new File(sharedtmpdir, "started-2"); sharedtmpdir.mkdir(); flag.delete(); - started.delete(); + started1.delete(); + started2.delete(); DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "WaitForFlagFile"); + Object lock = new Object(); opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); opts.addDockerOpts("--volume", sharedtmpdir.getAbsolutePath() + ":/tmp/"); opts.addJavaOpts("-Xlog:os+container=trace", "-Xlog:perf+memops=debug", "-cp", "/test-classes/"); Thread t1 = new Thread() { public void run() { + synchronized(lock) { + opts.addClassOptions("1"); + } try { out1 = Common.run(opts); } catch (Exception e) { e.printStackTrace(); } } }; @@ -84,13 +90,16 @@ public class ShareTmpDir { Thread t2 = new Thread() { public void run() { + synchronized(lock) { + opts.addClassOptions("2"); + } try { out2 = Common.run(opts); } catch (Exception e) { e.printStackTrace(); } } }; t2.start(); - while (!started.exists()) { - System.out.println("Wait for at least one JVM to start"); + while (!started1.exists() || !started2.exists()) { + System.out.println("Waiting for all two JVMs to start"); Thread.sleep(1000); } diff --git a/test/hotspot/jtreg/containers/docker/WaitForFlagFile.java b/test/hotspot/jtreg/containers/docker/WaitForFlagFile.java index 64596830a3f..ed549e33cf1 100644 --- a/test/hotspot/jtreg/containers/docker/WaitForFlagFile.java +++ b/test/hotspot/jtreg/containers/docker/WaitForFlagFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ public class WaitForFlagFile { public static void main(String[] args) throws Exception { System.out.println("WaitForFlagFile: Entering"); - File started = new File("/tmp/started"); + File started = new File("/tmp/started-" + args.length); FileOutputStream fout = new FileOutputStream(started); fout.close(); From da14813a5bdadaf0a1f81fa57ff6e1b103eaf113 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 7 Jan 2026 12:37:52 +0000 Subject: [PATCH 006/204] 8373453: C2 SuperWord: must handle load slices that have loads with different memory inputs Reviewed-by: kvn, thartmann, qamai --- src/hotspot/share/opto/vectorization.cpp | 23 ++++- src/hotspot/share/opto/vectorization.hpp | 6 +- ...oadSliceWithMultipleMemoryInputStates.java | 94 +++++++++++++++++++ 3 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/superword/TestLoadSliceWithMultipleMemoryInputStates.java diff --git a/src/hotspot/share/opto/vectorization.cpp b/src/hotspot/share/opto/vectorization.cpp index f81ee1b7ddb..1755b0453eb 100644 --- a/src/hotspot/share/opto/vectorization.cpp +++ b/src/hotspot/share/opto/vectorization.cpp @@ -187,7 +187,10 @@ VStatus VLoopAnalyzer::setup_submodules_helper() { return body_status; } - _memory_slices.find_memory_slices(); + VStatus slices_status = _memory_slices.find_memory_slices(); + if (!slices_status.is_success()) { + return slices_status; + } // If there is no memory slice detected, it means there is no store. // If there is no reduction and no store, then we give up, because @@ -207,9 +210,11 @@ VStatus VLoopAnalyzer::setup_submodules_helper() { } // There are 2 kinds of slices: -// - No memory phi: only loads. All have the same input memory state from before the loop. +// - No memory phi: only loads. +// - Usually, all loads have the same input memory state from before the loop. +// - Only rarely this is not the case, and we just bail out for now. // - With memory phi. Chain of memory operations inside the loop. -void VLoopMemorySlices::find_memory_slices() { +VStatus VLoopMemorySlices::find_memory_slices() { Compile* C = _vloop.phase()->C; // We iterate over the body, which is topologically sorted. Hence, if there is a phi // in a slice, we will find it first, and the loads and stores afterwards. @@ -228,8 +233,15 @@ void VLoopMemorySlices::find_memory_slices() { PhiNode* head = _heads.at(alias_idx); if (head == nullptr) { // We did not find a phi on this slice yet -> must be a slice with only loads. - assert(_inputs.at(alias_idx) == nullptr || _inputs.at(alias_idx) == load->in(1), - "not yet touched or the same input"); + // For now, we can only handle slices with a single memory input before the loop, + // so if we find multiple, we bail out of auto vectorization. If this becomes + // too restrictive in the fututure, we could consider tracking multiple inputs. + // Different memory inputs can for example happen if one load has its memory state + // optimized, and the other load fails to have it optimized, for example because + // it does not end up on the IGVN worklist any more. + if (_inputs.at(alias_idx) != nullptr && _inputs.at(alias_idx) != load->in(1)) { + return VStatus::make_failure(FAILURE_DIFFERENT_MEMORY_INPUT); + } _inputs.at_put(alias_idx, load->in(1)); } // else: the load belongs to a slice with a phi that already set heads and inputs. #ifdef ASSERT @@ -243,6 +255,7 @@ void VLoopMemorySlices::find_memory_slices() { } } NOT_PRODUCT( if (_vloop.is_trace_memory_slices()) { print(); } ) + return VStatus::make_success(); } #ifndef PRODUCT diff --git a/src/hotspot/share/opto/vectorization.hpp b/src/hotspot/share/opto/vectorization.hpp index 9308712f78a..b187435c04d 100644 --- a/src/hotspot/share/opto/vectorization.hpp +++ b/src/hotspot/share/opto/vectorization.hpp @@ -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. * Copyright (c) 2023, Arm Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -504,6 +504,8 @@ private: // class VLoopMemorySlices : public StackObj { private: + static constexpr char const* FAILURE_DIFFERENT_MEMORY_INPUT = "Load only slice has multiple memory inputs"; + const VLoop& _vloop; const VLoopBody& _body; @@ -521,7 +523,7 @@ public: const GrowableArray& inputs() const { return _inputs; } const GrowableArray& heads() const { return _heads; } - void find_memory_slices(); + VStatus find_memory_slices(); void get_slice_in_reverse_order(PhiNode* head, MemNode* tail, GrowableArray& slice) const; bool same_memory_slice(MemNode* m1, MemNode* m2) const; diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestLoadSliceWithMultipleMemoryInputStates.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestLoadSliceWithMultipleMemoryInputStates.java new file mode 100644 index 00000000000..7bd85d343cd --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestLoadSliceWithMultipleMemoryInputStates.java @@ -0,0 +1,94 @@ +/* + * 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 id=all-flags + * @summary Test a case where we can have one memory slice that has only loads, + * but the loads from the slice do not have all the same input memory + * state from before the loop. This is rather rare but it can happen. + * @bug 8373453 + * @run main/othervm + * -XX:CompileCommand=compileonly,${test.main.class}::test + * -Xbatch -XX:-TieredCompilation + * ${test.main.class} + */ + +/* + * @test id=fewer-flags + * @bug 8373453 + * @run main/othervm + * -XX:CompileCommand=compileonly,${test.main.class}::test + * ${test.main.class} + */ + +/* + * @test id=vanilla + * @bug 8373453 + * @run main ${test.main.class} + */ + +package compiler.loopopts.superword; + +public class TestLoadSliceWithMultipleMemoryInputStates { + static void test() { + // The relevant slice is the value field of the Byte Objects. + Byte x = 1; + + for (int i = 0; i < 2; i++) { + if ((i & 1) == 0) { + // Not sure what this loop is needed for, but it is very sensitive, + // I cannot even replace N with 32. + int N = 32; + for (int j = 0; j < N; j++) { + if (j == 1) { + x = (byte) x; + } + } + + for (int j = 0; j < 32; j++) { + // The call below has an effect on the memory state + // If we optimize the Load for Byte::value, we can bypass + // this call, since we know that Byte::value cannot be + // modified during the call. + Object o = 1; + o.toString(); + + for (int k = 0; k < 32; k++) { // OSR around here + // Loads of x byte field have different memory input states + // This is because some loads can split their memory state + // through a phi further up, and others are not put back on + // the IGVN worklist and are thus not optimized and keep + // the old memory state. Both are correct though. + x = (byte) (x + 1); + } + } + } + } + } + + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + test(); + } + } +} From 3541bc8635ad8f5f4151758de3a134c9c105cebd Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Wed, 7 Jan 2026 15:38:20 +0000 Subject: [PATCH 007/204] 8373538: Migrate all tests to null-safe "SimpleSSLContext" methods Reviewed-by: djelinski, jpai --- .../net/httpserver/ClearTextServerSSL.java | 4 +- .../SetAuthenticator/HTTPTest.java | 18 ++++---- .../SetAuthenticator/HTTPTestClient.java | 4 +- test/jdk/java/net/URLPermission/URLTest.java | 6 +-- .../DummyCacheResponse.java | 5 +-- .../net/ssl/HttpsURLConnection/Equals.java | 5 +-- .../ssl/HttpsURLConnection/HttpsSession.java | 6 +-- .../HttpsURLConnection/SubjectAltNameIP.java | 6 +-- .../net/www/protocol/http/RedirectOnPost.java | 4 +- test/jdk/sun/security/krb5/auto/HttpsCB.java | 4 +- .../testLinkOption/TestRedirectLinks.java | 7 +--- .../jdk/test/lib/net/SimpleSSLContext.java | 42 +++++-------------- 12 files changed, 38 insertions(+), 73 deletions(-) diff --git a/test/jdk/com/sun/net/httpserver/ClearTextServerSSL.java b/test/jdk/com/sun/net/httpserver/ClearTextServerSSL.java index 75e4f3fcf45..896f02b178d 100644 --- a/test/jdk/com/sun/net/httpserver/ClearTextServerSSL.java +++ b/test/jdk/com/sun/net/httpserver/ClearTextServerSSL.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/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 @@ -79,7 +79,7 @@ public class ClearTextServerSSL { @Test public void test() throws Exception { - var sslContext = new SimpleSSLContext().get(); + var sslContext = SimpleSSLContext.findSSLContext(); var handler = new TestHandler(); var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR, 0), 0); server.createContext(path(""), handler); diff --git a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTest.java b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTest.java index 2dc6963a63e..8965a942153 100644 --- a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTest.java +++ b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2019, 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 @@ -142,16 +142,12 @@ public class HTTPTest { } static { - try { - HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { - public boolean verify(String hostname, SSLSession session) { - return true; - } - }); - SSLContext.setDefault(new SimpleSSLContext().get()); - } catch (IOException ex) { - throw new ExceptionInInitializerError(ex); - } + HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { + public boolean verify(String hostname, SSLSession session) { + return true; + } + }); + SSLContext.setDefault(SimpleSSLContext.findSSLContext()); } static final Logger logger = Logger.getLogger ("com.sun.net.httpserver"); diff --git a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestClient.java b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestClient.java index 3f436ef6d02..57790da171f 100644 --- a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestClient.java +++ b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 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 @@ -112,7 +112,7 @@ public class HTTPTestClient extends HTTPTest { // anything here. Otherwise it could look like: // HttpsURLConnection httpsConn = (HttpsURLConnection)conn; // httpsConn.setSSLSocketFactory( - // new SimpleSSLContext().get().getSocketFactory()); + // SimpleSSLContext.findSSLContext().getSocketFactory()); } } diff --git a/test/jdk/java/net/URLPermission/URLTest.java b/test/jdk/java/net/URLPermission/URLTest.java index 907ba6072ec..9d1b4e840e6 100644 --- a/test/jdk/java/net/URLPermission/URLTest.java +++ b/test/jdk/java/net/URLPermission/URLTest.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 @@ -223,7 +223,7 @@ public class URLTest { static HttpsServer httpsServer; static HttpContext c, cs; static ExecutorService e, es; - static SSLContext ctx; + private static final SSLContext ctx = SimpleSSLContext.findSSLContext(); static int httpPort; static int httpsPort; static String httpAuth; @@ -243,8 +243,6 @@ public class URLTest { es = Executors.newCachedThreadPool(); httpServer.setExecutor(e); httpsServer.setExecutor(es); - - ctx = new SimpleSSLContext().get(); httpsServer.setHttpsConfigurator(new HttpsConfigurator (ctx)); httpServer.start(); diff --git a/test/jdk/javax/net/ssl/HttpsURLConnection/DummyCacheResponse.java b/test/jdk/javax/net/ssl/HttpsURLConnection/DummyCacheResponse.java index f18dca8b0f7..e0d9a3cdc01 100644 --- a/test/jdk/javax/net/ssl/HttpsURLConnection/DummyCacheResponse.java +++ b/test/jdk/javax/net/ssl/HttpsURLConnection/DummyCacheResponse.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 @@ -43,7 +43,7 @@ import jdk.test.lib.net.SimpleSSLContext; import com.sun.net.httpserver.*; public class DummyCacheResponse extends SecureCacheResponse { - static SSLContext sslContext; + private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); private final SSLSession cachedSession; private final Map> rqstHeaders; @@ -61,7 +61,6 @@ public class DummyCacheResponse extends SecureCacheResponse { executor = Executors.newCachedThreadPool(); httpsServer.setExecutor(executor); - sslContext = new SimpleSSLContext().get(); httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); httpsServer.start(); diff --git a/test/jdk/javax/net/ssl/HttpsURLConnection/Equals.java b/test/jdk/javax/net/ssl/HttpsURLConnection/Equals.java index 35913edd2bf..1e43547c59d 100644 --- a/test/jdk/javax/net/ssl/HttpsURLConnection/Equals.java +++ b/test/jdk/javax/net/ssl/HttpsURLConnection/Equals.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 @@ -49,7 +49,7 @@ public class Equals { */ private static final boolean debug = false; - static SSLContext ctx; + private static final SSLContext ctx = SimpleSSLContext.findSSLContext(); public static void main(String[] args) throws Exception { if (debug) { @@ -65,7 +65,6 @@ public class Equals { HttpContext c2 = s2.createContext("/test1", h); executor = Executors.newCachedThreadPool(); s2.setExecutor(executor); - ctx = new SimpleSSLContext().get(); s2.setHttpsConfigurator(new HttpsConfigurator(ctx)); s2.start(); int httpsport = s2.getAddress().getPort(); diff --git a/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsSession.java b/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsSession.java index e17ff6c95b2..7b6ac18c4c9 100644 --- a/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsSession.java +++ b/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsSession.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 @@ -41,7 +41,7 @@ import jdk.test.lib.net.SimpleSSLContext; public class HttpsSession { - static SSLContext sslContext; + private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); public static void main(String[] args) throws Exception { HttpsServer httpsServer = null; @@ -53,8 +53,6 @@ public class HttpsSession { executor = Executors.newCachedThreadPool(); httpsServer.setExecutor(executor); - - sslContext = new SimpleSSLContext().get(); httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); httpsServer.start(); diff --git a/test/jdk/javax/net/ssl/HttpsURLConnection/SubjectAltNameIP.java b/test/jdk/javax/net/ssl/HttpsURLConnection/SubjectAltNameIP.java index cbd2089e7bd..de747922d7b 100644 --- a/test/jdk/javax/net/ssl/HttpsURLConnection/SubjectAltNameIP.java +++ b/test/jdk/javax/net/ssl/HttpsURLConnection/SubjectAltNameIP.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,7 @@ public class SubjectAltNameIP { */ void doServerSide() throws Exception { SSLServerSocketFactory sslssf = - new SimpleSSLContext().get().getServerSocketFactory(); + SimpleSSLContext.findSSLContext().getServerSocketFactory(); SSLServerSocket sslServerSocket = (SSLServerSocket) sslssf.createServerSocket( serverPort, 0, @@ -139,7 +139,7 @@ public class SubjectAltNameIP { throw new RuntimeException("Server failed to start.", serverException); } - SSLSocketFactory sf = new SimpleSSLContext().get().getSocketFactory(); + SSLSocketFactory sf = SimpleSSLContext.findSSLContext().getSocketFactory(); URI uri = new URI("https://" + hostName + ":" + serverPort + "/index.html"); HttpsURLConnection conn = (HttpsURLConnection)uri.toURL().openConnection(); diff --git a/test/jdk/sun/net/www/protocol/http/RedirectOnPost.java b/test/jdk/sun/net/www/protocol/http/RedirectOnPost.java index 1f88c851f63..69da47651b7 100644 --- a/test/jdk/sun/net/www/protocol/http/RedirectOnPost.java +++ b/test/jdk/sun/net/www/protocol/http/RedirectOnPost.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 @@ -46,7 +46,7 @@ public class RedirectOnPost { public static void main(String[] args) throws Exception { ExecutorService e= Executors.newFixedThreadPool(5); - SSLContext ctx = new SimpleSSLContext().get(); + SSLContext ctx = SimpleSSLContext.findSSLContext(); HttpServer httpServer = getHttpServer(e); HttpsServer httpsServer = getHttpsServer(e, ctx); diff --git a/test/jdk/sun/security/krb5/auto/HttpsCB.java b/test/jdk/sun/security/krb5/auto/HttpsCB.java index 1f9f0de33ac..278e72592be 100644 --- a/test/jdk/sun/security/krb5/auto/HttpsCB.java +++ b/test/jdk/sun/security/krb5/auto/HttpsCB.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 @@ -214,7 +214,7 @@ public class HttpsCB { MyHttpHandler h = new MyHttpHandler(); HttpsServer server = HttpsServer.create(new InetSocketAddress(0), 0); server.setHttpsConfigurator( - new HttpsConfigurator(new SimpleSSLContext().get())); + new HttpsConfigurator(SimpleSSLContext.findSSLContext())); server.createContext("/", h).setAuthenticator( new MyServerAuthenticator(scheme, principal, ktab)); server.start(); diff --git a/test/langtools/jdk/javadoc/doclet/testLinkOption/TestRedirectLinks.java b/test/langtools/jdk/javadoc/doclet/testLinkOption/TestRedirectLinks.java index 2a415141229..a4fd5e3f68e 100644 --- a/test/langtools/jdk/javadoc/doclet/testLinkOption/TestRedirectLinks.java +++ b/test/langtools/jdk/javadoc/doclet/testLinkOption/TestRedirectLinks.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 @@ -221,10 +221,7 @@ public class TestRedirectLinks extends JavadocTester { out.println("Starting old server (" + oldServer.getClass().getSimpleName() + ") on " + oldURL); oldServer.start(); - SSLContext sslContext = new SimpleSSLContext().get(); - if (sslContext == null) { - throw new AssertionError("Could not create a SSLContext"); - } + SSLContext sslContext = SimpleSSLContext.findSSLContext(); newServer = HttpsServer.create(new InetSocketAddress(loopback, 0), 0); String newURL = URIBuilder.newBuilder() .scheme("https") diff --git a/test/lib/jdk/test/lib/net/SimpleSSLContext.java b/test/lib/jdk/test/lib/net/SimpleSSLContext.java index 3e0d362cc23..ae2b479c271 100644 --- a/test/lib/jdk/test/lib/net/SimpleSSLContext.java +++ b/test/lib/jdk/test/lib/net/SimpleSSLContext.java @@ -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 @@ -23,12 +23,16 @@ package jdk.test.lib.net; +import java.io.File; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; -import java.io.*; -import java.security.*; -import javax.net.ssl.*; +import java.security.KeyStore; +import java.util.Collections; +import java.util.Objects; +import java.util.StringTokenizer; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; /** * Utility for creating a simple usable {@link SSLContext} for testing purposes. @@ -39,17 +43,7 @@ public final class SimpleSSLContext { private static final String DEFAULT_KEY_STORE_FILE_REL_PATH = "jdk/test/lib/net/testkeys"; - private final SSLContext ssl; - - // Made `public` for backward compatibility - public SimpleSSLContext() throws IOException { - this.ssl = findSSLContext(DEFAULT_KEY_STORE_FILE_REL_PATH, DEFAULT_PROTOCOL); - } - - // Kept for backward compatibility - public SimpleSSLContext(String keyStoreFileRelPath) throws IOException { - this.ssl = findSSLContext(Objects.requireNonNull(keyStoreFileRelPath), DEFAULT_PROTOCOL); - } + private SimpleSSLContext() {} /** * {@return a new {@link SSLContext} instance by searching for a key store @@ -136,20 +130,4 @@ public final class SimpleSSLContext { } } - // Kept for backward compatibility - public static SSLContext getContext(String protocol) throws IOException { - try { - return protocol == null || protocol.isEmpty() - ? findSSLContext() - : findSSLContext(protocol); - } catch (RuntimeException re) { - throw new IOException(re); - } - } - - // Kept for backward compatibility - public SSLContext get() { - return ssl; - } - } From 640343f7d94894b0378ea5b1768eeac203a9aaf8 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Wed, 7 Jan 2026 17:00:57 +0000 Subject: [PATCH 008/204] 8373724: Assertion failure in TestSignumVector.java with UseAPX Reviewed-by: sviswanathan --- src/hotspot/cpu/x86/x86.ad | 159 +++++++++++++++++++------------------ 1 file changed, 80 insertions(+), 79 deletions(-) diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 5958db5d1eb..93b306c37d6 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -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. // 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,17 +2633,19 @@ bool Matcher::supports_vector_calling_convention(void) { return EnableVectorSupport; } +static bool is_ndd_demotable_opr1(const MachNode* mdef) { + return ((mdef->flags() & Node::PD::Flag_ndd_demotable_opr1) != 0); +} + +static bool is_ndd_demotable_opr2(const MachNode* mdef) { + return ((mdef->flags() & Node::PD::Flag_ndd_demotable_opr2) != 0); +} + +#ifdef ASSERT static bool is_ndd_demotable(const MachNode* mdef) { - return ((mdef->flags() & Node::PD::Flag_ndd_demotable) != 0); -} - -static bool is_ndd_demotable_commutative(const MachNode* mdef) { - return ((mdef->flags() & Node::PD::Flag_ndd_demotable_commutative) != 0); -} - -static bool is_demotion_candidate(const MachNode* mdef) { - return (is_ndd_demotable(mdef) || is_ndd_demotable_commutative(mdef)); + return (is_ndd_demotable_opr1(mdef) || is_ndd_demotable_opr2(mdef)); } +#endif bool Matcher::is_register_biasing_candidate(const MachNode* mdef, int oper_index) { @@ -2653,8 +2655,8 @@ bool Matcher::is_register_biasing_candidate(const MachNode* mdef, if (mdef->num_opnds() <= oper_index || mdef->operand_index(oper_index) < 0 || mdef->in(mdef->operand_index(oper_index)) == nullptr) { - assert(oper_index != 1 || !is_demotion_candidate(mdef), "%s", mdef->Name()); - assert(oper_index != 2 || !is_ndd_demotable_commutative(mdef), "%s", mdef->Name()); + assert(oper_index != 1 || !is_ndd_demotable_opr1(mdef), "%s", mdef->Name()); + assert(oper_index != 2 || !is_ndd_demotable_opr2(mdef), "%s", mdef->Name()); return false; } @@ -2662,14 +2664,13 @@ bool Matcher::is_register_biasing_candidate(const MachNode* mdef, // address computation. Biasing def towards any address component will not // result in NDD demotion by assembler. if (mdef->operand_num_edges(oper_index) != 1) { - assert(!is_ndd_demotable(mdef), "%s", mdef->Name()); return false; } // Demotion candidate must be register mask compatible with definition. const RegMask& oper_mask = mdef->in_RegMask(mdef->operand_index(oper_index)); if (!oper_mask.overlap(mdef->out_RegMask())) { - assert(!is_demotion_candidate(mdef), "%s", mdef->Name()); + assert(!is_ndd_demotable(mdef), "%s", mdef->Name()); return false; } @@ -2681,12 +2682,12 @@ bool Matcher::is_register_biasing_candidate(const MachNode* mdef, // EVEX prefix with shorter REX/REX2 encoding. Demotion candidates // are decorated with a special flag by instruction selector. case 1: - return is_demotion_candidate(mdef); + return is_ndd_demotable_opr1(mdef); // Definition operand of commutative operation can be biased towards second // operand. case 2: - return is_ndd_demotable_commutative(mdef); + return is_ndd_demotable_opr2(mdef); // Current scheme only selects up to two biasing candidates default: @@ -2888,9 +2889,9 @@ public: Flag_clears_zero_flag = Node::_last_flag << 9, Flag_clears_overflow_flag = Node::_last_flag << 10, Flag_clears_sign_flag = Node::_last_flag << 11, - Flag_ndd_demotable = Node::_last_flag << 12, - Flag_ndd_demotable_commutative = Node::_last_flag << 13, - _last_flag = Flag_ndd_demotable_commutative + Flag_ndd_demotable_opr1 = Node::_last_flag << 12, + Flag_ndd_demotable_opr2 = Node::_last_flag << 13, + _last_flag = Flag_ndd_demotable_opr2 }; }; @@ -9872,7 +9873,7 @@ instruct addI_rReg_ndd(rRegI dst, rRegI src1, rRegI src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (AddI src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); format %{ "eaddl $dst, $src1, $src2\t# int ndd" %} ins_encode %{ @@ -9900,7 +9901,7 @@ instruct addI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (AddI src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); format %{ "eaddl $dst, $src1, $src2\t# int ndd" %} ins_encode %{ @@ -9943,7 +9944,7 @@ instruct addI_rReg_rReg_mem_ndd(rRegI dst, rRegI src1, memory src2, rFlagsReg cr predicate(UseAPX); match(Set dst (AddI src1 (LoadI src2))); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); ins_cost(150); format %{ "eaddl $dst, $src1, $src2\t# int ndd" %} @@ -10000,7 +10001,7 @@ instruct incI_rReg_ndd(rRegI dst, rRegI src, immI_1 val, rFlagsReg cr) predicate(UseAPX && UseIncDec); match(Set dst (AddI src val)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "eincl $dst, $src\t# int ndd" %} ins_encode %{ @@ -10055,7 +10056,7 @@ instruct decI_rReg_ndd(rRegI dst, rRegI src, immI_M1 val, rFlagsReg cr) predicate(UseAPX && UseIncDec); match(Set dst (AddI src val)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "edecl $dst, $src\t# int ndd" %} ins_encode %{ @@ -10162,7 +10163,7 @@ instruct addL_rReg_ndd(rRegL dst, rRegL src1, rRegL src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (AddL src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); format %{ "eaddq $dst, $src1, $src2\t# long ndd" %} ins_encode %{ @@ -10190,7 +10191,7 @@ instruct addL_rReg_rReg_imm_ndd(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr predicate(UseAPX); match(Set dst (AddL src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); format %{ "eaddq $dst, $src1, $src2\t# long ndd" %} ins_encode %{ @@ -10233,7 +10234,7 @@ instruct addL_rReg_rReg_mem_ndd(rRegL dst, rRegL src1, memory src2, rFlagsReg cr predicate(UseAPX); match(Set dst (AddL src1 (LoadL src2))); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); ins_cost(150); format %{ "eaddq $dst, $src1, $src2\t# long ndd" %} @@ -10289,7 +10290,7 @@ instruct incL_rReg_ndd(rRegL dst, rRegI src, immL1 val, rFlagsReg cr) predicate(UseAPX && UseIncDec); match(Set dst (AddL src val)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "eincq $dst, $src\t# long ndd" %} ins_encode %{ @@ -10344,7 +10345,7 @@ instruct decL_rReg_ndd(rRegL dst, rRegL src, immL_M1 val, rFlagsReg cr) predicate(UseAPX && UseIncDec); match(Set dst (AddL src val)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "edecq $dst, $src\t# long ndd" %} ins_encode %{ @@ -11059,7 +11060,7 @@ instruct subI_rReg_ndd(rRegI dst, rRegI src1, rRegI src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (SubI src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); format %{ "esubl $dst, $src1, $src2\t# int ndd" %} ins_encode %{ @@ -11073,7 +11074,7 @@ instruct subI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (SubI src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); format %{ "esubl $dst, $src1, $src2\t# int ndd" %} ins_encode %{ @@ -11116,7 +11117,7 @@ instruct subI_rReg_rReg_mem_ndd(rRegI dst, rRegI src1, memory src2, rFlagsReg cr predicate(UseAPX); match(Set dst (SubI src1 (LoadI src2))); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); ins_cost(150); format %{ "esubl $dst, $src1, $src2\t# int ndd" %} @@ -11174,7 +11175,7 @@ instruct subL_rReg_ndd(rRegL dst, rRegL src1, rRegL src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (SubL src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); format %{ "esubq $dst, $src1, $src2\t# long ndd" %} ins_encode %{ @@ -11188,7 +11189,7 @@ instruct subL_rReg_rReg_imm_ndd(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr predicate(UseAPX); match(Set dst (SubL src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); format %{ "esubq $dst, $src1, $src2\t# long ndd" %} ins_encode %{ @@ -11231,7 +11232,7 @@ instruct subL_rReg_rReg_mem_ndd(rRegL dst, rRegL src1, memory src2, rFlagsReg cr predicate(UseAPX); match(Set dst (SubL src1 (LoadL src2))); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); ins_cost(150); format %{ "esubq $dst, $src1, $src2\t# long ndd" %} @@ -11303,7 +11304,7 @@ instruct negI_rReg_ndd(rRegI dst, rRegI src, immI_0 zero, rFlagsReg cr) predicate(UseAPX); match(Set dst (SubI zero src)); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr2); format %{ "enegl $dst, $src\t# int ndd" %} ins_encode %{ @@ -11331,7 +11332,7 @@ instruct negI_rReg_2_ndd(rRegI dst, rRegI src, rFlagsReg cr) predicate(UseAPX); match(Set dst (NegI src)); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); format %{ "enegl $dst, $src\t# int ndd" %} ins_encode %{ @@ -11372,7 +11373,7 @@ instruct negL_rReg_ndd(rRegL dst, rRegL src, immL0 zero, rFlagsReg cr) predicate(UseAPX); match(Set dst (SubL zero src)); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr2); format %{ "enegq $dst, $src\t# long ndd" %} ins_encode %{ @@ -11400,7 +11401,7 @@ instruct negL_rReg_2_ndd(rRegL dst, rRegL src, rFlagsReg cr) predicate(UseAPX); match(Set dst (NegL src)); effect(KILL cr); - flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_ndd_demotable_opr1); format %{ "enegq $dst, $src\t# long ndd" %} ins_encode %{ @@ -11445,7 +11446,7 @@ instruct mulI_rReg_ndd(rRegI dst, rRegI src1, rRegI src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (MulI src1 src2)); effect(KILL cr); - flag(PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); ins_cost(300); format %{ "eimull $dst, $src1, $src2\t# int ndd" %} @@ -11487,7 +11488,7 @@ instruct mulI_rReg_rReg_mem_ndd(rRegI dst, rRegI src1, memory src2, rFlagsReg cr predicate(UseAPX); match(Set dst (MulI src1 (LoadI src2))); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); ins_cost(350); format %{ "eimull $dst, $src1, $src2\t# int ndd" %} @@ -11539,7 +11540,7 @@ instruct mulL_rReg_ndd(rRegL dst, rRegL src1, rRegL src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (MulL src1 src2)); effect(KILL cr); - flag(PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); ins_cost(300); format %{ "eimulq $dst, $src1, $src2\t# long ndd" %} @@ -11581,7 +11582,7 @@ instruct mulL_rReg_rReg_mem_ndd(rRegL dst, rRegL src1, memory src2, rFlagsReg cr predicate(UseAPX); match(Set dst (MulL src1 (LoadL src2))); effect(KILL cr); - flag(PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); ins_cost(350); format %{ "eimulq $dst, $src1, $src2 \t# long" %} @@ -11856,7 +11857,7 @@ instruct salI_rReg_immI2_ndd(rRegI dst, rRegI src, immI2 shift, rFlagsReg cr) predicate(UseAPX); match(Set dst (LShiftI src shift)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "esall $dst, $src, $shift\t# int(ndd)" %} ins_encode %{ @@ -11885,7 +11886,7 @@ instruct salI_rReg_imm_ndd(rRegI dst, rRegI src, immI8 shift, rFlagsReg cr) predicate(UseAPX); match(Set dst (LShiftI src shift)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "esall $dst, $src, $shift\t# int (ndd)" %} ins_encode %{ @@ -11992,7 +11993,7 @@ instruct sarI_rReg_imm_ndd(rRegI dst, rRegI src, immI8 shift, rFlagsReg cr) predicate(UseAPX); match(Set dst (RShiftI src shift)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "esarl $dst, $src, $shift\t# int (ndd)" %} ins_encode %{ @@ -12099,7 +12100,7 @@ instruct shrI_rReg_imm_ndd(rRegI dst, rRegI src, immI8 shift, rFlagsReg cr) predicate(UseAPX); match(Set dst (URShiftI src shift)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "eshrl $dst, $src, $shift\t # int (ndd)" %} ins_encode %{ @@ -12207,7 +12208,7 @@ instruct salL_rReg_immI2_ndd(rRegL dst, rRegL src, immI2 shift, rFlagsReg cr) predicate(UseAPX); match(Set dst (LShiftL src shift)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "esalq $dst, $src, $shift\t# long (ndd)" %} ins_encode %{ @@ -12236,7 +12237,7 @@ instruct salL_rReg_imm_ndd(rRegL dst, rRegL src, immI8 shift, rFlagsReg cr) predicate(UseAPX); match(Set dst (LShiftL src shift)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "esalq $dst, $src, $shift\t# long (ndd)" %} ins_encode %{ @@ -12343,7 +12344,7 @@ instruct sarL_rReg_imm_ndd(rRegL dst, rRegL src, immI shift, rFlagsReg cr) predicate(UseAPX); match(Set dst (RShiftL src shift)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "esarq $dst, $src, $shift\t# long (ndd)" %} ins_encode %{ @@ -12450,7 +12451,7 @@ instruct shrL_rReg_imm_ndd(rRegL dst, rRegL src, immI8 shift, rFlagsReg cr) predicate(UseAPX); match(Set dst (URShiftL src shift)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "eshrq $dst, $src, $shift\t# long (ndd)" %} ins_encode %{ @@ -12622,7 +12623,7 @@ instruct rolI_rReg_Var_ndd(rRegI dst, rRegI src, rcx_RegI shift, rFlagsReg cr) predicate(UseAPX && n->bottom_type()->basic_type() == T_INT); match(Set dst (RotateLeft src shift)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "eroll $dst, $src, $shift\t# rotate left (int ndd)" %} ins_encode %{ @@ -12687,7 +12688,7 @@ instruct rorI_rReg_Var_ndd(rRegI dst, rRegI src, rcx_RegI shift, rFlagsReg cr) predicate(UseAPX && n->bottom_type()->basic_type() == T_INT); match(Set dst (RotateRight src shift)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "erorl $dst, $src, $shift\t# rotate right(int ndd)" %} ins_encode %{ @@ -12754,7 +12755,7 @@ instruct rolL_rReg_Var_ndd(rRegL dst, rRegL src, rcx_RegI shift, rFlagsReg cr) predicate(UseAPX && n->bottom_type()->basic_type() == T_LONG); match(Set dst (RotateLeft src shift)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "erolq $dst, $src, $shift\t# rotate left(long ndd)" %} ins_encode %{ @@ -12819,7 +12820,7 @@ instruct rorL_rReg_Var_ndd(rRegL dst, rRegL src, rcx_RegI shift, rFlagsReg cr) predicate(UseAPX && n->bottom_type()->basic_type() == T_LONG); match(Set dst (RotateRight src shift)); effect(KILL cr); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "erorq $dst, $src, $shift\t# rotate right(long ndd)" %} ins_encode %{ @@ -12897,7 +12898,7 @@ instruct andI_rReg_ndd(rRegI dst, rRegI src1, rRegI src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (AndI src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); format %{ "eandl $dst, $src1, $src2\t# int ndd" %} ins_encode %{ @@ -12990,7 +12991,7 @@ instruct andI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (AndI src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1); format %{ "eandl $dst, $src1, $src2\t# int ndd" %} ins_encode %{ @@ -13034,7 +13035,7 @@ instruct andI_rReg_rReg_mem_ndd(rRegI dst, rRegI src1, memory src2, rFlagsReg cr predicate(UseAPX); match(Set dst (AndI src1 (LoadI src2))); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); ins_cost(150); format %{ "eandl $dst, $src1, $src2\t# int ndd" %} @@ -13234,7 +13235,7 @@ instruct orI_rReg_ndd(rRegI dst, rRegI src1, rRegI src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (OrI src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); format %{ "eorl $dst, $src1, $src2\t# int ndd" %} ins_encode %{ @@ -13263,7 +13264,7 @@ instruct orI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (OrI src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1); format %{ "eorl $dst, $src1, $src2\t# int ndd" %} ins_encode %{ @@ -13277,7 +13278,7 @@ instruct orI_rReg_imm_rReg_ndd(rRegI dst, immI src1, rRegI src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (OrI src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1); format %{ "eorl $dst, $src2, $src1\t# int ndd" %} ins_encode %{ @@ -13321,7 +13322,7 @@ instruct orI_rReg_rReg_mem_ndd(rRegI dst, rRegI src1, memory src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (OrI src1 (LoadI src2))); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); ins_cost(150); format %{ "eorl $dst, $src1, $src2\t# int ndd" %} @@ -13397,7 +13398,7 @@ instruct xorI_rReg_ndd(rRegI dst, rRegI src1, rRegI src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (XorI src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); format %{ "exorl $dst, $src1, $src2\t# int ndd" %} ins_encode %{ @@ -13423,7 +13424,7 @@ instruct xorI_rReg_im1_ndd(rRegI dst, rRegI src, immI_M1 imm) %{ match(Set dst (XorI src imm)); predicate(UseAPX); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "enotl $dst, $src" %} ins_encode %{ @@ -13454,7 +13455,7 @@ instruct xorI_rReg_rReg_imm_ndd(rRegI dst, rRegI src1, immI src2, rFlagsReg cr) predicate(UseAPX && n->in(2)->bottom_type()->is_int()->get_con() != -1); match(Set dst (XorI src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1); format %{ "exorl $dst, $src1, $src2\t# int ndd" %} ins_encode %{ @@ -13500,7 +13501,7 @@ instruct xorI_rReg_rReg_mem_ndd(rRegI dst, rRegI src1, memory src2, rFlagsReg cr predicate(UseAPX); match(Set dst (XorI src1 (LoadI src2))); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); ins_cost(150); format %{ "exorl $dst, $src1, $src2\t# int ndd" %} @@ -13579,7 +13580,7 @@ instruct andL_rReg_ndd(rRegL dst, rRegL src1, rRegL src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (AndL src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); format %{ "eandq $dst, $src1, $src2\t# long ndd" %} ins_encode %{ @@ -13635,7 +13636,7 @@ instruct andL_rReg_rReg_imm_ndd(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr predicate(UseAPX); match(Set dst (AndL src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1); format %{ "eandq $dst, $src1, $src2\t# long ndd" %} ins_encode %{ @@ -13679,7 +13680,7 @@ instruct andL_rReg_rReg_mem_ndd(rRegL dst, rRegL src1, memory src2, rFlagsReg cr predicate(UseAPX); match(Set dst (AndL src1 (LoadL src2))); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); ins_cost(150); format %{ "eandq $dst, $src1, $src2\t# long ndd" %} @@ -13882,7 +13883,7 @@ instruct orL_rReg_ndd(rRegL dst, rRegL src1, rRegL src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (OrL src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); format %{ "eorq $dst, $src1, $src2\t# long ndd" %} ins_encode %{ @@ -13937,7 +13938,7 @@ instruct orL_rReg_rReg_imm_ndd(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (OrL src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1); format %{ "eorq $dst, $src1, $src2\t# long ndd" %} ins_encode %{ @@ -13951,7 +13952,7 @@ instruct orL_rReg_imm_rReg_ndd(rRegL dst, immL32 src1, rRegL src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (OrL src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1); format %{ "eorq $dst, $src2, $src1\t# long ndd" %} ins_encode %{ @@ -13996,7 +13997,7 @@ instruct orL_rReg_rReg_mem_ndd(rRegL dst, rRegL src1, memory src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (OrL src1 (LoadL src2))); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); ins_cost(150); format %{ "eorq $dst, $src1, $src2\t# long ndd" %} @@ -14075,7 +14076,7 @@ instruct xorL_rReg_ndd(rRegL dst, rRegL src1, rRegL src2, rFlagsReg cr) predicate(UseAPX); match(Set dst (XorL src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); format %{ "exorq $dst, $src1, $src2\t# long ndd" %} ins_encode %{ @@ -14101,7 +14102,7 @@ instruct xorL_rReg_im1_ndd(rRegL dst,rRegL src, immL_M1 imm) %{ predicate(UseAPX); match(Set dst (XorL src imm)); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); format %{ "enotq $dst, $src" %} ins_encode %{ @@ -14132,7 +14133,7 @@ instruct xorL_rReg_rReg_imm(rRegL dst, rRegL src1, immL32 src2, rFlagsReg cr) predicate(UseAPX && n->in(2)->bottom_type()->is_long()->get_con() != -1L); match(Set dst (XorL src1 src2)); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1); format %{ "exorq $dst, $src1, $src2\t# long ndd" %} ins_encode %{ @@ -14178,7 +14179,7 @@ instruct xorL_rReg_rReg_mem_ndd(rRegL dst, rRegL src1, memory src2, rFlagsReg cr predicate(UseAPX); match(Set dst (XorL src1 (LoadL src2))); effect(KILL cr); - flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_commutative); + flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag, PD::Flag_ndd_demotable_opr1, PD::Flag_ndd_demotable_opr2); ins_cost(150); format %{ "exorq $dst, $src1, $src2\t# long ndd" %} @@ -16633,7 +16634,7 @@ instruct minI_rReg_ndd(rRegI dst, rRegI src1, rRegI src2) predicate(UseAPX); match(Set dst (MinI src1 src2)); effect(DEF dst, USE src1, USE src2); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); ins_cost(200); expand %{ @@ -16685,7 +16686,7 @@ instruct maxI_rReg_ndd(rRegI dst, rRegI src1, rRegI src2) predicate(UseAPX); match(Set dst (MaxI src1 src2)); effect(DEF dst, USE src1, USE src2); - flag(PD::Flag_ndd_demotable); + flag(PD::Flag_ndd_demotable_opr1); ins_cost(200); expand %{ From dd20e9150666f247af61dfa524a170ef7dd96c03 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 7 Jan 2026 18:10:06 +0000 Subject: [PATCH 009/204] 8374521: Support fine-grained native debug levels Reviewed-by: erikj, krk, clanger --- .github/workflows/build-alpine-linux.yml | 1 + .github/workflows/build-cross-compile.yml | 1 + .github/workflows/build-linux.yml | 1 + .github/workflows/build-macos.yml | 1 + make/autoconf/flags-cflags.m4 | 27 +++++++++++++++++++---- 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-alpine-linux.yml b/.github/workflows/build-alpine-linux.yml index c356a8a0f2a..569893d5ccc 100644 --- a/.github/workflows/build-alpine-linux.yml +++ b/.github/workflows/build-alpine-linux.yml @@ -97,6 +97,7 @@ jobs: --with-zlib=system --with-jmod-compress=zip-1 --with-external-symbols-in-bundles=none + --with-debug-info-level=1 ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( echo "Dumping config.log:" && cat config.log && diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index c2ed63aa439..4860228c7de 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -180,6 +180,7 @@ jobs: --with-sysroot=sysroot --with-jmod-compress=zip-1 --with-external-symbols-in-bundles=none + --with-debug-info-level=1 CC=${{ matrix.gnu-arch }}-linux-gnu${{ matrix.gnu-abi}}-gcc-${{ inputs.gcc-major-version }} CXX=${{ matrix.gnu-arch }}-linux-gnu${{ matrix.gnu-abi}}-g++-${{ inputs.gcc-major-version }} ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index a668cb5a0f9..121416106f2 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -144,6 +144,7 @@ jobs: --with-zlib=system --with-jmod-compress=zip-1 --with-external-symbols-in-bundles=none + --with-debug-info-level=1 ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( echo "Dumping config.log:" && cat config.log && diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 4535d0dd760..32c5d43acbc 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -111,6 +111,7 @@ jobs: --with-zlib=system --with-jmod-compress=zip-1 --with-external-symbols-in-bundles=none + --with-debug-info-level=1 ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( echo "Dumping config.log:" && cat config.log && diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index 6298bcae416..b7b2a6b5e17 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -69,6 +69,23 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS], # Debug prefix mapping if supported by compiler DEBUG_PREFIX_CFLAGS= + UTIL_ARG_WITH(NAME: debug-info-level, TYPE: string, + DEFAULT: "", + RESULT: DEBUG_INFO_LEVEL, + DESC: [Sets the debug info level, when debug info generation is enabled (GCC and Clang only)], + DEFAULT_DESC: [default]) + AC_SUBST(DEBUG_INFO_LEVEL) + + if test "x${TOOLCHAIN_TYPE}" = xgcc || \ + test "x${TOOLCHAIN_TYPE}" = xclang; then + DEBUG_INFO_LEVEL_FLAGS="-g" + if test "x${DEBUG_INFO_LEVEL}" != "x"; then + DEBUG_INFO_LEVEL_FLAGS="-g${DEBUG_INFO_LEVEL}" + FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${DEBUG_INFO_LEVEL_FLAGS}], + IF_FALSE: AC_MSG_ERROR("Debug info level ${DEBUG_INFO_LEVEL} is not supported")) + fi + fi + # Debug symbols if test "x$TOOLCHAIN_TYPE" = xgcc; then if test "x$ALLOW_ABSOLUTE_PATHS_IN_OUTPUT" = "xfalse"; then @@ -93,8 +110,9 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS], ) fi - CFLAGS_DEBUG_SYMBOLS="-g -gdwarf-4" - ASFLAGS_DEBUG_SYMBOLS="-g" + # Debug info level should follow the debug format to be effective. + CFLAGS_DEBUG_SYMBOLS="-gdwarf-4 ${DEBUG_INFO_LEVEL_FLAGS}" + ASFLAGS_DEBUG_SYMBOLS="${DEBUG_INFO_LEVEL_FLAGS}" elif test "x$TOOLCHAIN_TYPE" = xclang; then if test "x$ALLOW_ABSOLUTE_PATHS_IN_OUTPUT" = "xfalse"; then # Check if compiler supports -fdebug-prefix-map. If so, use that to make @@ -113,8 +131,9 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS], FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${GDWARF_FLAGS}], IF_FALSE: [GDWARF_FLAGS=""]) - CFLAGS_DEBUG_SYMBOLS="-g ${GDWARF_FLAGS}" - ASFLAGS_DEBUG_SYMBOLS="-g" + # Debug info level should follow the debug format to be effective. + CFLAGS_DEBUG_SYMBOLS="${GDWARF_FLAGS} ${DEBUG_INFO_LEVEL_FLAGS}" + ASFLAGS_DEBUG_SYMBOLS="${DEBUG_INFO_LEVEL_FLAGS}" elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then CFLAGS_DEBUG_SYMBOLS="-Z7" fi From 383fe1efc3a23385b8576e20f458f91085c6325e Mon Sep 17 00:00:00 2001 From: Erik Joelsson Date: Wed, 7 Jan 2026 21:52:12 +0000 Subject: [PATCH 010/204] 8374642: EscapeHash macro fails with GNU make 4.3 and 4.4 Reviewed-by: tbell, shade --- make/common/Utils.gmk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/common/Utils.gmk b/make/common/Utils.gmk index c0ebabca3f7..b4bb949d3ee 100644 --- a/make/common/Utils.gmk +++ b/make/common/Utils.gmk @@ -114,7 +114,7 @@ EscapeDollar = $(subst $$,\$$,$(subst \$$,$$,$(strip $1))) ################################################################################ # This macro works just like EscapeDollar above, but for #. -EscapeHash = $(subst \#,\\\#,$(subst \\\#,\#,$(strip $1))) +EscapeHash = $(subst $(HASH),\$(HASH),$(subst \$(HASH),$(HASH),$(strip $1))) ################################################################################ # This macro translates $ into $$ to protect the string from make itself. From 9a944e558733950d135b5a91d093b7a28e934f59 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Wed, 7 Jan 2026 22:23:39 +0000 Subject: [PATCH 011/204] 8372754: Add wrapper for 8369205: AIX build break in forbiddenFunctions.hpp Reviewed-by: mdoerr, tschatzl --- src/hotspot/cpu/aarch64/immediate_aarch64.cpp | 8 +- src/hotspot/cpu/aarch64/immediate_aarch64.hpp | 9 +- src/hotspot/os/aix/libodm_aix.cpp | 8 +- src/hotspot/os/aix/libperfstat_aix.hpp | 5 +- src/hotspot/os/aix/os_perf_aix.cpp | 4 +- src/hotspot/os/bsd/memMapPrinter_macosx.cpp | 4 +- src/hotspot/os/linux/os_linux.cpp | 4 +- src/hotspot/os/linux/os_perf_linux.cpp | 4 +- .../os/posix/forbiddenFunctions_posix.hpp | 11 +- src/hotspot/os/posix/os_posix.cpp | 3 +- .../posix/permitForbiddenFunctions_posix.hpp | 7 +- src/hotspot/os/windows/os_windows.cpp | 3 +- .../permitForbiddenFunctions_windows.hpp | 5 +- src/hotspot/os/windows/vmError_windows.cpp | 3 +- .../os_cpu/bsd_aarch64/os_bsd_aarch64.cpp | 4 +- src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp | 4 +- .../os_cpu/linux_aarch64/os_linux_aarch64.cpp | 4 +- src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp | 4 +- src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp | 4 +- .../os_cpu/linux_riscv/os_linux_riscv.cpp | 4 +- .../os_cpu/linux_s390/os_linux_s390.cpp | 4 +- src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp | 4 +- .../windows_aarch64/os_windows_aarch64.cpp | 4 +- src/hotspot/share/classfile/classLoader.cpp | 4 +- src/hotspot/share/cppstdlib/cstdlib.hpp | 105 ++++++++++++++++++ src/hotspot/share/utilities/byteswap.hpp | 3 +- .../share/utilities/forbiddenFunctions.hpp | 23 +--- .../share/utilities/globalDefinitions_gcc.hpp | 13 +-- .../utilities/globalDefinitions_visCPP.hpp | 5 +- src/hotspot/share/utilities/parseInteger.hpp | 4 +- .../utilities/permitForbiddenFunctions.hpp | 7 +- test/hotspot/gtest/gtestMain.cpp | 4 +- test/hotspot/gtest/unittest.hpp | 4 +- .../gtest/utilities/test_bitMap_setops.cpp | 5 +- 34 files changed, 193 insertions(+), 98 deletions(-) create mode 100644 src/hotspot/share/cppstdlib/cstdlib.hpp diff --git a/src/hotspot/cpu/aarch64/immediate_aarch64.cpp b/src/hotspot/cpu/aarch64/immediate_aarch64.cpp index 710a1f0677d..eb4cb23100c 100644 --- a/src/hotspot/cpu/aarch64/immediate_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/immediate_aarch64.cpp @@ -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. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,13 +23,13 @@ * */ -#include // do not reorder -#include // do not reorder - +#include "cppstdlib/cstdlib.hpp" #include "immediate_aarch64.hpp" #include "metaprogramming/primitiveConversions.hpp" #include "utilities/globalDefinitions.hpp" +#include + // there are at most 2^13 possible logical immediate encodings // however, some combinations of immr and imms are invalid static const unsigned LI_TABLE_SIZE = (1 << 13); diff --git a/src/hotspot/cpu/aarch64/immediate_aarch64.hpp b/src/hotspot/cpu/aarch64/immediate_aarch64.hpp index 0cbdb562088..fb38995417b 100644 --- a/src/hotspot/cpu/aarch64/immediate_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/immediate_aarch64.hpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2025, 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. * @@ -22,10 +23,10 @@ * */ -#ifndef _IMMEDIATE_H -#define _IMMEDIATE_H +#ifndef CPU_AARCH64_IMMEDIATE_AARCH64_HPP +#define CPU_AARCH64_IMMEDIATE_AARCH64_HPP -#include +#include /* * functions to map backwards and forwards between logical or floating @@ -51,4 +52,4 @@ uint32_t encoding_for_logical_immediate(uint64_t immediate); uint64_t fp_immediate_for_encoding(uint32_t imm8, int is_dp); uint32_t encoding_for_fp_immediate(float immediate); -#endif // _IMMEDIATE_H +#endif // CPU_AARCH64_IMMEDIATE_AARCH64_HPP diff --git a/src/hotspot/os/aix/libodm_aix.cpp b/src/hotspot/os/aix/libodm_aix.cpp index 035703a1771..38e8067181a 100644 --- a/src/hotspot/os/aix/libodm_aix.cpp +++ b/src/hotspot/os/aix/libodm_aix.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. * Copyright (c) 2015, 2019 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,15 +23,15 @@ * */ +#include "cppstdlib/cstdlib.hpp" #include "libodm_aix.hpp" #include "misc_aix.hpp" -#include -#include -#include #include "runtime/arguments.hpp" #include "runtime/os.hpp" #include "utilities/permitForbiddenFunctions.hpp" +#include +#include dynamicOdm::dynamicOdm() { const char* libodmname = "/usr/lib/libodm.a(shr_64.o)"; diff --git a/src/hotspot/os/aix/libperfstat_aix.hpp b/src/hotspot/os/aix/libperfstat_aix.hpp index 5a6ddfd8f4d..aff11cfb41f 100644 --- a/src/hotspot/os/aix/libperfstat_aix.hpp +++ b/src/hotspot/os/aix/libperfstat_aix.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. * Copyright (c) 2012, 2024 SAP SE. All rights reserved. * Copyright (c) 2022, IBM Corp. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -32,8 +32,9 @@ #ifndef OS_AIX_LIBPERFSTAT_AIX_HPP #define OS_AIX_LIBPERFSTAT_AIX_HPP +#include "cppstdlib/cstdlib.hpp" + #include -#include /////////////////////////////////////////////////////////////////////////////////////////////// // These are excerpts from the AIX 7.1 libperfstat.h - diff --git a/src/hotspot/os/aix/os_perf_aix.cpp b/src/hotspot/os/aix/os_perf_aix.cpp index 8444002b871..aa8819d035f 100644 --- a/src/hotspot/os/aix/os_perf_aix.cpp +++ b/src/hotspot/os/aix/os_perf_aix.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. * Copyright (c) 2022, 2024, IBM Corp. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -23,6 +23,7 @@ * */ +#include "cppstdlib/cstdlib.hpp" #include "jvm.h" #include "libperfstat_aix.hpp" #include "memory/allocation.inline.hpp" @@ -46,7 +47,6 @@ #include #include #include -#include #include typedef struct { diff --git a/src/hotspot/os/bsd/memMapPrinter_macosx.cpp b/src/hotspot/os/bsd/memMapPrinter_macosx.cpp index a7ddab04d85..6fd08d63e85 100644 --- a/src/hotspot/os/bsd/memMapPrinter_macosx.cpp +++ b/src/hotspot/os/bsd/memMapPrinter_macosx.cpp @@ -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. * Copyright (c) 2023, 2024, Red Hat, Inc. and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -25,6 +25,7 @@ #if defined(__APPLE__) +#include "cppstdlib/cstdlib.hpp" #include "nmt/memMapPrinter.hpp" #include "runtime/os.hpp" #include "utilities/align.hpp" @@ -34,7 +35,6 @@ #include #include -#include #include #include diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index a28f9850495..88e5e9b582a 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.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) 2015, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,6 +27,7 @@ #include "code/vtableStubs.hpp" #include "compiler/compileBroker.hpp" #include "compiler/disassembler.hpp" +#include "cppstdlib/cstdlib.hpp" #include "hugepages.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" @@ -96,7 +97,6 @@ # include # include # include -# include # include # include # include diff --git a/src/hotspot/os/linux/os_perf_linux.cpp b/src/hotspot/os/linux/os_perf_linux.cpp index 5708194521f..9f91f3b4c0d 100644 --- a/src/hotspot/os/linux/os_perf_linux.cpp +++ b/src/hotspot/os/linux/os_perf_linux.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 @@ -22,6 +22,7 @@ * */ +#include "cppstdlib/cstdlib.hpp" #include "jvm.h" #include "memory/allocation.inline.hpp" #include "os_linux.inline.hpp" @@ -40,7 +41,6 @@ #include #include #include -#include #include #include #include diff --git a/src/hotspot/os/posix/forbiddenFunctions_posix.hpp b/src/hotspot/os/posix/forbiddenFunctions_posix.hpp index 529cf22078e..1c91e9d2599 100644 --- a/src/hotspot/os/posix/forbiddenFunctions_posix.hpp +++ b/src/hotspot/os/posix/forbiddenFunctions_posix.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 @@ -36,15 +36,12 @@ #include #endif +// POSIX puts _exit in . +FORBID_IMPORTED_NORETURN_C_FUNCTION(void _exit(int), /* not noexcept */, "use os::exit") + // If needed, add os::strndup and use that instead. FORBID_C_FUNCTION(char* strndup(const char*, size_t), noexcept, "don't use"); -// These are unimplementable for Windows, and they aren't useful for a -// POSIX implementation of NMT either. -// https://stackoverflow.com/questions/62962839/stdaligned-alloc-missing-from-visual-studio-2019 -FORBID_C_FUNCTION(int posix_memalign(void**, size_t, size_t), noexcept, "don't use"); -FORBID_C_FUNCTION(void* aligned_alloc(size_t, size_t), noexcept, "don't use"); - // realpath with a null second argument mallocs a string for the result. // With a non-null second argument, there is a risk of buffer overrun. PRAGMA_DIAG_PUSH diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 07e1920c62d..4cae7d359e4 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.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 @@ -23,6 +23,7 @@ */ #include "classfile/classLoader.hpp" +#include "cppstdlib/cstdlib.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "jvmtifiles/jvmti.h" diff --git a/src/hotspot/os/posix/permitForbiddenFunctions_posix.hpp b/src/hotspot/os/posix/permitForbiddenFunctions_posix.hpp index 3ff8c383a31..b45cede2e5f 100644 --- a/src/hotspot/os/posix/permitForbiddenFunctions_posix.hpp +++ b/src/hotspot/os/posix/permitForbiddenFunctions_posix.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 @@ -25,15 +25,20 @@ #ifndef OS_POSIX_PERMITFORBIDDENFUNCTIONS_POSIX_HPP #define OS_POSIX_PERMITFORBIDDENFUNCTIONS_POSIX_HPP +#include "cppstdlib/cstdlib.hpp" #include "utilities/compilerWarnings.hpp" #include "utilities/globalDefinitions.hpp" +#include + // Provide wrappers for some functions otherwise forbidden from use in HotSpot. // See forbiddenFunctions.hpp for details. namespace permit_forbidden_function { BEGIN_ALLOW_FORBIDDEN_FUNCTIONS +[[noreturn]] inline void _exit(int status) { ::_exit(status); } + // Used by the POSIX implementation of os::realpath. inline char* realpath(const char* path, char* resolved_path) { return ::realpath(path, resolved_path); diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 0ac05e8a435..efbd1fe7c68 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.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 @@ -30,6 +30,7 @@ #include "code/vtableStubs.hpp" #include "compiler/compileBroker.hpp" #include "compiler/disassembler.hpp" +#include "cppstdlib/cstdlib.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "jvmtifiles/jvmti.h" diff --git a/src/hotspot/os/windows/permitForbiddenFunctions_windows.hpp b/src/hotspot/os/windows/permitForbiddenFunctions_windows.hpp index 99e77464fbd..fbd476ceb60 100644 --- a/src/hotspot/os/windows/permitForbiddenFunctions_windows.hpp +++ b/src/hotspot/os/windows/permitForbiddenFunctions_windows.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 @@ -25,6 +25,7 @@ #ifndef OS_WINDOWS_PERMITFORBIDDENFUNCTIONS_WINDOWS_HPP #define OS_WINDOWS_PERMITFORBIDDENFUNCTIONS_WINDOWS_HPP +#include "cppstdlib/cstdlib.hpp" #include "utilities/compilerWarnings.hpp" #include "utilities/globalDefinitions.hpp" @@ -34,6 +35,8 @@ namespace permit_forbidden_function { BEGIN_ALLOW_FORBIDDEN_FUNCTIONS +[[noreturn]] inline void _exit(int status) { ::_exit(status); } + // Used by the Windows implementation of os::realpath. inline char* _fullpath(char* absPath, const char* relPath, size_t maxLength) { return ::_fullpath(absPath, relPath, maxLength); diff --git a/src/hotspot/os/windows/vmError_windows.cpp b/src/hotspot/os/windows/vmError_windows.cpp index 22df4d82cbf..66df38d2734 100644 --- a/src/hotspot/os/windows/vmError_windows.cpp +++ b/src/hotspot/os/windows/vmError_windows.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 @@ -24,6 +24,7 @@ #include "cds/aotMetaspace.hpp" #include "cds/cdsConfig.hpp" +#include "cppstdlib/cstdlib.hpp" #include "runtime/arguments.hpp" #include "runtime/javaThread.hpp" #include "runtime/os.hpp" diff --git a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp index f6e2d39e315..62dba218b2f 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp +++ b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_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, Red Hat Inc. All rights reserved. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -29,6 +29,7 @@ #include "classfile/vmSymbols.hpp" #include "code/codeCache.hpp" #include "code/vtableStubs.hpp" +#include "cppstdlib/cstdlib.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "logging/log.hpp" @@ -63,7 +64,6 @@ # include # include # include -# include # include # include # include diff --git a/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp b/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp index 420e7b4cd8d..f1c80594eaf 100644 --- a/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp +++ b/src/hotspot/os_cpu/bsd_x86/os_bsd_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 @@ -26,6 +26,7 @@ #include "classfile/vmSymbols.hpp" #include "code/codeCache.hpp" #include "code/vtableStubs.hpp" +#include "cppstdlib/cstdlib.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "logging/log.hpp" @@ -58,7 +59,6 @@ # include # include # include -# include # include # include # include diff --git a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp index e565f353382..da9e7e159f1 100644 --- a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/os_linux_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, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,6 +28,7 @@ #include "code/codeCache.hpp" #include "code/vtableStubs.hpp" #include "code/nativeInst.hpp" +#include "cppstdlib/cstdlib.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "memory/allocation.inline.hpp" @@ -59,7 +60,6 @@ # include # include # include -# include # include # include # include diff --git a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp index f4196d89fe3..41a4dbea384 100644 --- a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp +++ b/src/hotspot/os_cpu/linux_arm/os_linux_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 @@ -25,6 +25,7 @@ #include "asm/assembler.inline.hpp" #include "classfile/vmSymbols.hpp" #include "code/vtableStubs.hpp" +#include "cppstdlib/cstdlib.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "memory/allocation.inline.hpp" @@ -57,7 +58,6 @@ # include # include # include -# include # include # include # include diff --git a/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp b/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp index 5909b5799b9..6854fb805a9 100644 --- a/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp +++ b/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.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) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,6 +28,7 @@ #include "classfile/vmSymbols.hpp" #include "code/codeCache.hpp" #include "code/vtableStubs.hpp" +#include "cppstdlib/cstdlib.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "memory/allocation.inline.hpp" @@ -62,7 +63,6 @@ # include # include # include -# include # include # include # include diff --git a/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp index 7a5929b0f41..d4a306d35b1 100644 --- a/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp +++ b/src/hotspot/os_cpu/linux_riscv/os_linux_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) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,6 +28,7 @@ #include "code/codeCache.hpp" #include "code/nativeInst.hpp" #include "code/vtableStubs.hpp" +#include "cppstdlib/cstdlib.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "memory/allocation.inline.hpp" @@ -61,7 +62,6 @@ # include # include # include -# include # include # include # include diff --git a/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp b/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp index 4e074512e34..9982b5860a8 100644 --- a/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp +++ b/src/hotspot/os_cpu/linux_s390/os_linux_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. * @@ -30,6 +30,7 @@ #include "code/nativeInst.hpp" #include "code/vtableStubs.hpp" #include "compiler/disassembler.hpp" +#include "cppstdlib/cstdlib.h" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "memory/allocation.inline.hpp" @@ -62,7 +63,6 @@ # include # include # include -# include # include # include # include diff --git a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp index a7f4e5ef688..07f53582a76 100644 --- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp +++ b/src/hotspot/os_cpu/linux_x86/os_linux_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 @@ -26,6 +26,7 @@ #include "classfile/vmSymbols.hpp" #include "code/codeCache.hpp" #include "code/vtableStubs.hpp" +#include "cppstdlib/cstdlib.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "logging/log.hpp" @@ -59,7 +60,6 @@ # include # include # include -# include # include # include # include diff --git a/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp b/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp index d99e0167cbd..2c2afb168fd 100644 --- a/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp +++ b/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, Microsoft Corporation. All rights reserved. - * 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 @@ -28,6 +28,7 @@ #include "code/codeCache.hpp" #include "code/vtableStubs.hpp" #include "code/nativeInst.hpp" +#include "cppstdlib/cstdlib.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "memory/allocation.inline.hpp" @@ -54,7 +55,6 @@ # include # include # include -# include # include # include diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index 12fbda899b9..d9a63cd154b 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.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 @@ -42,6 +42,7 @@ #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" #include "compiler/compileBroker.hpp" +#include "cppstdlib/cstdlib.hpp" #include "interpreter/bytecodeStream.hpp" #include "interpreter/oopMapCache.hpp" #include "jimage.hpp" @@ -81,7 +82,6 @@ #include "utilities/utf8.hpp" #include -#include // Entry point in java.dll for path canonicalization diff --git a/src/hotspot/share/cppstdlib/cstdlib.hpp b/src/hotspot/share/cppstdlib/cstdlib.hpp new file mode 100644 index 00000000000..2b58e737882 --- /dev/null +++ b/src/hotspot/share/cppstdlib/cstdlib.hpp @@ -0,0 +1,105 @@ +/* + * 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 + * 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_CPPSTDLIB_CSTDLIB_HPP +#define SHARE_CPPSTDLIB_CSTDLIB_HPP + +#include "utilities/compilerWarnings.hpp" + +// HotSpot usage for : +// +// Some functions are explicitly forbidden below. That may not be a complete +// list of all the functions we should forbid. +// +// We assume provides definitions in the global namespace, in +// addition to providing them in the std namespace. We prefer to use the names +// in the global namespace. + +BEGIN_ALLOW_FORBIDDEN_FUNCTIONS +#include "utilities/vmassert_uninstall.hpp" + +#include + +#include "utilities/vmassert_reinstall.hpp" // don't reorder +END_ALLOW_FORBIDDEN_FUNCTIONS + +// AIX may define malloc and calloc as macros when certain other features are +// present, causing us all sorts of grief. +// https://www.ibm.com/docs/en/openxl-c-and-cpp-aix/17.1.4?topic=compilers-memory-allocation +// Replace the macro definitions with something we can work with. +// AIX 7.3 no longer uses macro renaming when building with clang, instead +// using the same asm replacement approach as used below. This workaround can +// be removed once earlier versions are no longer supported as build platforms. +#if defined(AIX) && (defined(__VEC__) || defined(__AIXVEC)) +#if defined(malloc) || defined(calloc) +#if !defined(malloc) || !defined(calloc) +#error "Inconsistent alloc macro mappings, expected both to be mapped." +#endif +// Remove the macros. +#undef malloc +#undef calloc +// Implement the mapping using gcc/clang asm name mapping. +extern "C" { +extern void* malloc(size_t) noexcept asm("vec_malloc"); +extern void* calloc(size_t, size_t) noexcept asm("vec_calloc"); +} // extern "C" +// Because the macros are in place when brings names into the std +// namespace, macro replacement causes the expanded names to be added instead +// of the intended names. We can't remove std::vec_malloc and std::vec_calloc, +// but we do add the standard names in case someone uses them. +namespace std { +using ::malloc; +using ::calloc; +} // namespace std +#endif // Macro definition for malloc or calloc +#endif // AIX altivec allocator support + +// Prefer os:: variants of these. +FORBID_IMPORTED_NORETURN_C_FUNCTION(void exit(int), noexcept, "use os::exit") +FORBID_IMPORTED_NORETURN_C_FUNCTION(void _Exit(int), noexcept, "use os::exit") + +// Windows puts _exit in . POSIX puts it in . +// We can't forbid it here when using clang if it's not in - see +// the clang definition for FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE. +#ifdef _WINDOWS +FORBID_IMPORTED_NORETURN_C_FUNCTION(void _exit(int), /* not noexcept */, "use os::exit") +#endif // _WINDOWS + +// These functions return raw C-heap pointers or, in case of free(), take raw +// C-heap pointers. We generally want allocation to be done through NMT, using +// os::malloc and friends. +FORBID_IMPORTED_C_FUNCTION(void* malloc(size_t), noexcept, "use os::malloc"); +FORBID_IMPORTED_C_FUNCTION(void free(void*), noexcept, "use os::free"); +FORBID_IMPORTED_C_FUNCTION(void* calloc(size_t, size_t), noexcept, "use os::malloc and zero out manually"); +FORBID_IMPORTED_C_FUNCTION(void* realloc(void*, size_t), noexcept, "use os::realloc"); + +// These are not provided (and are unimplementable?) by Windows. +// https://stackoverflow.com/questions/62962839/stdaligned-alloc-missing-from-visual-studio-2019 +// They also aren't useful for a POSIX implementation of NMT. +#ifndef _WINDOWS +FORBID_C_FUNCTION(void* aligned_alloc(size_t, size_t), noexcept, "don't use"); +FORBID_C_FUNCTION(int posix_memalign(void**, size_t, size_t), noexcept, "don't use"); +#endif // !_WINDOWS + +#endif // SHARE_CPPSTDLIB_CSTDLIB_HPP diff --git a/src/hotspot/share/utilities/byteswap.hpp b/src/hotspot/share/utilities/byteswap.hpp index 371095bce49..9fc367c784e 100644 --- a/src/hotspot/share/utilities/byteswap.hpp +++ b/src/hotspot/share/utilities/byteswap.hpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, 2024, Google and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -141,7 +142,7 @@ struct ByteswapImpl : public ByteswapFallbackImpl {}; *****************************************************************************/ #elif defined(TARGET_COMPILER_visCPP) -#include +#include "cppstdlib/cstdlib.hpp" #pragma intrinsic(_byteswap_ushort) #pragma intrinsic(_byteswap_ulong) diff --git a/src/hotspot/share/utilities/forbiddenFunctions.hpp b/src/hotspot/share/utilities/forbiddenFunctions.hpp index 47becd7b4c7..9d1b88e6233 100644 --- a/src/hotspot/share/utilities/forbiddenFunctions.hpp +++ b/src/hotspot/share/utilities/forbiddenFunctions.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 @@ -32,12 +32,6 @@ #include #include -// Workaround for noreturn functions: exit, _exit, _Exit - see the clang -// definition of FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE. -#ifdef __clang__ -#include -#endif - #ifdef _WINDOWS #include "forbiddenFunctions_windows.hpp" #else @@ -49,12 +43,6 @@ // or have security concerns, either with preferred alternatives, or to be // avoided entirely. -FORBID_IMPORTED_NORETURN_C_FUNCTION(void exit(int), noexcept, "use os::exit") -FORBID_IMPORTED_NORETURN_C_FUNCTION(void _Exit(int), noexcept, "use os::exit") - -// Windows puts _exit in , POSIX in . -FORBID_IMPORTED_NORETURN_C_FUNCTION(void _exit(int), /* not noexcept */, "use os::exit") - FORBID_IMPORTED_C_FUNCTION(char* strerror(int), noexcept, "use os::strerror"); FORBID_IMPORTED_C_FUNCTION(char* strtok(char*, const char*), noexcept, "use strtok_r"); @@ -70,13 +58,8 @@ FORBID_C_FUNCTION(int vsprintf(char*, const char*, va_list), noexcept, "use os:: FORBID_C_FUNCTION(int vsnprintf(char*, size_t, const char*, va_list), noexcept, "use os::vsnprintf"); PRAGMA_DIAG_POP -// All of the following functions return raw C-heap pointers (sometimes as an -// option, e.g. realpath or getwd) or, in case of free(), take raw C-heap -// pointers. We generally want allocation to be done through NMT. -FORBID_IMPORTED_C_FUNCTION(void* malloc(size_t size), noexcept, "use os::malloc"); -FORBID_IMPORTED_C_FUNCTION(void free(void *ptr), noexcept, "use os::free"); -FORBID_IMPORTED_C_FUNCTION(void* calloc(size_t nmemb, size_t size), noexcept, "use os::malloc and zero out manually"); -FORBID_IMPORTED_C_FUNCTION(void* realloc(void *ptr, size_t size), noexcept, "use os::realloc"); +// All of the following functions return raw C-heap pointers. We generally +// want allocation to be done through NMT. FORBID_IMPORTED_C_FUNCTION(char* strdup(const char *s), noexcept, "use os::strdup"); FORBID_IMPORTED_C_FUNCTION(wchar_t* wcsdup(const wchar_t *s), noexcept, "don't use"); diff --git a/src/hotspot/share/utilities/globalDefinitions_gcc.hpp b/src/hotspot/share/utilities/globalDefinitions_gcc.hpp index f82f9b1386a..302a62de6c1 100644 --- a/src/hotspot/share/utilities/globalDefinitions_gcc.hpp +++ b/src/hotspot/share/utilities/globalDefinitions_gcc.hpp @@ -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 @@ -31,6 +31,8 @@ // globally used constants & types, class (forward) // declarations and a few frequently used utility functions. +#include "cppstdlib/cstdlib.hpp" + #include #include #include @@ -44,15 +46,6 @@ #include #include #include -#include -// In stdlib.h on AIX malloc is defined as a macro causing -// compiler errors when resolving them in different depths as it -// happens in the log tags. This avoids the macro. -#if (defined(__VEC__) || defined(__AIXVEC)) && defined(AIX) \ - && defined(__open_xl_version__) && __open_xl_version__ >= 17 - #undef malloc - extern void *malloc(size_t) asm("vec_malloc"); -#endif #include #include #include diff --git a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp index b9d25096cd5..dfd6f2f1880 100644 --- a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp +++ b/src/hotspot/share/utilities/globalDefinitions_visCPP.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 @@ -34,6 +34,8 @@ // Need this on windows to get the math constants (e.g., M_PI). #define _USE_MATH_DEFINES +#include "cppstdlib/cstdlib.hpp" + # include # include # include // for _isnan @@ -45,7 +47,6 @@ # include // for offsetof # include # include -# include # include # include # include diff --git a/src/hotspot/share/utilities/parseInteger.hpp b/src/hotspot/share/utilities/parseInteger.hpp index 3e3eafada87..8840275a8cb 100644 --- a/src/hotspot/share/utilities/parseInteger.hpp +++ b/src/hotspot/share/utilities/parseInteger.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. * Copyright (c) 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,6 +26,7 @@ #ifndef SHARE_UTILITIES_PARSE_INTEGER_HPP #define SHARE_UTILITIES_PARSE_INTEGER_HPP +#include "cppstdlib/cstdlib.hpp" #include "cppstdlib/limits.hpp" #include "metaprogramming/enableIf.hpp" #include "utilities/debug.hpp" @@ -33,7 +34,6 @@ #include "utilities/macros.hpp" #include -#include // ************************************************************************* // ** Attention compatibility! ** diff --git a/src/hotspot/share/utilities/permitForbiddenFunctions.hpp b/src/hotspot/share/utilities/permitForbiddenFunctions.hpp index 1ba42f8e386..71719ac8a76 100644 --- a/src/hotspot/share/utilities/permitForbiddenFunctions.hpp +++ b/src/hotspot/share/utilities/permitForbiddenFunctions.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 @@ -25,6 +25,7 @@ #ifndef SHARE_UTILITIES_PERMITFORBIDDENFUNCTIONS_HPP #define SHARE_UTILITIES_PERMITFORBIDDENFUNCTIONS_HPP +#include "cppstdlib/cstdlib.hpp" #include "utilities/compilerWarnings.hpp" #include "utilities/globalDefinitions.hpp" @@ -34,6 +35,9 @@ #include "permitForbiddenFunctions_posix.hpp" #endif +#include +#include + // Provide wrappers for some functions otherwise forbidden from use in HotSpot. // // There may be special circumstances where an otherwise forbidden function @@ -53,7 +57,6 @@ namespace permit_forbidden_function { BEGIN_ALLOW_FORBIDDEN_FUNCTIONS [[noreturn]] inline void exit(int status) { ::exit(status); } -[[noreturn]] inline void _exit(int status) { ::_exit(status); } ATTRIBUTE_PRINTF(3, 0) inline int vsnprintf(char* str, size_t size, const char* format, va_list ap) { diff --git a/test/hotspot/gtest/gtestMain.cpp b/test/hotspot/gtest/gtestMain.cpp index 842b6547b48..a1869ab5499 100644 --- a/test/hotspot/gtest/gtestMain.cpp +++ b/test/hotspot/gtest/gtestMain.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 @@ -22,6 +22,7 @@ * */ +#include "cppstdlib/cstdlib.hpp" #include "jni.h" #include "runtime/os.hpp" #include "runtime/thread.inline.hpp" @@ -31,7 +32,6 @@ #include #include -#include #ifdef __APPLE__ #include #endif diff --git a/test/hotspot/gtest/unittest.hpp b/test/hotspot/gtest/unittest.hpp index 336a2f0766e..6a2cd400cb6 100644 --- a/test/hotspot/gtest/unittest.hpp +++ b/test/hotspot/gtest/unittest.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. * 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,9 +24,9 @@ #ifndef UNITTEST_HPP #define UNITTEST_HPP +#include "cppstdlib/cstdlib.hpp" #include "utilities/globalDefinitions.hpp" -#include #include #define GTEST_DONT_DEFINE_TEST 1 diff --git a/test/hotspot/gtest/utilities/test_bitMap_setops.cpp b/test/hotspot/gtest/utilities/test_bitMap_setops.cpp index 2b1e67533df..1def9c89cfc 100644 --- a/test/hotspot/gtest/utilities/test_bitMap_setops.cpp +++ b/test/hotspot/gtest/utilities/test_bitMap_setops.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 @@ -21,6 +21,7 @@ * questions. */ +#include "cppstdlib/cstdlib.hpp" #include "runtime/os.hpp" #include "utilities/align.hpp" #include "utilities/bitMap.inline.hpp" @@ -29,8 +30,6 @@ #include "utilities/globalDefinitions.hpp" #include "unittest.hpp" -#include - typedef BitMap::idx_t idx_t; typedef BitMap::bm_word_t bm_word_t; From 0a1fa219214b985e4c7d9e612bd5cda1b0f25577 Mon Sep 17 00:00:00 2001 From: Chad Rakoczy Date: Thu, 8 Jan 2026 01:14:01 +0000 Subject: [PATCH 012/204] 8369150: NMethodRelocationTest fails when JVMTI events not published before JVM exit Reviewed-by: lmesnik, sspitsyn --- test/hotspot/jtreg/ProblemList.txt | 2 - .../NMethodRelocationTest.java | 119 ++++-------------- .../libNMethodRelocationTest.cpp | 115 +++++++++++------ test/lib/jdk/test/lib/jvmti/jvmti_common.hpp | 17 +++ 4 files changed, 121 insertions(+), 132 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 4e16e328ab4..13e1ea30a34 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -77,8 +77,6 @@ compiler/interpreter/Test6833129.java 8335266 generic-i586 compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 -serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java 8369150 generic-all - ############################################################################# # :hotspot_gc diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java index 6c465b357d7..10888dce1b4 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java @@ -23,23 +23,30 @@ /* * @test - * * @bug 8316694 * @summary Verify that nmethod relocation posts the correct JVMTI events - * @requires vm.jvmti - * @requires vm.gc == "null" | vm.gc == "Serial" + * @requires vm.jvmti & + * vm.gc != "Epsilon" & + * vm.flavor == "server" & + * !vm.emulatedClient & + * (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) * @library /test/lib /test/hotspot/jtreg * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm/native NMethodRelocationTest + * @run main/othervm/native -agentlib:NMethodRelocationTest + * --enable-native-access=ALL-UNNAMED + * -Xbootclasspath/a:. + * -Xbatch + * -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * -XX:+SegmentedCodeCache + * -XX:-TieredCompilation + * -XX:+UnlockExperimentalVMOptions + * -XX:+NMethodRelocation + * NMethodRelocationTest */ -import static compiler.whitebox.CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION; - import java.lang.reflect.Executable; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import jdk.test.lib.Asserts; import jdk.test.lib.process.OutputAnalyzer; @@ -48,52 +55,9 @@ import jdk.test.whitebox.WhiteBox; import jdk.test.whitebox.code.BlobType; import jdk.test.whitebox.code.NMethod; +import static compiler.whitebox.CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION; public class NMethodRelocationTest { - public static void main(String[] args) throws Exception { - ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( - "-agentlib:NMethodRelocationTest", - "--enable-native-access=ALL-UNNAMED", - "-Xbootclasspath/a:.", - "-XX:+UseSerialGC", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+WhiteBoxAPI", - "-XX:+SegmentedCodeCache", - "-XX:-TieredCompilation", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+NMethodRelocation", - "DoWork"); - - OutputAnalyzer oa = new OutputAnalyzer(pb.start()); - String output = oa.getOutput(); - if (oa.getExitValue() != 0) { - System.err.println(oa.getOutput()); - throw new RuntimeException("Non-zero exit code returned from the test"); - } - Asserts.assertTrue(oa.getExitValue() == 0); - - Pattern pattern = Pattern.compile("(?m)^Relocated nmethod from (0x[0-9a-f]{16}) to (0x[0-9a-f]{16})$"); - Matcher matcher = pattern.matcher(output); - - if (matcher.find()) { - String fromAddr = matcher.group(1); - String toAddr = matcher.group(2); - - // Confirm events sent for both original and relocated nmethod - oa.shouldContain(": name: compiledMethod, code: " + fromAddr); - oa.shouldContain(": name: compiledMethod, code: " + toAddr); - oa.shouldContain(": name: compiledMethod, code: " + fromAddr); - oa.shouldContain(": name: compiledMethod, code: " + toAddr); - } else { - System.err.println(oa.getOutput()); - throw new RuntimeException("Unable to find relocation information"); - } - } -} - -class DoWork { - - protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); /** Load native library if required. */ static { @@ -107,43 +71,18 @@ class DoWork { } } - /** - * Returns value of VM option. - * - * @param name option's name - * @return value of option or {@code null}, if option doesn't exist - * @throws NullPointerException if name is null - */ - protected static String getVMOption(String name) { - Objects.requireNonNull(name); - return Objects.toString(WHITE_BOX.getVMFlag(name), null); - } + protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); - /** - * Returns value of VM option or default value. - * - * @param name option's name - * @param defaultValue default value - * @return value of option or {@code defaultValue}, if option doesn't exist - * @throws NullPointerException if name is null - * @see #getVMOption(String) - */ - protected static String getVMOption(String name, String defaultValue) { - String result = getVMOption(name); - return result == null ? defaultValue : result; - } + native static boolean shouldExit(); - public static void main(String argv[]) throws Exception { - run(); - } - - public static void run() throws Exception { - Executable method = DoWork.class.getDeclaredMethod("compiledMethod"); + public static void main(String[] argv) throws Exception { + Executable method = NMethodRelocationTest.class.getDeclaredMethod("compiledMethod"); WHITE_BOX.testSetDontInlineMethod(method, true); WHITE_BOX.enqueueMethodForCompilation(method, COMP_LEVEL_FULL_OPTIMIZATION); - while (WHITE_BOX.isMethodQueuedForCompilation(method)) { - Thread.onSpinWait(); + + if (!WHITE_BOX.isMethodCompiled(method)) { + throw new AssertionError("Method not compiled"); } NMethod originalNMethod = NMethod.get(method, false); @@ -164,13 +103,9 @@ class DoWork { WHITE_BOX.deoptimizeAll(); - WHITE_BOX.fullGC(); - WHITE_BOX.fullGC(); - - WHITE_BOX.lockCompilation(); - - System.out.printf("Relocated nmethod from 0x%016x to 0x%016x%n", originalNMethod.code_begin, relocatedNMethod.code_begin); - System.out.flush(); + while (!shouldExit()) { + WHITE_BOX.fullGC(); + } } public static long compiledMethod() { diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp index 41ba6b10608..7f8ab7b78bc 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp @@ -21,10 +21,20 @@ * questions. */ +#include #include -#include -#include -#include +#include "jvmti_common.hpp" + +extern "C" { + +// Track nmethod addresses for LOAD and UNLOAD events +static const void* first_load_addr = nullptr; +static const void* second_load_addr = nullptr; +static const void* first_unload_addr = nullptr; +static const void* second_unload_addr = nullptr; + +// Keep track of test completion +static std::atomic should_exit{false}; /** * Callback for COMPILED_METHOD_LOAD event. @@ -34,18 +44,27 @@ callbackCompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method, jint code_size, const void* code_addr, jint map_length, const jvmtiAddrLocationMap* map, const void* compile_info) { - char* name = nullptr; - char* sig = nullptr; - if (jvmti->GetMethodName(method, &name, &sig, nullptr) != JVMTI_ERROR_NONE) { - printf(" [Could not retrieve method name]\n"); - fflush(stdout); + // Only track events for "compiledMethod" + char* name = get_method_name(jvmti, method); + if (strcmp(name, "compiledMethod") != 0) { return; } - printf(": name: %s, code: 0x%016" PRIxPTR "\n", - name, (uintptr_t)code_addr); - fflush(stdout); + LOG(": name: %s, code: 0x%016" PRIxPTR "\n", name, (uintptr_t)code_addr); + + if (first_load_addr == nullptr) { + first_load_addr = code_addr; + } else if (second_load_addr == nullptr) { + second_load_addr = code_addr; + + // Verify that the addresses are different + if (first_load_addr == second_load_addr) { + fatal("Load events for 'compiledMethod' are expected to use different addresses"); + } + } else { + fatal("Received too many load events for 'compiledMethod'"); + } } /** @@ -54,25 +73,50 @@ callbackCompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method, JNIEXPORT void JNICALL callbackCompiledMethodUnload(jvmtiEnv* jvmti, jmethodID method, const void* code_addr) { - char* name = nullptr; - char* sig = nullptr; - if (jvmti->GetMethodName(method, &name, &sig, nullptr) != JVMTI_ERROR_NONE) { - printf(" [Could not retrieve method name]\n"); - fflush(stdout); + // Only track events for "compiledMethod" + char* name = get_method_name(jvmti, method); + if (strcmp(name, "compiledMethod") != 0) { return; } - printf(": name: %s, code: 0x%016" PRIxPTR "\n", - name, (uintptr_t)code_addr); - fflush(stdout); + + LOG(": name: %s, code: 0x%016" PRIxPTR "\n", name, (uintptr_t)code_addr); + + // Validate both loads have occurred + if (first_load_addr == nullptr || second_load_addr == nullptr) { + fatal("UNLOAD event for 'compiledMethod' occurred before both LOAD events"); + } + + if (first_unload_addr == nullptr) { + first_unload_addr = code_addr; + } else if (second_unload_addr == nullptr) { + second_unload_addr = code_addr; + + // Verify that the addresses are different + if (first_unload_addr == second_unload_addr) { + fatal("Unload events for 'compiledMethod' are expected to use different addresses"); + } + + // LOAD and UNLOAD events should report the same two addresses, but the order of + // the UNLOADs is not guaranteed, since the GC may unload either nmethod first. + if ((first_load_addr == first_unload_addr && second_load_addr == second_unload_addr) || + (first_load_addr == second_unload_addr && second_load_addr == first_unload_addr)) { + + // Update should_exit to signal test completion + should_exit.store(true); + } else { + fatal("Address mismatch for 'compiledMethod' events"); + } + } else { + fatal("Received too many unload events for 'compiledMethod'"); + } } JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { jvmtiEnv* jvmti = nullptr; - jvmtiError error; if (jvm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_0) != JNI_OK) { - printf("Unable to access JVMTI!\n"); + LOG("Unable to access JVMTI!\n"); return JNI_ERR; } @@ -80,11 +124,8 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) jvmtiCapabilities caps; memset(&caps, 0, sizeof(caps)); caps.can_generate_compiled_method_load_events = 1; - error = jvmti->AddCapabilities(&caps); - if (error != JVMTI_ERROR_NONE) { - printf("ERROR: Unable to add capabilities, error=%d\n", error); - return JNI_ERR; - } + jvmtiError error = jvmti->AddCapabilities(&caps); + check_jvmti_error(error, "Unable to add capabilities"); // Set event callbacks jvmtiEventCallbacks eventCallbacks; @@ -92,23 +133,21 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) eventCallbacks.CompiledMethodLoad = callbackCompiledMethodLoad; eventCallbacks.CompiledMethodUnload = callbackCompiledMethodUnload; error = jvmti->SetEventCallbacks(&eventCallbacks, sizeof(eventCallbacks)); - if (error != JVMTI_ERROR_NONE) { - printf("ERROR: Unable to set event callbacks, error=%d\n", error); - return JNI_ERR; - } + check_jvmti_error(error, "Unable to set event callbacks"); // Enable events error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, nullptr); - if (error != JVMTI_ERROR_NONE) { - printf("ERROR: Unable to enable COMPILED_METHOD_LOAD event, error=%d\n", error); - return JNI_ERR; - } + check_jvmti_error(error, "Unable to enable COMPILED_METHOD_LOAD event"); error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, nullptr); - if (error != JVMTI_ERROR_NONE) { - printf("ERROR: Unable to enable COMPILED_METHOD_UNLOAD event, error=%d\n", error); - return JNI_ERR; - } + check_jvmti_error(error, "Unable to enable COMPILED_METHOD_UNLOAD event"); return JNI_OK; } + +JNIEXPORT jboolean JNICALL +Java_NMethodRelocationTest_shouldExit(JNIEnv *env, jclass cls) { + return should_exit.load(); +} + +} diff --git a/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp b/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp index 11600bd524b..3832d934b1e 100644 --- a/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp +++ b/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp @@ -123,6 +123,12 @@ char* julong_to_string(julong value, char *string) { return string; } +static void +fatal(const char* msg) { + LOG("FATAL ERROR: %s\n", msg); + abort(); +} + static void fatal(JNIEnv* jni, const char* msg) { jni->FatalError(msg); @@ -313,6 +319,17 @@ get_thread_name(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread) { return tname; } +static char* +get_method_name(jvmtiEnv *jvmti, jmethodID method) { + char* mname = nullptr; + jvmtiError err; + + err = jvmti->GetMethodName(method, &mname, nullptr, nullptr); + check_jvmti_error(err, "get_method_name: error in JVMTI GetMethodName call"); + + return mname; +} + static char* get_method_name(jvmtiEnv *jvmti, JNIEnv* jni, jmethodID method) { char* mname = nullptr; From 70669d0585c708e04befe0f9ba945f6154f9afec Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Thu, 8 Jan 2026 04:43:06 +0000 Subject: [PATCH 013/204] 8374712: AOTMappedHeapWriter::relocate_field_in_buffer should use CompressedOops::narrow_oop_cast Reviewed-by: kvn --- src/hotspot/share/cds/aotMappedHeapWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp index 9c67b2e0e8d..e73b980614a 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp @@ -696,7 +696,7 @@ template void AOTMappedHeapWriter::relocate_field_in_buffer(T* fiel // We use zero-based, 0-shift encoding, so the narrowOop is just the lower // 32 bits of request_referent intptr_t addr = cast_from_oop(request_referent); - *((narrowOop*)field_addr_in_buffer) = checked_cast(addr); + *((narrowOop*)field_addr_in_buffer) = CompressedOops::narrow_oop_cast(addr); } else { store_requested_oop_in_buffer(field_addr_in_buffer, request_referent); } From 95137580b81fb48474b0d8fb748d9d4af7a27850 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Thu, 8 Jan 2026 05:31:06 +0000 Subject: [PATCH 014/204] 8374662: Remove unused type check functions from javaClasses.hpp Reviewed-by: jsjolen --- src/hotspot/share/classfile/javaClasses.cpp | 10 +--------- src/hotspot/share/classfile/javaClasses.hpp | 16 +--------------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 77a94c8afa5..dd70d7b49ab 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.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 @@ -4297,10 +4297,6 @@ int jdk_internal_foreign_abi_NativeEntryPoint::_downcall_stub_address_offset; macro(_method_type_offset, k, "methodType", java_lang_invoke_MethodType_signature, false); \ macro(_downcall_stub_address_offset, k, "downcallStubAddress", long_signature, false); -bool jdk_internal_foreign_abi_NativeEntryPoint::is_instance(oop obj) { - return obj != nullptr && is_subclass(obj->klass()); -} - void jdk_internal_foreign_abi_NativeEntryPoint::compute_offsets() { InstanceKlass* k = vmClasses::NativeEntryPoint_klass(); NEP_FIELDS_DO(FIELD_COMPUTE_OFFSET); @@ -4337,10 +4333,6 @@ int jdk_internal_foreign_abi_ABIDescriptor::_scratch2_offset; macro(_scratch1_offset, k, "scratch1", jdk_internal_foreign_abi_VMStorage_signature, false); \ macro(_scratch2_offset, k, "scratch2", jdk_internal_foreign_abi_VMStorage_signature, false); -bool jdk_internal_foreign_abi_ABIDescriptor::is_instance(oop obj) { - return obj != nullptr && is_subclass(obj->klass()); -} - void jdk_internal_foreign_abi_ABIDescriptor::compute_offsets() { InstanceKlass* k = vmClasses::ABIDescriptor_klass(); ABIDescriptor_FIELDS_DO(FIELD_COMPUTE_OFFSET); diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index 33dc912404c..a8562a345c8 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.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 @@ -1179,13 +1179,6 @@ class jdk_internal_foreign_abi_NativeEntryPoint: AllStatic { static oop method_type(oop entry); static jlong downcall_stub_address(oop entry); - // Testers - static bool is_subclass(Klass* klass) { - return vmClasses::NativeEntryPoint_klass() != nullptr && - klass->is_subclass_of(vmClasses::NativeEntryPoint_klass()); - } - static bool is_instance(oop obj); - // Accessors for code generation: static int method_type_offset_in_bytes() { return _method_type_offset; } static int downcall_stub_address_offset_in_bytes() { return _downcall_stub_address_offset; } @@ -1216,13 +1209,6 @@ class jdk_internal_foreign_abi_ABIDescriptor: AllStatic { static jint shadowSpace(oop entry); static oop scratch1(oop entry); static oop scratch2(oop entry); - - // Testers - static bool is_subclass(Klass* klass) { - return vmClasses::ABIDescriptor_klass() != nullptr && - klass->is_subclass_of(vmClasses::ABIDescriptor_klass()); - } - static bool is_instance(oop obj); }; class jdk_internal_foreign_abi_VMStorage: AllStatic { From e6abf98e35079ed1b5547f2cc0ac6f518b78d67b Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Thu, 8 Jan 2026 07:01:03 +0000 Subject: [PATCH 015/204] 8374434: Several JShell tests report JUnit discovery warnings Reviewed-by: jpai --- test/langtools/jdk/jshell/ErrorTranslationTest.java | 3 +-- test/langtools/jdk/jshell/IdGeneratorTest.java | 3 +-- test/langtools/jdk/jshell/KullaCompletenessStressTest.java | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/test/langtools/jdk/jshell/ErrorTranslationTest.java b/test/langtools/jdk/jshell/ErrorTranslationTest.java index 1aa1b2c41dd..235f4443004 100644 --- a/test/langtools/jdk/jshell/ErrorTranslationTest.java +++ b/test/langtools/jdk/jshell/ErrorTranslationTest.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 @@ -149,7 +149,6 @@ public class ErrorTranslationTest extends ReplToolTesting { return sb.toString(); } - @Test public String getKind(Diagnostic.Kind kind) { switch (kind) { case WARNING: diff --git a/test/langtools/jdk/jshell/IdGeneratorTest.java b/test/langtools/jdk/jshell/IdGeneratorTest.java index 6c7f6177e03..521c87b0265 100644 --- a/test/langtools/jdk/jshell/IdGeneratorTest.java +++ b/test/langtools/jdk/jshell/IdGeneratorTest.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 @@ -44,7 +44,6 @@ import org.junit.jupiter.api.Test; public class IdGeneratorTest { - @Test public JShell.Builder getBuilder() { TestingInputStream inStream = new TestingInputStream(); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); diff --git a/test/langtools/jdk/jshell/KullaCompletenessStressTest.java b/test/langtools/jdk/jshell/KullaCompletenessStressTest.java index 69830880dcb..16274fa924e 100644 --- a/test/langtools/jdk/jshell/KullaCompletenessStressTest.java +++ b/test/langtools/jdk/jshell/KullaCompletenessStressTest.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 @@ -32,13 +32,11 @@ import java.io.File; import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class KullaCompletenessStressTest extends CompletenessStressTest { @Override - @Test public File[] getDirectoriesToTest() { String src = System.getProperty("test.src"); File file; From 1a6da4499cf8805ff3e1e517fbca81c2eeb987a9 Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Thu, 8 Jan 2026 08:14:57 +0000 Subject: [PATCH 016/204] 8374467: Incorrect ranges in jdk.internal.util.ByteArray JavaDoc Reviewed-by: rriggs --- .../share/classes/jdk/internal/util/ByteArray.java | 14 +++++++------- .../jdk/internal/util/ByteArrayLittleEndian.java | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArray.java b/src/java.base/share/classes/jdk/internal/util/ByteArray.java index 94395df2c34..92b9dbbef19 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArray.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArray.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 @@ -241,7 +241,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length] + * the range [0, array.length - 1] * @see #getBoolean(byte[], int) */ public static void setBoolean(byte[] array, int offset, boolean value) { @@ -328,7 +328,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 4] * @see #getFloat(byte[], int) */ public static void setFloat(byte[] array, int offset, float value) { @@ -350,7 +350,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 4] * @see #getFloatRaw(byte[], int) */ public static void setFloatRaw(byte[] array, int offset, float value) { @@ -368,7 +368,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 4] + * the range [0, array.length - 8] * @see #getLong(byte[], int) */ public static void setLong(byte[] array, int offset, long value) { @@ -387,7 +387,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 8] * @see #getDouble(byte[], int) */ public static void setDouble(byte[] array, int offset, double value) { @@ -409,7 +409,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 8] * @see #getDoubleRaw(byte[], int) */ public static void setDoubleRaw(byte[] array, int offset, double value) { diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java b/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java index fcd7ca2b9bf..cc50686429b 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.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 @@ -241,7 +241,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length] + * the range [0, array.length - 1] * @see #getBoolean(byte[], int) */ public static void setBoolean(byte[] array, int offset, boolean value) { @@ -328,7 +328,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 4] * @see #getFloat(byte[], int) */ public static void setFloat(byte[] array, int offset, float value) { @@ -350,7 +350,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 4] * @see #getFloatRaw(byte[], int) */ public static void setFloatRaw(byte[] array, int offset, float value) { @@ -368,7 +368,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 4] + * the range [0, array.length - 8] * @see #getLong(byte[], int) */ public static void setLong(byte[] array, int offset, long value) { @@ -387,7 +387,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 8] * @see #getDouble(byte[], int) */ public static void setDouble(byte[] array, int offset, double value) { @@ -409,7 +409,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 8] * @see #getDoubleRaw(byte[], int) */ public static void setDoubleRaw(byte[] array, int offset, double value) { From a71326a0e2660158fdb85282da4b59ce61c66ee3 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 8 Jan 2026 08:32:02 +0000 Subject: [PATCH 017/204] 8374528: C2 SuperWord: TestAliasingFuzzer.java strengthen no-multiversioning IR rule Reviewed-by: chagedorn, mhaessig --- .../superword/TestAliasingFuzzer.java | 45 +++++++------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java index 5d20ce659b9..825d5ca60bc 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.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 @@ -1052,35 +1052,20 @@ public class TestAliasingFuzzer { case Aliasing.CONTAINER_DIFFERENT, Aliasing.CONTAINER_SAME_ALIASING_NEVER, Aliasing.CONTAINER_UNKNOWN_ALIASING_NEVER -> - // We would have liked to check that there is no multiversioning. - // - // But sadly there are some cases that have issues with RCE and/or - // predicates, and so we end up using multiversioning anyway. We - // should fix those cases eventually, to strengthen the checks here. - // - // The array cases are a little more tame, and do not have the same - // issues as the MemorySegment cases. - (containerKind == ContainerKind.ARRAY) - ? """ - // Aliasing check should never fail at runtime, so the predicate - // should never fail, and we do not have to use multiversioning. - // Failure could have a few causes: - // - issues with doing RCE / missing predicates - // -> other loop-opts need to be fixed - // - predicate fails: recompile with multiversioning - // -> logic in runtime check may be wrong - @IR(counts = {".*multiversion.*", "= 0"}, - phase = CompilePhase.PRINT_IDEAL, - applyIf = {"UseAutoVectorizationPredicate", "true"}, - applyIfPlatform = {"64-bit", "true"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) - """ - : """ - // Due to cases like JDK-8360204 and JDK-8365982, there can be issues - // with RCE leading cases where we remove predicates and then unroll again - // and then end up multiversioning. These cases seem relatively rare but - // prevent us from asserting that there is never multiversioning in these cases. - """; + """ + // Aliasing check should never fail at runtime, so the predicate + // should never fail, and we do not have to use multiversioning. + // Failure could have a few causes: + // - issues with doing RCE / missing predicates + // -> other loop-opts need to be fixed + // - predicate fails: recompile with multiversioning + // -> logic in runtime check may be wrong + @IR(counts = {".*multiversion.*", "= 0"}, + phase = CompilePhase.PRINT_IDEAL, + applyIf = {"UseAutoVectorizationPredicate", "true"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + """; case Aliasing.CONTAINER_SAME_ALIASING_UNKNOWN, Aliasing.CONTAINER_UNKNOWN_ALIASING_UNKNOWN -> """ From 08ff16f0aa8eaa9596da52d568720c69c897f3c5 Mon Sep 17 00:00:00 2001 From: Ramkumar Sunderbabu Date: Thu, 8 Jan 2026 09:25:11 +0000 Subject: [PATCH 018/204] 8374576: Disable MemoryEaterMT for VirtualThread Reviewed-by: lmesnik, dholmes --- .../vmTestbase/gc/gctests/MemoryEaterMT/MemoryEaterMT.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/vmTestbase/gc/gctests/MemoryEaterMT/MemoryEaterMT.java b/test/hotspot/jtreg/vmTestbase/gc/gctests/MemoryEaterMT/MemoryEaterMT.java index c97353006d4..d88e34ae5e0 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/gctests/MemoryEaterMT/MemoryEaterMT.java +++ b/test/hotspot/jtreg/vmTestbase/gc/gctests/MemoryEaterMT/MemoryEaterMT.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * 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 @@ * @summary converted from VM Testbase gc/gctests/MemoryEaterMT. * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent] * + * @requires test.thread.factory != "Virtual" * @library /vmTestbase * /test/lib * @run main/othervm -XX:-UseGCOverheadLimit gc.gctests.MemoryEaterMT.MemoryEaterMT From 067fd3cb2fa6a4a0484a922df8efbde03325ad3d Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Thu, 8 Jan 2026 09:32:51 +0000 Subject: [PATCH 019/204] 8374768: S390X builds are failing after JDK-8372754 Reviewed-by: stefank, mdoerr --- src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp b/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp index 9982b5860a8..749f6bec032 100644 --- a/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp +++ b/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp @@ -30,7 +30,7 @@ #include "code/nativeInst.hpp" #include "code/vtableStubs.hpp" #include "compiler/disassembler.hpp" -#include "cppstdlib/cstdlib.h" +#include "cppstdlib/cstdlib.hpp" #include "interpreter/interpreter.hpp" #include "jvm.h" #include "memory/allocation.inline.hpp" From 904ba5f5ed7d3ac1a3606ff7532ba3c206a2d9b9 Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Thu, 8 Jan 2026 10:24:03 +0000 Subject: [PATCH 020/204] 8374718: Generation of CompilerProperties can fail in subtle ways Reviewed-by: jlahoda --- .../tools/propertiesparser/gen/ClassGenerator.java | 9 +++++++-- .../resources/templates.properties | 2 +- .../classes/com/sun/tools/javac/code/Lint.java | 14 +++++++++----- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/make/langtools/tools/propertiesparser/gen/ClassGenerator.java b/make/langtools/tools/propertiesparser/gen/ClassGenerator.java index 247537b4676..14e8c4fb00a 100644 --- a/make/langtools/tools/propertiesparser/gen/ClassGenerator.java +++ b/make/langtools/tools/propertiesparser/gen/ClassGenerator.java @@ -286,7 +286,7 @@ public class ClassGenerator { diagnosticFlags.isEmpty() ? StubKind.DIAGNOSTIC_FLAGS_EMPTY.format() : StubKind.DIAGNOSTIC_FLAGS_NON_EMPTY.format(diagnosticFlags), - StubKind.LINT_CATEGORY.format("\"" + lintCategory + "\""), + StubKind.LINT_CATEGORY.format(toLintFieldName(lintCategory)), "\"" + keyParts[0] + "\"", "\"" + Stream.of(keyParts).skip(2).collect(Collectors.joining(".")) + "\"", javadoc); @@ -314,7 +314,7 @@ public class ClassGenerator { diagnosticFlags.isEmpty() ? StubKind.DIAGNOSTIC_FLAGS_EMPTY.format() : StubKind.DIAGNOSTIC_FLAGS_NON_EMPTY.format(diagnosticFlags), - StubKind.LINT_CATEGORY.format("\"" + lintCategory + "\""), + StubKind.LINT_CATEGORY.format(toLintFieldName(lintCategory)), "\"" + keyParts[0] + "\"", "\"" + Stream.of(keyParts).skip(2).collect(Collectors.joining(".")) + "\"", argNames.stream().collect(Collectors.joining(", "))); @@ -329,6 +329,11 @@ public class ClassGenerator { } } + String toLintFieldName(String lintCategory) { + return lintCategory.toUpperCase() + .replaceAll("-", "_"); + } + /** * Form the name of a factory method/field given a resource key. */ diff --git a/make/langtools/tools/propertiesparser/resources/templates.properties b/make/langtools/tools/propertiesparser/resources/templates.properties index 81a9be2552c..f8ff07a878f 100644 --- a/make/langtools/tools/propertiesparser/resources/templates.properties +++ b/make/langtools/tools/propertiesparser/resources/templates.properties @@ -87,7 +87,7 @@ suppress.warnings=\ @SuppressWarnings("rawtypes")\n lint.category=\ - LintCategory.get({0}).get() + LintCategory.{0} diagnostic.flags.empty=\ EnumSet.noneOf(DiagnosticFlag.class) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index 3a8b4e5dbea..773c573c201 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -37,11 +37,7 @@ import java.util.Set; import java.util.stream.Stream; import com.sun.tools.javac.main.Option; -import com.sun.tools.javac.tree.JCTree.*; -import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; -import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Options; @@ -178,6 +174,14 @@ public class Lint { /** * Categories of warnings that can be generated by the compiler. + * Each lint category has a logical name (a string), which is the string used e.g. in a {@code SuppressWarning} annotation. + * To ensure automation, the enum field name for a lint category string {@code C} should be obtained by: + *

    + *
  1. capitalize all the letters in {@code C}, and
  2. + *
  3. replacing any occurrence of {@code -} with {@code _}
  4. + *
+ * For instance, the lint category string {@code dangling-doc-comments} corresponds to the enum field + * {@code DANGLING_DOC_COMMENTS}. */ public enum LintCategory { /** @@ -320,7 +324,7 @@ public class Lint { /** * Warn about unchecked operations on raw types. */ - RAW("rawtypes"), + RAWTYPES("rawtypes"), /** * Warn about use of deprecated-for-removal items. From c5159fc9fa0fd81dec629cd821b3411b4a6df967 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Thu, 8 Jan 2026 11:07:08 +0000 Subject: [PATCH 021/204] 8374328: Convert simple AtomicAccess uses in gc/shared to use Atomic Reviewed-by: dholmes, tschatzl --- .../share/gc/shared/barrierSetNMethod.cpp | 7 +++--- .../share/gc/shared/concurrentGCThread.cpp | 14 +++++------ .../share/gc/shared/concurrentGCThread.hpp | 7 +++--- src/hotspot/share/gc/shared/gcLocker.cpp | 23 ++++++++----------- src/hotspot/share/gc/shared/gcLocker.hpp | 7 +++--- .../share/gc/shared/gcLocker.inline.hpp | 8 +++---- src/hotspot/share/gc/shared/pretouchTask.cpp | 8 +++---- src/hotspot/share/gc/shared/pretouchTask.hpp | 6 +++-- .../share/gc/shared/suspendibleThreadSet.cpp | 8 +++---- .../share/gc/shared/suspendibleThreadSet.hpp | 8 +++---- src/hotspot/share/gc/shared/workerThread.cpp | 15 +++++++----- src/hotspot/share/gc/shared/workerThread.hpp | 7 +++--- 12 files changed, 62 insertions(+), 56 deletions(-) diff --git a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp index cb5d6b5a886..ab94bae079a 100644 --- a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp +++ b/src/hotspot/share/gc/shared/barrierSetNMethod.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 @@ -33,6 +33,7 @@ #include "memory/universe.hpp" #include "oops/access.inline.hpp" #include "oops/method.inline.hpp" +#include "runtime/atomic.hpp" #include "runtime/frame.inline.hpp" #include "runtime/javaThread.hpp" #include "runtime/threads.hpp" @@ -196,8 +197,8 @@ int BarrierSetNMethod::nmethod_stub_entry_barrier(address* return_address_ptr) { // Diagnostic option to force deoptimization 1 in 10 times. It is otherwise // a very rare event. if (DeoptimizeNMethodBarriersALot && !nm->is_osr_method()) { - static volatile uint32_t counter=0; - if (AtomicAccess::add(&counter, 1u) % 10 == 0) { + static Atomic counter{0}; + if (counter.add_then_fetch(1u) % 10 == 0) { may_enter = false; } } diff --git a/src/hotspot/share/gc/shared/concurrentGCThread.cpp b/src/hotspot/share/gc/shared/concurrentGCThread.cpp index ac281f82f8a..ed6c1b4d283 100644 --- a/src/hotspot/share/gc/shared/concurrentGCThread.cpp +++ b/src/hotspot/share/gc/shared/concurrentGCThread.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 @@ -23,7 +23,7 @@ */ #include "gc/shared/concurrentGCThread.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/init.hpp" #include "runtime/jniHandles.hpp" #include "runtime/mutexLocker.hpp" @@ -48,7 +48,7 @@ void ConcurrentGCThread::run() { // Signal thread has terminated MonitorLocker ml(Terminator_lock); - AtomicAccess::release_store(&_has_terminated, true); + _has_terminated.release_store(true); ml.notify_all(); } @@ -57,21 +57,21 @@ void ConcurrentGCThread::stop() { assert(!has_terminated(), "Invalid state"); // Signal thread to terminate - AtomicAccess::release_store_fence(&_should_terminate, true); + _should_terminate.release_store_fence(true); stop_service(); // Wait for thread to terminate MonitorLocker ml(Terminator_lock); - while (!_has_terminated) { + while (!_has_terminated.load_relaxed()) { ml.wait(); } } bool ConcurrentGCThread::should_terminate() const { - return AtomicAccess::load_acquire(&_should_terminate); + return _should_terminate.load_acquire(); } bool ConcurrentGCThread::has_terminated() const { - return AtomicAccess::load_acquire(&_has_terminated); + return _has_terminated.load_acquire(); } diff --git a/src/hotspot/share/gc/shared/concurrentGCThread.hpp b/src/hotspot/share/gc/shared/concurrentGCThread.hpp index 630abeaeb9f..0c764546045 100644 --- a/src/hotspot/share/gc/shared/concurrentGCThread.hpp +++ b/src/hotspot/share/gc/shared/concurrentGCThread.hpp @@ -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 @@ -25,14 +25,15 @@ #ifndef SHARE_GC_SHARED_CONCURRENTGCTHREAD_HPP #define SHARE_GC_SHARED_CONCURRENTGCTHREAD_HPP +#include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" #include "runtime/nonJavaThread.hpp" #include "utilities/debug.hpp" class ConcurrentGCThread: public NamedThread { private: - volatile bool _should_terminate; - volatile bool _has_terminated; + Atomic _should_terminate; + Atomic _has_terminated; protected: void create_and_start(ThreadPriority prio = NearMaxPriority); diff --git a/src/hotspot/share/gc/shared/gcLocker.cpp b/src/hotspot/share/gc/shared/gcLocker.cpp index 01d17b1117d..898588e6b06 100644 --- a/src/hotspot/share/gc/shared/gcLocker.cpp +++ b/src/hotspot/share/gc/shared/gcLocker.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 @@ -28,7 +28,7 @@ #include "logging/log.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.inline.hpp" #include "runtime/safepoint.hpp" @@ -60,16 +60,13 @@ public: }; Monitor* GCLocker::_lock; -volatile bool GCLocker::_is_gc_request_pending; +Atomic GCLocker::_is_gc_request_pending{false}; -DEBUG_ONLY(uint64_t GCLocker::_verify_in_cr_count;) +DEBUG_ONLY(Atomic GCLocker::_verify_in_cr_count{0};) void GCLocker::initialize() { assert(JNICritical_lock != nullptr, "inv"); _lock = JNICritical_lock; - _is_gc_request_pending = false; - - DEBUG_ONLY(_verify_in_cr_count = 0;) } bool GCLocker::is_active() { @@ -84,11 +81,11 @@ bool GCLocker::is_active() { void GCLocker::block() { // _lock is held from the beginning of block() to the end of of unblock(). _lock->lock(); - assert(AtomicAccess::load(&_is_gc_request_pending) == false, "precondition"); + assert(_is_gc_request_pending.load_relaxed() == false, "precondition"); GCLockerTimingDebugLogger logger("Thread blocked to start GC."); - AtomicAccess::store(&_is_gc_request_pending, true); + _is_gc_request_pending.store_relaxed(true); // The _is_gc_request_pending and _jni_active_critical (inside // in_critical_atomic()) variables form a Dekker duality. On the GC side, the @@ -112,14 +109,14 @@ void GCLocker::block() { #ifdef ASSERT // Matching the storestore in GCLocker::exit. OrderAccess::loadload(); - assert(AtomicAccess::load(&_verify_in_cr_count) == 0, "inv"); + assert(_verify_in_cr_count.load_relaxed() == 0, "inv"); #endif } void GCLocker::unblock() { - assert(AtomicAccess::load(&_is_gc_request_pending) == true, "precondition"); + assert(_is_gc_request_pending.load_relaxed() == true, "precondition"); - AtomicAccess::store(&_is_gc_request_pending, false); + _is_gc_request_pending.store_relaxed(false); _lock->unlock(); } @@ -139,7 +136,7 @@ void GCLocker::enter_slow(JavaThread* current_thread) { // Same as fast path. OrderAccess::fence(); - if (!AtomicAccess::load(&_is_gc_request_pending)) { + if (!_is_gc_request_pending.load_relaxed()) { return; } diff --git a/src/hotspot/share/gc/shared/gcLocker.hpp b/src/hotspot/share/gc/shared/gcLocker.hpp index c0e28b66539..be3c0c3593e 100644 --- a/src/hotspot/share/gc/shared/gcLocker.hpp +++ b/src/hotspot/share/gc/shared/gcLocker.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 @@ -27,6 +27,7 @@ #include "gc/shared/gcCause.hpp" #include "memory/allStatic.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutex.hpp" // GCLocker provides synchronization between the garbage collector (GC) and @@ -43,11 +44,11 @@ class GCLocker: public AllStatic { static Monitor* _lock; - static volatile bool _is_gc_request_pending; + static Atomic _is_gc_request_pending; #ifdef ASSERT // Debug-only: to track the number of java threads in critical-region. - static uint64_t _verify_in_cr_count; + static Atomic _verify_in_cr_count; #endif static void enter_slow(JavaThread* current_thread); diff --git a/src/hotspot/share/gc/shared/gcLocker.inline.hpp b/src/hotspot/share/gc/shared/gcLocker.inline.hpp index 050b9570280..135bd9eab62 100644 --- a/src/hotspot/share/gc/shared/gcLocker.inline.hpp +++ b/src/hotspot/share/gc/shared/gcLocker.inline.hpp @@ -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 @@ -38,13 +38,13 @@ void GCLocker::enter(JavaThread* current_thread) { // Matching the fence in GCLocker::block. OrderAccess::fence(); - if (AtomicAccess::load(&_is_gc_request_pending)) { + if (_is_gc_request_pending.load_relaxed()) { current_thread->exit_critical(); // slow-path enter_slow(current_thread); } - DEBUG_ONLY(AtomicAccess::add(&_verify_in_cr_count, (uint64_t)1);) + DEBUG_ONLY(_verify_in_cr_count.add_then_fetch(1u);) } else { current_thread->enter_critical(); } @@ -55,7 +55,7 @@ void GCLocker::exit(JavaThread* current_thread) { #ifdef ASSERT if (current_thread->in_last_critical()) { - AtomicAccess::add(&_verify_in_cr_count, (uint64_t)-1); + _verify_in_cr_count.sub_then_fetch(1u); // Matching the loadload in GCLocker::block. OrderAccess::storestore(); } diff --git a/src/hotspot/share/gc/shared/pretouchTask.cpp b/src/hotspot/share/gc/shared/pretouchTask.cpp index cc84c8c449d..c999c98ea99 100644 --- a/src/hotspot/share/gc/shared/pretouchTask.cpp +++ b/src/hotspot/share/gc/shared/pretouchTask.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 @@ -25,7 +25,7 @@ #include "gc/shared/gc_globals.hpp" #include "gc/shared/pretouchTask.hpp" #include "logging/log.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "runtime/os.hpp" #include "utilities/align.hpp" @@ -52,11 +52,11 @@ size_t PretouchTask::chunk_size() { void PretouchTask::work(uint worker_id) { while (true) { - char* cur_start = AtomicAccess::load(&_cur_addr); + char* cur_start = _cur_addr.load_relaxed(); char* cur_end = cur_start + MIN2(_chunk_size, pointer_delta(_end_addr, cur_start, 1)); if (cur_start >= cur_end) { break; - } else if (cur_start == AtomicAccess::cmpxchg(&_cur_addr, cur_start, cur_end)) { + } else if (cur_start == _cur_addr.compare_exchange(cur_start, cur_end)) { os::pretouch_memory(cur_start, cur_end, _page_size); } // Else attempt to claim chunk failed, so try again. } diff --git a/src/hotspot/share/gc/shared/pretouchTask.hpp b/src/hotspot/share/gc/shared/pretouchTask.hpp index 7c66c8b717c..6355d61edf7 100644 --- a/src/hotspot/share/gc/shared/pretouchTask.hpp +++ b/src/hotspot/share/gc/shared/pretouchTask.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, 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,9 +26,11 @@ #define SHARE_GC_SHARED_PRETOUCH_HPP #include "gc/shared/workerThread.hpp" +#include "runtime/atomic.hpp" +#include "utilities/globalDefinitions.hpp" class PretouchTask : public WorkerTask { - char* volatile _cur_addr; + Atomic _cur_addr; char* const _end_addr; size_t _page_size; size_t _chunk_size; diff --git a/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp b/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp index 83783b31ad9..834a3d04c00 100644 --- a/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp +++ b/src/hotspot/share/gc/shared/suspendibleThreadSet.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 @@ -30,7 +30,7 @@ uint SuspendibleThreadSet::_nthreads = 0; uint SuspendibleThreadSet::_nthreads_stopped = 0; -volatile bool SuspendibleThreadSet::_suspend_all = false; +Atomic SuspendibleThreadSet::_suspend_all{false}; double SuspendibleThreadSet::_suspend_all_start = 0.0; static Semaphore* _synchronize_wakeup = nullptr; @@ -96,7 +96,7 @@ void SuspendibleThreadSet::synchronize() { { MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); assert(!should_yield(), "Only one at a time"); - AtomicAccess::store(&_suspend_all, true); + _suspend_all.store_relaxed(true); if (is_synchronized()) { return; } @@ -127,6 +127,6 @@ void SuspendibleThreadSet::desynchronize() { MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); assert(should_yield(), "STS not synchronizing"); assert(is_synchronized(), "STS not synchronized"); - AtomicAccess::store(&_suspend_all, false); + _suspend_all.store_relaxed(false); ml.notify_all(); } diff --git a/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp b/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp index 38568c015bc..72fca5302e7 100644 --- a/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp +++ b/src/hotspot/share/gc/shared/suspendibleThreadSet.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,7 +26,7 @@ #define SHARE_GC_SHARED_SUSPENDIBLETHREADSET_HPP #include "memory/allocation.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" // A SuspendibleThreadSet is a set of threads that can be suspended. // A thread can join and later leave the set, and periodically yield. @@ -43,7 +43,7 @@ class SuspendibleThreadSet : public AllStatic { private: static uint _nthreads; static uint _nthreads_stopped; - static volatile bool _suspend_all; + static Atomic _suspend_all; static double _suspend_all_start; static bool is_synchronized(); @@ -59,7 +59,7 @@ private: public: // Returns true if an suspension is in progress. - static bool should_yield() { return AtomicAccess::load(&_suspend_all); } + static bool should_yield() { return _suspend_all.load_relaxed(); } // Suspends the current thread if a suspension is in progress. static void yield() { diff --git a/src/hotspot/share/gc/shared/workerThread.cpp b/src/hotspot/share/gc/shared/workerThread.cpp index 3a999da59dc..7a9404a195a 100644 --- a/src/hotspot/share/gc/shared/workerThread.cpp +++ b/src/hotspot/share/gc/shared/workerThread.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 @@ -42,7 +42,7 @@ WorkerTaskDispatcher::WorkerTaskDispatcher() : void WorkerTaskDispatcher::coordinator_distribute_task(WorkerTask* task, uint num_workers) { // No workers are allowed to read the state variables until they have been signaled. _task = task; - _not_finished = num_workers; + _not_finished.store_relaxed(num_workers); // Dispatch 'num_workers' number of tasks. _start_semaphore.signal(num_workers); @@ -51,9 +51,12 @@ void WorkerTaskDispatcher::coordinator_distribute_task(WorkerTask* task, uint nu _end_semaphore.wait(); // No workers are allowed to read the state variables after the coordinator has been signaled. - assert(_not_finished == 0, "%d not finished workers?", _not_finished); +#ifdef ASSERT + uint not_finished = _not_finished.load_relaxed(); + assert(not_finished == 0, "%u not finished workers?", not_finished); +#endif // ASSERT _task = nullptr; - _started = 0; + _started.store_relaxed(0); } void WorkerTaskDispatcher::worker_run_task() { @@ -61,7 +64,7 @@ void WorkerTaskDispatcher::worker_run_task() { _start_semaphore.wait(); // Get and set worker id. - const uint worker_id = AtomicAccess::fetch_then_add(&_started, 1u); + const uint worker_id = _started.fetch_then_add(1u); WorkerThread::set_worker_id(worker_id); // Run task. @@ -70,7 +73,7 @@ void WorkerTaskDispatcher::worker_run_task() { // Mark that the worker is done with the task. // The worker is not allowed to read the state variables after this line. - const uint not_finished = AtomicAccess::sub(&_not_finished, 1u); + const uint not_finished = _not_finished.sub_then_fetch(1u); // The last worker signals to the coordinator that all work is completed. if (not_finished == 0) { diff --git a/src/hotspot/share/gc/shared/workerThread.hpp b/src/hotspot/share/gc/shared/workerThread.hpp index d4e92797039..a1f7282abe4 100644 --- a/src/hotspot/share/gc/shared/workerThread.hpp +++ b/src/hotspot/share/gc/shared/workerThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, 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 @@ -27,6 +27,7 @@ #include "gc/shared/gcId.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "runtime/nonJavaThread.hpp" #include "runtime/semaphore.hpp" #include "utilities/debug.hpp" @@ -58,8 +59,8 @@ class WorkerTaskDispatcher { // The task currently being dispatched to the WorkerThreads. WorkerTask* _task; - volatile uint _started; - volatile uint _not_finished; + Atomic _started; + Atomic _not_finished; // Semaphore used to start the WorkerThreads. Semaphore _start_semaphore; From 78b1ca6cc14e1a92bf25cbcfb687067ac17af92b Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 8 Jan 2026 12:44:08 +0000 Subject: [PATCH 022/204] 8374711: Hotspot runtime/CommandLine/OptionsValidation/TestOptionsWithRanges fails without printing the option name Reviewed-by: mdoerr, dholmes --- .../OptionsValidation/common/optionsvalidation/JVMOption.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java b/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java index 78a508343d6..d87ca25fc3c 100644 --- a/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java +++ b/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java @@ -116,7 +116,7 @@ public abstract class JVMOption { default: throw new Error("Expected only \"int\", \"intx\", \"size_t\", " + "\"uint\", \"uintx\", \"uint64_t\", or \"double\" " - + "option types! Got " + type + " type!"); + + "option types! Got " + type + " type for option " + name + "!"); } return parameter; From ec657349ff654dcb41b9f17178aeea638329101e Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 8 Jan 2026 16:28:10 +0000 Subject: [PATCH 023/204] 8374641: Remove java/nio/channels/AsyncCloseAndInterrupt.java from problem list Reviewed-by: iris --- test/jdk/ProblemList.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index addcc8830af..9cfc23ea8da 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -1,6 +1,6 @@ ########################################################################### # -# Copyright (c) 2009, 2025, 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 @@ -561,8 +561,6 @@ java/net/MulticastSocket/Test.java 7145658,8308807 # jdk_nio -java/nio/channels/AsyncCloseAndInterrupt.java 8368290 macosx-26.0.1 - java/nio/channels/DatagramChannel/AdaptorMulticasting.java 8308807,8144003 aix-ppc64,macosx-all java/nio/channels/DatagramChannel/AfterDisconnect.java 8308807 aix-ppc64 java/nio/channels/DatagramChannel/ManySourcesAndTargets.java 8264385 macosx-aarch64 From 677572b42d6d0ee62063c3f19ffad1e501ac9bf3 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 8 Jan 2026 16:28:43 +0000 Subject: [PATCH 024/204] 8372377: Test java/io/File/GetXSpace.java failed: The system cannot find the path specified Reviewed-by: alanb, jpai --- test/jdk/java/io/File/GetXSpace.java | 46 ++++++++++++++++------------ test/jdk/java/io/File/libGetXSpace.c | 11 +++---- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/test/jdk/java/io/File/GetXSpace.java b/test/jdk/java/io/File/GetXSpace.java index 96ce4ede1b2..f1529c18fb2 100644 --- a/test/jdk/java/io/File/GetXSpace.java +++ b/test/jdk/java/io/File/GetXSpace.java @@ -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 @@ -44,7 +44,6 @@ import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import jdk.test.lib.Platform; -import jdk.test.lib.Platform; import static java.lang.System.err; import static java.lang.System.out; @@ -103,16 +102,11 @@ public class GetXSpace { private final long free; private final long available; - Space(String name) { + Space(String name) throws IOException { this.name = name; long[] sizes = new long[4]; if (Platform.isWindows() && isCDDrive(name)) { - try { - getCDDriveSpace(name, sizes); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException("can't get CDDrive sizes"); - } + getCDDriveSpace(name, sizes); } else { if (getSpace(name, sizes)) System.err.println("WARNING: total space is estimated"); @@ -170,7 +164,7 @@ public class GetXSpace { return al; } - private static void compare(Space s) { + private static void compare(Space s) throws IOException { File f = new File(s.name()); long ts = f.getTotalSpace(); long fs = f.getFreeSpace(); @@ -318,7 +312,7 @@ public class GetXSpace { } } - private static int testFile(Path dir) { + private static int testFile(Path dir) throws IOException { String dirName = dir.toString(); out.format("--- Testing %s%n", dirName); compare(new Space(dir.getRoot().toString())); @@ -333,10 +327,11 @@ public class GetXSpace { return fail != 0 ? 1 : 0; } - private static int testVolumes() { + private static int testVolumes() throws IOException { out.println("--- Testing volumes"); // Find all of the partitions on the machine and verify that the sizes - // returned by File::getXSpace are equivalent to those from getSpace or getCDDriveSpace + // returned by File::getXSpace are equivalent to those from getSpace + // or getCDDriveSpace ArrayList l; try { l = paths(); @@ -350,7 +345,18 @@ public class GetXSpace { throw new RuntimeException("no partitions?"); for (var p : l) { - Space s = new Space(p); + Space s; + try { + s = new Space(p); + } catch (IOException x) { + // Avoid failing for transient file systems on Windows + if (Platform.isWindows()) { + File f = new File(p); + if (!f.exists()) + continue; + } + throw new IOException("Failure for volume " + p, x); + } compare(s); compareZeroNonExist(); compareZeroExist(); @@ -408,19 +414,19 @@ public class GetXSpace { // size[2] free space: number of free bytes in the volume // size[3] usable space: number of bytes available to the caller // - private static native boolean getSpace0(String root, long[] space); + private static native boolean getSpace0(String root, long[] space) + throws IOException; private static native boolean isCDDrive(String root); - private static boolean getSpace(String root, long[] space) { + private static boolean getSpace(String root, long[] space) + throws IOException { try { return getSpace0(root, space); - } catch (RuntimeException e) { + } catch (IOException e) { File f = new File(root); - boolean exists = f.exists(); - boolean readable = f.canRead(); System.err.printf("getSpace0 failed for %s (%s, %s)%n", - root, exists, readable); + root, f.exists(), f.canRead()); throw e; } } diff --git a/test/jdk/java/io/File/libGetXSpace.c b/test/jdk/java/io/File/libGetXSpace.c index 59805e0adcb..9297721b8f4 100644 --- a/test/jdk/java/io/File/libGetXSpace.c +++ b/test/jdk/java/io/File/libGetXSpace.c @@ -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 @@ -85,7 +85,7 @@ Java_GetXSpace_getSpace0 BOOL hres = pfnGetDiskSpaceInformation(path, &diskSpaceInfo); (*env)->ReleaseStringChars(env, root, strchars); if (FAILED(hres)) { - JNU_ThrowByNameWithLastError(env, "java/lang/RuntimeException", + JNU_ThrowByNameWithLastError(env, "java/io/IOException", "GetDiskSpaceInformationW"); return totalSpaceIsEstimated; } @@ -113,7 +113,7 @@ Java_GetXSpace_getSpace0 &totalNumberOfBytes, &totalNumberOfFreeBytes); (*env)->ReleaseStringChars(env, root, strchars); if (FAILED(hres)) { - JNU_ThrowByNameWithLastError(env, "java/lang/RuntimeException", + JNU_ThrowByNameWithLastError(env, "java/io/IOException", "GetDiskFreeSpaceExW"); return totalSpaceIsEstimated; } @@ -131,8 +131,7 @@ Java_GetXSpace_getSpace0 char* chars = (char*)malloc((len + 1)*sizeof(char)); if (chars == NULL) { (*env)->ReleaseStringChars(env, root, strchars); - JNU_ThrowByNameWithLastError(env, "java/lang/RuntimeException", - "malloc"); + JNU_ThrowOutOfMemoryError(env, "malloc"); return JNI_FALSE; } @@ -146,7 +145,7 @@ Java_GetXSpace_getSpace0 int result = statfs(chars, &buf); free(chars); if (result < 0) { - JNU_ThrowByNameWithLastError(env, "java/lang/RuntimeException", + JNU_ThrowByNameWithLastError(env, "java/io/IOException", strerror(errno)); return totalSpaceIsEstimated; } From fa2eb626478806dc64fe03d8729f53f7ed26a172 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Thu, 8 Jan 2026 16:34:39 +0000 Subject: [PATCH 025/204] 8367949: JFR: MethodTrace double-counts methods that catch their own exceptions Reviewed-by: mgronlun --- .../jfr/internal/tracing/Instrumentation.java | 22 +- .../jdk/jfr/internal/tracing/Method.java | 2 +- .../jdk/jfr/internal/tracing/Transform.java | 199 ++++++++++++++++-- .../jfr/event/tracing/TestConstructors.java | 167 +++++++++++++++ .../event/tracing/TestInstrumentation.java | 4 + 5 files changed, 363 insertions(+), 31 deletions(-) create mode 100644 test/jdk/jdk/jfr/event/tracing/TestConstructors.java diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java index dbafca4ed3c..e06d361b203 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java @@ -58,7 +58,13 @@ final class Instrumentation { } public void addMethod(long methodId, String name, String signature, int modification) { - modificationMap.put(name + signature, new Method(methodId, Modification.valueOf(modification), className + "::" + name)); + Method method = new Method( + methodId, + Modification.valueOf(modification), + name.equals(""), + className + "::" + name + ); + modificationMap.put(name + signature, method); } public List getMethods() { @@ -71,7 +77,7 @@ final class Instrumentation { ClassModel classModel = classFile.parse(bytecode); byte[] generated = classFile.build(classModel.thisClass().asSymbol(), classBuilder -> { for (var ce : classModel) { - if (modifyClassElement(classBuilder, ce)) { + if (modifyClassElement(classModel, classBuilder, ce)) { modified[0] = true; } else { classBuilder.with(ce); @@ -93,7 +99,7 @@ final class Instrumentation { } } - private boolean modifyClassElement(ClassBuilder classBuilder, ClassElement ce) { + private boolean modifyClassElement(ClassModel classModel, ClassBuilder classBuilder, ClassElement ce) { if (ce instanceof MethodModel mm) { String method = mm.methodName().stringValue(); String signature = mm.methodType().stringValue(); @@ -102,15 +108,15 @@ final class Instrumentation { if (tm != null) { Modification m = tm.modification(); if (m.tracing() || m.timing()) { - return modifyMethod(classBuilder, mm, tm); + return modifyMethod(classModel, classBuilder, mm, tm); } } } return false; } - private boolean modifyMethod(ClassBuilder classBuilder, MethodModel m, Method method) { - var code = m.code(); + private boolean modifyMethod(ClassModel classModel, ClassBuilder classBuilder, MethodModel methodModel, Method method) { + var code = methodModel.code(); if (code.isPresent()) { if (classLoader == null && ExcludeList.containsMethod(method.name())) { String msg = "Risk of recursion, skipping bytecode generation of " + method.name(); @@ -118,9 +124,9 @@ final class Instrumentation { return false; } MethodTransform s = MethodTransform.ofStateful( - () -> MethodTransform.transformingCode(new Transform(method)) + () -> MethodTransform.transformingCode(new Transform(classModel, code.get(), method)) ); - classBuilder.transformMethod(m, s); + classBuilder.transformMethod(methodModel, s); return true; } return false; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java index d685083153d..d85e458e9d5 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java @@ -31,7 +31,7 @@ import jdk.jfr.internal.Logger; /** * Class that holds information about an instrumented method. */ -record Method(long methodId, Modification modification, String name) { +record Method(long methodId, Modification modification, boolean constructor, String name) { @Override public String toString() { return name + (modification.timing() ? " +timing" : " -timing") + (modification.tracing() ? " +tracing" : " -tracing") + " (Method ID: " + String.format("0x%08X)", methodId); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java index cd65a119cee..377eede7925 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java @@ -24,13 +24,19 @@ */ package jdk.jfr.internal.tracing; +import java.lang.classfile.ClassModel; import java.lang.classfile.CodeBuilder; import java.lang.classfile.CodeElement; +import java.lang.classfile.CodeModel; import java.lang.classfile.CodeTransform; +import java.lang.classfile.Label; import java.lang.classfile.TypeKind; +import java.lang.classfile.instruction.InvokeInstruction; import java.lang.classfile.instruction.ReturnInstruction; import java.lang.classfile.instruction.ThrowInstruction; import java.lang.constant.ClassDesc; +import java.util.ArrayList; +import java.util.List; import jdk.jfr.internal.util.Bytecode; import jdk.jfr.internal.util.Bytecode.MethodDesc; @@ -43,59 +49,208 @@ import jdk.jfr.tracing.MethodTracer; * The method ID is determined by native code. */ final class Transform implements CodeTransform { + private static class TryBlock { + Label start; + Label end; + } private static final ClassDesc METHOD_TRACER_CLASS = ClassDesc.of(MethodTracer.class.getName()); private static final MethodDesc TRACE_METHOD = MethodDesc.of("trace", "(JJ)V"); private static final MethodDesc TIMING_METHOD = MethodDesc.of("timing", "(JJ)V"); private static final MethodDesc TRACE_TIMING_METHOD = MethodDesc.of("traceTiming", "(JJ)V"); private static final MethodDesc TIMESTAMP_METHOD = MethodDesc.of("timestamp", "()J"); + private final List tryBlocks = new ArrayList<>(); + private final boolean simplifiedInstrumentation; + private final ClassModel classModel; private final Method method; private int timestampSlot = -1; - Transform(Method method) { + Transform(ClassModel classModel, CodeModel model, Method method) { this.method = method; + this.classModel = classModel; + // The JVMS (not the JLS) allows multiple mutually exclusive super/this. + // invocations in a constructor body as long as only one lies on any given + // execution path. For example, this is valid bytecode: + // + // Foo(boolean value) { + // if (value) { + // staticMethodThatMayThrow(); + // super(); + // } else { + // try { + // if (value == 0) { + // throw new Exception(""); + // } + // } catch (Throwable t) { + // throw t; + // } + // super(); + // } + // } + // + // If such a method is found, instrumentation falls back to instrumenting only + // RET and ATHROW. This can cause exceptions to be missed or counted twice. + // + // An effect of this heuristic is that constructors like the one below + // will also trigger simplified instrumentation. + // + // class Bar { + // } + // + // class Foo extends Bar { + // Foo() { + // new Bar(); + // } + // } + // + // java.lang.Object:: with zero constructor invocations should use simplified instrumentation + this.simplifiedInstrumentation = method.constructor() && constructorInvocations(model.elementList()) != 1; + } + + private int constructorInvocations(List elementList) { + int count = 0; + for (CodeElement e : elementList) { + if (isConstructorInvocation(e)) { + count++; + } + } + return count; + } + + private boolean isConstructorInvocation(CodeElement element) { + if (element instanceof InvokeInstruction inv && inv.name().equalsString("")) { + if (classModel.thisClass().equals(inv.owner())) { + return true; + } + if (classModel.superclass().isPresent()) { + return classModel.superclass().get().equals(inv.owner()); + } + } + return false; } @Override - public final void accept(CodeBuilder builder, CodeElement element) { + public void accept(CodeBuilder builder, CodeElement element) { + if (simplifiedInstrumentation) { + acceptSimplifiedInstrumentation(builder, element); + return; + } + if (method.constructor()) { + acceptConstructor(builder, element, isConstructorInvocation(element)); + } else { + acceptMethod(builder, element); + } + } + + @Override + public void atEnd(CodeBuilder builder) { + endTryBlock(builder); + for (TryBlock block : tryBlocks) { + addCatchHandler(block, builder); + } + } + + private void acceptConstructor(CodeBuilder builder, CodeElement element, boolean isConstructorInvocation) { + if (timestampSlot == -1) { + timestampSlot = invokeTimestamp(builder); + builder.lstore(timestampSlot); + if (!isConstructorInvocation) { + beginTryBlock(builder); + } + } + if (isConstructorInvocation) { + endTryBlock(builder); + builder.with(element); + beginTryBlock(builder); + return; + } + if (element instanceof ReturnInstruction) { + addTracing(builder); + } + builder.with(element); + } + + private void endTryBlock(CodeBuilder builder) { + if (tryBlocks.isEmpty()) { + return; + } + TryBlock last = tryBlocks.getLast(); + if (last.end == null) { + last.end = builder.newBoundLabel(); + } + } + + private void beginTryBlock(CodeBuilder builder) { + TryBlock block = new TryBlock(); + block.start = builder.newBoundLabel(); + tryBlocks.add(block); + } + + private void acceptSimplifiedInstrumentation(CodeBuilder builder, CodeElement element) { if (timestampSlot == -1) { timestampSlot = invokeTimestamp(builder); builder.lstore(timestampSlot); } if (element instanceof ReturnInstruction || element instanceof ThrowInstruction) { - builder.lload(timestampSlot); - builder.ldc(method.methodId()); - Modification modification = method.modification(); - boolean objectInit = method.name().equals("java.lang.Object::"); - String suffix = objectInit ? "ObjectInit" : ""; - if (modification.timing()) { - if (modification.tracing()) { - invokeTraceTiming(builder, suffix); - } else { - invokeTiming(builder, suffix); - } - } else { - if (modification.tracing()) { - invokeTrace(builder, suffix); - } - } + addTracing(builder); } builder.with(element); } - public static void invokeTiming(CodeBuilder builder, String suffix) { + private void acceptMethod(CodeBuilder builder, CodeElement element) { + if (timestampSlot == -1) { + timestampSlot = invokeTimestamp(builder); + builder.lstore(timestampSlot); + beginTryBlock(builder); + } + if (element instanceof ReturnInstruction) { + addTracing(builder); + } + builder.with(element); + } + + private void addCatchHandler(TryBlock block, CodeBuilder builder) { + Label catchHandler = builder.newBoundLabel(); + int exceptionSlot = builder.allocateLocal(TypeKind.REFERENCE); + builder.astore(exceptionSlot); + addTracing(builder); + builder.aload(exceptionSlot); + builder.athrow(); + builder.exceptionCatchAll(block.start, block.end, catchHandler); + } + + private void addTracing(CodeBuilder builder) { + builder.lload(timestampSlot); + builder.ldc(method.methodId()); + Modification modification = method.modification(); + boolean objectInit = method.name().equals("java.lang.Object::"); + String suffix = objectInit ? "ObjectInit" : ""; + if (modification.timing()) { + if (modification.tracing()) { + invokeTraceTiming(builder, suffix); + } else { + invokeTiming(builder, suffix); + } + } else { + if (modification.tracing()) { + invokeTrace(builder, suffix); + } + } + } + + private static void invokeTiming(CodeBuilder builder, String suffix) { builder.invokestatic(METHOD_TRACER_CLASS, TIMING_METHOD.name() + suffix, TIMING_METHOD.descriptor()); } - public static void invokeTrace(CodeBuilder builder, String suffix) { + private static void invokeTrace(CodeBuilder builder, String suffix) { builder.invokestatic(METHOD_TRACER_CLASS, TRACE_METHOD.name() + suffix, TRACE_METHOD.descriptor()); } - public static void invokeTraceTiming(CodeBuilder builder, String suffix) { + private static void invokeTraceTiming(CodeBuilder builder, String suffix) { builder.invokestatic(METHOD_TRACER_CLASS, TRACE_TIMING_METHOD.name() + suffix, TRACE_TIMING_METHOD.descriptor()); } - public static int invokeTimestamp(CodeBuilder builder) { + private static int invokeTimestamp(CodeBuilder builder) { Bytecode.invokestatic(builder, METHOD_TRACER_CLASS, TIMESTAMP_METHOD); return builder.allocateLocal(TypeKind.LONG); } diff --git a/test/jdk/jdk/jfr/event/tracing/TestConstructors.java b/test/jdk/jdk/jfr/event/tracing/TestConstructors.java new file mode 100644 index 00000000000..26548646b49 --- /dev/null +++ b/test/jdk/jdk/jfr/event/tracing/TestConstructors.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.event.tracing; + +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Tests that constructors are instrumented correctly. + * @requires vm.flagless + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm -Xlog:jfr+methodtrace=debug + * jdk.jfr.event.tracing.TestConstructors + **/ +public class TestConstructors { + static private void methodThatThrows() { + throw new RuntimeException(); + } + + public static class Cat { + Cat() { + new String(); + methodThatThrows(); + super(); + methodThatThrows(); + } + } + + public static class Dog { + Dog() { + super(); + methodThatThrows(); + } + } + + public static class Tiger { + Tiger() { + methodThatThrows(); + super(); + } + } + + public static class Zebra { + Zebra(boolean shouldThrow) { + this(shouldThrow ? 1 : 0); + } + + Zebra(int shouldThrow) { + if (shouldThrow == 1) { + throw new RuntimeException(); + } + } + } + + public static class Snake { + Snake() { + try { + throw new RuntimeException(); + } catch (Exception e) { + // Ignore + } + super(); + } + } + + public static void main(String... args) throws Exception { + try (Recording r = new Recording()) { + r.enable("jdk.MethodTrace").with("filter", Dog.class.getName() + ";" + Cat.class.getName() + ";" + Tiger.class.getName() + ";" + Zebra.class.getName() + ";" + Snake.class.getName()); + r.start(); + try { + new Cat(); + } catch (Exception e) { + // ignore + } + try { + new Dog(); + } catch (Exception e) { + // ignore + } + try { + new Tiger(); + } catch (Exception e) { + // ignore + } + try { + new Zebra(true); + } catch (Exception e) { + // ignore + } + try { + new Zebra(false); + } catch (Exception e) { + // ignore + } + try { + new Snake(); + } catch (Exception e) { + // ignore + } + r.stop(); + List events = Events.fromRecording(r); + var methods = buildMethodMap(events); + if (methods.size() != 5) { + throw new Exception("Expected 5 different methods"); + } + assertMethodCount(methods, "Cat", 1); + assertMethodCount(methods, "Dog", 1); + assertMethodCount(methods, "Snake", 1); + assertMethodCount(methods, "Tiger", 1); + assertMethodCount(methods, "Zebra", 3); + } + } + + private static void assertMethodCount(Map methods, String className, int expectedCount) throws Exception { + String name = TestConstructors.class.getName() + "$" + className + "::"; + Long count = methods.get(name); + if (count == null) { + throw new Exception("Could not find traced method " + name); + } + if (count != expectedCount) { + throw new Exception("Expected " + expectedCount + " trace event for " + name); + } + } + + private static Map buildMethodMap(List events) { + Map map = new TreeMap<>(); + for (RecordedEvent e : events) { + RecordedMethod m = e.getValue("method"); + String name = m.getType().getName() + "::" + m.getName(); + map.compute(name, (_, value) -> (value == null) ? 1 : value + 1); + } + for (var e : map.entrySet()) { + System.out.println(e.getKey() + " " + e.getValue()); + } + return map; + } +} \ No newline at end of file diff --git a/test/jdk/jdk/jfr/event/tracing/TestInstrumentation.java b/test/jdk/jdk/jfr/event/tracing/TestInstrumentation.java index 834d4ab4989..5709c95812a 100644 --- a/test/jdk/jdk/jfr/event/tracing/TestInstrumentation.java +++ b/test/jdk/jdk/jfr/event/tracing/TestInstrumentation.java @@ -93,6 +93,8 @@ public class TestInstrumentation { assertMethod(map, "exception", 2); assertMethod(map, "switchExpression", 3); assertMethod(map, "recursive", 4); + assertMethod(map, "deepException", 1); + assertMethod(map, "whileTrue", 1); assertMethod(map, "multipleReturns", 5); if (!map.isEmpty()) { throw new Exception("Found unexpected methods " + map.keySet()); @@ -105,6 +107,8 @@ public class TestInstrumentation { assertMethod(map, "exception", 2); assertMethod(map, "switchExpression", 3); assertMethod(map, "recursive", 4); + assertMethod(map, "deepException", 1); + assertMethod(map, "whileTrue", 1); assertMethod(map, "multipleReturns", 5); for (var entry : map.entrySet()) { long invocations = entry.getValue(); From c834e4c641bf6c73e88b93c0cdba40a83f3192c1 Mon Sep 17 00:00:00 2001 From: Jonas Norlinder Date: Thu, 8 Jan 2026 16:46:28 +0000 Subject: [PATCH 026/204] 8373647: Avoid fstat when opening file for write with RandomAccessFile or FileOutputStream Reviewed-by: redestad, alanb --- .../unix/native/libjava/io_util_md.c | 31 ++++++++++++------- .../org/openjdk/bench/java/io/FileWrite.java | 28 ++++++++++++++++- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/java.base/unix/native/libjava/io_util_md.c b/src/java.base/unix/native/libjava/io_util_md.c index 2e81cbd05c2..bcac334191c 100644 --- a/src/java.base/unix/native/libjava/io_util_md.c +++ b/src/java.base/unix/native/libjava/io_util_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "jvm.h" #include "io_util.h" #include "io_util_md.h" +#include #include #include @@ -75,20 +76,26 @@ FD handleOpen(const char *path, int oflag, int mode) { FD fd; RESTARTABLE(open(path, oflag, mode), fd); - if (fd != -1) { - struct stat buf; - int result; - RESTARTABLE(fstat(fd, &buf), result); - if (result != -1) { - if (S_ISDIR(buf.st_mode)) { - close(fd); - errno = EISDIR; - fd = -1; - } - } else { + // No further checking is needed if the file is not a + // directory or open returned an error + if (fd == -1 || ((oflag & O_ACCMODE) != O_RDONLY) != 0) { + return fd; + } + + // FileInputStream is specified to throw if the + // file is a directory + struct stat buf; + int result; + RESTARTABLE(fstat(fd, &buf), result); + if (result != -1) { + if (S_ISDIR(buf.st_mode)) { close(fd); + errno = EISDIR; fd = -1; } + } else { + close(fd); + fd = -1; } return fd; } diff --git a/test/micro/org/openjdk/bench/java/io/FileWrite.java b/test/micro/org/openjdk/bench/java/io/FileWrite.java index 21b0c2f8f54..c5a5aee70d8 100644 --- a/test/micro/org/openjdk/bench/java/io/FileWrite.java +++ b/test/micro/org/openjdk/bench/java/io/FileWrite.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,6 +25,7 @@ package org.openjdk.bench.java.io; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.RandomAccessFile; import java.io.IOException; import java.util.concurrent.TimeUnit; @@ -81,4 +82,29 @@ public class FileWrite { } } + @State(Scope.Benchmark) + @Warmup(iterations = 3, time = 2) + @Measurement(iterations = 5, time = 5) + @BenchmarkMode(Mode.SampleTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + @Threads(1) + @Fork(value = 10) + public static class OpenFileForWritingBench { + final byte[] payload = "something".getBytes(); + final String path = System.getProperty("os.name", "unknown").toLowerCase().contains("win") ? "NUL" : "/dev/null"; + + @Benchmark + public void testFileOutputStream() throws IOException { + try (FileOutputStream f = new FileOutputStream(path)) { + f.write(payload); + } + } + + @Benchmark + public void testRandomAccessFile() throws IOException { + try (RandomAccessFile f = new RandomAccessFile(path, "rw")) { + f.write(payload); + } + } + } } From 7e1051bfcc01aad538376c86354e16e25d2eaf7a Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Thu, 8 Jan 2026 16:46:48 +0000 Subject: [PATCH 027/204] 8352728: InternalError loading java.security due to Windows parent folder permissions Reviewed-by: weijun, mullan --- .../share/classes/java/security/Security.java | 37 +++-- .../ExtraFileAndIncludes.java} | 154 ++++++++++-------- .../SecurityPropFile/LinuxAnonymousFiles.java | 83 ++++++++++ .../SecurityPropFile/SecurityPropFile.file | 1 - .../SecurityPropFile/SecurityPropFile.java | 42 ----- .../WindowsParentDirPermissions.java | 84 ++++++++++ 6 files changed, 273 insertions(+), 128 deletions(-) rename test/jdk/java/security/Security/{ConfigFileTest.java => SecurityPropFile/ExtraFileAndIncludes.java} (88%) create mode 100644 test/jdk/java/security/Security/SecurityPropFile/LinuxAnonymousFiles.java delete mode 100644 test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.file delete mode 100644 test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.java create mode 100644 test/jdk/java/security/Security/SecurityPropFile/WindowsParentDirPermissions.java diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index 6969fe8a8e1..30a22b05742 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, 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 @@ -34,6 +34,7 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; @@ -112,7 +113,7 @@ public final class Security { private static Path currentPath; - private static final Set activePaths = new HashSet<>(); + private static final List activePaths = new ArrayList<>(); static void loadAll() { // first load the master properties file to @@ -262,30 +263,40 @@ public final class Security { } } + private static void checkCyclicInclude(Path path) { + for (Path activePath : activePaths) { + try { + if (Files.isSameFile(path, activePath)) { + throw new InternalError( + "Cyclic include of '" + path + "'"); + } + } catch (IOException e) { + if (sdebug != null) { + sdebug.println("skipped exception when checking for " + + "cyclic inclusion of " + path + ":"); + e.printStackTrace(); + } + } + } + } + private static void loadFromPath(Path path, LoadingMode mode) throws IOException { - boolean isRegularFile = Files.isRegularFile(path); - if (isRegularFile) { - path = path.toRealPath(); - } else if (Files.isDirectory(path)) { + if (Files.isDirectory(path)) { throw new IOException("Is a directory"); - } else { - path = path.toAbsolutePath(); - } - if (activePaths.contains(path)) { - throw new InternalError("Cyclic include of '" + path + "'"); } try (InputStream is = Files.newInputStream(path)) { + checkCyclicInclude(path); reset(mode); Path previousPath = currentPath; - currentPath = isRegularFile ? path : null; + currentPath = Files.isRegularFile(path) ? path : null; activePaths.add(path); try { debugLoad(true, path); props.load(is); debugLoad(false, path); } finally { - activePaths.remove(path); + activePaths.removeLast(); currentPath = previousPath; } } diff --git a/test/jdk/java/security/Security/ConfigFileTest.java b/test/jdk/java/security/Security/SecurityPropFile/ExtraFileAndIncludes.java similarity index 88% rename from test/jdk/java/security/Security/ConfigFileTest.java rename to test/jdk/java/security/Security/SecurityPropFile/ExtraFileAndIncludes.java index caf657005e1..4cf723f856a 100644 --- a/test/jdk/java/security/Security/ConfigFileTest.java +++ b/test/jdk/java/security/Security/SecurityPropFile/ExtraFileAndIncludes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, 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 @@ -62,13 +62,13 @@ import java.util.stream.Stream; * @test * @summary Tests security properties passed through java.security, * java.security.properties or included from other properties files. - * @bug 8155246 8292297 8292177 8281658 8319332 + * @bug 4303068 8155246 8292297 8292177 8281658 8319332 * @modules java.base/sun.net.www * @library /test/lib - * @run main ConfigFileTest + * @run main ExtraFileAndIncludes */ -public class ConfigFileTest { +public class ExtraFileAndIncludes { static final String SEPARATOR_THIN = "----------------------------"; private static void printTestHeader(String testName) { @@ -91,7 +91,8 @@ public class ConfigFileTest { } else { // Executed by the test JVM. try (FilesManager filesMgr = new FilesManager()) { - for (Method m : ConfigFileTest.class.getDeclaredMethods()) { + for (Method m : + ExtraFileAndIncludes.class.getDeclaredMethods()) { if (m.getName().startsWith("test")) { printTestHeader(m.getName()); Executor.run(m, filesMgr); @@ -120,7 +121,7 @@ public class ConfigFileTest { static void testIncludeBasic(Executor ex, FilesManager filesMgr) throws Exception { PropsFile masterFile = filesMgr.newMasterFile(); - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(ExtraMode.FILE_URI); PropsFile file0 = filesMgr.newFile("file0.properties"); PropsFile file1 = filesMgr.newFile("dir1/file1.properties"); PropsFile file2 = filesMgr.newFile("dir1/dir2/file2.properties"); @@ -130,7 +131,7 @@ public class ConfigFileTest { file2.addAbsoluteInclude(file1); ex.setMasterFile(masterFile); - ex.setExtraFile(extraFile, Executor.ExtraMode.FILE_URI, false); + ex.setExtraFile(extraFile, false); ex.assertSuccess(); } @@ -152,7 +153,7 @@ public class ConfigFileTest { static void testIncludeWithOverrideAll(Executor ex, FilesManager filesMgr) throws Exception { PropsFile masterFile = filesMgr.newMasterFile(); - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(ExtraMode.HTTP_SERVED); PropsFile file0 = filesMgr.newFile("file0.properties"); PropsFile file1 = filesMgr.newFile("dir1/file1.properties"); @@ -160,40 +161,40 @@ public class ConfigFileTest { extraFile.addAbsoluteInclude(file1); ex.setMasterFile(masterFile); - ex.setExtraFile(extraFile, Executor.ExtraMode.HTTP_SERVED, true); + ex.setExtraFile(extraFile, true); ex.assertSuccess(); } static void extraPropertiesByHelper(Executor ex, FilesManager filesMgr, - Executor.ExtraMode mode) throws Exception { - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraMode mode) throws Exception { + ExtraPropsFile extraFile = filesMgr.newExtraFile(mode); PropsFile file0 = filesMgr.newFile("file0.properties"); extraFile.addRelativeInclude(file0); ex.setMasterFile(filesMgr.newMasterFile()); - ex.setExtraFile(extraFile, mode, true); + ex.setExtraFile(extraFile, true); ex.assertSuccess(); } static void testExtraPropertiesByPathAbsolute(Executor ex, FilesManager filesMgr) throws Exception { - extraPropertiesByHelper(ex, filesMgr, Executor.ExtraMode.PATH_ABS); + extraPropertiesByHelper(ex, filesMgr, ExtraMode.PATH_ABS); } static void testExtraPropertiesByPathRelative(Executor ex, FilesManager filesMgr) throws Exception { - extraPropertiesByHelper(ex, filesMgr, Executor.ExtraMode.PATH_REL); + extraPropertiesByHelper(ex, filesMgr, ExtraMode.PATH_REL); } static void specialCharsIncludes(Executor ex, FilesManager filesMgr, - char specialChar, Executor.ExtraMode extraMode, - boolean useRelativeIncludes) throws Exception { + char specialChar, ExtraMode extraMode, boolean useRelativeIncludes) + throws Exception { String suffix = specialChar + ".properties"; ExtraPropsFile extraFile; PropsFile file0, file1; try { - extraFile = filesMgr.newExtraFile("extra" + suffix); + extraFile = filesMgr.newExtraFile("extra" + suffix, extraMode); file0 = filesMgr.newFile("file0" + suffix); file1 = filesMgr.newFile("file1" + suffix); } catch (InvalidPathException ipe) { @@ -210,20 +211,18 @@ public class ConfigFileTest { extraFile.addAbsoluteInclude(file1); ex.setMasterFile(filesMgr.newMasterFile()); - ex.setExtraFile(extraFile, extraMode, false); + ex.setExtraFile(extraFile, false); ex.assertSuccess(); } static void testUnicodeIncludes1(Executor ex, FilesManager filesMgr) throws Exception { - specialCharsIncludes(ex, filesMgr, '\u2022', - Executor.ExtraMode.PATH_ABS, true); + specialCharsIncludes(ex, filesMgr, '\u2022', ExtraMode.PATH_ABS, true); } static void testUnicodeIncludes2(Executor ex, FilesManager filesMgr) throws Exception { - specialCharsIncludes(ex, filesMgr, '\u2022', - Executor.ExtraMode.FILE_URI, true); + specialCharsIncludes(ex, filesMgr, '\u2022', ExtraMode.FILE_URI, true); } static void testUnicodeIncludes3(Executor ex, FilesManager filesMgr) @@ -232,7 +231,7 @@ public class ConfigFileTest { // file:/tmp/extra•.properties are supported for the extra file. // However, relative includes are not allowed in these cases. specialCharsIncludes(ex, filesMgr, '\u2022', - Executor.ExtraMode.RAW_FILE_URI1, false); + ExtraMode.RAW_FILE_URI1, false); } static void testUnicodeIncludes4(Executor ex, FilesManager filesMgr) @@ -241,19 +240,17 @@ public class ConfigFileTest { // file:///tmp/extra•.properties are supported for the extra file. // However, relative includes are not allowed in these cases. specialCharsIncludes(ex, filesMgr, '\u2022', - Executor.ExtraMode.RAW_FILE_URI2, false); + ExtraMode.RAW_FILE_URI2, false); } static void testSpaceIncludes1(Executor ex, FilesManager filesMgr) throws Exception { - specialCharsIncludes(ex, filesMgr, ' ', - Executor.ExtraMode.PATH_ABS, true); + specialCharsIncludes(ex, filesMgr, ' ', ExtraMode.PATH_ABS, true); } static void testSpaceIncludes2(Executor ex, FilesManager filesMgr) throws Exception { - specialCharsIncludes(ex, filesMgr, ' ', - Executor.ExtraMode.FILE_URI, true); + specialCharsIncludes(ex, filesMgr, ' ', ExtraMode.FILE_URI, true); } static void testSpaceIncludes3(Executor ex, FilesManager filesMgr) @@ -261,8 +258,7 @@ public class ConfigFileTest { // Backward compatibility check. Malformed URLs such as // file:/tmp/extra .properties are supported for the extra file. // However, relative includes are not allowed in these cases. - specialCharsIncludes(ex, filesMgr, ' ', - Executor.ExtraMode.RAW_FILE_URI1, false); + specialCharsIncludes(ex, filesMgr, ' ', ExtraMode.RAW_FILE_URI1, false); } static void testSpaceIncludes4(Executor ex, FilesManager filesMgr) @@ -270,8 +266,7 @@ public class ConfigFileTest { // Backward compatibility check. Malformed URLs such as // file:///tmp/extra .properties are supported for the extra file. // However, relative includes are not allowed in these cases. - specialCharsIncludes(ex, filesMgr, ' ', - Executor.ExtraMode.RAW_FILE_URI2, false); + specialCharsIncludes(ex, filesMgr, ' ', ExtraMode.RAW_FILE_URI2, false); } static void notOverrideOnFailureHelper(Executor ex, FilesManager filesMgr, @@ -370,13 +365,13 @@ public class ConfigFileTest { static void testCannotResolveRelativeFromHTTPServed(Executor ex, FilesManager filesMgr) throws Exception { - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(ExtraMode.HTTP_SERVED); PropsFile file0 = filesMgr.newFile("file0.properties"); extraFile.addRelativeInclude(file0); ex.setMasterFile(filesMgr.newMasterFile()); - ex.setExtraFile(extraFile, Executor.ExtraMode.HTTP_SERVED, true); + ex.setExtraFile(extraFile, true); ex.assertError("InternalError: Cannot resolve '" + file0.fileName + "' relative path when included from a non-regular " + "properties file (e.g. HTTP served file)"); @@ -394,14 +389,15 @@ public class ConfigFileTest { masterFile.addRelativeInclude(file0); ex.setMasterFile(masterFile); - ex.assertError( - "InternalError: Cyclic include of '" + masterFile.path + "'"); + ex.assertError("Cyclic include"); + ex.getOutputAnalyzer().stderrShouldMatch("\\QInternalError: Cyclic " + + "include of '\\E[^']+\\Q" + masterFile.fileName + "'\\E"); } static void testCannotIncludeURL(Executor ex, FilesManager filesMgr) throws Exception { PropsFile masterFile = filesMgr.newMasterFile(); - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(ExtraMode.HTTP_SERVED); masterFile.addRawProperty("include", extraFile.url.toString()); @@ -432,8 +428,7 @@ public class ConfigFileTest { // Launch a JDK without a master java.security file present, but with an // extra file passed. Since the "security.overridePropertiesFile=true" // security property is missing, it should fail anyway. - ex.setExtraFile( - filesMgr.newExtraFile(), Executor.ExtraMode.FILE_URI, true); + ex.setExtraFile(filesMgr.newExtraFile(ExtraMode.FILE_URI), true); ex.assertError("InternalError: Error loading java.security file"); } } @@ -455,17 +450,24 @@ sealed class PropsFile permits ExtraPropsFile { static Include of(PropsFile propsFile, String value) { return new Include(propsFile, value); } + + void assertProcessed(OutputAnalyzer oa) { + oa.shouldContain("processing include: '" + value + "'"); + oa.shouldContain("finished processing " + propsFile.displayPath); + } } protected final List includes = new ArrayList<>(); protected final PrintWriter writer; protected boolean includedFromExtra = false; + protected Path displayPath; final String fileName; final Path path; PropsFile(String fileName, Path path) throws IOException { this.fileName = fileName; this.path = path; + this.displayPath = path; this.writer = new PrintWriter(Files.newOutputStream(path, StandardOpenOption.CREATE, StandardOpenOption.APPEND), true); } @@ -513,8 +515,9 @@ sealed class PropsFile permits ExtraPropsFile { } void addRelativeInclude(PropsFile propsFile) { - addIncludeDefinition(Include.of(propsFile, - path.getParent().relativize(propsFile.path).toString())); + Path rel = path.getParent().relativize(propsFile.path); + addIncludeDefinition(Include.of(propsFile, rel.toString())); + propsFile.displayPath = displayPath.getParent().resolve(rel); } void assertApplied(OutputAnalyzer oa) { @@ -522,8 +525,7 @@ sealed class PropsFile permits ExtraPropsFile { FilesManager.APPLIED_PROP_VALUE); for (Include include : includes) { include.propsFile.assertApplied(oa); - oa.shouldContain("processing include: '" + include.value + "'"); - oa.shouldContain("finished processing " + include.propsFile.path); + include.assertProcessed(oa); } } @@ -534,8 +536,7 @@ sealed class PropsFile permits ExtraPropsFile { if (!include.propsFile.includedFromExtra) { include.propsFile.assertWasOverwritten(oa); } - oa.shouldContain("processing include: '" + include.value + "'"); - oa.shouldContain("finished processing " + include.propsFile.path); + include.assertProcessed(oa); } } @@ -556,13 +557,24 @@ sealed class PropsFile permits ExtraPropsFile { } } +enum ExtraMode { + HTTP_SERVED, FILE_URI, RAW_FILE_URI1, RAW_FILE_URI2, PATH_ABS, PATH_REL +} + final class ExtraPropsFile extends PropsFile { + private static final Path CWD = Path.of(".").toAbsolutePath(); private final Map systemProps = new LinkedHashMap<>(); + private final ExtraMode mode; final URI url; - ExtraPropsFile(String fileName, URI url, Path path) throws IOException { + ExtraPropsFile(String fileName, URI url, Path path, ExtraMode mode) + throws IOException { super(fileName, path); this.url = url; + this.mode = mode; + if (mode == ExtraMode.PATH_REL) { + this.displayPath = CWD.relativize(path); + } } @Override @@ -578,14 +590,25 @@ final class ExtraPropsFile extends PropsFile { super.addIncludeDefinition(include); } + String getSysPropValue() { + return switch (mode) { + case HTTP_SERVED -> url.toString(); + case FILE_URI -> path.toUri().toString(); + case RAW_FILE_URI1 -> "file:" + path; + case RAW_FILE_URI2 -> + "file://" + (path.startsWith("/") ? "" : "/") + path; + case PATH_ABS, PATH_REL -> displayPath.toString(); + }; + } + Map getSystemProperties() { return Collections.unmodifiableMap(systemProps); } } final class FilesManager implements Closeable { - private static final Path ROOT_DIR = - Path.of(ConfigFileTest.class.getSimpleName()).toAbsolutePath(); + private static final Path ROOT_DIR = Path.of( + ExtraFileAndIncludes.class.getSimpleName()).toAbsolutePath(); private static final Path PROPS_DIR = ROOT_DIR.resolve("properties"); private static final Path JDK_DIR = ROOT_DIR.resolve("jdk"); private static final Path MASTER_FILE = @@ -684,11 +707,11 @@ final class FilesManager implements Closeable { propsFile.addComment("Property to determine if this properties file " + "was parsed and not overwritten:"); propsFile.addRawProperty(fileName, APPLIED_PROP_VALUE); - propsFile.addComment(ConfigFileTest.SEPARATOR_THIN); + propsFile.addComment(ExtraFileAndIncludes.SEPARATOR_THIN); propsFile.addComment("Property to be overwritten by every properties " + "file (master, extra or included):"); propsFile.addRawProperty(LAST_FILE_PROP_NAME, fileName); - propsFile.addComment(ConfigFileTest.SEPARATOR_THIN); + propsFile.addComment(ExtraFileAndIncludes.SEPARATOR_THIN); createdFiles.add(propsFile); return propsFile; } @@ -702,16 +725,17 @@ final class FilesManager implements Closeable { return newFile(MASTER_FILE, PropsFile::new); } - ExtraPropsFile newExtraFile() throws IOException { - return newExtraFile("extra.properties"); + ExtraPropsFile newExtraFile(ExtraMode mode) throws IOException { + return newExtraFile("extra.properties", mode); } - ExtraPropsFile newExtraFile(String extraFileName) throws IOException { + ExtraPropsFile newExtraFile(String extraFileName, ExtraMode mode) + throws IOException { return (ExtraPropsFile) newFile(PROPS_DIR.resolve(extraFileName), (fileName, path) -> { URI uri = serverUri.resolve(ParseUtil.encodePath( ROOT_DIR.relativize(path).toString())); - return new ExtraPropsFile(fileName, uri, path); + return new ExtraPropsFile(fileName, uri, path, mode); }); } @@ -719,7 +743,7 @@ final class FilesManager implements Closeable { for (PropsFile propsFile : createdFiles) { System.err.println(); System.err.println(propsFile.path.toString()); - System.err.println(ConfigFileTest.SEPARATOR_THIN.repeat(3)); + System.err.println(ExtraFileAndIncludes.SEPARATOR_THIN.repeat(3)); try (Stream lines = Files.lines(propsFile.path)) { long lineNumber = 1L; Iterator it = lines.iterator(); @@ -757,9 +781,6 @@ final class FilesManager implements Closeable { } final class Executor { - enum ExtraMode { - HTTP_SERVED, FILE_URI, RAW_FILE_URI1, RAW_FILE_URI2, PATH_ABS, PATH_REL - } static final String RUNNER_ARG = "runner"; static final String INITIAL_PROP_LOG_MSG = "Initial security property: "; private static final String OVERRIDING_LOG_MSG = @@ -769,7 +790,6 @@ final class Executor { INITIAL_PROP_LOG_MSG + "postInitTest=shouldNotRecord", INITIAL_PROP_LOG_MSG + "include=", }; - private static final Path CWD = Path.of(".").toAbsolutePath(); private static final String JAVA_SEC_PROPS = "java.security.properties"; private static final String CLASS_PATH = Objects.requireNonNull( System.getProperty("test.classes"), "unspecified test.classes"); @@ -812,20 +832,10 @@ final class Executor { this.masterPropsFile = masterPropsFile; } - void setExtraFile(ExtraPropsFile extraPropsFile, ExtraMode mode, - boolean overrideAll) { + void setExtraFile(ExtraPropsFile extraPropsFile, boolean overrideAll) { this.extraPropsFile = extraPropsFile; expectedOverrideAll = overrideAll; - setRawExtraFile(switch (mode) { - case HTTP_SERVED -> extraPropsFile.url.toString(); - case FILE_URI -> extraPropsFile.path.toUri().toString(); - case RAW_FILE_URI1 -> "file:" + extraPropsFile.path; - case RAW_FILE_URI2 -> "file://" + - (extraPropsFile.path.startsWith("/") ? "" : "/") + - extraPropsFile.path; - case PATH_ABS -> extraPropsFile.path.toString(); - case PATH_REL -> CWD.relativize(extraPropsFile.path).toString(); - }, overrideAll); + setRawExtraFile(extraPropsFile.getSysPropValue(), overrideAll); } void setIgnoredExtraFile(String extraPropsFile, boolean overrideAll) { @@ -841,7 +851,7 @@ final class Executor { List command = new ArrayList<>(jvmArgs); Collections.addAll(command, Utils.getTestJavaOpts()); addSystemPropertiesAsJvmArgs(command); - command.add(ConfigFileTest.class.getSimpleName()); + command.add(ExtraFileAndIncludes.class.getSimpleName()); command.add(RUNNER_ARG); oa = ProcessTools.executeProcess(new ProcessBuilder(command)); oa.shouldHaveExitValue(successExpected ? 0 : 1); diff --git a/test/jdk/java/security/Security/SecurityPropFile/LinuxAnonymousFiles.java b/test/jdk/java/security/Security/SecurityPropFile/LinuxAnonymousFiles.java new file mode 100644 index 00000000000..7ca2a7c0f8b --- /dev/null +++ b/test/jdk/java/security/Security/SecurityPropFile/LinuxAnonymousFiles.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026, Red Hat, Inc. + * + * 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.process.ProcessTools; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; + +/* + * @test + * @summary Ensures the java executable is able to load extra security + * properties files from anonymous files and pipes. + * @bug 8352728 + * @requires os.family == "linux" + * @modules java.base/java.io:+open + * @library /test/lib + * @run main LinuxAnonymousFiles + */ + +public class LinuxAnonymousFiles { + private static final String TEST_PROP = "property.name=PROPERTY_VALUE"; + + private static final class AnonymousFile implements AutoCloseable { + public final Path fdPath; + private final FileInputStream fis; + + private AnonymousFile(CharSequence content) throws Exception { + Path tmp = Files.createTempFile("anonymous-file-", ""); + Files.writeString(tmp, content + System.lineSeparator()); + fis = new FileInputStream(tmp.toFile()); + Files.delete(tmp); + // Now the file is regular but anonymous, and will be unlinked + // when we close the last file descriptor referring to it. The + // fis instance ensures we keep it alive until close() is invoked. + Field field = FileDescriptor.class.getDeclaredField("fd"); + field.setAccessible(true); + int fd = field.getInt(fis.getFD()); + fdPath = Path.of("/proc/self").toRealPath().resolve("fd/" + fd); + } + + @Override + public void close() throws IOException { + fis.close(); + } + } + + public static void main(String[] args) throws Exception { + Path java = Path.of(System.getProperty("test.jdk"), "bin", "java"); + try (AnonymousFile af = new AnonymousFile("include /dev/stdin")) { + ProcessTools.executeProcess(new ProcessBuilder(java.toString(), + "-Djava.security.debug=properties", + "-Djava.security.properties=" + af.fdPath, + "-XshowSettings:security:properties", "-version"), + TEST_PROP).shouldHaveExitValue(0).shouldContain(TEST_PROP); + } + System.out.println("TEST PASS - OK"); + } +} diff --git a/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.file b/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.file deleted file mode 100644 index 2b4c08c6903..00000000000 --- a/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.file +++ /dev/null @@ -1 +0,0 @@ -policy.url.2=file:${test.src}/SecurityPropFile.policy diff --git a/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.java b/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.java deleted file mode 100644 index b0ba6c60854..00000000000 --- a/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2001, 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. - */ - -/* - * @test - * @bug 4303068 - * @summary be allowed to specify the security properties file - * as a -D system property - * - * @run main/othervm -Djava.security.properties=${test.src}/SecurityPropFile.file -Djava.security.debug=properties SecurityPropFile - */ - -public class SecurityPropFile { - public static void main(String[] args) { - System.out.println(java.security.Security.getProperty - ("policy.provider")); - System.out.println(java.security.Security.getProperty - ("policy.url.1")); - System.out.println(java.security.Security.getProperty - ("policy.url.2")); - } -} diff --git a/test/jdk/java/security/Security/SecurityPropFile/WindowsParentDirPermissions.java b/test/jdk/java/security/Security/SecurityPropFile/WindowsParentDirPermissions.java new file mode 100644 index 00000000000..a41fd2f3535 --- /dev/null +++ b/test/jdk/java/security/Security/SecurityPropFile/WindowsParentDirPermissions.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026, Red Hat, Inc. + * + * 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.process.ProcessTools; +import jdk.test.lib.util.FileUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.AclEntry; +import java.nio.file.attribute.AclEntryType; +import java.nio.file.attribute.AclFileAttributeView; +import java.util.List; + +/* + * @test + * @summary Ensures java.security is loadable in Windows, even when the user + * does not have permissions on one of the parent directories. + * @bug 8352728 + * @requires os.family == "windows" + * @library /test/lib + * @run main WindowsParentDirPermissions + */ + +public class WindowsParentDirPermissions { + private static AutoCloseable restrictedAcl(Path path) throws IOException { + AclFileAttributeView view = + Files.getFileAttributeView(path, AclFileAttributeView.class); + List originalAcl = List.copyOf(view.getAcl()); + view.setAcl(List.of(AclEntry.newBuilder().setType(AclEntryType.DENY) + .setPrincipal(Files.getOwner(path)).build())); + return () -> view.setAcl(originalAcl); + } + + public static void main(String[] args) throws Exception { + Path temp = Files.createTempDirectory("JDK-8352728-tmp-"); + try (AutoCloseable a1 = () -> FileUtils.deleteFileTreeUnchecked(temp)) { + // Copy the jdk to a different directory + Path originalJdk = Path.of(System.getProperty("test.jdk")); + Path jdk = temp.resolve("jdk-parent-dir", "jdk"); + Files.createDirectories(jdk); + FileUtils.copyDirectory(originalJdk, jdk); + + // Remove current user permissions from jdk-parent-dir + try (AutoCloseable a2 = restrictedAcl(jdk.getParent())) { + // Make sure the permissions are affecting the current user + try { + jdk.toRealPath(); + throw new jtreg.SkippedException("Must run non-elevated!"); + } catch (IOException expected) { } + + // Execute the copied jdk, ensuring java.security.Security is + // loaded (i.e. use -XshowSettings:security:properties) + ProcessTools.executeProcess(new ProcessBuilder( + List.of(jdk.resolve("bin", "java.exe").toString(), + "-Djava.security.debug=properties", + "-XshowSettings:security:properties", + "-version"))).shouldHaveExitValue(0); + } + } + System.out.println("TEST PASS - OK"); + } +} From afd216ec3f5bfd1be88c6f4d4f53b763205c4fee Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Thu, 8 Jan 2026 17:19:12 +0000 Subject: [PATCH 028/204] 8374752: Add more JLS links to javax.lang.model.element.* Reviewed-by: liach --- .../javax/lang/model/element/ExecutableElement.java | 9 ++++++++- .../javax/lang/model/element/PackageElement.java | 5 ++++- .../javax/lang/model/element/Parameterizable.java | 6 +++++- .../javax/lang/model/element/QualifiedNameable.java | 6 +++++- .../classes/javax/lang/model/element/TypeElement.java | 11 ++++++++++- .../lang/model/element/TypeParameterElement.java | 7 ++++++- .../javax/lang/model/element/VariableElement.java | 10 +++++++++- 7 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java b/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java index 18923463248..cc0924fda61 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.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 @@ -37,6 +37,13 @@ import javax.lang.model.type.*; * clause, among other restrictions; see JLS {@jls 9.6.1} for details. * * @see ExecutableType + * @jls 8.4 Method Declarations + * @jls 8.6 Instance Initializers + * @jls 8.7 Static Initializers + * @jls 8.8 Constructor Declarations + * @jls 9.4 Method Declarations + * @jls 9.6.1 Annotation Interface Elements + * * @since 1.6 */ public interface ExecutableElement extends Element, Parameterizable { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/PackageElement.java b/src/java.compiler/share/classes/javax/lang/model/element/PackageElement.java index 651bbe70eee..79b1fb84683 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/PackageElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/PackageElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, 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 @@ -58,6 +58,8 @@ import javax.lang.model.type.TypeMirror; * * * @see javax.lang.model.util.Elements#getPackageOf + * @jls 7.4 Package Declarations + * * @since 1.6 */ public interface PackageElement extends Element, QualifiedNameable { @@ -87,6 +89,7 @@ public interface PackageElement extends Element, QualifiedNameable { * @return the fully qualified name of this package, or an * empty name if this is an unnamed package * @jls 6.7 Fully Qualified Names and Canonical Names + * @jls 7.4.1 Named Packages */ Name getQualifiedName(); diff --git a/src/java.compiler/share/classes/javax/lang/model/element/Parameterizable.java b/src/java.compiler/share/classes/javax/lang/model/element/Parameterizable.java index cf0bbdfba37..a4280fbcf29 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/Parameterizable.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/Parameterizable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2022, 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 @@ -30,6 +30,10 @@ import java.util.List; /** * A mixin interface for an element that has type parameters. * + * @jls 4.5 Parameterized Types + * @jls 8.4.4 Generic Methods + * @jls 8.8.4 Generic Constructors + * * @since 1.7 */ public interface Parameterizable extends Element { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/QualifiedNameable.java b/src/java.compiler/share/classes/javax/lang/model/element/QualifiedNameable.java index 1b0316f893f..555d90c1b13 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/QualifiedNameable.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/QualifiedNameable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2022, 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 @@ -28,6 +28,10 @@ package javax.lang.model.element; /** * A mixin interface for an element that has a qualified name. * + * @jls 6.5.3.2 Qualified Package Names + * @jls 6.5.5.2 Qualified Type Names + * @jls 6.7 Fully Qualified Names and Canonical Names + * * @since 1.7 */ public interface QualifiedNameable extends Element { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java b/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java index abf95bcefad..3dcc8e8c7fd 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.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 @@ -80,7 +80,16 @@ import javax.lang.model.util.*; * javax.lang.model.util.Elements#getAllTypeElements(CharSequence) * queried for} in the configured environment * + * * @see DeclaredType + * @jls 8.1 Class Declarations + * @jls 8.5 Member Class and Interface Declarations + * @jls 8.9 Enum Classes + * @jls 8.10 Record Classes + * @jls 9.1 Interface Declarations + * @jls 9.5 Member Class and Interface Declarations + * @jls 9.6 Annotation Interfaces + * * @since 1.6 */ public interface TypeElement extends Element, Parameterizable, QualifiedNameable { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/TypeParameterElement.java b/src/java.compiler/share/classes/javax/lang/model/element/TypeParameterElement.java index cbaa8969126..abd06a3e038 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/TypeParameterElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/TypeParameterElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, 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 @@ -35,6 +35,11 @@ import javax.lang.model.type.TypeVariable; * A type parameter declares a {@link TypeVariable}. * * @see TypeVariable + * @jls 8.1.2 Generic Classes and Type Parameters + * @jls 8.4.4 Generic Methods + * @jls 8.8.4 Generic Constructors + * @jls 9.1.2 Generic Interfaces and Type Parameters + * * @since 1.6 */ public interface TypeParameterElement extends Element { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/VariableElement.java b/src/java.compiler/share/classes/javax/lang/model/element/VariableElement.java index 4d2b2582901..b8f42c46b1e 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/VariableElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/VariableElement.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 @@ -34,6 +34,14 @@ import javax.lang.model.type.TypeKind; * parameter, local variable, resource variable, or exception * parameter. * + * @jls 8.3 Field Declaration + * @jls 8.9.1 Enum Constants + * @jls 8.4.1 Formal Parameters + * @jls 8.8.1 Formal Parameters + * @jls 14.4 Local Variable Declarations + * @jls 14.20 The {@code try} statement + * @jls 14.20.3 {@code try}-with-resources + * @since 1.6 */ public interface VariableElement extends Element { From 92abc6dfe43a2c1f10dcfcf1e197fc9369f70ee3 Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Thu, 8 Jan 2026 17:35:43 +0000 Subject: [PATCH 029/204] 8369282: Distrust TLS server certificates anchored by Chunghwa ePKI Root CA Reviewed-by: mullan --- .../security/validator/CADistrustPolicy.java | 18 ++- .../security/validator/ChunghwaTLSPolicy.java | 103 ++++++++++++++++++ .../share/conf/security/java.security | 6 +- .../distrust/Chunghwa.java | 69 ++++++++++++ .../chunghwa/chunghwaepkirootca-chain.pem | 50 +++++++++ 5 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 src/java.base/share/classes/sun/security/validator/ChunghwaTLSPolicy.java create mode 100644 test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Chunghwa.java create mode 100644 test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/chunghwa/chunghwaepkirootca-chain.pem diff --git a/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java b/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java index 9c64402c123..44c9ed2d075 100644 --- a/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java +++ b/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.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 @@ -83,6 +83,22 @@ enum CADistrustPolicy { } CamerfirmaTLSPolicy.checkDistrust(chain); } + }, + + /** + * Distrust TLS Server certificates anchored by the Chunghwa ePKI root CA + * and issued after March 17, 2026. If enabled, this policy is currently + * enforced by the PKIX and SunX509 TrustManager implementations + * of the SunJSSE provider implementation. + */ + CHUNGHWA_TLS { + void checkDistrust(String variant, X509Certificate[] chain) + throws ValidatorException { + if (!variant.equals(Validator.VAR_TLS_SERVER)) { + return; + } + ChunghwaTLSPolicy.checkDistrust(chain); + } }; /** diff --git a/src/java.base/share/classes/sun/security/validator/ChunghwaTLSPolicy.java b/src/java.base/share/classes/sun/security/validator/ChunghwaTLSPolicy.java new file mode 100644 index 00000000000..114f5043fbf --- /dev/null +++ b/src/java.base/share/classes/sun/security/validator/ChunghwaTLSPolicy.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. 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 sun.security.validator; + +import java.security.cert.X509Certificate; +import java.time.LocalDate; +import java.time.Month; +import java.time.ZoneOffset; +import java.util.Date; +import java.util.Map; +import java.util.Set; + +import sun.security.util.Debug; +import sun.security.x509.X509CertImpl; + +/** + * This class checks if Chunghwa issued TLS Server certificates should be + * restricted. + */ +final class ChunghwaTLSPolicy { + + private static final Debug debug = Debug.getInstance("certpath"); + + // SHA-256 certificate fingerprint of distrusted root for TLS + // cacerts alias: chunghwaepkirootca + // DN: OU=ePKI Root Certification Authority, + // O="Chunghwa Telecom Co., Ltd.", C=TW + private static final String FINGERPRINT = + "C0A6F4DC63A24BFDCF54EF2A6A082A0A72DE35803E2FF5FF527AE5D87206DFD5"; + + // Any TLS Server certificate that is anchored by the Chunghwa + // root above and is issued after this date will be distrusted. + private static final LocalDate MARCH_17_2026 = + LocalDate.of(2026, Month.MARCH, 17); + + /** + * This method assumes the eeCert is a TLS Server Cert and chains back to + * the anchor. + * + * @param chain the end-entity's certificate chain. The end entity cert + * is at index 0, the trust anchor at index n-1. + * @throws ValidatorException if the certificate is distrusted + */ + static void checkDistrust(X509Certificate[] chain) + throws ValidatorException { + X509Certificate anchor = chain[chain.length-1]; + String fp = fingerprint(anchor); + if (fp == null) { + throw new ValidatorException("Cannot generate fingerprint for " + + "trust anchor of TLS server certificate"); + } + if (FINGERPRINT.equalsIgnoreCase(fp)) { + Date notBefore = chain[0].getNotBefore(); + LocalDate ldNotBefore = LocalDate.ofInstant(notBefore.toInstant(), + ZoneOffset.UTC); + // reject if certificate is issued after March 17, 2026 + checkNotBefore(ldNotBefore, MARCH_17_2026, anchor); + } + } + + private static String fingerprint(X509Certificate cert) { + return X509CertImpl.getFingerprint("SHA-256", cert, debug); + } + + // Check whether the certificate's notBeforeDate is after the + // distrust date for the anchor (root CA). Throw ValidatorException + // if it is after the distrust date. + private static void checkNotBefore(LocalDate notBeforeDate, + LocalDate distrustDate, X509Certificate anchor) + throws ValidatorException { + if (notBeforeDate.isAfter(distrustDate)) { + throw new ValidatorException + ("TLS Server certificate issued after " + distrustDate + + " and anchored by a distrusted legacy Chunghwa root CA: " + + anchor.getSubjectX500Principal(), + ValidatorException.T_UNTRUSTED_CERT, anchor); + } + } + + private ChunghwaTLSPolicy() {} +} diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 3d26c1dcd96..b5cbce413b2 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -1437,6 +1437,9 @@ jdk.sasl.disabledMechanisms= # CAMERFIRMA_TLS : Distrust TLS Server certificates anchored by # a Camerfirma root CA and issued after April 15, 2025. # +# CHUNGHWA_TLS : Distrust TLS Server certificates anchored by +# a Chunghwa root CA and issued after March 17, 2026. +# # Leading and trailing whitespace surrounding each value are ignored. # Unknown values are ignored. If the property is commented out or set to the # empty String, no policies are enforced. @@ -1448,7 +1451,8 @@ jdk.sasl.disabledMechanisms= # jdk.certpath.disabledAlgorithms; those restrictions are still enforced even # if this property is not enabled. # -jdk.security.caDistrustPolicies=SYMANTEC_TLS,ENTRUST_TLS,CAMERFIRMA_TLS +jdk.security.caDistrustPolicies=SYMANTEC_TLS,ENTRUST_TLS,CAMERFIRMA_TLS,\ + CHUNGHWA_TLS # # FilePermission path canonicalization diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Chunghwa.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Chunghwa.java new file mode 100644 index 00000000000..8512640bb01 --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Chunghwa.java @@ -0,0 +1,69 @@ +/* + * 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 javax.net.ssl.X509TrustManager; +import java.io.File; +import java.security.Security; +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Date; + +/* + * @test + * @bug 8369282 + * @summary Check that TLS Server certificates chaining back to distrusted + * Chunghwa root are invalid + * @library /test/lib + * @modules java.base/sun.security.validator + * @run main/othervm Chunghwa after policyOn invalid + * @run main/othervm Chunghwa after policyOff valid + * @run main/othervm Chunghwa before policyOn valid + * @run main/othervm Chunghwa before policyOff valid + */ + +public class Chunghwa { + + private static final String CERT_PATH = "chains" + File.separator + "chunghwa"; + + // The ePKI root has a test certificate chain stored in a file + // named "-chain.pem". + private static final String ROOT_TO_TEST = "chunghwaepkirootca"; + + // Date after the restrictions take effect + private static final ZonedDateTime DISTRUST_DATE = + LocalDate.of(2026, 03, 18).atStartOfDay(ZoneOffset.UTC); + + public static void main(String[] args) throws Exception { + + Distrust distrust = new Distrust(args); + + X509TrustManager[] tms = new X509TrustManager[]{ + distrust.getTMF("PKIX", null), + distrust.getTMF("SunX509", null) + }; + + Date notBefore = distrust.getNotBefore(DISTRUST_DATE); + distrust.testCertificateChain(CERT_PATH, notBefore, tms, ROOT_TO_TEST); + } +} diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/chunghwa/chunghwaepkirootca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/chunghwa/chunghwaepkirootca-chain.pem new file mode 100644 index 00000000000..fdaa6ee04df --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/chunghwa/chunghwaepkirootca-chain.pem @@ -0,0 +1,50 @@ +Owner: CN=HiPKI Root CA - G1, + O="Chunghwa Telecom Co., Ltd.", C=TW +Issuer: OU=ePKI Root Certification Authority, + O="Chunghwa Telecom Co., Ltd.", C=TW +Serial number: 23fba648360e15e92ba78aedb67a0ae5 +Valid from: Wed Dec 20 19:11:23 MST 2023 until: Tue Dec 19 08:59:59 MST 2034 +Certificate fingerprints: + SHA1: 87:F1:DD:3B:8E:F1:E0:8C:A8:CA:CB:9B:CE:4E:26:5A:E4:4E:05:F2 + SHA256: 68:07:C9:72:35:C5:EC:60:90:26:9A:4B:5F:ED:FA:B4:69:86:E4:2F:4D:67:D2:ED:DD:CF:6E:45:CF:0D:FA:80 +Signature algorithm name: SHA256withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 + +-----BEGIN CERTIFICATE----- +MIIGjDCCBHSgAwIBAgIQI/umSDYOFekrp4rttnoK5TANBgkqhkiG9w0BAQsFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0yMzEyMjEwMjExMjNaFw0zNDEyMTkxNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQ +S0kgUm9vdCBDQSAtIEcxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +9B5/UnMyDHPkvRN0o9QwqNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh +8Ge6zCFovkRTv4354twvVcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux5 +5199QmQ5eiY29yTw1S+6lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEu +iAU+TCK72h8q3VJGZDnzQs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRt +U6M9/Aes1MU3guvklQgZKILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfd +hSi8MEyr48KxRURHH+CKFgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXT +T3OUM3ECoWqj1jOXTyFjHluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK +9p/7qxj3ccC2HTHsOyDry+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8 +b3ti6RZsR1pl8w4Rm0bZ/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8Pg +cSojt/ewsTu8mL3WmKgMa/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NV +vxaXxA/VLGGEqnKG/uY6fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaOCAVMwggFPMB8G +A1UdIwQYMBaAFB4M97Zn8uGSJglFwFU5Lnc/QkqiMB0GA1UdDgQWBBTydxf6Xqj+ +9j1x1Wi6yUYMONivsDAOBgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYv +aHR0cDovL2VjYS5oaW5ldC5uZXQvcmVwb3NpdG9yeS9DUkxfU0hBMi9DQS5jcmww +gYIGCCsGAQUFBwEBBHYwdDA7BggrBgEFBQcwAoYvaHR0cDovL2VjYS5oaW5ldC5u +ZXQvcmVwb3NpdG9yeS9DZXJ0cy9lQ0FHMS5jcnQwNQYIKwYBBQUHMAGGKWh0dHA6 +Ly9vY3NwLmVjYS5oaW5ldC5uZXQvT0NTUC9vY3NwRzFzaGEyMBIGA1UdEwEB/wQI +MAYBAf8CAQEwIgYDVR0gBBswGTAIBgZngQwBAgIwDQYLKwYBBAGBtyNkAAMwDQYJ +KoZIhvcNAQELBQADggIBACY9pps8fqk3p8Xqv/qr26I1aFA4jOEG3VWd2bqn68Y9 +InOMZozTMVh7iOnOfat7mEqn/RNhikvR5MOV3qAeg4gwgNb1OMuGltwfXWGiuGeT +vhimsV6E2hhJFAmZyXtfuoV9vSrnr1a5pCWqhVYWSCvoAQ/8Kv0tATKbIe21CYXz +NIo7O9QBSXt0BiaP9+CVQtJAYYuy2MNAcXgzgL4rownrYYAixhPmkxQE0Dt1gVbW +s2htBLJGse0z1fJDblY0Zar4t2ly+kIScx5DhRrrd8XKMK0YvID9Ythb+ao8m7Wd +Kymqr36benGL3GsvmSypLPlqZtfEqVITFhXwQiL8ruxoL+3WfNQJ09x0iV4xaP+E +bZSLLVzIiyhU49YdFHaqKyAJQvzgF2Za3DOwQWlP7OngtUx0ScEGHsoo78AM+Y0T +eLFxmr82kuyH18wZkUT9bLZlot11P2aC8VTprBGr+jEAMJjpmEjSA83ja/ttmqgh +qjj29Jnw3Lgy91XIhzBFMxMYo+hhYeBRmBFWl5+Y5oxBgPVLZpDJvg2rKa8xdqim +KgvF0DMKHntE0hhVy7JfUCnKovNQ0pf0NodLfjpqcCS2GBZ1mNcsW2MG2uBPANcn +LRXmt7N4XX11mctQTADwt8yZZ+2HDrST4kghOz+FXgftrPBdtDtM0T6WJcHWR1uS +-----END CERTIFICATE----- From 1fb5030ab351a52b4a7455cbdd57f5b50aab9bd5 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Thu, 8 Jan 2026 17:58:35 +0000 Subject: [PATCH 030/204] 8374767: Amend JDK-8374521 with new option name Reviewed-by: clanger, krk --- .github/workflows/build-alpine-linux.yml | 2 +- .github/workflows/build-cross-compile.yml | 2 +- .github/workflows/build-linux.yml | 2 +- .github/workflows/build-macos.yml | 2 +- make/autoconf/flags-cflags.m4 | 28 +++++++++++------------ 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-alpine-linux.yml b/.github/workflows/build-alpine-linux.yml index 569893d5ccc..c39962fa07f 100644 --- a/.github/workflows/build-alpine-linux.yml +++ b/.github/workflows/build-alpine-linux.yml @@ -97,7 +97,7 @@ jobs: --with-zlib=system --with-jmod-compress=zip-1 --with-external-symbols-in-bundles=none - --with-debug-info-level=1 + --with-native-debug-symbols-level=1 ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( echo "Dumping config.log:" && cat config.log && diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index 4860228c7de..a0642d469aa 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -180,7 +180,7 @@ jobs: --with-sysroot=sysroot --with-jmod-compress=zip-1 --with-external-symbols-in-bundles=none - --with-debug-info-level=1 + --with-native-debug-symbols-level=1 CC=${{ matrix.gnu-arch }}-linux-gnu${{ matrix.gnu-abi}}-gcc-${{ inputs.gcc-major-version }} CXX=${{ matrix.gnu-arch }}-linux-gnu${{ matrix.gnu-abi}}-g++-${{ inputs.gcc-major-version }} ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 121416106f2..791b53a3f04 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -144,7 +144,7 @@ jobs: --with-zlib=system --with-jmod-compress=zip-1 --with-external-symbols-in-bundles=none - --with-debug-info-level=1 + --with-native-debug-symbols-level=1 ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( echo "Dumping config.log:" && cat config.log && diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 32c5d43acbc..484e616fad7 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -111,7 +111,7 @@ jobs: --with-zlib=system --with-jmod-compress=zip-1 --with-external-symbols-in-bundles=none - --with-debug-info-level=1 + --with-native-debug-symbols-level=1 ${{ inputs.extra-conf-options }} ${{ inputs.configure-arguments }} || ( echo "Dumping config.log:" && cat config.log && diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index b7b2a6b5e17..5a9fdc57c74 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -69,20 +69,20 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS], # Debug prefix mapping if supported by compiler DEBUG_PREFIX_CFLAGS= - UTIL_ARG_WITH(NAME: debug-info-level, TYPE: string, + UTIL_ARG_WITH(NAME: native-debug-symbols-level, TYPE: string, DEFAULT: "", - RESULT: DEBUG_INFO_LEVEL, - DESC: [Sets the debug info level, when debug info generation is enabled (GCC and Clang only)], - DEFAULT_DESC: [default]) - AC_SUBST(DEBUG_INFO_LEVEL) + RESULT: DEBUG_SYMBOLS_LEVEL, + DESC: [set the native debug symbol level (GCC and Clang only)], + DEFAULT_DESC: [toolchain default]) + AC_SUBST(DEBUG_SYMBOLS_LEVEL) if test "x${TOOLCHAIN_TYPE}" = xgcc || \ test "x${TOOLCHAIN_TYPE}" = xclang; then - DEBUG_INFO_LEVEL_FLAGS="-g" - if test "x${DEBUG_INFO_LEVEL}" != "x"; then - DEBUG_INFO_LEVEL_FLAGS="-g${DEBUG_INFO_LEVEL}" - FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${DEBUG_INFO_LEVEL_FLAGS}], - IF_FALSE: AC_MSG_ERROR("Debug info level ${DEBUG_INFO_LEVEL} is not supported")) + DEBUG_SYMBOLS_LEVEL_FLAGS="-g" + if test "x${DEBUG_SYMBOLS_LEVEL}" != "x"; then + DEBUG_SYMBOLS_LEVEL_FLAGS="-g${DEBUG_SYMBOLS_LEVEL}" + FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${DEBUG_SYMBOLS_LEVEL_FLAGS}], + IF_FALSE: AC_MSG_ERROR("Debug info level ${DEBUG_SYMBOLS_LEVEL} is not supported")) fi fi @@ -111,8 +111,8 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS], fi # Debug info level should follow the debug format to be effective. - CFLAGS_DEBUG_SYMBOLS="-gdwarf-4 ${DEBUG_INFO_LEVEL_FLAGS}" - ASFLAGS_DEBUG_SYMBOLS="${DEBUG_INFO_LEVEL_FLAGS}" + CFLAGS_DEBUG_SYMBOLS="-gdwarf-4 ${DEBUG_SYMBOLS_LEVEL_FLAGS}" + ASFLAGS_DEBUG_SYMBOLS="${DEBUG_SYMBOLS_LEVEL_FLAGS}" elif test "x$TOOLCHAIN_TYPE" = xclang; then if test "x$ALLOW_ABSOLUTE_PATHS_IN_OUTPUT" = "xfalse"; then # Check if compiler supports -fdebug-prefix-map. If so, use that to make @@ -132,8 +132,8 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS], IF_FALSE: [GDWARF_FLAGS=""]) # Debug info level should follow the debug format to be effective. - CFLAGS_DEBUG_SYMBOLS="${GDWARF_FLAGS} ${DEBUG_INFO_LEVEL_FLAGS}" - ASFLAGS_DEBUG_SYMBOLS="${DEBUG_INFO_LEVEL_FLAGS}" + CFLAGS_DEBUG_SYMBOLS="${GDWARF_FLAGS} ${DEBUG_SYMBOLS_LEVEL_FLAGS}" + ASFLAGS_DEBUG_SYMBOLS="${DEBUG_SYMBOLS_LEVEL_FLAGS}" elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then CFLAGS_DEBUG_SYMBOLS="-Z7" fi From 9fd86e37492c419fbae0837f69aab26a201c927e Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Thu, 8 Jan 2026 18:42:20 +0000 Subject: [PATCH 031/204] 8374639: Static archive with AOTClassLinking breaks dynamic archive Reviewed-by: coleenp, matsaave --- .../share/cds/aotConstantPoolResolver.cpp | 6 +- src/hotspot/share/cds/aotMetaspace.cpp | 3 +- src/hotspot/share/cds/archiveBuilder.cpp | 4 +- src/hotspot/share/cds/dynamicArchive.cpp | 119 +----------- src/hotspot/share/cds/dynamicArchive.hpp | 16 +- ...DynamicDumpWithAOTLinkedStaticArchive.java | 60 +++++++ .../appcds/dynamicArchive/ArrayKlasses.java | 170 ------------------ .../test-classes/ArrayKlassesApp.java | 77 -------- test/lib/jdk/test/lib/cds/CDSAppTester.java | 17 +- .../jdk/test/lib/cds/SimpleCDSAppTester.java | 25 ++- 10 files changed, 106 insertions(+), 391 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java delete mode 100644 test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArrayKlasses.java delete mode 100644 test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ArrayKlassesApp.java diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp index c4bb26f6fb1..93145940955 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.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 @@ -116,6 +116,10 @@ bool AOTConstantPoolResolver::is_class_resolution_deterministic(InstanceKlass* c return false; } } else if (resolved_class->is_objArray_klass()) { + if (CDSConfig::is_dumping_dynamic_archive()) { + // This is difficult to handle. See JDK-8374639 + return false; + } Klass* elem = ObjArrayKlass::cast(resolved_class)->bottom_klass(); if (elem->is_instance_klass()) { return is_class_resolution_deterministic(cp_holder, InstanceKlass::cast(elem)); diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 3824a2be3e2..79d789e0c70 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.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 @@ -2159,7 +2159,6 @@ void AOTMetaspace::initialize_shared_spaces() { intptr_t* buffer = (intptr_t*)dynamic_mapinfo->serialized_data(); ReadClosure rc(&buffer, (intptr_t)SharedBaseAddress); DynamicArchive::serialize(&rc); - DynamicArchive::setup_array_klasses(); } LogStreamHandle(Info, aot) lsh; diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 3c7f25a950b..6bbefea5cd9 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.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 @@ -984,8 +984,6 @@ void ArchiveBuilder::make_klasses_shareable() { #undef STATS_FORMAT #undef STATS_PARAMS - - DynamicArchive::make_array_klasses_shareable(); } void ArchiveBuilder::make_training_data_shareable() { diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp index 8fae8dabf8c..d39cf3775e4 100644 --- a/src/hotspot/share/cds/dynamicArchive.cpp +++ b/src/hotspot/share/cds/dynamicArchive.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 @@ -48,6 +48,7 @@ #include "logging/log.hpp" #include "memory/metaspaceClosure.hpp" #include "memory/resourceArea.hpp" +#include "oops/array.hpp" #include "oops/klass.inline.hpp" #include "runtime/arguments.hpp" #include "runtime/os.hpp" @@ -95,7 +96,6 @@ public: void sort_methods(InstanceKlass* ik) const; void remark_pointers_for_instance_klass(InstanceKlass* k, bool should_mark) const; void write_archive(char* serialized_data, AOTClassLocationConfig* cl_config); - void gather_array_klasses(); public: // Do this before and after the archive dump to see if any corruption @@ -132,7 +132,6 @@ public: init_header(); gather_source_objs(); - gather_array_klasses(); reserve_buffer(); log_info(cds, dynamic)("Copying %d klasses and %d symbols", @@ -159,7 +158,6 @@ public: ArchiveBuilder::OtherROAllocMark mark; SystemDictionaryShared::write_to_archive(false); cl_config = AOTClassLocationConfig::dumptime()->write_to_archive(); - DynamicArchive::dump_array_klasses(); serialized_data = ro_region()->top(); WriteClosure wc(ro_region()); @@ -175,8 +173,6 @@ public: write_archive(serialized_data, cl_config); release_header(); - DynamicArchive::post_dump(); - post_dump(); verify_universe("After CDS dynamic dump"); @@ -185,30 +181,6 @@ public: virtual void iterate_roots(MetaspaceClosure* it) { AOTArtifactFinder::all_cached_classes_do(it); SystemDictionaryShared::dumptime_classes_do(it); - iterate_primitive_array_klasses(it); - } - - void iterate_primitive_array_klasses(MetaspaceClosure* it) { - for (int i = T_BOOLEAN; i <= T_LONG; i++) { - assert(is_java_primitive((BasicType)i), "sanity"); - Klass* k = Universe::typeArrayKlass((BasicType)i); // this give you "[I", etc - assert(AOTMetaspace::in_aot_cache_static_region((void*)k), - "one-dimensional primitive array should be in static archive"); - ArrayKlass* ak = ArrayKlass::cast(k); - while (ak != nullptr && ak->in_aot_cache()) { - Klass* next_k = ak->array_klass_or_null(); - if (next_k != nullptr) { - ak = ArrayKlass::cast(next_k); - } else { - ak = nullptr; - } - } - if (ak != nullptr) { - assert(ak->dimension() > 1, "sanity"); - // this is the lowest dimension that's not in the static archive - it->push(&ak); - } - } } }; @@ -367,26 +339,6 @@ void DynamicArchiveBuilder::write_archive(char* serialized_data, AOTClassLocatio log_info(cds, dynamic)("%d klasses; %d symbols", klasses()->length(), symbols()->length()); } -void DynamicArchiveBuilder::gather_array_klasses() { - for (int i = 0; i < klasses()->length(); i++) { - if (klasses()->at(i)->is_objArray_klass()) { - ObjArrayKlass* oak = ObjArrayKlass::cast(klasses()->at(i)); - Klass* elem = oak->element_klass(); - if (AOTMetaspace::in_aot_cache_static_region(elem)) { - // Only capture the array klass whose element_klass is in the static archive. - // During run time, setup (see DynamicArchive::setup_array_klasses()) is needed - // so that the element_klass can find its array klasses from the dynamic archive. - DynamicArchive::append_array_klass(oak); - } else { - // The element_klass and its array klasses are in the same archive. - assert(!AOTMetaspace::in_aot_cache_static_region(oak), - "we should not gather klasses that are already in the static archive"); - } - } - } - log_debug(aot)("Total array klasses gathered for dynamic archive: %d", DynamicArchive::num_array_klasses()); -} - class VM_PopulateDynamicDumpSharedSpace: public VM_Heap_Sync_Operation { DynamicArchiveBuilder _builder; public: @@ -403,76 +355,9 @@ public: } }; -// _array_klasses and _dynamic_archive_array_klasses only hold the array klasses -// which have element klass in the static archive. -GrowableArray* DynamicArchive::_array_klasses = nullptr; -Array* DynamicArchive::_dynamic_archive_array_klasses = nullptr; - void DynamicArchive::serialize(SerializeClosure* soc) { SymbolTable::serialize_shared_table_header(soc, false); SystemDictionaryShared::serialize_dictionary_headers(soc, false); - soc->do_ptr(&_dynamic_archive_array_klasses); -} - -void DynamicArchive::append_array_klass(ObjArrayKlass* ak) { - if (_array_klasses == nullptr) { - _array_klasses = new (mtClassShared) GrowableArray(50, mtClassShared); - } - _array_klasses->append(ak); -} - -void DynamicArchive::dump_array_klasses() { - assert(CDSConfig::is_dumping_dynamic_archive(), "sanity"); - if (_array_klasses != nullptr) { - ArchiveBuilder* builder = ArchiveBuilder::current(); - int num_array_klasses = _array_klasses->length(); - _dynamic_archive_array_klasses = - ArchiveBuilder::new_ro_array(num_array_klasses); - for (int i = 0; i < num_array_klasses; i++) { - builder->write_pointer_in_buffer(_dynamic_archive_array_klasses->adr_at(i), _array_klasses->at(i)); - } - } -} - -void DynamicArchive::setup_array_klasses() { - if (_dynamic_archive_array_klasses != nullptr) { - for (int i = 0; i < _dynamic_archive_array_klasses->length(); i++) { - ObjArrayKlass* oak = _dynamic_archive_array_klasses->at(i); - Klass* elm = oak->element_klass(); - assert(AOTMetaspace::in_aot_cache_static_region((void*)elm), "must be"); - - if (elm->is_instance_klass()) { - assert(InstanceKlass::cast(elm)->array_klasses() == nullptr, "must be"); - InstanceKlass::cast(elm)->set_array_klasses(oak); - } else { - assert(elm->is_array_klass(), "sanity"); - assert(ArrayKlass::cast(elm)->higher_dimension() == nullptr, "must be"); - ArrayKlass::cast(elm)->set_higher_dimension(oak); - } - } - log_debug(aot)("Total array klasses read from dynamic archive: %d", _dynamic_archive_array_klasses->length()); - } -} - -void DynamicArchive::make_array_klasses_shareable() { - if (_array_klasses != nullptr) { - int num_array_klasses = _array_klasses->length(); - for (int i = 0; i < num_array_klasses; i++) { - ObjArrayKlass* k = ArchiveBuilder::current()->get_buffered_addr(_array_klasses->at(i)); - k->remove_unshareable_info(); - } - } -} - -void DynamicArchive::post_dump() { - if (_array_klasses != nullptr) { - delete _array_klasses; - _array_klasses = nullptr; - } -} - -int DynamicArchive::num_array_klasses() { - return _array_klasses != nullptr ? _array_klasses->length() : 0; } void DynamicArchive::dump_impl(bool jcmd_request, const char* archive_name, TRAPS) { diff --git a/src/hotspot/share/cds/dynamicArchive.hpp b/src/hotspot/share/cds/dynamicArchive.hpp index c42c4b7dfde..63a48e252ee 100644 --- a/src/hotspot/share/cds/dynamicArchive.hpp +++ b/src/hotspot/share/cds/dynamicArchive.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,13 +26,8 @@ #define SHARE_CDS_DYNAMICARCHIVE_HPP #include "cds/filemap.hpp" -#include "classfile/compactHashtable.hpp" #include "memory/allStatic.hpp" -#include "memory/memRegion.hpp" -#include "oops/array.hpp" -#include "oops/oop.hpp" #include "utilities/exceptions.hpp" -#include "utilities/growableArray.hpp" #include "utilities/macros.hpp" #if INCLUDE_CDS @@ -59,22 +54,13 @@ public: }; class DynamicArchive : AllStatic { -private: - static GrowableArray* _array_klasses; - static Array* _dynamic_archive_array_klasses; public: static void dump_for_jcmd(const char* archive_name, TRAPS); static void dump_at_exit(JavaThread* current); static void dump_impl(bool jcmd_request, const char* archive_name, TRAPS); static bool is_mapped() { return FileMapInfo::dynamic_info() != nullptr; } static bool validate(FileMapInfo* dynamic_info); - static void dump_array_klasses(); - static void setup_array_klasses(); - static void append_array_klass(ObjArrayKlass* oak); static void serialize(SerializeClosure* soc); - static void make_array_klasses_shareable(); - static void post_dump(); - static int num_array_klasses(); }; #endif // INCLUDE_CDS #endif // SHARE_CDS_DYNAMICARCHIVE_HPP diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java new file mode 100644 index 00000000000..8014ed685ee --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java @@ -0,0 +1,60 @@ +/* + * 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 + * 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 8374639 + * @requires vm.cds.supports.aot.class.linking + * @library /test/lib + * @build DynamicDumpWithAOTLinkedStaticArchive jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar TestApp + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. DynamicDumpWithAOTLinkedStaticArchive + */ + +import jdk.test.lib.cds.SimpleCDSAppTester; +import jdk.test.lib.process.OutputAnalyzer; + +public class DynamicDumpWithAOTLinkedStaticArchive { + public static void main(String... args) throws Exception { + SimpleCDSAppTester.of("DynamicDumpWithAOTLinkedStaticArchive") + .classpath("app.jar") + .appCommandLine("TestApp") + .setGenerateBaseArchive(true) + .setBaseArchiveOptions("-XX:+AOTClassLinking") + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldContain("HelloWorld"); + }) + .runDynamicWorkflow(); + } +} + +class TestApp { + public static void main(String[] args) { + System.out.println("HelloWorld"); + System[][][] x = new System[0][0][0]; + System.out.println(x); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArrayKlasses.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArrayKlasses.java deleted file mode 100644 index e476382c8c9..00000000000 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArrayKlasses.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -/* - * @test - * @summary handling of the existence of InstanceKlass::array_klasses() - * @requires vm.cds - * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds - * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes - * @build ArrayKlassesApp - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar ArrayKlasses.jar ArrayKlassesApp - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. ArrayKlasses - */ - -import jdk.test.lib.helpers.ClassFileInstaller; - -public class ArrayKlasses extends DynamicArchiveTestBase { - public static void main(String[] args) throws Exception { - runTest(ArrayKlasses::test); - } - - static void test() throws Exception { - String topArchiveName = getNewArchiveName(); - final String appJar = ClassFileInstaller.getJarPath("ArrayKlasses.jar"); - final String mainClass = "ArrayKlassesApp"; - final String runtimeLogOptions = - "-Xlog:class+load,class+load+array=debug,cds+dynamic=debug,cds=debug,aot+unshareable=trace"; - - // Case 1 - // Create a dynamic archive with the ArrayKlassesApp app class and its - // array classes. - dump2(null, topArchiveName, - "-Xlog:cds+dynamic=debug,cds+class=debug", - "-cp", appJar, mainClass) - .assertNormalExit(output -> { - output.shouldMatch("cds.class.*klasses.*array \\[LArrayKlassesApp;") - .shouldMatch("cds.class.*klasses.*array \\[\\[LArrayKlassesApp;") - .shouldMatch("cds.class.*klasses.*array \\[\\[\\[LArrayKlassesApp;"); - }); - - // Case 1 - // At runtime , the ArrayKlasesApp and its array class should be loaded - // from the dynamic archive. - run2(null, topArchiveName, runtimeLogOptions, - "-cp", appJar, mainClass) - .assertNormalExit(output -> { - output.shouldContain("ArrayKlassesApp source: shared objects file (top)") - .shouldContain("restore: ArrayKlassesApp with class loader: jdk.internal.loader.ClassLoaders$AppClassLoader") - .shouldContain("[LArrayKlassesApp; source: shared objects file (top)") - .shouldContain("restore: [LArrayKlassesApp; with class loader: jdk.internal.loader.ClassLoaders$AppClassLoader") - .shouldContain("[[LArrayKlassesApp; source: shared objects file (top)") - .shouldContain("restore: [[LArrayKlassesApp; with class loader: jdk.internal.loader.ClassLoaders$AppClassLoader") - .shouldContain("[[[LArrayKlassesApp; source: shared objects file (top)") - .shouldContain("restore: [[[LArrayKlassesApp; with class loader: jdk.internal.loader.ClassLoaders$AppClassLoader") - .shouldHaveExitValue(0); - }); - - // Case 2 - // Create a dynamic archive with the array classes of java/util/Date which - // is in the default CDS archive. - topArchiveName = getNewArchiveName(); - dump2(null, topArchiveName, - "-Xlog:class+load,cds+dynamic=debug,cds+class=debug", - "-cp", appJar, mainClass, "system") - .assertNormalExit(output -> { - output.shouldContain("java.util.Date source: shared objects file") - .shouldMatch("cds.class.*klasses.*array \\[Ljava.util.Date;") - .shouldMatch("cds.class.*klasses.*array \\[\\[Ljava.util.Date;") - .shouldMatch("cds.class.*klasses.*array \\[\\[\\[Ljava.util.Date;"); - }); - - // Case 2 - // At runtime, the java/util/Date class should be loaded from the default - // CDS archive; its array class should be loaded from the dynamic archive. - run2(null, topArchiveName, runtimeLogOptions, - "-cp", appJar, mainClass, "system") - .assertNormalExit(output -> { - output.shouldContain("java.util.Date source: shared objects file") - .shouldContain("restore: java.util.Date with class loader: boot") - .shouldContain("[Ljava.util.Date; source: shared objects file (top)") - .shouldContain("restore: [Ljava.util.Date; with class loader: boot") - .shouldContain("[[Ljava.util.Date; source: shared objects file (top)") - .shouldContain("restore: [[Ljava.util.Date; with class loader: boot") - .shouldContain("[[[Ljava.util.Date; source: shared objects file (top)") - .shouldContain("restore: [[[Ljava.util.Date; with class loader: boot") - .shouldHaveExitValue(0); - }); - - // Case 3 - // Create a dynamic archive with primitive arrays [[J and [[[J with [J - // already in the default CDS archive - topArchiveName = getNewArchiveName(); - dump2(null, topArchiveName, - "-Xlog:class+load,cds+dynamic=debug,cds+class=debug", - "-cp", appJar, mainClass, "primitive") - .assertNormalExit(output -> { - output.shouldMatch("cds.class.*klasses.*array \\[\\[J") - .shouldMatch("cds.class.*klasses.*array \\[\\[\\[J"); - }); - - // Case 3 - // At runtime, the [J should be loaded from the default CDS archive; - // the higher-dimension array should be loaded from the dynamic archive. - run2(null, topArchiveName, runtimeLogOptions, - "-cp", appJar, mainClass, "primitive") - .assertNormalExit(output -> { - output.shouldContain("[J source: shared objects file") - .shouldContain("restore: [J with class loader: boot") - .shouldContain("[[J source: shared objects file (top)") - .shouldContain("restore: [[J with class loader: boot") - .shouldContain("[[[J source: shared objects file (top)") - .shouldContain("restore: [[[J with class loader: boot") - .shouldHaveExitValue(0); - }); - - // Case 4 - // Create a dynamic archive with 2-, 3- and 4-dimension arrays of java/lang/Integer. - // The java/lang/Integer class and the 1-dimension array is in the default archive. - topArchiveName = getNewArchiveName(); - dump2(null, topArchiveName, - "-Xlog:class+load,cds+dynamic=debug,cds+class=debug", - "-cp", appJar, mainClass, "integer-array") - .assertNormalExit(output -> { - output.shouldMatch("cds.class.*klasses.*array \\[\\[Ljava.lang.Integer;") - .shouldMatch("cds.class.*klasses.*array \\[\\[\\[Ljava.lang.Integer;") - .shouldMatch("cds.class.*klasses.*array \\[\\[\\[\\[Ljava.lang.Integer;"); - }); - - // Case 4 - // At runtime, the 4-dimension array of java/lang/Integer should be - // loaded from the dynamic archive. - run2(null, topArchiveName, runtimeLogOptions, - "-cp", appJar, mainClass, "integer-array") - .assertNormalExit(output -> { - output.shouldContain("java.lang.Integer source: shared objects file") - .shouldContain("restore: java.lang.Integer with class loader: boot") - .shouldContain("restore: [Ljava.lang.Integer; with class loader: boot") - .shouldContain("[[Ljava.lang.Integer; source: shared objects file (top)") - .shouldContain("restore: [[Ljava.lang.Integer; with class loader: boot") - .shouldContain("[[[Ljava.lang.Integer; source: shared objects file (top)") - .shouldContain("restore: [[[Ljava.lang.Integer; with class loader: boot") - .shouldContain("[[[[Ljava.lang.Integer; source: shared objects file (top)") - .shouldContain("restore: [[[[Ljava.lang.Integer; with class loader: boot") - .shouldHaveExitValue(0); - }); - } -} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ArrayKlassesApp.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ArrayKlassesApp.java deleted file mode 100644 index 3c78e3e8dbe..00000000000 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ArrayKlassesApp.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2019, 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. - * - */ - -import java.lang.reflect.Array; -import java.util.Date; - -public class ArrayKlassesApp { - public static void main(String args[]) { - if (args.length == 1) { - if (args[0].equals("system")) { - Date[][][] array = new Date[1][2][2]; - int count = 0; - for (int i=0; i<1; i++) { - for (int j=0; j<2; j++) { - for (int k=0; k<2; k++) { - array[i][j][k] = new Date(); - count++; - array[i][j][k].setTime(20000 * count); - } - } - } - } else if (args[0].equals("primitive")) { - long[][][] larray = new long[1][2][2]; - long lcount = 0; - for (int i=0; i<1; i++) { - for (int j=0; j<2; j++) { - for (int k=0; k<2; k++) { - lcount++; - larray[i][j][k] = lcount; - } - } - } - } else if (args[0].equals("integer-array")) { - Integer[][][][] iarray = new Integer[4][4][4][4]; - int count = 0; - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - for (int k = 0; k < 4; k++) { - for (int l = 0; l < 4; l++) { - count++; - iarray[i][j][k][l] = new Integer(count); - } - } - } - } - System.out.println(iarray); - System.out.println(iarray.getClass()); - } - } else { - Object x = Array.newInstance(ArrayKlassesApp.class, 3,3,3); - System.out.println(x); - System.out.println(x.getClass()); - System.out.println(Array.getLength(x)); - } - } -} diff --git a/test/lib/jdk/test/lib/cds/CDSAppTester.java b/test/lib/jdk/test/lib/cds/CDSAppTester.java index fd244c6acc6..18356dd8aa9 100644 --- a/test/lib/jdk/test/lib/cds/CDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/CDSAppTester.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 @@ -57,6 +57,8 @@ abstract public class CDSAppTester { private int numProductionRuns = 0; private String whiteBoxJar = null; private boolean inOneStepTraining = false; + private boolean generateBaseArchive = false; + private String[] baseArchiveOptions = new String[0]; /** * All files created in the CDS/AOT workflow will be name + extension. E.g. @@ -343,7 +345,7 @@ abstract public class CDSAppTester { // VM options used by this test, we need to create a temporary static archive to be used with -XX:ArchiveClassesAtExit. private String getBaseArchiveForDynamicArchive() throws Exception { WhiteBox wb = WhiteBox.getWhiteBox(); - if (wb.isSharingEnabled()) { + if (wb.isSharingEnabled() && !generateBaseArchive) { // This current JVM is able to use a default CDS archive included by the JDK, so // if we launch a JVM child process (with the same set of options as the current JVM), // that process is also able to use the same default CDS archive for creating @@ -356,6 +358,7 @@ abstract public class CDSAppTester { if (!f.exists()) { CDSOptions opts = new CDSOptions(); opts.setArchiveName(tempBaseArchiveFile); + opts.addSuffix(baseArchiveOptions); opts.addSuffix("-Djava.class.path="); OutputAnalyzer out = CDSTestUtils.createArchive(opts); CDSTestUtils.checkBaseDump(out); @@ -364,6 +367,16 @@ abstract public class CDSAppTester { } } + public CDSAppTester setGenerateBaseArchive(boolean b) { + this.generateBaseArchive = b; + return this; + } + + public CDSAppTester setBaseArchiveOptions(String... opts) { + this.baseArchiveOptions = opts; + return this; + } + private OutputAnalyzer dumpDynamicArchive() throws Exception { RunMode runMode = RunMode.DUMP_DYNAMIC; String[] cmdLine = new String[0]; diff --git a/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java b/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java index c869cfa366b..a8bba9c76e2 100644 --- a/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.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 @@ -58,9 +58,11 @@ public class SimpleCDSAppTester { private String modulepath; private String[] appCommandLine; private String[] vmArgs = new String[] {}; + private Tester tester; private SimpleCDSAppTester(String name) { this.name = name; + this.tester = new Tester(name); } public static SimpleCDSAppTester of(String name) { @@ -101,6 +103,16 @@ public class SimpleCDSAppTester { return this; } + public SimpleCDSAppTester setGenerateBaseArchive(boolean b) { + tester.setGenerateBaseArchive(b); + return this; + } + + public SimpleCDSAppTester setBaseArchiveOptions(String... opts) { + tester.setBaseArchiveOptions(opts); + return this; + } + public SimpleCDSAppTester setTrainingChecker(BiConsumer checker) { this.trainingChecker = checker; return this; @@ -181,17 +193,22 @@ public class SimpleCDSAppTester { } public SimpleCDSAppTester runStaticWorkflow() throws Exception { - (new Tester(name)).runStaticWorkflow(); + tester.runStaticWorkflow(); + return this; + } + + public SimpleCDSAppTester runDynamicWorkflow() throws Exception { + tester.runDynamicWorkflow(); return this; } public SimpleCDSAppTester runAOTWorkflow() throws Exception { - (new Tester(name)).runAOTWorkflow(); + tester.runAOTWorkflow(); return this; } public SimpleCDSAppTester run(String args[]) throws Exception { - (new Tester(name)).run(args); + tester.run(args); return this; } } From 8212993ac331d8761ddb7c0eef23dbfcc6ca0c7d Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Thu, 8 Jan 2026 18:51:25 +0000 Subject: [PATCH 032/204] 8374540: Add comment describing implementation choices of Math.fma Reviewed-by: rgiulietti --- .../share/classes/java/lang/Math.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java index 0f39ecf0a8a..55659bed57b 100644 --- a/src/java.base/share/classes/java/lang/Math.java +++ b/src/java.base/share/classes/java/lang/Math.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2378,6 +2378,20 @@ public final class Math { */ @IntrinsicCandidate public static double fma(double a, double b, double c) { + // Implementation note: this method is intentionally coded in + // a straightforward manner relying on BigDecimal for the + // heavy-lifting of the numerical computation. It would be + // possible for the computation to be done solely using binary + // floating-point and integer operations, at the cost of more + // complicated logic. Since most processors have hardware + // support for fma and this method is an intrinsic candidate, + // the software implementation below would only be used on + // processors without native fma support (and also possibly on + // processors with native fma support while running in the + // interpreter). Therefore, the direct performance of the code + // is less of a concern than the code's simplicity, + // maintainability, and testability. + /* * Infinity and NaN arithmetic is not quite the same with two * roundings as opposed to just one so the simple expression @@ -2492,6 +2506,8 @@ public final class Math { */ @IntrinsicCandidate public static float fma(float a, float b, float c) { + // See implementation note in fma(double, double, double). + if (Float.isFinite(a) && Float.isFinite(b) && Float.isFinite(c)) { if (a == 0.0 || b == 0.0) { return a * b + c; // Handled signed zero cases From 1342db0bde25c111b25f4339ae2a858dc3b15687 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Thu, 8 Jan 2026 19:02:06 +0000 Subject: [PATCH 033/204] 8374051: Incorrect parameterized testing of exceptions in AbstractDateTimeTest.java Reviewed-by: naoto, rriggs --- .../tck/java/time/AbstractDateTimeTest.java | 227 +++++++----------- .../java/time/tck/java/time/TCKInstant.java | 5 +- .../java/time/tck/java/time/TCKLocalDate.java | 4 +- .../java/time/tck/java/time/TCKLocalTime.java | 5 +- .../java/time/tck/java/time/TCKMonthDay.java | 4 +- .../time/tck/java/time/TCKOffsetDateTime.java | 5 +- .../time/tck/java/time/TCKOffsetTime.java | 5 +- .../java/time/tck/java/time/TCKYearMonth.java | 4 +- .../time/tck/java/time/TCKZonedDateTime.java | 4 +- 9 files changed, 112 insertions(+), 151 deletions(-) diff --git a/test/jdk/java/time/tck/java/time/AbstractDateTimeTest.java b/test/jdk/java/time/tck/java/time/AbstractDateTimeTest.java index f96ab522517..cf138419c83 100644 --- a/test/jdk/java/time/tck/java/time/AbstractDateTimeTest.java +++ b/test/jdk/java/time/tck/java/time/AbstractDateTimeTest.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,8 +59,11 @@ */ package tck.java.time; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.DateTimeException; import java.time.temporal.TemporalAccessor; @@ -68,8 +71,8 @@ import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; import java.util.List; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import test.java.time.temporal.MockFieldNoValue; @@ -99,183 +102,137 @@ public abstract class AbstractDateTimeTest extends AbstractTCKTest { //----------------------------------------------------------------------- // isSupported(TemporalField) //----------------------------------------------------------------------- - @Test() - public void basicTest_isSupported_TemporalField_supported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : validFields()) { - assertEquals(true, sample.isSupported(field), "Failed on " + sample + " " + field); - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_isSupported_TemporalField_supported(TemporalAccessor sample) { + for (TemporalField field : validFields()) { + assertTrue(sample.isSupported(field), "Failed on " + sample + " " + field); } } - @Test() - public void basicTest_isSupported_TemporalField_unsupported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : invalidFields()) { - assertEquals(false, sample.isSupported(field), "Failed on " + sample + " " + field); - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_isSupported_TemporalField_unsupported(TemporalAccessor sample) { + for (TemporalField field : invalidFields()) { + assertFalse(sample.isSupported(field), "Failed on " + sample + " " + field); } } - @Test() - public void basicTest_isSupported_TemporalField_null() { - for (TemporalAccessor sample : samples()) { - assertEquals(false, sample.isSupported(null), "Failed on " + sample); - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_isSupported_TemporalField_null(TemporalAccessor sample) { + assertFalse(sample.isSupported(null), "Failed on " + sample); } //----------------------------------------------------------------------- // range(TemporalField) //----------------------------------------------------------------------- - @Test() - public void basicTest_range_TemporalField_supported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : validFields()) { - sample.range(field); // no exception - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_range_TemporalField_supported(TemporalAccessor sample) { + for (TemporalField field : validFields()) { + assertDoesNotThrow(() -> sample.range(field)); } } - @Test() - public void basicTest_range_TemporalField_unsupported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : invalidFields()) { - try { - sample.range(field); - fail("Failed on " + sample + " " + field); - } catch (DateTimeException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_range_TemporalField_unsupported(TemporalAccessor sample) { + for (TemporalField field : invalidFields()) { + assertThrows(DateTimeException.class, + () -> sample.range(field), "Failed on " + sample + " " + field); } } - @Test() - public void basicTest_range_TemporalField_null() { - for (TemporalAccessor sample : samples()) { - try { - sample.range(null); - fail("Failed on " + sample); - } catch (NullPointerException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_range_TemporalField_null(TemporalAccessor sample) { + assertThrows(NullPointerException.class, + () -> sample.range(null), "Failed on " + sample); } //----------------------------------------------------------------------- // get(TemporalField) //----------------------------------------------------------------------- - @Test() - public void basicTest_get_TemporalField_supported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : validFields()) { - if (sample.range(field).isIntValue()) { - sample.get(field); // no exception - } else { - try { - sample.get(field); - fail("Failed on " + sample + " " + field); - } catch (DateTimeException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_get_TemporalField_supported(TemporalAccessor sample) { + for (TemporalField field : validFields()) { + if (sample.range(field).isIntValue()) { + assertDoesNotThrow(() -> sample.get(field)); + } else { + assertThrows(DateTimeException.class, + () -> sample.get(field), "Failed on " + sample + " " + field); } } } - @Test() - public void basicTest_get_TemporalField_unsupported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : invalidFields()) { - try { - sample.get(field); - fail("Failed on " + sample + " " + field); - } catch (DateTimeException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_get_TemporalField_unsupported(TemporalAccessor sample) { + for (TemporalField field : invalidFields()) { + assertThrows(DateTimeException.class, + () -> sample.get(field), "Failed on " + sample + " " + field); } } - @Test - public void test_get_TemporalField_invalidField() { - Assertions.assertThrows(DateTimeException.class, () -> { - for (TemporalAccessor sample : samples()) { - sample.get(MockFieldNoValue.INSTANCE); - } - }); + @ParameterizedTest + @MethodSource("samples") + public void test_get_TemporalField_invalidField(TemporalAccessor sample) { + assertThrows(DateTimeException.class, + () -> sample.get(MockFieldNoValue.INSTANCE)); } - @Test() - public void basicTest_get_TemporalField_null() { - for (TemporalAccessor sample : samples()) { - try { - sample.get(null); - fail("Failed on " + sample); - } catch (NullPointerException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_get_TemporalField_null(TemporalAccessor sample) { + assertThrows(NullPointerException.class, + () -> sample.get(null), "Failed on " + sample); } //----------------------------------------------------------------------- // getLong(TemporalField) //----------------------------------------------------------------------- - @Test() - public void basicTest_getLong_TemporalField_supported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : validFields()) { - sample.getLong(field); // no exception - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_getLong_TemporalField_supported(TemporalAccessor sample) { + for (TemporalField field : validFields()) { + sample.getLong(field); } } - @Test() - public void basicTest_getLong_TemporalField_unsupported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : invalidFields()) { - try { - sample.getLong(field); - fail("Failed on " + sample + " " + field); - } catch (DateTimeException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_getLong_TemporalField_unsupported(TemporalAccessor sample) { + for (TemporalField field : invalidFields()) { + assertThrows(DateTimeException.class, + () -> sample.getLong(field), "Failed on " + sample + " " + field); } } - @Test - public void test_getLong_TemporalField_invalidField() { - Assertions.assertThrows(DateTimeException.class, () -> { - for (TemporalAccessor sample : samples()) { - sample.getLong(MockFieldNoValue.INSTANCE); - } - }); + @ParameterizedTest + @MethodSource("samples") + public void test_getLong_TemporalField_invalidField(TemporalAccessor sample) { + assertThrows(DateTimeException.class, + () -> sample.getLong(MockFieldNoValue.INSTANCE)); } - @Test() - public void basicTest_getLong_TemporalField_null() { - for (TemporalAccessor sample : samples()) { - try { - sample.getLong(null); - fail("Failed on " + sample); - } catch (NullPointerException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_getLong_TemporalField_null(TemporalAccessor sample) { + assertThrows(NullPointerException.class, + () -> sample.getLong(null), "Failed on " + sample); } //----------------------------------------------------------------------- - @Test - public void basicTest_query() { - for (TemporalAccessor sample : samples()) { - assertEquals("foo", sample.query(new TemporalQuery() { - @Override - public String queryFrom(TemporalAccessor temporal) { - return "foo"; - } - })); - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_query(TemporalAccessor sample) { + assertEquals("foo", sample.query(new TemporalQuery() { + @Override + public String queryFrom(TemporalAccessor temporal) { + return "foo"; + } + })); } - } diff --git a/test/jdk/java/time/tck/java/time/TCKInstant.java b/test/jdk/java/time/tck/java/time/TCKInstant.java index 4a512d37665..8c245d69810 100644 --- a/test/jdk/java/time/tck/java/time/TCKInstant.java +++ b/test/jdk/java/time/tck/java/time/TCKInstant.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. * 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,7 +139,8 @@ public class TCKInstant extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_12345_123456789, Instant.MIN, Instant.MAX, Instant.EPOCH}; + TemporalAccessor[] array = {Instant.ofEpochSecond(12345, 123456789), + Instant.MIN, Instant.MAX, Instant.EPOCH}; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKLocalDate.java b/test/jdk/java/time/tck/java/time/TCKLocalDate.java index 281007e7665..132aa31ed97 100644 --- a/test/jdk/java/time/tck/java/time/TCKLocalDate.java +++ b/test/jdk/java/time/tck/java/time/TCKLocalDate.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -170,7 +170,7 @@ public class TCKLocalDate extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_2007_07_15, LocalDate.MAX, LocalDate.MIN, }; + TemporalAccessor[] array = {LocalDate.of(2007, 7, 15), LocalDate.MAX, LocalDate.MIN, }; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKLocalTime.java b/test/jdk/java/time/tck/java/time/TCKLocalTime.java index bdec611ad76..e2fc4ea2ca7 100644 --- a/test/jdk/java/time/tck/java/time/TCKLocalTime.java +++ b/test/jdk/java/time/tck/java/time/TCKLocalTime.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -161,7 +161,8 @@ public class TCKLocalTime extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_12_30_40_987654321, LocalTime.MIN, LocalTime.MAX, LocalTime.MIDNIGHT, LocalTime.NOON}; + TemporalAccessor[] array = {LocalTime.of(12, 30, 40, 987654321), + LocalTime.MIN, LocalTime.MAX, LocalTime.MIDNIGHT, LocalTime.NOON}; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKMonthDay.java b/test/jdk/java/time/tck/java/time/TCKMonthDay.java index 84326a4c47a..36ad4af069e 100644 --- a/test/jdk/java/time/tck/java/time/TCKMonthDay.java +++ b/test/jdk/java/time/tck/java/time/TCKMonthDay.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -118,7 +118,7 @@ public class TCKMonthDay extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_07_15, }; + TemporalAccessor[] array = {MonthDay.of(7, 15), }; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKOffsetDateTime.java b/test/jdk/java/time/tck/java/time/TCKOffsetDateTime.java index f2b98ee9972..6ba0318890f 100644 --- a/test/jdk/java/time/tck/java/time/TCKOffsetDateTime.java +++ b/test/jdk/java/time/tck/java/time/TCKOffsetDateTime.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. * 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,8 @@ public class TCKOffsetDateTime extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_2008_6_30_11_30_59_000000500, OffsetDateTime.MIN, OffsetDateTime.MAX}; + TemporalAccessor[] array = {OffsetDateTime.of(2008, 6, 30, 11, 30, 59, 500, OFFSET_PONE), + OffsetDateTime.MIN, OffsetDateTime.MAX}; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKOffsetTime.java b/test/jdk/java/time/tck/java/time/TCKOffsetTime.java index 350b21574fa..7ea9504edbc 100644 --- a/test/jdk/java/time/tck/java/time/TCKOffsetTime.java +++ b/test/jdk/java/time/tck/java/time/TCKOffsetTime.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -149,7 +149,8 @@ public class TCKOffsetTime extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_11_30_59_500_PONE, OffsetTime.MIN, OffsetTime.MAX}; + TemporalAccessor[] array = {OffsetTime.of(11, 30, 59, 500, OFFSET_PONE), + OffsetTime.MIN, OffsetTime.MAX}; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKYearMonth.java b/test/jdk/java/time/tck/java/time/TCKYearMonth.java index 3f4fda2a924..e39bbe90cf9 100644 --- a/test/jdk/java/time/tck/java/time/TCKYearMonth.java +++ b/test/jdk/java/time/tck/java/time/TCKYearMonth.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -134,7 +134,7 @@ public class TCKYearMonth extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_2008_06, }; + TemporalAccessor[] array = {YearMonth.of(2008, 6), }; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKZonedDateTime.java b/test/jdk/java/time/tck/java/time/TCKZonedDateTime.java index 847035f121a..73adbbba88c 100644 --- a/test/jdk/java/time/tck/java/time/TCKZonedDateTime.java +++ b/test/jdk/java/time/tck/java/time/TCKZonedDateTime.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -176,7 +176,7 @@ public class TCKZonedDateTime extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_DATE_TIME, }; + TemporalAccessor[] array = {ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30, 59, 500), ZONE_0100), }; return Arrays.asList(array); } From 982aa3f8ead84817be5373c3257d48feab1758d3 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 8 Jan 2026 19:47:01 +0000 Subject: [PATCH 034/204] 8336654: [lworld] Tests depending on sun.awt.AppContext can fail when run with migrated classes Reviewed-by: serb, azvegint --- .../classes/com/apple/laf/AquaUtils.java | 41 +++++-- .../share/classes/sun/awt/AppContext.java | 19 --- .../classes/sun/awt/image/ImageCache.java | 8 +- .../swing/Security/6657138/bug6657138.java | 108 ------------------ 4 files changed, 33 insertions(+), 143 deletions(-) delete mode 100644 test/jdk/javax/swing/Security/6657138/bug6657138.java diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java index da1785a9a63..5e68cc7a82c 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java @@ -35,8 +35,6 @@ import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.UIResource; -import sun.awt.AppContext; - import sun.lwawt.macosx.CPlatformWindow; import sun.swing.SwingUtilities2; @@ -150,13 +148,34 @@ final class AquaUtils { protected abstract T create(); } - abstract static class RecyclableSingleton { - final T get() { - return AppContext.getSoftReferenceValue(this, () -> getInstance()); - } + abstract static class LazySingleton { + T instance; - void reset() { - AppContext.getAppContext().remove(this); + final T get() { + if (instance == null) { + instance = getInstance(); + } + return instance; + } + + abstract T getInstance(); + } + + abstract static class RecyclableSingleton { + + SoftReference ref; + + final T get() { + T instance; + if (ref != null) { + instance = ref.get(); + if (instance != null) { + return instance; + } + } + instance = getInstance(); + ref = new SoftReference<>(instance); + return instance; } abstract T getInstance(); @@ -197,11 +216,11 @@ final class AquaUtils { protected abstract V getInstance(K key); } - private static final RecyclableSingleton enableAnimations = new RecyclableSingleton() { + private static final LazySingleton enableAnimations = new LazySingleton() { @Override protected Boolean getInstance() { - final String sizeProperty = System.getProperty(ANIMATIONS_PROPERTY); - return !"false".equals(sizeProperty); // should be true by default + final String animationsProperty = System.getProperty(ANIMATIONS_PROPERTY); + return !"false".equals(animationsProperty); // should be true by default } }; private static boolean animationsEnabled() { diff --git a/src/java.desktop/share/classes/sun/awt/AppContext.java b/src/java.desktop/share/classes/sun/awt/AppContext.java index 39df737badb..01b1dd09887 100644 --- a/src/java.desktop/share/classes/sun/awt/AppContext.java +++ b/src/java.desktop/share/classes/sun/awt/AppContext.java @@ -47,7 +47,6 @@ import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; /** * The AppContext is a table referenced by ThreadGroup which stores @@ -735,24 +734,6 @@ public final class AppContext { } return changeSupport.getPropertyChangeListeners(propertyName); } - - public static T getSoftReferenceValue(Object key, - Supplier supplier) { - - final AppContext appContext = AppContext.getAppContext(); - @SuppressWarnings("unchecked") - SoftReference ref = (SoftReference) appContext.get(key); - if (ref != null) { - final T object = ref.get(); - if (object != null) { - return object; - } - } - final T object = supplier.get(); - ref = new SoftReference<>(object); - appContext.put(key, ref); - return object; - } } final class MostRecentKeyValue { diff --git a/src/java.desktop/share/classes/sun/awt/image/ImageCache.java b/src/java.desktop/share/classes/sun/awt/image/ImageCache.java index eed1aa5bdb7..b7ac69923da 100644 --- a/src/java.desktop/share/classes/sun/awt/image/ImageCache.java +++ b/src/java.desktop/share/classes/sun/awt/image/ImageCache.java @@ -29,7 +29,6 @@ import java.awt.*; import java.lang.ref.*; import java.util.*; import java.util.concurrent.locks.*; -import sun.awt.AppContext; /** * ImageCache - A fixed pixel count sized cache of Images keyed by arbitrary @@ -37,8 +36,6 @@ import sun.awt.AppContext; * dropped by the GC if heap memory gets tight. When our size hits max pixel * count least recently requested images are removed first. * - * The ImageCache must be used from the thread with an AppContext only. - * */ public final class ImageCache { @@ -56,9 +53,10 @@ public final class ImageCache { // Reference queue for tracking lost softreferences to images in the cache private final ReferenceQueue referenceQueue = new ReferenceQueue<>(); + private static final ImageCache instance = new ImageCache(); + public static ImageCache getInstance() { - return AppContext.getSoftReferenceValue(ImageCache.class, - () -> new ImageCache()); + return instance; } ImageCache(final int maxPixelCount) { diff --git a/test/jdk/javax/swing/Security/6657138/bug6657138.java b/test/jdk/javax/swing/Security/6657138/bug6657138.java deleted file mode 100644 index 4670d57595c..00000000000 --- a/test/jdk/javax/swing/Security/6657138/bug6657138.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. - * 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 6657138 - * @summary Verifies that buttons and labels don't share their ui's across appContexts - * @author Alexander Potochkin - * @modules java.desktop/sun.awt - */ - -import sun.awt.SunToolkit; - -import javax.swing.*; -import javax.swing.plaf.ButtonUI; -import javax.swing.plaf.ComponentUI; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class bug6657138 implements Runnable { - - private static Map> componentMap = - Collections.synchronizedMap( - new HashMap>()); - - public void run() { - SunToolkit.createNewAppContext(); - try { - testUIMap(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static void testUIMap() throws Exception { - UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels(); - Set components = componentMap.keySet(); - for (JComponent c : components) { - Map uiMap = componentMap.get(c); - - for (UIManager.LookAndFeelInfo laf : lafs) { - if ("Nimbus".equals(laf.getName())) { - // for some unclear reasons - // Nimbus ui delegate for a button is null - // when this method is called from the new AppContext - continue; - } - String className = laf.getClassName(); - try { - UIManager.setLookAndFeel(className); - } catch (final UnsupportedLookAndFeelException ignored) { - continue; - } - ComponentUI ui = UIManager.getUI(c); - if (ui == null) { - throw new RuntimeException("UI is null for " + c); - } - if (ui == uiMap.get(laf.getName())) { - throw new RuntimeException( - "Two AppContexts share the same UI delegate! \n" + - c + "\n" + ui); - } - uiMap.put(laf.getName(), ui); - } - } - } - - public static void main(String[] args) throws Exception { - componentMap.put(new JButton("JButton"), - new HashMap()); - componentMap.put(new JToggleButton("JToggleButton"), - new HashMap()); - componentMap.put(new JRadioButton("JRadioButton"), - new HashMap()); - componentMap.put(new JCheckBox("JCheckBox"), - new HashMap()); - componentMap.put(new JCheckBox("JLabel"), - new HashMap()); - testUIMap(); - ThreadGroup group = new ThreadGroup("6657138"); - Thread thread = new Thread(group, new bug6657138()); - thread.start(); - thread.join(); - } -} - From 385c4f8180d30c0e41b848eb4b2c1c8788211422 Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Thu, 8 Jan 2026 20:46:38 +0000 Subject: [PATCH 035/204] 8373714: Shenandoah: Register heuristic penalties following a degenerated GC Reviewed-by: wkemper --- .../gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp | 4 ++-- .../gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp | 2 +- .../share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp | 2 +- .../share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp | 2 +- .../gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp | 4 ++-- .../gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp | 2 +- src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp | 4 ++++ 7 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index 41535f302d7..7a8bd55c795 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -183,8 +183,8 @@ void ShenandoahAdaptiveHeuristics::record_success_concurrent() { } } -void ShenandoahAdaptiveHeuristics::record_success_degenerated() { - ShenandoahHeuristics::record_success_degenerated(); +void ShenandoahAdaptiveHeuristics::record_degenerated() { + ShenandoahHeuristics::record_degenerated(); // Adjust both trigger's parameters in the case of a degenerated GC because // either of them should have triggered earlier to avoid this case. adjust_margin_of_error(DEGENERATE_PENALTY_SD); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp index 66bfc3375a3..1ba18f37c2b 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp @@ -114,7 +114,7 @@ public: virtual void record_cycle_start() override; virtual void record_success_concurrent() override; - virtual void record_success_degenerated() override; + virtual void record_degenerated() override; virtual void record_success_full() override; virtual bool should_start_gc() override; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index 478c5696188..eb740cfac61 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -243,7 +243,7 @@ void ShenandoahHeuristics::record_success_concurrent() { adjust_penalty(Concurrent_Adjust); } -void ShenandoahHeuristics::record_success_degenerated() { +void ShenandoahHeuristics::record_degenerated() { adjust_penalty(Degenerated_Penalty); } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp index 3cd2cb1d171..fb8cfb36353 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -218,7 +218,7 @@ public: virtual void record_success_concurrent(); - virtual void record_success_degenerated(); + virtual void record_degenerated(); virtual void record_success_full(); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index 8bf068df0a8..029a4dd98fb 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -766,10 +766,10 @@ void ShenandoahOldHeuristics::record_success_concurrent() { this->ShenandoahHeuristics::record_success_concurrent(); } -void ShenandoahOldHeuristics::record_success_degenerated() { +void ShenandoahOldHeuristics::record_degenerated() { // Forget any triggers that occurred while OLD GC was ongoing. If we really need to start another, it will retrigger. clear_triggers(); - this->ShenandoahHeuristics::record_success_degenerated(); + this->ShenandoahHeuristics::record_degenerated(); } void ShenandoahOldHeuristics::record_success_full() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp index 288d3d68e56..f38194c1ee7 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp @@ -201,7 +201,7 @@ public: void record_success_concurrent() override; - void record_success_degenerated() override; + void record_degenerated() override; void record_success_full() override; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index cd079d29afe..333bdbc6e72 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -313,8 +313,12 @@ void ShenandoahDegenGC::op_degenerated() { policy->record_degenerated(_generation->is_young(), _abbreviated, progress); if (progress) { heap->notify_gc_progress(); + _generation->heuristics()->record_degenerated(); } else if (!heap->mode()->is_generational() || policy->generational_should_upgrade_degenerated_gc()) { + // Upgrade to full GC, register full-GC impact on heuristics. op_degenerated_futile(); + } else { + _generation->heuristics()->record_degenerated(); } } From 368de9ff2e46e4c66ee57b5fb961804c5d25c42a Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Fri, 9 Jan 2026 02:09:37 +0000 Subject: [PATCH 036/204] 8374721: containers/docker/ShareTmpDir.java timed out after 8362087 Reviewed-by: cnorrbin, sgehwolf --- test/hotspot/jtreg/containers/docker/ShareTmpDir.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java b/test/hotspot/jtreg/containers/docker/ShareTmpDir.java index bbf08c0dae7..986cd044737 100644 --- a/test/hotspot/jtreg/containers/docker/ShareTmpDir.java +++ b/test/hotspot/jtreg/containers/docker/ShareTmpDir.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 @@ -88,6 +88,11 @@ public class ShareTmpDir { }; t1.start(); + while (!started1.exists()) { + System.out.println("Waiting for first JVM to start"); + Thread.sleep(1000); + } + Thread t2 = new Thread() { public void run() { synchronized(lock) { @@ -98,8 +103,8 @@ public class ShareTmpDir { }; t2.start(); - while (!started1.exists() || !started2.exists()) { - System.out.println("Waiting for all two JVMs to start"); + while (!started2.exists()) { + System.out.println("Waiting for second JVM to start"); Thread.sleep(1000); } From 9932c78c238f9b7959e28a056c37a88a7f6ce958 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Fri, 9 Jan 2026 02:27:16 +0000 Subject: [PATCH 037/204] 8374749: Clarify AnnotationValue specification Reviewed-by: liach, iris --- .../lang/model/element/AnnotationMirror.java | 4 +++- .../lang/model/element/AnnotationValue.java | 18 ++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java index 2d92504edb6..2ffed3f7942 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationMirror.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 @@ -36,6 +36,8 @@ import javax.lang.model.type.DeclaredType; * method. There is no guarantee that any particular annotation will * always be represented by the same object. * + * @jls 9.6 Annotation Interfaces + * @jls 9.7 Annotations * @since 1.6 */ public interface AnnotationMirror { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationValue.java b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationValue.java index 77521dac191..71594d31150 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/AnnotationValue.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/AnnotationValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, 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 @@ -27,14 +27,16 @@ package javax.lang.model.element; /** * Represents a value of an annotation interface element. - * A value is of one of the following types: - *
  • a wrapper class (such as {@link Integer}) for a primitive type - *
  • {@code String} - *
  • {@code TypeMirror} - *
  • {@code VariableElement} (representing an enum constant) - *
  • {@code AnnotationMirror} + * A value is of one of the following types (JLS {@jls 9.6.1}): + *
    • a {@linkplain java.lang##wrapperClass wrapper class} to hold a + * primitive type, such as an {@code Integer} object to hold an + * {@code int} + *
    • {@code String} representing a {@code String} + *
    • {@link javax.lang.model.type.TypeMirror TypeMirror} representing a {@code Class} literal + *
    • {@link VariableElement} representing an enum constant + *
    • {@link AnnotationMirror} representing an annotation *
    • {@code List} - * (representing the elements, in declared order, if the value is an array) + * representing the elements, in declared order, if the value is an array *
    * * @since 1.6 From 775f48de6129092d05650fec17dad171944e6d89 Mon Sep 17 00:00:00 2001 From: Jasmine Karthikeyan Date: Fri, 9 Jan 2026 05:16:32 +0000 Subject: [PATCH 038/204] 8365570: C2 fails assert(false) failed: Unexpected node in SuperWord truncation: CastII Reviewed-by: chagedorn, thartmann, epeter --- src/hotspot/share/opto/superword.cpp | 7 ++++- .../vectorization/TestSubwordTruncation.java | 28 ++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index 35b46d22732..31cc8fa0460 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2482,6 +2482,11 @@ static bool can_subword_truncate(Node* in, const Type* type) { return false; } + // Since casts specifically change the type of a node, stay on the safe side and do not truncate them. + if (in->is_ConstraintCast()) { + return false; + } + // Cannot be truncated: switch (opc) { case Op_AbsI: diff --git a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java index 26a49aba7bf..53c1b89a203 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.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 @@ -479,6 +479,32 @@ public class TestSubwordTruncation { return new Object[] { in, res }; } + @Test + @IR(counts = { IRNode.CAST_II, ">0" }) + @Warmup(0) + public Object[] testCastII() { + byte[] bytes = new byte[400]; + intField = 6; + int i = 0; + int j = 1; + + do { + bytes[j] = (byte) i; + int k = 1; + + do { + i <<= intField; + i += (k ^ i); + i -= j; + + for (int u = 1; 1 > u; u++) { + } + } while (++k < 8); + } while (++j < 191); + + return new Object[] { bytes }; + } + public static void main(String[] args) { TestFramework.run(); } From a4fb07ee3e26c2f0ed3111c39c3a22167d292d04 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Fri, 9 Jan 2026 06:26:16 +0000 Subject: [PATCH 039/204] 8374644: Regression in GZIPInputStream performance after JDK-7036144 Reviewed-by: lancea, alanb --- .../java/util/zip/GZIPInputStream.java | 60 +++++++++++++++---- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java index ab7ea53793f..ebcb9e3204c 100644 --- a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java +++ b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, 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 @@ -79,7 +79,11 @@ public class GZIPInputStream extends InflaterInputStream { super(in, createInflater(in, size), size); usesDefaultInflater = true; try { - readHeader(in); + // we don't expect the stream to be at EOF + // and if it is, then we want readHeader to + // raise an exception, so we pass "true" for + // the "failOnEOF" param. + readHeader(in, true); } catch (IOException ioe) { this.inf.end(); throw ioe; @@ -190,12 +194,40 @@ public class GZIPInputStream extends InflaterInputStream { /* * Reads GZIP member header and returns the total byte number * of this member header. + * If failOnEOF is false and if the given InputStream has already + * reached EOF when this method was invoked, then this method returns + * -1 (indicating that there's no GZIP member header). + * In all other cases of malformed header or EOF being detected + * when reading the header, this method will throw an IOException. */ - private int readHeader(InputStream this_in) throws IOException { + private int readHeader(InputStream this_in, boolean failOnEOF) throws IOException { CheckedInputStream in = new CheckedInputStream(this_in, crc); crc.reset(); + + int magic; + if (!failOnEOF) { + // read an unsigned short value representing the GZIP magic header. + // this is the same as calling readUShort(in), except that here, + // when reading the first byte, we don't raise an EOFException + // if the stream has already reached EOF. + + // read unsigned byte + int b = in.read(); + if (b == -1) { // EOF + crc.reset(); + return -1; // represents no header bytes available + } + checkUnexpectedByte(b); + // read the next unsigned byte to form the unsigned + // short. we throw the usual EOFException/ZipException + // from this point on if there is no more data or + // the data doesn't represent a header. + magic = (readUByte(in) << 8) | b; + } else { + magic = readUShort(in); + } // Check header magic - if (readUShort(in) != GZIP_MAGIC) { + if (magic != GZIP_MAGIC) { throw new ZipException("Not in GZIP format"); } // Check compression method @@ -261,7 +293,11 @@ public class GZIPInputStream extends InflaterInputStream { // try concatenated case int m = 8; // this.trailer try { - m += readHeader(in); // next.header + int numNextHeaderBytes = readHeader(in, false); // next.header (if available) + if (numNextHeaderBytes == -1) { + return true; // end of stream reached + } + m += numNextHeaderBytes; } catch (IOException ze) { return true; // ignore any malformed, do nothing } @@ -295,14 +331,18 @@ public class GZIPInputStream extends InflaterInputStream { if (b == -1) { throw new EOFException(); } - if (b < -1 || b > 255) { - // Report on this.in, not argument in; see read{Header, Trailer}. - throw new IOException(this.in.getClass().getName() - + ".read() returned value out of range -1..255: " + b); - } + checkUnexpectedByte(b); return b; } + private void checkUnexpectedByte(final int b) throws IOException { + if (b < -1 || b > 255) { + // report the InputStream type which returned this unexpected byte + throw new IOException(this.in.getClass().getName() + + ".read() returned value out of range -1..255: " + b); + } + } + private byte[] tmpbuf = new byte[128]; /* From 423132895d4ee787d13daa412f9a3f9438834117 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 9 Jan 2026 07:16:58 +0000 Subject: [PATCH 040/204] 8374698: Stub names should look more like identifiers Reviewed-by: adinn, kvn --- src/hotspot/share/opto/callnode.cpp | 9 +++++++++ src/hotspot/share/opto/callnode.hpp | 1 + src/hotspot/share/opto/escape.cpp | 6 ++---- .../share/runtime/stubDeclarations.hpp | 3 +-- src/hotspot/share/runtime/stubInfo.cpp | 20 +++++++++---------- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 4cf72fbde4b..fac8596173f 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -1031,6 +1031,15 @@ bool CallNode::is_call_to_arraycopystub() const { return false; } +bool CallNode::is_call_to_multianewarray_stub() const { + if (_name != nullptr && + strstr(_name, "multianewarray") != nullptr && + strstr(_name, "C2 runtime") != nullptr) { + return true; + } + return false; +} + //============================================================================= uint CallJavaNode::size_of() const { return sizeof(*this); } bool CallJavaNode::cmp( const Node &n ) const { diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index de4b8941359..0bb064efc43 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -758,6 +758,7 @@ public: virtual uint match_edge(uint idx) const; bool is_call_to_arraycopystub() const; + bool is_call_to_multianewarray_stub() const; virtual void copy_call_debug_info(PhaseIterGVN* phase, SafePointNode* sfpt) {} diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index b067d93176a..7b5c3855a9e 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -2066,8 +2066,7 @@ void ConnectionGraph::add_call_node(CallNode* call) { // Use bytecode estimator to record whether the call's return value escapes. ciMethod* meth = call->as_CallJava()->method(); if (meth == nullptr) { - const char* name = call->as_CallStaticJava()->_name; - assert(strncmp(name, "C2 Runtime multianewarray", 25) == 0, "TODO: add failed case check"); + assert(call->as_CallStaticJava()->is_call_to_multianewarray_stub(), "TODO: add failed case check"); // Returns a newly allocated non-escaped object. add_java_object(call, PointsToNode::NoEscape); set_not_scalar_replaceable(ptnode_adr(call_idx) NOT_PRODUCT(COMMA "is result of multinewarray")); @@ -2775,8 +2774,7 @@ int ConnectionGraph::find_init_values_phantom(JavaObjectNode* pta) { assert(pta->arraycopy_dst() || alloc->as_CallStaticJava(), "sanity"); #ifdef ASSERT if (!pta->arraycopy_dst() && alloc->as_CallStaticJava()->method() == nullptr) { - const char* name = alloc->as_CallStaticJava()->_name; - assert(strncmp(name, "C2 Runtime multianewarray", 25) == 0, "sanity"); + assert(alloc->as_CallStaticJava()->is_call_to_multianewarray_stub(), "sanity"); } #endif // Non-escaped allocation returned from Java or runtime call have unknown values in fields. diff --git a/src/hotspot/share/runtime/stubDeclarations.hpp b/src/hotspot/share/runtime/stubDeclarations.hpp index f9080364dc4..c478eda3e7c 100644 --- a/src/hotspot/share/runtime/stubDeclarations.hpp +++ b/src/hotspot/share/runtime/stubDeclarations.hpp @@ -191,8 +191,7 @@ // // C2 stub blob/field names // -// C2 stubs are provided with names in the format "C2 Runtime -// _blob". +// C2 stubs are provided with names in the format "_blob (C2 runtime)". // // A stub creation method OptoRuntime::generate(ciEnv* env) is // generated which invokes the C2 compiler to generate each stub in diff --git a/src/hotspot/share/runtime/stubInfo.cpp b/src/hotspot/share/runtime/stubInfo.cpp index 47a31fe7967..ee90631145a 100644 --- a/src/hotspot/share/runtime/stubInfo.cpp +++ b/src/hotspot/share/runtime/stubInfo.cpp @@ -479,7 +479,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, #define PROCESS_SHARED_BLOB(name, type) \ process_shared_blob(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Shared Runtime " # name "_blob", \ + #name "_blob (shared runtime)", \ BlobId:: JOIN3(shared, name, id), \ StubId:: JOIN3(shared, name, id), \ EntryId:: JOIN3(shared, name, id), \ @@ -488,7 +488,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, #define PROCESS_C1_BLOB(name) \ process_c1_blob(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "C1 Runtime " # name "_blob", \ + #name "_blob (C1 runtime)", \ BlobId:: JOIN3(c1, name, id), \ StubId:: JOIN3(c1, name, id), \ EntryId:: JOIN3(c1, name, id)); \ @@ -496,7 +496,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, #define PROCESS_C2_BLOB(name, type) \ process_c2_blob(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "C2 Runtime " # name "_blob", \ + #name "_blob (C2 runtime)", \ BlobId:: JOIN3(c2, name, id), \ StubId:: JOIN3(c2, name, id), \ EntryId:: JOIN3(c2, name, id)); \ @@ -504,7 +504,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, #define PROCESS_C2_STUB(name, fancy_jump, pass_tls, return_pc) \ process_c2_blob(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "C2 Runtime " # name "_blob", \ + #name "_blob (C2 runtime)", \ BlobId:: JOIN3(c2, name, id), \ StubId:: JOIN3(c2, name, id), \ EntryId:: JOIN3(c2, name, id)); \ @@ -512,20 +512,20 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, #define PROCESS_STUBGEN_BLOB(blob) \ process_stubgen_blob(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Stub Generator " # blob "_blob", \ + #blob "_blob (stub gen)", \ BlobId:: JOIN3(stubgen, blob, id)); \ #define PROCESS_STUBGEN_STUB(blob, stub) \ process_stubgen_stub(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Stub Generator " # stub "_stub", \ + #stub "_stub (stub gen)", \ BlobId:: JOIN3(stubgen, blob, id), \ StubId:: JOIN3(stubgen, stub, id)); \ #define PROCESS_STUBGEN_ENTRY(blob, stub, field_name, getter_name) \ process_stubgen_entry(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Stub Generator " # field_name "_entry", \ + #field_name "_entry (stub gen)", \ BlobId:: JOIN3(stubgen, blob, id), \ StubId:: JOIN3(stubgen, stub, id), \ EntryId:: JOIN3(stubgen, field_name, id), \ @@ -535,7 +535,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, init_funcion) \ process_stubgen_entry(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Stub Generator " # field_name "_entry", \ + #field_name "_entry (stub gen)", \ BlobId:: JOIN3(stubgen, blob, id), \ StubId:: JOIN3(stubgen, stub, id), \ EntryId:: JOIN3(stubgen, field_name, id), \ @@ -545,7 +545,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, count) \ process_stubgen_entry(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Stub Generator " # field_name "_entry", \ + #field_name "_entry (stub gen)", \ BlobId:: JOIN3(stubgen, blob, id), \ StubId:: JOIN3(stubgen, stub, id), \ EntryId:: JOIN3(stubgen, field_name, id), \ @@ -567,7 +567,7 @@ void StubInfo::process_stubgen_entry(StubGroup& group_cursor, init_function) \ process_stubgen_entry(_group_cursor, _blob_cursor, \ _stub_cursor, _entry_cursor, \ - "Stub Generator " # arch_name "_" # field_name "_entry", \ + #arch_name "_" # field_name "_entry (stub gen)",\ BlobId:: JOIN3(stubgen, blob, id), \ StubId:: JOIN3(stubgen, stub, id), \ EntryId:: JOIN4(stubgen, arch_name, \ From a855224305e025aea80165ae63ee921dca299b9c Mon Sep 17 00:00:00 2001 From: Jonas Norlinder Date: Fri, 9 Jan 2026 08:41:39 +0000 Subject: [PATCH 041/204] 8373695: G1: Using a value near integer max for ActiveProcessorCount causes fatal crash Reviewed-by: stefank, tschatzl --- src/hotspot/share/gc/g1/g1Arguments.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp index 2be0e008c22..58e76cdd43a 100644 --- a/src/hotspot/share/gc/g1/g1Arguments.cpp +++ b/src/hotspot/share/gc/g1/g1Arguments.cpp @@ -36,6 +36,7 @@ #include "gc/shared/fullGCForwarding.hpp" #include "gc/shared/gcArguments.hpp" #include "gc/shared/workerPolicy.hpp" +#include "runtime/flags/jvmFlagLimit.hpp" #include "runtime/globals.hpp" #include "runtime/globals_extension.hpp" #include "runtime/java.hpp" @@ -190,7 +191,8 @@ void G1Arguments::initialize() { } FLAG_SET_DEFAULT(G1ConcRefinementThreads, 0); } else if (FLAG_IS_DEFAULT(G1ConcRefinementThreads)) { - FLAG_SET_ERGO(G1ConcRefinementThreads, ParallelGCThreads); + const JVMTypedFlagLimit* conc_refinement_threads_limits = JVMFlagLimit::get_range_at(FLAG_MEMBER_ENUM(G1ConcRefinementThreads))->cast(); + FLAG_SET_ERGO(G1ConcRefinementThreads, MIN2(ParallelGCThreads, conc_refinement_threads_limits->max())); } if (FLAG_IS_DEFAULT(ConcGCThreads) || ConcGCThreads == 0) { From 2a965dffdd2791ab87a2dbfba8ed44f8adb996c7 Mon Sep 17 00:00:00 2001 From: Jeremy Wood Date: Fri, 9 Jan 2026 09:56:39 +0000 Subject: [PATCH 042/204] 8374377: PNGImageDecoder Slow For 8-bit PNGs Reviewed-by: jdv, prr --- .../sun/awt/image/PNGImageDecoder.java | 20 +- .../image/png/PngImageDecoder8BitTest.java | 223 ++++++++++++++++++ .../PNGImageDecoder_8bit_uninterlaced.java | 161 +++++++++++++ 3 files changed, 400 insertions(+), 4 deletions(-) create mode 100644 test/jdk/sun/awt/image/png/PngImageDecoder8BitTest.java create mode 100644 test/micro/org/openjdk/bench/java/awt/image/PNGImageDecoder_8bit_uninterlaced.java diff --git a/src/java.desktop/share/classes/sun/awt/image/PNGImageDecoder.java b/src/java.desktop/share/classes/sun/awt/image/PNGImageDecoder.java index 357ad3be43f..08909b802c5 100644 --- a/src/java.desktop/share/classes/sun/awt/image/PNGImageDecoder.java +++ b/src/java.desktop/share/classes/sun/awt/image/PNGImageDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, 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 @@ -302,8 +302,16 @@ public class PNGImageDecoder extends ImageDecoder int bitsPerPixel = samplesPerPixel*bitDepth; int bytesPerPixel = (bitsPerPixel+7)>>3; int pass, passLimit; - if(interlaceMethod==0) { pass = -1; passLimit = 0; } - else { pass = 0; passLimit = 7; } + boolean isDirectByteCopy; + if(interlaceMethod==0) { + pass = -1; + passLimit = 0; + isDirectByteCopy = bPixels != null && bitDepth == 8; + } else { + pass = 0; + passLimit = 7; + isDirectByteCopy = false; + } while(++pass<=passLimit) { int row = startingRow[pass]; int rowInc = rowIncrement[pass]; @@ -334,7 +342,11 @@ public class PNGImageDecoder extends ImageDecoder int spos=0; int pixel = 0; while (col < width) { - if(wPixels !=null) { + if (isDirectByteCopy) { + System.arraycopy(rowByteBuffer, spos, bPixels, col + rowOffset, width); + spos += width; + break; + } else if(wPixels !=null) { switch(combinedType) { case COLOR|ALPHA|(8<<3): wPixels[col+rowOffset] = diff --git a/test/jdk/sun/awt/image/png/PngImageDecoder8BitTest.java b/test/jdk/sun/awt/image/png/PngImageDecoder8BitTest.java new file mode 100644 index 00000000000..b2c134e09b2 --- /dev/null +++ b/test/jdk/sun/awt/image/png/PngImageDecoder8BitTest.java @@ -0,0 +1,223 @@ +/* + * 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 8374377 + * @summary This test confirms the PNGImageProducer decodes 8-bit interlaced + * and non-interlaced PNGs correctly. + */ + +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.stream.ImageOutputStream; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.IndexColorModel; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +/** + * The proposed change for 8374377 affects how 8-bit PNGs are decoded. + * So this test confirms that 8-bit PNGs (both interlaced and non-interlaced) + * are still decoded by the PNGImageDecoder so they match what ImageIO decodes. + * + * This test has never failed. + */ +public class PngImageDecoder8BitTest { + + static BufferedImage createBufferedImage(Image img) + throws ExecutionException, InterruptedException { + CompletableFuture future = new CompletableFuture<>(); + img.getSource().startProduction(new ImageConsumer() { + private int imageWidth, imageHeight; + private BufferedImage bi; + + @Override + public void setDimensions(int width, int height) { + imageWidth = width; + imageHeight = height; + } + + @Override + public void setProperties(Hashtable props) { + // intentionally empty + } + + @Override + public void setColorModel(ColorModel model) { + // intentionally empty + } + + @Override + public void setHints(int hintflags) { + // intentionally empty + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, + byte[] pixels, int off, int scansize) { + if (bi == null) { + bi = new BufferedImage(imageWidth, imageHeight, + BufferedImage.TYPE_BYTE_INDEXED, + (IndexColorModel) model); + } + + if (w == imageWidth && h == imageHeight) { + // this is how interlaced PNGs are decoded: + bi.getRaster().setDataElements(0, 0, + imageWidth, imageHeight, pixels); + return; + } + + if (h != 1) { + throw new UnsupportedOperationException( + "this test requires h = 1"); + } + if (off != 0) { + throw new UnsupportedOperationException( + "this test requires off = 0"); + } + + bi.getRaster().setDataElements(x, y, w, 1, pixels); + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, + int[] pixels, int off, int scansize) { + throw new UnsupportedOperationException(); + } + + @Override + public void imageComplete(int status) { + future.complete(bi); + } + }); + return future.get(); + } + + public static void main(String[] args) throws Exception { + BufferedImage expected = createImageData(); + for (boolean interlace : new boolean[] { false, true} ) { + System.out.println("Testing interlacing = "+ interlace); + byte[] imageData = encodePNG(expected, interlace); + + Image i = Toolkit.getDefaultToolkit().createImage(imageData); + BufferedImage actual = createBufferedImage(i); + + testCorrectness(expected, actual); + } + System.out.println("Confirmed that 8-bit PNGs decode correctly " + + "whether we use interlacing or not."); + } + + /** + * Create a large sample image stored as an 8-bit PNG. + */ + private static BufferedImage createImageData() { + BufferedImage bi = new BufferedImage(6000, 6000, + BufferedImage.TYPE_BYTE_INDEXED); + Random r = new Random(0); + Graphics2D g = bi.createGraphics(); + for (int a = 0; a < 20000; a++) { + g.setColor(new Color(r.nextInt(0xffffff))); + int radius = 10 + r.nextInt(90); + g.fillOval(r.nextInt(bi.getWidth()), r.nextInt(bi.getHeight()), + radius, radius); + } + g.dispose(); + return bi; + } + + /** + * Encode an image as 8-bit PNG. + */ + private static byte[] encodePNG(BufferedImage bi, boolean interlace) + throws IOException { + Iterator writers = + ImageIO.getImageWritersByFormatName("png"); + if (!writers.hasNext()) { + throw new IllegalStateException("No PNG writers found"); + } + ImageWriter writer = writers.next(); + + ImageWriteParam param = writer.getDefaultWriteParam(); + if (interlace) { + param.setProgressiveMode(ImageWriteParam.MODE_DEFAULT); + } + + try (ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + ImageOutputStream imageOut = + ImageIO.createImageOutputStream(byteOut)) { + writer.setOutput(imageOut); + writer.write(null, new IIOImage(bi, null, null), param); + return byteOut.toByteArray(); + } finally { + writer.dispose(); + } + } + + /** + * This throws an Error if the two images are not identical. + *

    + * This unit test is intended to accompany a performance enhancement for + * PNGImageDecoder. This method makes sure the enhancement didn't cost us + * any accuracy. + */ + private static void testCorrectness(BufferedImage expected, + BufferedImage actual) { + if (expected.getWidth() != actual.getWidth()) { + throw new RuntimeException("expected.getWidth() = " + + expected.getWidth() + ", actual.getWidth() = " + + actual.getWidth()); + } + if (expected.getHeight() != actual.getHeight()) { + throw new RuntimeException("expected.getHeight() = " + + expected.getHeight() + ", actual.getHeight() = " + + actual.getHeight()); + } + for (int y = 0; y < expected.getHeight(); y++) { + for (int x = 0; x < expected.getWidth(); x++) { + int argb1 = expected.getRGB(x, y); + int argb2 = actual.getRGB(x, y); + if (argb1 != argb2) { + throw new RuntimeException("x = " + x + ", y = " + y + + " argb1 = " + Integer.toUnsignedString(argb1, 16) + + " argb2 = " + Integer.toUnsignedString(argb2, 16)); + } + } + } + } +} diff --git a/test/micro/org/openjdk/bench/java/awt/image/PNGImageDecoder_8bit_uninterlaced.java b/test/micro/org/openjdk/bench/java/awt/image/PNGImageDecoder_8bit_uninterlaced.java new file mode 100644 index 00000000000..c91b3e24ba7 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/awt/image/PNGImageDecoder_8bit_uninterlaced.java @@ -0,0 +1,161 @@ +/* + * 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.awt.image; + +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +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.jmh.infra.Blackhole; + +import javax.imageio.ImageIO; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ImageConsumer; +import java.awt.image.IndexColorModel; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Hashtable; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 20) +@Fork(3) +@State(Scope.Thread) +public class PNGImageDecoder_8bit_uninterlaced { + + byte[] pngImageData; + + @Setup + public void setup() throws Exception { + pngImageData = createImageData(2_500); + } + + @Benchmark + public void measurePNGImageDecoder(Blackhole bh) throws Exception { + Image img = Toolkit.getDefaultToolkit().createImage(pngImageData); + BufferedImage bi = createBufferedImage(img); + bi.flush(); + bh.consume(bi); + } + + /** + * Create a large sample image stored as an 8-bit PNG. + * + * @return the byte representation of the PNG image. + */ + private static byte[] createImageData(int squareSize) throws Exception { + BufferedImage bi = new BufferedImage(squareSize, squareSize, + BufferedImage.TYPE_BYTE_INDEXED); + Random r = new Random(0); + Graphics2D g = bi.createGraphics(); + for (int a = 0; a < 20000; a++) { + g.setColor(new Color(r.nextInt(0xffffff))); + int radius = 10 + r.nextInt(90); + g.fillOval(r.nextInt(bi.getWidth()), r.nextInt(bi.getHeight()), + radius, radius); + } + g.dispose(); + + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + ImageIO.write(bi, "png", out); + return out.toByteArray(); + } + } + + static BufferedImage createBufferedImage(Image img) + throws ExecutionException, InterruptedException { + CompletableFuture future = new CompletableFuture<>(); + img.getSource().startProduction(new ImageConsumer() { + private int imageWidth, imageHeight; + private BufferedImage bi; + + @Override + public void setDimensions(int width, int height) { + imageWidth = width; + imageHeight = height; + } + + @Override + public void setProperties(Hashtable props) { + // intentionally empty + } + + @Override + public void setColorModel(ColorModel model) { + // intentionally empty + } + + @Override + public void setHints(int hintflags) { + // intentionally empty + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, + byte[] pixels, int off, int scansize) { + if (bi == null) { + bi = new BufferedImage(imageWidth, imageHeight, + BufferedImage.TYPE_BYTE_INDEXED, + (IndexColorModel) model); + } + if (h != 1) + throw new UnsupportedOperationException( + "this test expects sequential rows of pixels"); + if (off != 0) + throw new UnsupportedOperationException( + "this test expects the incoming pixels to start " + + "at index zero"); + + bi.getRaster().setDataElements(x, y, w, 1, pixels); + } + + @Override + public void setPixels(int x, int y, int w, int h, ColorModel model, + int[] pixels, int off, int scansize) { + throw new UnsupportedOperationException(); + } + + @Override + public void imageComplete(int status) { + future.complete(bi); + } + }); + return future.get(); + } +} \ No newline at end of file From c8c6e7007aec9a568c25dcd5d4242b7911a83bfe Mon Sep 17 00:00:00 2001 From: Kevin Walls Date: Fri, 9 Jan 2026 10:23:03 +0000 Subject: [PATCH 043/204] 8374825: vmTestbase comment typo: lunch Reviewed-by: tschatzl, shade --- .../vmTestbase/nsk/share/gc/Algorithms.java | 7 ++----- .../nsk/share/gc/gp/GarbageUtils.java | 17 ++++------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/Algorithms.java b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/Algorithms.java index 404dc6498f7..518a932e16b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/Algorithms.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/Algorithms.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 @@ -73,10 +73,7 @@ public class Algorithms { *

    * * Note that this method can throw Failure if any exception - * is thrown while eating memory. To avoid OOM while allocating - * exception we preallocate it before the lunch starts. It means - * that exception stack trace does not correspond to the place - * where exception is thrown, but points at start of the method. + * is thrown while eating memory. * * This method uses nsk.share.test.Stresser class to control * it's execution. Consumed number of iterations depends on diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/gp/GarbageUtils.java b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/gp/GarbageUtils.java index 1c7a8c6172c..529b299fd15 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/gc/gp/GarbageUtils.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/gc/gp/GarbageUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -181,10 +181,7 @@ public final class GarbageUtils { * Eat memory using default(byte[]) garbage producer. * * Note that this method can throw Failure if any exception - * is thrown while eating memory. To avoid OOM while allocating - * exception we preallocate it before the lunch starts. It means - * that exception stack trace does not correspond to the place - * where exception is thrown, but points at start of the method. + * is thrown while eating memory. * * @param stresser stresser * @param initialFactor determines which portion of initial memory initial chunk will be @@ -200,10 +197,7 @@ public final class GarbageUtils { * Eat memory using given garbage producer. * * Note that this method can throw Failure if any exception - * is thrown while eating memory. To avoid OOM while allocating - * exception we preallocate it before the lunch starts. It means - * that exception stack trace does not correspond to the place - * where exception is thrown, but points at start of the method. + * is thrown while eating memory. * * @param stresser stresser to use * @param gp garbage producer @@ -270,10 +264,7 @@ public final class GarbageUtils { * Eat memory using given garbage producer. * * Note that this method can throw Failure if any exception - * is thrown while eating memory. To avoid OOM while allocating - * exception we preallocate it before the lunch starts. It means - * that exception stack trace does not correspond to the place - * where exception is thrown, but points at start of the method. + * is thrown while eating memory. * * @param stresser stresser to use * @param gp garbage producer From 47e19353cd3661ad9aed00f6a415818da45cdfef Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 9 Jan 2026 12:24:13 +0000 Subject: [PATCH 044/204] 8373941: Epsilon: Robust counter updates in early VM phases Reviewed-by: stefank, tschatzl --- src/hotspot/share/gc/epsilon/epsilonHeap.cpp | 28 ++++--- src/hotspot/share/gc/epsilon/epsilonHeap.hpp | 2 +- .../gc/epsilon/epsilonMonitoringSupport.cpp | 10 +++ .../gc/epsilon/epsilonMonitoringSupport.hpp | 3 + .../jtreg/gc/epsilon/TestInitAllocs.java | 76 +++++++++++++++++++ 5 files changed, 106 insertions(+), 13 deletions(-) create mode 100644 test/hotspot/jtreg/gc/epsilon/TestInitAllocs.java diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp index e5ae673ef0c..24182c22a23 100644 --- a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp @@ -77,6 +77,7 @@ jint EpsilonHeap::initialize() { void EpsilonHeap::initialize_serviceability() { _pool = new EpsilonMemoryPool(this); _memory_manager.add_pool(_pool); + _monitoring_support->mark_ready(); } GrowableArray EpsilonHeap::memory_managers() { @@ -101,7 +102,7 @@ EpsilonHeap* EpsilonHeap::heap() { return named_heap(CollectedHeap::Epsilon); } -HeapWord* EpsilonHeap::allocate_work(size_t size, bool verbose) { +HeapWord* EpsilonHeap::allocate_work(size_t size) { assert(is_object_aligned(size), "Allocation size should be aligned: %zu", size); HeapWord* res = nullptr; @@ -151,19 +152,23 @@ HeapWord* EpsilonHeap::allocate_work(size_t size, bool verbose) { size_t used = _space->used(); - // Allocation successful, update counters - if (verbose) { - size_t last = _last_counter_update; - if ((used - last >= _step_counter_update) && AtomicAccess::cmpxchg(&_last_counter_update, last, used) == last) { + // Allocation successful, update counters and print status. + // At this point, some diagnostic subsystems might not yet be initialized. + // We pretend the printout happened either way. This keeps allocation path + // from obsessively checking the subsystems' status on every allocation. + size_t last_counter = AtomicAccess::load(&_last_counter_update); + if ((used - last_counter >= _step_counter_update) && + AtomicAccess::cmpxchg(&_last_counter_update, last_counter, used) == last_counter) { + if (_monitoring_support->is_ready()) { _monitoring_support->update_counters(); } } - // ...and print the occupancy line, if needed - if (verbose) { - size_t last = _last_heap_print; - if ((used - last >= _step_heap_print) && AtomicAccess::cmpxchg(&_last_heap_print, last, used) == last) { - print_heap_info(used); + size_t last_heap = AtomicAccess::load(&_last_heap_print); + if ((used - last_heap >= _step_heap_print) && + AtomicAccess::cmpxchg(&_last_heap_print, last_heap, used) == last_heap) { + print_heap_info(used); + if (Metaspace::initialized()) { print_metaspace_info(); } } @@ -265,8 +270,7 @@ HeapWord* EpsilonHeap::mem_allocate(size_t size) { } HeapWord* EpsilonHeap::allocate_loaded_archive_space(size_t size) { - // Cannot use verbose=true because Metaspace is not initialized - return allocate_work(size, /* verbose = */false); + return allocate_work(size); } void EpsilonHeap::collect(GCCause::Cause cause) { diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp index 4f812bde8b3..9693c63b15c 100644 --- a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp @@ -83,7 +83,7 @@ public: bool requires_barriers(stackChunkOop obj) const override { return false; } // Allocation - HeapWord* allocate_work(size_t size, bool verbose = true); + HeapWord* allocate_work(size_t size); HeapWord* mem_allocate(size_t size) override; HeapWord* allocate_new_tlab(size_t min_size, size_t requested_size, diff --git a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp index 51d0a8356d2..38be736df74 100644 --- a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp +++ b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp @@ -96,9 +96,11 @@ public: EpsilonMonitoringSupport::EpsilonMonitoringSupport(EpsilonHeap* heap) { _heap_counters = new EpsilonGenerationCounters(heap); _space_counters = new EpsilonSpaceCounters("Heap", 0, heap->max_capacity(), 0, _heap_counters); + _ready = false; } void EpsilonMonitoringSupport::update_counters() { + assert(is_ready(), "Must be ready"); MemoryService::track_memory_usage(); if (UsePerfData) { @@ -110,3 +112,11 @@ void EpsilonMonitoringSupport::update_counters() { MetaspaceCounters::update_performance_counters(); } } + +bool EpsilonMonitoringSupport::is_ready() { + return AtomicAccess::load_acquire(&_ready); +} + +void EpsilonMonitoringSupport::mark_ready() { + return AtomicAccess::release_store(&_ready, true); +} diff --git a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.hpp b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.hpp index 67a60d92778..76cdac6df1b 100644 --- a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.hpp +++ b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.hpp @@ -35,9 +35,12 @@ class EpsilonMonitoringSupport : public CHeapObj { private: EpsilonGenerationCounters* _heap_counters; EpsilonSpaceCounters* _space_counters; + volatile bool _ready; public: EpsilonMonitoringSupport(EpsilonHeap* heap); + bool is_ready(); + void mark_ready(); void update_counters(); }; diff --git a/test/hotspot/jtreg/gc/epsilon/TestInitAllocs.java b/test/hotspot/jtreg/gc/epsilon/TestInitAllocs.java new file mode 100644 index 00000000000..353daaef8b6 --- /dev/null +++ b/test/hotspot/jtreg/gc/epsilon/TestInitAllocs.java @@ -0,0 +1,76 @@ +/* + * 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.epsilon; + +/** + * @test TestInitAllocs + * @requires vm.gc.Epsilon + * @summary Test that allocation path taken in early JVM phases works + * + * @run main/othervm -Xmx256m + * -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC + * gc.epsilon.TestInitAllocs + * + * @run main/othervm -Xmx256m + * -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC + * -XX:+UseTLAB + * -XX:+UseCompressedOops + * -XX:EpsilonMinHeapExpand=1024 + * -XX:EpsilonUpdateCountersStep=1 + * -XX:EpsilonPrintHeapSteps=1000000 + * gc.epsilon.TestInitAllocs + * + * @run main/othervm -Xmx256m + * -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC + * -XX:+UseTLAB + * -XX:-UseCompressedOops + * -XX:EpsilonMinHeapExpand=1024 + * -XX:EpsilonUpdateCountersStep=1 + * -XX:EpsilonPrintHeapSteps=1000000 + * gc.epsilon.TestInitAllocs + * + * @run main/othervm -Xmx256m + * -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC + * -XX:-UseTLAB + * -XX:+UseCompressedOops + * -XX:EpsilonMinHeapExpand=1024 + * -XX:EpsilonUpdateCountersStep=1 + * -XX:EpsilonPrintHeapSteps=1000000 + * gc.epsilon.TestInitAllocs + * + * @run main/othervm -Xmx256m + * -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC + * -XX:-UseTLAB + * -XX:-UseCompressedOops + * -XX:EpsilonMinHeapExpand=1024 + * -XX:EpsilonUpdateCountersStep=1 + * -XX:EpsilonPrintHeapSteps=1000000 + * gc.epsilon.TestInitAllocs + */ + +public class TestInitAllocs { + public static void main(String[] args) throws Exception { + System.out.println("Hello World"); + } +} From 6d1bfdf7a92e44ff855307f86d1734fad909ea3d Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Fri, 9 Jan 2026 13:14:25 +0000 Subject: [PATCH 045/204] 8374796: CompressedOops versions of runtime/cds/TestDefaultArchiveLoading.java aren't run Reviewed-by: stefank, shade --- .../jtreg/runtime/cds/TestDefaultArchiveLoading.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/runtime/cds/TestDefaultArchiveLoading.java b/test/hotspot/jtreg/runtime/cds/TestDefaultArchiveLoading.java index acff3191300..8b07cd86a72 100644 --- a/test/hotspot/jtreg/runtime/cds/TestDefaultArchiveLoading.java +++ b/test/hotspot/jtreg/runtime/cds/TestDefaultArchiveLoading.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. * Copyright (c) 2024, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -55,7 +55,7 @@ * @requires vm.cds.default.archive.available * @requires vm.cds.write.archived.java.heap * @requires vm.bits == 64 - * @requires !vm.gc.Z + * @requires vm.gc != "Z" * @library /test/lib * @modules java.base/jdk.internal.misc * java.management @@ -69,7 +69,7 @@ * @requires vm.cds.default.archive.available * @requires vm.cds.write.archived.java.heap * @requires vm.bits == 64 - * @requires !vm.gc.Z + * @requires vm.gc != "Z" * @library /test/lib * @modules java.base/jdk.internal.misc * java.management From 8737a8ca73952d60129e7fc2f7e17eea3b800af7 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 9 Jan 2026 14:49:52 +0000 Subject: [PATCH 046/204] 8373448: jpackage: StackOverflowError when processing a very long argument Reviewed-by: almatvee --- .../jpackage/internal/cli/StandardOption.java | 15 +++++++--- .../internal/cli/StandardOptionTest.java | 28 +++++++++++++++++-- 2 files changed, 37 insertions(+), 6 deletions(-) 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 ec63c1fb498..0fa0af296dc 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 @@ -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 @@ -727,8 +727,15 @@ public final class StandardOption { // // regexp for parsing args (for example, for additional launchers) - private static Pattern pattern = Pattern.compile( - "(?:(?:([\"'])(?:\\\\\\1|.)*?(?:\\1|$))|(?:\\\\[\"'\\s]|[^\\s]))++"); + private static Pattern PATTERN = Pattern.compile(String.format( + "(?:(?:%s|%s)|(?:\\\\[\"'\\s]|\\S))++", + createPatternComponent('\''), + createPatternComponent('\"'))); + + private static String createPatternComponent(char quoteChar) { + var str = Character.toString(quoteChar); + return String.format("(?:%s(?:\\\\%s|[^%s])*+(?:%s|$))", str, str, str, str); + } static List getArgumentList(String inputString) { Objects.requireNonNull(inputString); @@ -741,7 +748,7 @@ public final class StandardOption { // The "pattern" regexp attempts to abide to the rule that // strings are delimited by whitespace unless surrounded by // quotes, then it is anything (including spaces) in the quotes. - Matcher m = pattern.matcher(inputString); + Matcher m = PATTERN.matcher(inputString); while (m.find()) { String s = inputString.substring(m.start(), m.end()).trim(); // Ensure we do not have an empty string. trim() will take care of 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 b786f484cb4..4aa3d5f72c1 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 @@ -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 @@ -341,9 +341,33 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { Arguments.of("abc", List.of("abc")), Arguments.of("a b c", List.of("a", "b", "c")), Arguments.of("a=10 -Dorg.acme.name='John Smith' c=\\\"foo\\\"", List.of("a=10", "-Dorg.acme.name=John Smith", "c=\"foo\"")), + Arguments.of(" foo \"a b c\" v=' John Smith ' 'H e ll o' ", List.of("foo", "a b c", "v= John Smith ", "H e ll o")), Arguments.of("\"\"", List.of("")), Arguments.of(" ", List.of()), - Arguments.of("", List.of()) + Arguments.of(" ", List.of()), + Arguments.of(" foo ", List.of("foo")), + Arguments.of("", List.of()), + Arguments.of("'fo\"o'\\ buzz \"b a r\"", List.of("fo\"o\\ buzz", "b a r")), + Arguments.of("a\\ 'b\"c'\\ d", List.of("a\\ b\"c\\ d")), + Arguments.of("\"a 'bc' d\"", List.of("a 'bc' d")), + Arguments.of("\'a 'bc' d\'", List.of("a bc d")), + Arguments.of("\"a \\'bc\\' d\"", List.of("a 'bc' d")), + Arguments.of("\'a \\'bc\\' d\'", List.of("a 'bc' d")), + Arguments.of("'a b c' 'd e f'", List.of("a b c", "d e f")), + Arguments.of("'a b c' \"'d e f' h", List.of("a b c", "'d e f' h")), + Arguments.of("'a b c' \"'d e f' \t ", List.of("a b c", "'d e f'")), + Arguments.of(" a='' '' \t '\\'\\'' \"\" \"\\\"\\\"\" ", List.of("a=", "", "\'\'", "", "\"\"")), + Arguments.of("' \'foo '", List.of(" foo", "")), + Arguments.of("' \'foo ' bar", List.of(" foo", " bar")), + Arguments.of("' \'foo\\ '", List.of(" foo\\ ")), + Arguments.of("'fo\"o buzz \"b a r\"", List.of("fo\"o buzz \"b a r\"")), + Arguments.of("'", List.of("")), + Arguments.of("' f g ", List.of(" f g")), + Arguments.of("' f g", List.of(" f g")), + Arguments.of("'\\'", List.of("'")), + Arguments.of("'\\' ", List.of("'")), + Arguments.of("'\\' a ", List.of("' a")), + Arguments.of("\"" + "\\\"".repeat(10000) + "A", List.of("\"".repeat(10000) + "A")) ); } From f5fa9e40b09b7b6322edb5f057a6350d44980e14 Mon Sep 17 00:00:00 2001 From: Kevin Walls Date: Fri, 9 Jan 2026 16:49:04 +0000 Subject: [PATCH 047/204] 8374745: Test vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters004/TestDescription.java failed Reviewed-by: lmesnik, sspitsyn --- .../CollectionCounters001.java | 21 +++++++++++++------ .../TestDescription.java | 12 +++++++---- .../TestDescription.java | 12 +++++++---- .../TestDescription.java | 12 +++++++---- .../TestDescription.java | 12 +++++++---- 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters001/CollectionCounters001.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters001/CollectionCounters001.java index 7c089abb6ec..e2f1b9cbc71 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters001/CollectionCounters001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters001/CollectionCounters001.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * 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,15 +24,19 @@ /* * @test - * @key randomness * * @summary converted from VM Testbase nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters001. * VM Testbase keywords: [monitoring] * - * @requires vm.opt.DisableExplicitGC != "true" * @library /vmTestbase * /test/lib - * @run main/othervm -XX:-UseGCOverheadLimit + * + * @build jdk.test.whitebox.WhiteBox + * @requires vm.opt.DisableExplicitGC != "true" + * @requires vm.compMode != "Xcomp" + * + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseGCOverheadLimit * nsk.monitoring.GarbageCollectorMXBean.CollectionCounters.CollectionCounters001.CollectionCounters001 * -testMode=directly * -iterations=5 @@ -42,10 +46,12 @@ package nsk.monitoring.GarbageCollectorMXBean.CollectionCounters.CollectionCount import java.util.List; import java.lang.management.*; + +import jdk.test.whitebox.WhiteBox; + import nsk.share.TestFailure; import nsk.share.test.*; import nsk.monitoring.share.*; -import nsk.share.gc.Algorithms; import nsk.share.runner.RunParams; import nsk.share.runner.RunParamsAware; @@ -80,12 +86,15 @@ public class CollectionCounters001 extends MonitoringTestBase implements RunPara private void runOne(ExecutionController stresser) { updateCounters(); validate(false /* don't check gc count increases */); - Algorithms.eatMemory(stresser); + + WhiteBox.getWhiteBox().fullGC(); updateCounters(); validate(true); + System.gc(); updateCounters(); validate(true); + memory.gc(); updateCounters(); validate(true); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters002/TestDescription.java index 51abb4e7a0b..2c1e21fcb15 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters002/TestDescription.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 @@ -24,15 +24,19 @@ /* * @test - * @key randomness * * @summary converted from VM Testbase nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters002. * VM Testbase keywords: [monitoring] * - * @requires vm.opt.DisableExplicitGC != "true" * @library /vmTestbase * /test/lib - * @run main/othervm -XX:-UseGCOverheadLimit + * + * @build jdk.test.whitebox.WhiteBox + * @requires vm.opt.DisableExplicitGC != "true" + * @requires vm.compMode != "Xcomp" + * + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseGCOverheadLimit * nsk.monitoring.GarbageCollectorMXBean.CollectionCounters.CollectionCounters001.CollectionCounters001 * -testMode=server * -MBeanServer=default diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters003/TestDescription.java index 20e266f50f1..241b1912d0f 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters003/TestDescription.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 @@ -24,15 +24,19 @@ /* * @test - * @key randomness * * @summary converted from VM Testbase nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters003. * VM Testbase keywords: [monitoring] * - * @requires vm.opt.DisableExplicitGC != "true" * @library /vmTestbase * /test/lib - * @run main/othervm -XX:-UseGCOverheadLimit + * + * @build jdk.test.whitebox.WhiteBox + * @requires vm.opt.DisableExplicitGC != "true" + * @requires vm.compMode != "Xcomp" + * + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseGCOverheadLimit * nsk.monitoring.GarbageCollectorMXBean.CollectionCounters.CollectionCounters001.CollectionCounters001 * -testMode=server * -MBeanServer=custom diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters004/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters004/TestDescription.java index db5a04cede8..cd0ed660ed0 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters004/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters004/TestDescription.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 @@ -24,15 +24,19 @@ /* * @test - * @key randomness * * @summary converted from VM Testbase nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters004. * VM Testbase keywords: [monitoring] * - * @requires vm.opt.DisableExplicitGC != "true" * @library /vmTestbase * /test/lib - * @run main/othervm -XX:-UseGCOverheadLimit + * + * @build jdk.test.whitebox.WhiteBox + * @requires vm.opt.DisableExplicitGC != "true" + * @requires vm.compMode != "Xcomp" + * + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseGCOverheadLimit * nsk.monitoring.GarbageCollectorMXBean.CollectionCounters.CollectionCounters001.CollectionCounters001 * -testMode=proxy * -MBeanServer=default diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters005/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters005/TestDescription.java index 68c50f026b7..c3c6fac62e7 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters005/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters005/TestDescription.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 @@ -24,15 +24,19 @@ /* * @test - * @key randomness * * @summary converted from VM Testbase nsk/monitoring/GarbageCollectorMXBean/CollectionCounters/CollectionCounters005. * VM Testbase keywords: [monitoring] * - * @requires vm.opt.DisableExplicitGC != "true" * @library /vmTestbase * /test/lib - * @run main/othervm -XX:-UseGCOverheadLimit + * + * @build jdk.test.whitebox.WhiteBox + * @requires vm.opt.DisableExplicitGC != "true" + * @requires vm.compMode != "Xcomp" + * + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-UseGCOverheadLimit * nsk.monitoring.GarbageCollectorMXBean.CollectionCounters.CollectionCounters001.CollectionCounters001 * -testMode=proxy * -MBeanServer=custom From 663a08331a83c852622b8b11900f12b0dc3dbe82 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 9 Jan 2026 22:20:05 +0000 Subject: [PATCH 048/204] 8374219: Fix issues in jpackage's Executor class Reviewed-by: almatvee --- .../jpackage/internal/DesktopIntegration.java | 3 +- .../jpackage/internal/LibProvidersLookup.java | 33 +- .../internal/LinuxBundlingEnvironment.java | 53 +- .../jpackage/internal/LinuxDebPackager.java | 20 +- .../internal/LinuxDebSystemEnvironment.java | 4 +- .../LinuxDebSystemEnvironmentMixin.java | 4 +- .../jpackage/internal/LinuxFromOptions.java | 14 +- .../internal/LinuxLaunchersAsServices.java | 3 +- .../jpackage/internal/LinuxPackageArch.java | 103 +- .../internal/LinuxPackageBuilder.java | 10 +- .../internal/LinuxRpmSystemEnvironment.java | 4 +- .../LinuxRpmSystemEnvironmentMixin.java | 4 +- .../internal/LinuxSystemEnvironment.java | 19 +- .../jdk/jpackage/internal/AppImageSigner.java | 11 +- .../jdk/jpackage/internal/Codesign.java | 13 +- .../internal/MacBundlingEnvironment.java | 14 +- .../internal/MacCertificateUtils.java | 4 +- .../jdk/jpackage/internal/MacDmgPackager.java | 240 ++- .../internal/MacDmgSystemEnvironment.java | 59 +- .../jdk/jpackage/internal/MacPkgPackager.java | 15 +- .../jdk/jpackage/internal/TempKeychain.java | 17 +- .../internal/DefaultBundlingEnvironment.java | 17 +- .../jdk/jpackage/internal/Executor.java | 464 ++-- .../jpackage/internal/ExecutorFactory.java | 33 + .../jdk/jpackage/internal/Globals.java | 71 + .../jdk/jpackage/internal/IOUtils.java | 56 +- .../internal/JLinkRuntimeBuilder.java | 30 +- .../classes/jdk/jpackage/internal/Log.java | 31 +- .../jdk/jpackage/internal/ObjectFactory.java | 72 + .../jdk/jpackage/internal/RetryExecutor.java | 136 -- .../internal/RetryExecutorFactory.java | 35 + .../jpackage/internal/SystemEnvironment.java | 4 +- .../jdk/jpackage/internal/ToolValidator.java | 26 +- .../jdk/jpackage/internal/cli/Main.java | 42 +- .../resources/MainResources.properties | 3 +- .../internal/util/CommandLineFormat.java | 52 + .../internal/util/CommandOutputControl.java | 1904 +++++++++++++++++ .../internal/{ => util}/Enquoter.java | 42 +- .../jpackage/internal/util/RetryExecutor.java | 194 ++ .../internal/util/TeeOutputStream.java | 89 + .../internal/UnixLaunchersAsServices.java | 7 +- .../internal/WinBundlingEnvironment.java | 23 +- .../jdk/jpackage/internal/WixTool.java | 8 +- .../jdk/jpackage/test/ExecutorTest.java | 401 ---- .../jdk/jpackage/test/PackageTestTest.java | 7 +- .../helpers/jdk/jpackage/test/Executor.java | 798 ++----- .../jdk/jpackage/test/JPackageCommand.java | 33 +- .../jdk/jpackage/test/LinuxHelper.java | 6 +- .../helpers/jdk/jpackage/test/MacHelper.java | 102 +- .../helpers/jdk/jpackage/test/MacSign.java | 4 +- .../jdk/jpackage/test/MacSignVerify.java | 21 +- .../jdk/jpackage/test/WindowsHelper.java | 12 +- .../jdk/jpackage/test/mock/CommandAction.java | 76 + .../jpackage/test/mock/CommandActionSpec.java | 86 + .../test/mock/CommandActionSpecs.java | 185 ++ .../jdk/jpackage/test/mock/CommandMock.java | 128 ++ .../jpackage/test/mock/CommandMockExit.java | 60 + .../jpackage/test/mock/CommandMockSpec.java | 63 + .../test/mock/CompletableCommandMock.java | 31 + .../jpackage/test/mock/MockIOException.java | 39 + .../test/mock/MockIllegalStateException.java | 35 + .../test/mock/MockingToolProvider.java | 164 ++ .../jdk/jpackage/test/mock/Script.java | 297 +++ .../jdk/jpackage/test/mock/ScriptSpec.java | 179 ++ .../jpackage/test/mock/ScriptSpecInDir.java | 66 + .../test/mock/ToolProviderCommandMock.java | 29 + .../ToolProviderCompletableCommandMock.java | 27 + .../test/mock/VerbatimCommandMock.java | 28 + .../internal/LibProvidersLookupTest.java | 54 + .../internal/LinuxPackageArchTest.java | 154 ++ .../internal/LinuxSystemEnvironmentTest.java | 101 + .../jdk/tools/jpackage/junit/linux/junit.java | 34 +- .../jpackage/internal/MacDmgPackagerTest.java | 420 ++++ .../internal/MacDmgSystemEnvironmentTest.java | 157 ++ .../tools/jpackage/junit/macosx/junit.java | 24 +- .../DefaultBundlingEnvironmentTest.java | 227 +- .../jdk/jpackage/internal/ExecutorTest.java | 165 ++ .../jdk/jpackage/internal/MockUtils.java | 235 ++ .../cli/OptionsValidationFailTest.excludes | 1 - .../cli/OptionsValidationFailTest.java | 4 +- .../util/CommandOutputControlTest.java | 1846 ++++++++++++++++ .../util/CommandOutputControlTestUtils.java | 168 ++ .../internal/{ => util}/EnquoterTest.java | 50 +- .../internal/util/RetryExecutorTest.java | 331 +++ test/jdk/tools/jpackage/share/ErrorTest.java | 5 +- .../jpackage/share/PostImageScriptTest.java | 4 +- 86 files changed, 8912 insertions(+), 1931 deletions(-) create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/ExecutorFactory.java create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/ObjectFactory.java delete mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutor.java create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutorFactory.java create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandLineFormat.java create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java rename src/jdk.jpackage/share/classes/jdk/jpackage/internal/{ => util}/Enquoter.java (77%) create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RetryExecutor.java create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/TeeOutputStream.java delete mode 100644 test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/ExecutorTest.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpec.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMock.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockExit.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockSpec.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CompletableCommandMock.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIOException.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockingToolProvider.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/Script.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpec.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpecInDir.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCommandMock.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCompletableCommandMock.java create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/VerbatimCommandMock.java create mode 100644 test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LibProvidersLookupTest.java create mode 100644 test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxPackageArchTest.java create mode 100644 test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxSystemEnvironmentTest.java create mode 100644 test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java create mode 100644 test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgSystemEnvironmentTest.java create mode 100644 test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/ExecutorTest.java create mode 100644 test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java create mode 100644 test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java create mode 100644 test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTestUtils.java rename test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/{ => util}/EnquoterTest.java (57%) create mode 100644 test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RetryExecutorTest.java diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java index dbaa5e3eec6..523b6c4821c 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.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 @@ -50,6 +50,7 @@ import jdk.jpackage.internal.model.LinuxLauncher; import jdk.jpackage.internal.model.LinuxPackage; import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.util.CompositeProxy; +import jdk.jpackage.internal.util.Enquoter; import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.XmlUtils; diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LibProvidersLookup.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LibProvidersLookup.java index 55200b908cd..6faacbca528 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LibProvidersLookup.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LibProvidersLookup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, 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 @@ -27,13 +27,12 @@ package jdk.jpackage.internal; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; import java.util.Collection; -import java.util.Objects; import java.util.Collections; -import java.util.Set; -import java.util.ArrayList; import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -48,9 +47,6 @@ public final class LibProvidersLookup { return (new ToolValidator(TOOL_LDD).validate() == null); } - public LibProvidersLookup() { - } - LibProvidersLookup setPackageLookup(PackageLookup v) { packageLookup = v; return this; @@ -87,23 +83,20 @@ public final class LibProvidersLookup { } private static List getNeededLibsForFile(Path path) throws IOException { - List result = new ArrayList<>(); - int ret = Executor.of(TOOL_LDD, path.toString()).setOutputConsumer(lines -> { - lines.map(line -> { - Matcher matcher = LIB_IN_LDD_OUTPUT_REGEX.matcher(line); - if (matcher.find()) { - return matcher.group(1); - } - return null; - }).filter(Objects::nonNull).map(Path::of).forEach(result::add); - }).execute(); + final var result = Executor.of(TOOL_LDD, path.toString()).saveOutput().execute(); - if (ret != 0) { + if (result.getExitCode() != 0) { // objdump failed. This is OK if the tool was applied to not a binary file return Collections.emptyList(); } - return result; + return result.stdout().stream().map(line -> { + Matcher matcher = LIB_IN_LDD_OUTPUT_REGEX.matcher(line); + if (matcher.find()) { + return matcher.group(1); + } + return null; + }).filter(Objects::nonNull).map(Path::of).toList(); } private static Collection getNeededLibsForFiles(List paths) { diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.java index 6e438e66a26..d2169ede461 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxBundlingEnvironment.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 @@ -33,6 +33,7 @@ import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_LINUX_R import java.util.Map; import java.util.Optional; +import java.util.function.Supplier; import java.util.stream.Stream; import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.cli.StandardBundlingOperation; @@ -44,19 +45,34 @@ import jdk.jpackage.internal.util.Result; public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment { public LinuxBundlingEnvironment() { - super(build() - .defaultOperation(() -> { - return LazyLoad.SYS_ENV.value().map(LinuxSystemEnvironment::nativePackageType).map(DESCRIPTORS::get); - }) - .bundler(CREATE_LINUX_APP_IMAGE, LinuxBundlingEnvironment::createAppImage) - .bundler(CREATE_LINUX_DEB, LazyLoad::debSysEnv, LinuxBundlingEnvironment::createDebPackage) - .bundler(CREATE_LINUX_RPM, LazyLoad::rpmSysEnv, LinuxBundlingEnvironment::createRpmPackage)); + super(build().mutate(builder -> { + + // Wrap the generic Linux system environment supplier in the run-once wrapper + // as this supplier is called from both RPM and DEB Linux system environment suppliers. + var sysEnv = runOnce(() -> { + return LinuxSystemEnvironment.create(); + }); + + Supplier> debSysEnv = () -> { + return LinuxDebSystemEnvironment.create(sysEnv.get()); + }; + + Supplier> rpmSysEnv = () -> { + return LinuxRpmSystemEnvironment.create(sysEnv.get()); + }; + + builder.defaultOperation(() -> { + return sysEnv.get().value().map(LinuxSystemEnvironment::nativePackageType).map(DESCRIPTORS::get); + }) + .bundler(CREATE_LINUX_DEB, debSysEnv, LinuxBundlingEnvironment::createDebPackage) + .bundler(CREATE_LINUX_RPM, rpmSysEnv, LinuxBundlingEnvironment::createRpmPackage); + }).bundler(CREATE_LINUX_APP_IMAGE, LinuxBundlingEnvironment::createAppImage)); } private static void createDebPackage(Options options, LinuxDebSystemEnvironment sysEnv) { createNativePackage(options, - LinuxFromOptions.createLinuxDebPackage(options), + LinuxFromOptions.createLinuxDebPackage(options, sysEnv), buildEnv()::create, LinuxBundlingEnvironment::buildPipeline, (env, pkg, outputDir) -> { @@ -67,7 +83,7 @@ public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment { private static void createRpmPackage(Options options, LinuxRpmSystemEnvironment sysEnv) { createNativePackage(options, - LinuxFromOptions.createLinuxRpmPackage(options), + LinuxFromOptions.createLinuxRpmPackage(options, sysEnv), buildEnv()::create, LinuxBundlingEnvironment::buildPipeline, (env, pkg, outputDir) -> { @@ -90,23 +106,6 @@ public class LinuxBundlingEnvironment extends DefaultBundlingEnvironment { return new BuildEnvFromOptions().predefinedAppImageLayout(APPLICATION_LAYOUT); } - private static final class LazyLoad { - - static Result debSysEnv() { - return DEB_SYS_ENV; - } - - static Result rpmSysEnv() { - return RPM_SYS_ENV; - } - - private static final Result SYS_ENV = LinuxSystemEnvironment.create(); - - private static final Result DEB_SYS_ENV = LinuxDebSystemEnvironment.create(SYS_ENV); - - private static final Result RPM_SYS_ENV = LinuxRpmSystemEnvironment.create(SYS_ENV); - } - private static final Map DESCRIPTORS = Stream.of( CREATE_LINUX_DEB, CREATE_LINUX_RPM diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackager.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackager.java index 64a0368e9a0..0ec6a77e683 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackager.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackager.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. * 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 jdk.jpackage.internal; -import static jdk.jpackage.internal.model.StandardPackageType.LINUX_DEB; import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import java.io.IOException; @@ -76,11 +75,11 @@ final class LinuxDebPackager extends LinuxPackager { try { // Try the real path first as it works better on newer Ubuntu versions - return findProvidingPackages(realPath, sysEnv.dpkg()); + return findProvidingPackages(realPath, sysEnv); } catch (IOException ex) { // Try the default path if differ if (!realPath.equals(file)) { - return findProvidingPackages(file, sysEnv.dpkg()); + return findProvidingPackages(file, sysEnv); } else { throw ex; } @@ -107,7 +106,7 @@ final class LinuxDebPackager extends LinuxPackager { properties.forEach(property -> cmdline.add(property.name)); - Map actualValues = Executor.of(cmdline.toArray(String[]::new)) + Map actualValues = Executor.of(cmdline) .saveOutput(true) .executeExpectSuccess() .getOutput().stream() @@ -158,9 +157,8 @@ final class LinuxDebPackager extends LinuxPackager { cmdline.addAll(List.of("-b", env.appImageDir().toString(), debFile.toAbsolutePath().toString())); // run dpkg - RetryExecutor.retryOnKnownErrorMessage( - "semop(1): encountered an error: Invalid argument").execute( - cmdline.toArray(String[]::new)); + Executor.of(cmdline).retryOnKnownErrorMessage( + "semop(1): encountered an error: Invalid argument").execute(); Log.verbose(I18N.format("message.output-to-location", debFile.toAbsolutePath())); } @@ -233,7 +231,7 @@ final class LinuxDebPackager extends LinuxPackager { } } - private static Stream findProvidingPackages(Path file, Path dpkg) throws IOException { + private static Stream findProvidingPackages(Path file, LinuxDebSystemEnvironment sysEnv) throws IOException { // // `dpkg -S` command does glob pattern lookup. If not the absolute path // to the file is specified it might return mltiple package names. @@ -279,9 +277,9 @@ final class LinuxDebPackager extends LinuxPackager { Set archPackages = new HashSet<>(); Set otherPackages = new HashSet<>(); - var debArch = LinuxPackageArch.getValue(LINUX_DEB); + var debArch = sysEnv.packageArch().value(); - Executor.of(dpkg.toString(), "-S", file.toString()) + Executor.of(sysEnv.dpkg().toString(), "-S", file.toString()) .saveOutput(true).executeExpectSuccess() .getOutput().forEach(line -> { Matcher matcher = PACKAGE_NAME_REGEX.matcher(line); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironment.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironment.java index 5b5decb7a67..d5480361452 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironment.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironment.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,7 @@ import static jdk.jpackage.internal.LinuxSystemEnvironment.mixin; import jdk.jpackage.internal.util.Result; -public interface LinuxDebSystemEnvironment extends LinuxSystemEnvironment, LinuxDebSystemEnvironmentMixin { +interface LinuxDebSystemEnvironment extends LinuxSystemEnvironment, LinuxDebSystemEnvironmentMixin { static Result create(Result base) { return mixin(LinuxDebSystemEnvironment.class, base, LinuxDebSystemEnvironmentMixin::create); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironmentMixin.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironmentMixin.java index 8688327b353..2cf3e9e36e8 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironmentMixin.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebSystemEnvironmentMixin.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 @@ -29,7 +29,7 @@ import java.util.Objects; import java.util.stream.Stream; import jdk.jpackage.internal.util.Result; -public interface LinuxDebSystemEnvironmentMixin { +interface LinuxDebSystemEnvironmentMixin { Path dpkg(); Path dpkgdeb(); Path fakeroot(); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java index 799c92ce2e1..0791c79c662 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.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 @@ -70,9 +70,9 @@ final class LinuxFromOptions { return LinuxApplication.create(appBuilder.create()); } - static LinuxRpmPackage createLinuxRpmPackage(Options options) { + static LinuxRpmPackage createLinuxRpmPackage(Options options, LinuxRpmSystemEnvironment sysEnv) { - final var superPkgBuilder = createLinuxPackageBuilder(options, LINUX_RPM); + final var superPkgBuilder = createLinuxPackageBuilder(options, sysEnv, LINUX_RPM); final var pkgBuilder = new LinuxRpmPackageBuilder(superPkgBuilder); @@ -81,9 +81,9 @@ final class LinuxFromOptions { return pkgBuilder.create(); } - static LinuxDebPackage createLinuxDebPackage(Options options) { + static LinuxDebPackage createLinuxDebPackage(Options options, LinuxDebSystemEnvironment sysEnv) { - final var superPkgBuilder = createLinuxPackageBuilder(options, LINUX_DEB); + final var superPkgBuilder = createLinuxPackageBuilder(options, sysEnv, LINUX_DEB); final var pkgBuilder = new LinuxDebPackageBuilder(superPkgBuilder); @@ -99,7 +99,7 @@ final class LinuxFromOptions { return pkg; } - private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, StandardPackageType type) { + private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, LinuxSystemEnvironment sysEnv, StandardPackageType type) { final var app = createLinuxApplication(options); @@ -107,6 +107,8 @@ final class LinuxFromOptions { final var pkgBuilder = new LinuxPackageBuilder(superPkgBuilder); + pkgBuilder.arch(sysEnv.packageArch()); + LINUX_PACKAGE_DEPENDENCIES.ifPresentIn(options, pkgBuilder::additionalDependencies); LINUX_APP_CATEGORY.ifPresentIn(options, pkgBuilder::category); LINUX_MENU_GROUP.ifPresentIn(options, pkgBuilder::menuGroupName); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java index b14404d67b1..40ff26bcfac 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxLaunchersAsServices.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,6 +32,7 @@ import java.util.List; import java.util.Map; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.Package; +import jdk.jpackage.internal.util.Enquoter; /** * Helper to install launchers as services using "systemd". diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageArch.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageArch.java index 836d1fb2c37..b1df92ae312 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageArch.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageArch.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,18 +25,20 @@ package jdk.jpackage.internal; import java.io.IOException; -import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; +import java.util.ArrayList; import jdk.jpackage.internal.model.StandardPackageType; +import jdk.jpackage.internal.util.CommandOutputControl; +import jdk.jpackage.internal.util.Result; -final class LinuxPackageArch { +record LinuxPackageArch(String value) { - static String getValue(StandardPackageType pkgType) { + static Result create(StandardPackageType pkgType) { switch (pkgType) { case LINUX_RPM -> { - return RpmPackageArch.VALUE; + return rpm().map(LinuxPackageArch::new); } case LINUX_DEB -> { - return DebPackageArch.VALUE; + return deb().map(LinuxPackageArch::new); } default -> { throw new IllegalArgumentException(); @@ -44,62 +46,51 @@ final class LinuxPackageArch { } } - private static class DebPackageArch { - - static final String VALUE = toSupplier(DebPackageArch::getValue).get(); - - private static String getValue() throws IOException { - return Executor.of("dpkg", "--print-architecture").saveOutput(true) - .executeExpectSuccess().getOutput().get(0); - } + private static Result deb() { + var exec = Executor.of("dpkg", "--print-architecture").saveOutput(true); + return Result.of(exec::executeExpectSuccess, IOException.class) + .flatMap(LinuxPackageArch::getStdoutFirstLine); } - private static class RpmPackageArch { - - /* - * Various ways to get rpm arch. Needed to address JDK-8233143. rpmbuild is mandatory for - * rpm packaging, try it first. rpm is optional and may not be available, use as the last - * resort. - */ - private static enum RpmArchReader { - Rpmbuild("rpmbuild", "--eval=%{_target_cpu}"), - Rpm("rpm", "--eval=%{_target_cpu}"); - - RpmArchReader(String... cmdline) { - this.cmdline = cmdline; + private static Result rpm() { + var errors = new ArrayList(); + for (var tool : RpmArchReader.values()) { + var result = tool.getRpmArch(); + if (result.hasValue()) { + return result; + } else { + errors.addAll(result.errors()); } - - String getRpmArch() throws IOException { - Executor exec = Executor.of(cmdline).saveOutput(true); - switch (this) { - case Rpm -> { - exec.executeExpectSuccess(); - } - case Rpmbuild -> { - if (exec.execute() != 0) { - return null; - } - } - default -> { - throw new UnsupportedOperationException(); - } - } - return exec.getOutput().get(0); - } - - private final String[] cmdline; } - static final String VALUE = toSupplier(RpmPackageArch::getValue).get(); + return Result.ofErrors(errors); + } - private static String getValue() throws IOException { - for (var rpmArchReader : RpmArchReader.values()) { - var rpmArchStr = rpmArchReader.getRpmArch(); - if (rpmArchStr != null) { - return rpmArchStr; - } - } - throw new RuntimeException("error.rpm-arch-not-detected"); + /* + * Various ways to get rpm arch. Needed to address JDK-8233143. rpmbuild is mandatory for + * rpm packaging, try it first. rpm is optional and may not be available, use as the last + * resort. + */ + private enum RpmArchReader { + RPMBUILD("rpmbuild", "--eval=%{_target_cpu}"), + RPM("rpm", "--eval=%{_target_cpu}"); + + RpmArchReader(String... cmdline) { + this.cmdline = cmdline; } + + Result getRpmArch() { + var exec = Executor.of(cmdline).saveOutput(true); + return Result.of(exec::executeExpectSuccess, IOException.class) + .flatMap(LinuxPackageArch::getStdoutFirstLine); + } + + private final String[] cmdline; + } + + private static Result getStdoutFirstLine(CommandOutputControl.Result result) { + return Result.of(() -> { + return result.stdout().stream().findFirst().orElseThrow(result::unexpected); + }, IOException.class); } } diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java index bc7c301ace2..cd4d674432e 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackageBuilder.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 @@ -83,7 +83,7 @@ final class LinuxPackageBuilder { category(), Optional.ofNullable(additionalDependencies), release(), - pkg.asStandardPackageType().map(LinuxPackageArch::getValue).orElseThrow())); + arch.value())); } LinuxPackageBuilder literalName(String v) { @@ -119,6 +119,11 @@ final class LinuxPackageBuilder { return Optional.ofNullable(release); } + LinuxPackageBuilder arch(LinuxPackageArch v) { + arch = v; + return this; + } + private static LinuxApplicationLayout usrTreePackageLayout(Path prefix, String packageName) { final var lib = prefix.resolve(Path.of("lib", packageName)); return LinuxApplicationLayout.create( @@ -184,6 +189,7 @@ final class LinuxPackageBuilder { private String category; private String additionalDependencies; private String release; + private LinuxPackageArch arch; private final PackageBuilder pkgBuilder; diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironment.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironment.java index 58c10668227..e56551be325 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironment.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironment.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,7 @@ import static jdk.jpackage.internal.LinuxSystemEnvironment.mixin; import jdk.jpackage.internal.util.Result; -public interface LinuxRpmSystemEnvironment extends LinuxSystemEnvironment, LinuxRpmSystemEnvironmentMixin { +interface LinuxRpmSystemEnvironment extends LinuxSystemEnvironment, LinuxRpmSystemEnvironmentMixin { static Result create(Result base) { return mixin(LinuxRpmSystemEnvironment.class, base, LinuxRpmSystemEnvironmentMixin::create); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironmentMixin.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironmentMixin.java index b741495f5ed..4cbd3ce4a9c 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironmentMixin.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmSystemEnvironmentMixin.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 @@ -32,7 +32,7 @@ import java.util.stream.Stream; import jdk.jpackage.internal.model.DottedVersion; import jdk.jpackage.internal.util.Result; -public interface LinuxRpmSystemEnvironmentMixin { +interface LinuxRpmSystemEnvironmentMixin { Path rpm(); Path rpmbuild(); diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxSystemEnvironment.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxSystemEnvironment.java index 1a70cc938b8..e347c58ae21 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxSystemEnvironment.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxSystemEnvironment.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,6 @@ package jdk.jpackage.internal; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; import jdk.jpackage.internal.model.PackageType; @@ -35,9 +34,10 @@ import jdk.jpackage.internal.model.StandardPackageType; import jdk.jpackage.internal.util.CompositeProxy; import jdk.jpackage.internal.util.Result; -public interface LinuxSystemEnvironment extends SystemEnvironment { +interface LinuxSystemEnvironment extends SystemEnvironment { boolean soLookupAvailable(); PackageType nativePackageType(); + LinuxPackageArch packageArch(); static Result create() { return detectNativePackageType().map(LinuxSystemEnvironment::create).orElseGet(() -> { @@ -45,7 +45,7 @@ public interface LinuxSystemEnvironment extends SystemEnvironment { }); } - static Optional detectNativePackageType() { + static Optional detectNativePackageType() { if (Internal.isDebian()) { return Optional.of(StandardPackageType.LINUX_DEB); } else if (Internal.isRpm()) { @@ -55,13 +55,14 @@ public interface LinuxSystemEnvironment extends SystemEnvironment { } } - static Result create(PackageType nativePackageType) { - return Result.ofValue(new Stub(LibProvidersLookup.supported(), - Objects.requireNonNull(nativePackageType))); + static Result create(StandardPackageType nativePackageType) { + return LinuxPackageArch.create(nativePackageType).map(arch -> { + return new Stub(LibProvidersLookup.supported(), nativePackageType, arch); + }); } static U createWithMixin(Class type, LinuxSystemEnvironment base, T mixin) { - return CompositeProxy.create(type, base, mixin); + return CompositeProxy.build().invokeTunnel(CompositeProxyTunnel.INSTANCE).create(type, base, mixin); } static Result mixin(Class type, @@ -79,7 +80,7 @@ public interface LinuxSystemEnvironment extends SystemEnvironment { } } - record Stub(boolean soLookupAvailable, PackageType nativePackageType) implements LinuxSystemEnvironment { + record Stub(boolean soLookupAvailable, PackageType nativePackageType, LinuxPackageArch packageArch) implements LinuxSystemEnvironment { } static final class Internal { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java index 71f87dd8705..81e04ad7ed1 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.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 @@ -48,6 +48,7 @@ import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.MacApplication; import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.util.PathUtils; +import jdk.jpackage.internal.util.Result; import jdk.jpackage.internal.util.function.ExceptionBox; @@ -188,11 +189,9 @@ final class AppImageSigner { } private static boolean isXcodeDevToolsInstalled() { - try { - return Executor.of("/usr/bin/xcrun", "--help").setQuiet(true).execute() == 0; - } catch (IOException ex) { - return false; - } + return Result.of( + Executor.of("/usr/bin/xcrun", "--help").setQuiet(true)::executeExpectSuccess, + IOException.class).hasValue(); } private static void unsign(Path path) throws IOException { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/Codesign.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/Codesign.java index 920b75df398..a7cd17b06b9 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/Codesign.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/Codesign.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 @@ -34,7 +34,6 @@ import java.util.Objects; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Supplier; -import java.util.stream.Stream; public final class Codesign { @@ -94,14 +93,12 @@ public final class Codesign { public void applyTo(Path path) throws IOException, CodesignException { - var exec = Executor.of(Stream.concat( - cmdline.stream(), - Stream.of(path.toString())).toArray(String[]::new) - ).saveOutput(true); + var exec = Executor.of(cmdline).args(path.toString()).saveOutput(true); configureExecutor.ifPresent(configure -> configure.accept(exec)); - if (exec.execute() != 0) { - throw new CodesignException(exec.getOutput().toArray(String[]::new)); + var result = exec.execute(); + if (result.getExitCode() != 0) { + throw new CodesignException(result.getOutput().toArray(String[]::new)); } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java index 3cecb2cd243..0531559e052 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.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,6 @@ import java.util.Optional; import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.model.MacPackage; import jdk.jpackage.internal.model.Package; -import jdk.jpackage.internal.util.Result; public class MacBundlingEnvironment extends DefaultBundlingEnvironment { @@ -45,7 +44,7 @@ public class MacBundlingEnvironment extends DefaultBundlingEnvironment { .defaultOperation(CREATE_MAC_DMG) .bundler(SIGN_MAC_APP_IMAGE, MacBundlingEnvironment::signAppImage) .bundler(CREATE_MAC_APP_IMAGE, MacBundlingEnvironment::createAppImage) - .bundler(CREATE_MAC_DMG, LazyLoad::dmgSysEnv, MacBundlingEnvironment::createDmdPackage) + .bundler(CREATE_MAC_DMG, MacDmgSystemEnvironment::create, MacBundlingEnvironment::createDmdPackage) .bundler(CREATE_MAC_PKG, MacBundlingEnvironment::createPkgPackage)); } @@ -98,13 +97,4 @@ public class MacBundlingEnvironment extends DefaultBundlingEnvironment { .predefinedAppImageLayout(APPLICATION_LAYOUT) .predefinedRuntimeImageLayout(MacPackage::guessRuntimeLayout); } - - private static final class LazyLoad { - - static Result dmgSysEnv() { - return DMG_SYS_ENV; - } - - private static final Result DMG_SYS_ENV = MacDmgSystemEnvironment.create(); - } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificateUtils.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificateUtils.java index fe593e347fc..24a236ae15d 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificateUtils.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacCertificateUtils.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,7 +53,7 @@ public final class MacCertificateUtils { keychain.map(Keychain::asCliArg).ifPresent(args::add); return toSupplier(() -> { - final var output = Executor.of(args.toArray(String[]::new)) + final var output = Executor.of(args) .setQuiet(true).saveOutput(true).executeExpectSuccess() .getOutput(); diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java index 4ccc459109f..20a687487ef 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.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 @@ -33,11 +33,15 @@ import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.function.Function; import jdk.jpackage.internal.PackagingPipeline.PackageTaskID; import jdk.jpackage.internal.PackagingPipeline.TaskID; import jdk.jpackage.internal.model.MacDmgPackage; @@ -105,6 +109,10 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, return env.configDir().resolve(pkg.app().name() + "-license.plist"); } + private Path finalDmg() { + return outputDir.resolve(pkg.packageFileNameWithSuffix()); + } + Path protoDmg() { return dmgWorkdir().resolve("proto.dmg"); } @@ -128,6 +136,10 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, } } + private Executor hdiutil(String... args) { + return Executor.of(sysEnv.hdiutil().toString()).args(args).storeOutputInFiles(); + } + private void prepareDMGSetupScript() throws IOException { Path dmgSetup = volumeScript(); Log.verbose(MessageFormat.format( @@ -211,13 +223,17 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, } } + private String hdiUtilVerbosityFlag() { + return env.verbose() ? "-verbose" : "-quiet"; + } + private void buildDMG() throws IOException { boolean copyAppImage = false; - Path protoDMG = protoDmg(); - Path finalDMG = outputDir.resolve(pkg.packageFileNameWithSuffix()); + final Path protoDMG = protoDmg(); + final Path finalDMG = finalDmg(); - Path srcFolder = env.appImageDir(); + final Path srcFolder = env.appImageDir(); Log.verbose(MessageFormat.format(I18N.getString( "message.creating-dmg-file"), finalDMG.toAbsolutePath())); @@ -233,21 +249,17 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, Files.createDirectories(protoDMG.getParent()); Files.createDirectories(finalDMG.getParent()); - String hdiUtilVerbosityFlag = env.verbose() ? - "-verbose" : "-quiet"; + final String hdiUtilVerbosityFlag = hdiUtilVerbosityFlag(); // create temp image - ProcessBuilder pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "create", - hdiUtilVerbosityFlag, - "-srcfolder", normalizedAbsolutePathString(srcFolder), - "-volname", volumeName(), - "-ov", normalizedAbsolutePathString(protoDMG), - "-fs", "HFS+", - "-format", "UDRW"); try { - IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT); + hdiutil("create", + hdiUtilVerbosityFlag, + "-srcfolder", normalizedAbsolutePathString(srcFolder), + "-volname", volumeName(), + "-ov", normalizedAbsolutePathString(protoDMG), + "-fs", "HFS+", + "-format", "UDRW").executeExpectSuccess(); } catch (IOException ex) { Log.verbose(ex); // Log exception @@ -260,31 +272,26 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, // not be bigger, but it will able to hold additional 50 megabytes of data. // We need extra room for icons and background image. When we providing // actual files to hdiutil, it will create DMG with ~50 megabytes extra room. - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "create", - hdiUtilVerbosityFlag, - "-size", String.valueOf(size), - "-volname", volumeName(), - "-ov", normalizedAbsolutePathString(protoDMG), - "-fs", "HFS+"); - new RetryExecutor() - .setMaxAttemptsCount(10) - .setAttemptTimeoutMillis(3000) - .setWriteOutputToFile(true) - .execute(pb); + hdiutil( + "create", + hdiUtilVerbosityFlag, + "-size", String.valueOf(size), + "-volname", volumeName(), + "-ov", normalizedAbsolutePathString(protoDMG), + "-fs", "HFS+" + ).retry() + .setMaxAttemptsCount(10) + .setAttemptTimeout(3, TimeUnit.SECONDS) + .execute(); } + final Path mountedVolume = volumePath(); + // mount temp image - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "attach", + hdiutil("attach", normalizedAbsolutePathString(protoDMG), hdiUtilVerbosityFlag, - "-mountroot", protoDMG.getParent().toString()); - IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT); - - final Path mountedVolume = volumePath(); + "-mountroot", mountedVolume.getParent().toString()).executeExpectSuccess(); // Copy app image, since we did not create DMG with it, but instead we created // empty one. @@ -302,9 +309,13 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, // to install-dir in DMG as critical error, since it can fail in // headless environment. try { - pb = new ProcessBuilder(sysEnv.osascript().toString(), - normalizedAbsolutePathString(volumeScript())); - IOUtils.exec(pb, 180); // Wait 3 minutes. See JDK-8248248. + Executor.of( + sysEnv.osascript().toString(), + normalizedAbsolutePathString(volumeScript()) + ) + // Wait 3 minutes. See JDK-8248248. + .timeout(3, TimeUnit.MINUTES) + .executeExpectSuccess(); } catch (IOException ex) { Log.verbose(ex); } @@ -325,18 +336,18 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, // but it seems Finder excepts these bytes to be // "icnC" for the volume icon // (might not work on Mac 10.13 with old XCode) - pb = new ProcessBuilder( + Executor.of( sysEnv.setFileUtility().orElseThrow().toString(), "-c", "icnC", - normalizedAbsolutePathString(volumeIconFile)); - IOUtils.exec(pb); + normalizedAbsolutePathString(volumeIconFile) + ).executeExpectSuccess(); volumeIconFile.toFile().setReadOnly(); - pb = new ProcessBuilder( + Executor.of( sysEnv.setFileUtility().orElseThrow().toString(), "-a", "C", - normalizedAbsolutePathString(mountedVolume)); - IOUtils.exec(pb); + normalizedAbsolutePathString(mountedVolume) + ).executeExpectSuccess(); } catch (IOException ex) { Log.error(ex.getMessage()); Log.verbose("Cannot enable custom icon using SetFile utility"); @@ -347,85 +358,23 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, } finally { // Detach the temporary image - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "detach", - hdiUtilVerbosityFlag, - normalizedAbsolutePathString(mountedVolume)); - // "hdiutil detach" might not work right away due to resource busy error, so - // repeat detach several times. - RetryExecutor retryExecutor = new RetryExecutor(); - // Image can get detach even if we got resource busy error, so stop - // trying to detach it if it is no longer attached. - retryExecutor.setExecutorInitializer(exec -> { - if (!Files.exists(mountedVolume)) { - retryExecutor.abort(); - } - }); - try { - // 10 times with 6 second delays. - retryExecutor.setMaxAttemptsCount(10).setAttemptTimeoutMillis(6000) - .execute(pb); - } catch (IOException ex) { - if (!retryExecutor.isAborted()) { - // Now force to detach if it still attached - if (Files.exists(mountedVolume)) { - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "detach", - "-force", - hdiUtilVerbosityFlag, - normalizedAbsolutePathString(mountedVolume)); - IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT); - } - } - } + detachVolume(); } // Compress it to a new image - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "convert", - normalizedAbsolutePathString(protoDMG), - hdiUtilVerbosityFlag, - "-format", "UDZO", - "-o", normalizedAbsolutePathString(finalDMG)); - try { - new RetryExecutor() - .setMaxAttemptsCount(10) - .setAttemptTimeoutMillis(3000) - .execute(pb); - } catch (Exception ex) { - // Convert might failed if something holds file. Try to convert copy. - Path protoCopyDMG = protoCopyDmg(); - Files.copy(protoDMG, protoCopyDMG); - try { - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), - "convert", - normalizedAbsolutePathString(protoCopyDMG), - hdiUtilVerbosityFlag, - "-format", "UDZO", - "-o", normalizedAbsolutePathString(finalDMG)); - IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT); - } finally { - Files.deleteIfExists(protoCopyDMG); - } - } + convertProtoDmg(); //add license if needed if (pkg.licenseFile().isPresent()) { - pb = new ProcessBuilder( - sysEnv.hdiutil().toString(), + hdiutil( "udifrez", normalizedAbsolutePathString(finalDMG), "-xml", normalizedAbsolutePathString(licenseFile()) - ); - new RetryExecutor() - .setMaxAttemptsCount(10) - .setAttemptTimeoutMillis(3000) - .execute(pb); + ).retry() + .setMaxAttemptsCount(10) + .setAttemptTimeout(3, TimeUnit.SECONDS) + .execute(); } try { @@ -441,6 +390,69 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, } + private void detachVolume() throws IOException { + var mountedVolume = volumePath(); + + // "hdiutil detach" might not work right away due to resource busy error, so + // repeat detach several times. + Globals.instance().objectFactory().retryExecutor(IOException.class).setExecutable(context -> { + + List cmdline = new ArrayList<>(); + cmdline.add("detach"); + + if (context.isLastAttempt()) { + // The last attempt, force detach. + cmdline.add("-force"); + } + + cmdline.addAll(List.of( + hdiUtilVerbosityFlag(), + normalizedAbsolutePathString(mountedVolume) + )); + + // The image can get detached even if we get a resource busy error, + // so execute the detach command without checking the exit code. + var result = hdiutil(cmdline.toArray(String[]::new)).execute(); + + if (result.getExitCode() == 0 || !Files.exists(mountedVolume)) { + // Detached successfully! + return null; + } else { + throw result.unexpected(); + } + }).setMaxAttemptsCount(10).setAttemptTimeout(6, TimeUnit.SECONDS).execute(); + } + + private void convertProtoDmg() throws IOException { + + Function convert = srcDmg -> { + return hdiutil( + "convert", + normalizedAbsolutePathString(srcDmg), + hdiUtilVerbosityFlag(), + "-format", "UDZO", + "-o", normalizedAbsolutePathString(finalDmg())); + }; + + // Convert it to a new image. + try { + convert.apply(protoDmg()).retry() + .setMaxAttemptsCount(10) + .setAttemptTimeout(3, TimeUnit.SECONDS) + .execute(); + } catch (IOException ex) { + Log.verbose(ex); + // Something holds the file, try to convert a copy. + Path copyDmg = protoCopyDmg(); + Files.copy(protoDmg(), copyDmg); + try { + convert.apply(copyDmg).executeExpectSuccess(); + } finally { + Files.deleteIfExists(copyDmg); + } + } + } + // Background image name in resources private static final String DEFAULT_BACKGROUND_IMAGE = "background_dmg.tiff"; private static final String DEFAULT_DMG_SETUP_SCRIPT = "DMGsetup.scpt"; diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgSystemEnvironment.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgSystemEnvironment.java index 54eb0c6f4fe..12d105b99b5 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgSystemEnvironment.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgSystemEnvironment.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,11 @@ package jdk.jpackage.internal; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.jpackage.internal.util.Result; @@ -54,41 +55,31 @@ record MacDmgSystemEnvironment(Path hdiutil, Path osascript, Optional setF // Location of SetFile utility may be different depending on MacOS version // We look for several known places and if none of them work will // try to find it - private static Optional findSetFileUtility() { - String typicalPaths[] = {"/Developer/Tools/SetFile", - "/usr/bin/SetFile", "/Developer/usr/bin/SetFile"}; + static Optional findSetFileUtility() { + return SETFILE_KNOWN_PATHS.stream().filter(setFilePath -> { + // Validate SetFile, if Xcode is not installed it will run, but exit with error code + return Result.of( + Executor.of(setFilePath.toString(), "-h").setQuiet(true)::executeExpectSuccess, + IOException.class).hasValue(); + }).findFirst().or(() -> { + // generic find attempt + final var executor = Executor.of("/usr/bin/xcrun", "-find", "SetFile").setQuiet(true).saveFirstLineOfOutput(); - final var setFilePath = Stream.of(typicalPaths).map(Path::of).filter(Files::isExecutable).findFirst(); - if (setFilePath.isPresent()) { - // Validate SetFile, if Xcode is not installed it will run, but exit with error - // code - try { - if (Executor.of(setFilePath.orElseThrow().toString(), "-h").setQuiet(true).execute() == 0) { - return setFilePath; - } - } catch (Exception ignored) { - // No need for generic find attempt. We found it, but it does not work. - // Probably due to missing xcode. - return Optional.empty(); - } - } - - // generic find attempt - try { - final var executor = Executor.of("/usr/bin/xcrun", "-find", "SetFile"); - final var code = executor.setQuiet(true).saveOutput(true).execute(); - if (code == 0 && !executor.getOutput().isEmpty()) { - final var firstLine = executor.getOutput().getFirst(); - Path f = Path.of(firstLine); - if (new ToolValidator(f).checkExistsOnly().validate() == null) { - return Optional.of(f.toAbsolutePath()); - } - } - } catch (IOException ignored) {} - - return Optional.empty(); + return Result.of(executor::executeExpectSuccess, IOException.class).flatMap(execResult -> { + return Result.of(() -> { + return execResult.stdout().stream().findFirst().map(Path::of).orElseThrow(execResult::unexpected); + }, Exception.class); + }).value().filter(v -> { + return new ToolValidator(v).checkExistsOnly().validate() == null; + }).map(Path::toAbsolutePath); + }); } + static final List SETFILE_KNOWN_PATHS = Stream.of( + "/Developer/Tools/SetFile", + "/usr/bin/SetFile", + "/Developer/usr/bin/SetFile").map(Path::of).collect(Collectors.toUnmodifiableList()); + private static final Path HDIUTIL = Path.of("/usr/bin/hdiutil"); private static final Path OSASCRIPT = Path.of("/usr/bin/osascript"); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java index 127b3232661..126248e2330 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.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 @@ -32,7 +32,6 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; import java.net.URISyntaxException; -import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.text.MessageFormat; @@ -57,6 +56,7 @@ import jdk.jpackage.internal.PackagingPipeline.PackageTaskID; import jdk.jpackage.internal.PackagingPipeline.TaskID; import jdk.jpackage.internal.model.MacPkgPackage; import jdk.jpackage.internal.resources.ResourceLocator; +import jdk.jpackage.internal.util.Enquoter; import jdk.jpackage.internal.util.XmlUtils; import org.xml.sax.SAXException; @@ -108,7 +108,7 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional servic cmdline.addAll(allPkgbuildArgs()); try { Files.createDirectories(path.getParent()); - IOUtils.exec(new ProcessBuilder(cmdline), false, null, true, Executor.INFINITE_TIMEOUT); + Executor.of(cmdline).executeExpectSuccess(); } catch (IOException ex) { throw new UncheckedIOException(ex); } @@ -487,15 +487,13 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional servic Files.createDirectories(cpl.getParent()); - final var pb = new ProcessBuilder("/usr/bin/pkgbuild", + Executor.of("/usr/bin/pkgbuild", "--root", normalizedAbsolutePathString(env.appImageDir()), "--install-location", normalizedAbsolutePathString(installLocation()), "--analyze", - normalizedAbsolutePathString(cpl)); - - IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT); + normalizedAbsolutePathString(cpl)).executeExpectSuccess(); patchCPLFile(cpl); } @@ -544,8 +542,7 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional servic } commandLine.add(normalizedAbsolutePathString(finalPkg)); - final var pb = new ProcessBuilder(commandLine); - IOUtils.exec(pb, false, null, true, Executor.INFINITE_TIMEOUT); + Executor.of(commandLine).executeExpectSuccess(); } private static Optional createServices(BuildEnv env, MacPkgPackage pkg) { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/TempKeychain.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/TempKeychain.java index b38faecd96f..2f616aafba1 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/TempKeychain.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/TempKeychain.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,15 +27,17 @@ package jdk.jpackage.internal; import java.io.Closeable; import java.io.IOException; +import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.function.Consumer; import jdk.internal.util.OSVersion; -import jdk.jpackage.internal.util.function.ThrowingConsumer; final class TempKeychain implements Closeable { - static void withKeychains(ThrowingConsumer, ? extends Exception> keychainConsumer, List keychains) throws Exception { + static void withKeychains(Consumer> keychainConsumer, List keychains) { + keychains.forEach(Objects::requireNonNull); if (keychains.isEmpty() || OSVersion.current().compareTo(new OSVersion(10, 12)) < 0) { keychainConsumer.accept(keychains); @@ -43,11 +45,14 @@ final class TempKeychain implements Closeable { // we need this for OS X 10.12+ try (var tempKeychain = new TempKeychain(keychains)) { keychainConsumer.accept(tempKeychain.keychains); + } catch (IOException ex) { + throw new UncheckedIOException(ex); } } } - static void withKeychain(ThrowingConsumer keychainConsumer, Keychain keychain) throws Exception { + static void withKeychain(Consumer keychainConsumer, Keychain keychain) { + Objects.requireNonNull(keychainConsumer); withKeychains(keychains -> { keychainConsumer.accept(keychains.getFirst()); @@ -78,7 +83,7 @@ final class TempKeychain implements Closeable { args.addAll(missingKeychains.stream().map(Keychain::asCliArg).toList()); - Executor.of(args.toArray(String[]::new)).executeExpectSuccess(); + Executor.of(args).executeExpectSuccess(); } } @@ -89,7 +94,7 @@ final class TempKeychain implements Closeable { @Override public void close() throws IOException { if (!restoreKeychainsCmd.isEmpty()) { - Executor.of(restoreKeychainsCmd.toArray(String[]::new)).executeExpectSuccess(); + Executor.of(restoreKeychainsCmd).executeExpectSuccess(); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java index 05e080f240a..e4473b1e5ce 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.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,10 +65,10 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { Map>>> bundlers) { this.bundlers = bundlers.entrySet().stream().collect(toMap(Map.Entry::getKey, e -> { - return new CachingSupplier<>(e.getValue()); + return runOnce(e.getValue()); })); - this.defaultOperationSupplier = Objects.requireNonNull(defaultOperationSupplier).map(CachingSupplier::new); + this.defaultOperationSupplier = Objects.requireNonNull(defaultOperationSupplier).map(DefaultBundlingEnvironment::runOnce); } @@ -98,6 +98,11 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { return bundler(op, () -> Result.ofValue(bundler)); } + Builder mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + private Supplier> defaultOperationSupplier; private final Map>>> bundlers = new HashMap<>(); } @@ -107,6 +112,10 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { return new Builder(); } + static Supplier runOnce(Supplier supplier) { + return new CachingSupplier<>(supplier); + } + static Supplier>> createBundlerSupplier( Supplier> sysEnvResultSupplier, BiConsumer bundler) { Objects.requireNonNull(sysEnvResultSupplier); @@ -279,5 +288,5 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { private final Map>>> bundlers; - private final Optional>> defaultOperationSupplier; + private final Optional>> defaultOperationSupplier; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java index dd1cc4a24b4..ca7a630b6d1 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.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,53 +25,153 @@ package jdk.jpackage.internal; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.file.Files; -import java.nio.file.Path; +import java.io.PrintStream; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -import java.util.function.Supplier; +import java.util.function.UnaryOperator; +import java.util.spi.ToolProvider; import java.util.stream.Stream; +import jdk.jpackage.internal.util.CommandLineFormat; +import jdk.jpackage.internal.util.CommandOutputControl; +import jdk.jpackage.internal.util.CommandOutputControl.ProcessAttributes; +import jdk.jpackage.internal.util.CommandOutputControl.Result; +import jdk.jpackage.internal.util.RetryExecutor; +import jdk.jpackage.internal.util.function.ExceptionBox; -public final class Executor { +final class Executor { - Executor() { + static Executor of(String... cmdline) { + return of(List.of(cmdline)); } - Executor setOutputConsumer(Consumer> v) { - outputConsumer = v; - return this; + static Executor of(List cmdline) { + return of(new ProcessBuilder(cmdline)); + } + + static Executor of(ProcessBuilder pb) { + return Globals.instance().objectFactory().executor().processBuilder(pb); + } + + public Executor() { + commandOutputControl = new CommandOutputControl(); + args = new ArrayList<>(); + } + + private Executor(Executor other) { + commandOutputControl = other.commandOutputControl.copy(); + quietCommand = other.quietCommand; + args = new ArrayList<>(other.args); + processBuilder = other.processBuilder; + toolProvider = other.toolProvider; + timeout = other.timeout; + mapper = other.mapper; } Executor saveOutput(boolean v) { - saveOutput = v; + commandOutputControl.saveOutput(v); return this; } - Executor setWriteOutputToFile(boolean v) { - writeOutputToFile = v; + Executor saveOutput() { + return saveOutput(true); + } + + Executor saveFirstLineOfOutput() { + commandOutputControl.saveFirstLineOfOutput(); return this; } - Executor setTimeout(long v) { + Executor charset(Charset v) { + commandOutputControl.charset(v); + return this; + } + + Executor storeOutputInFiles(boolean v) { + commandOutputControl.storeOutputInFiles(v); + return this; + } + + Executor storeOutputInFiles() { + return storeOutputInFiles(true); + } + + Executor binaryOutput(boolean v) { + commandOutputControl.binaryOutput(v); + return this; + } + + Executor binaryOutput() { + return binaryOutput(true); + } + + Executor discardStdout(boolean v) { + commandOutputControl.discardStdout(v); + return this; + } + + Executor discardStdout() { + return discardStdout(true); + } + + Executor discardStderr(boolean v) { + commandOutputControl.discardStderr(v); + return this; + } + + Executor discardStderr() { + return discardStderr(true); + } + + Executor timeout(long v, TimeUnit unit) { + return timeout(Duration.of(v, unit.toChronoUnit())); + } + + Executor timeout(Duration v) { timeout = v; - if (timeout != INFINITE_TIMEOUT) { - // Redirect output to file if timeout is requested, otherwise we will - // reading until process ends and timeout will never be reached. - setWriteOutputToFile(true); - } return this; } - Executor setProcessBuilder(ProcessBuilder v) { - pb = v; + Executor toolProvider(ToolProvider v) { + toolProvider = Objects.requireNonNull(v); + processBuilder = null; return this; } - Executor setCommandLine(String... cmdline) { - return setProcessBuilder(new ProcessBuilder(cmdline)); + Optional toolProvider() { + return Optional.ofNullable(toolProvider); + } + + Executor processBuilder(ProcessBuilder v) { + processBuilder = Objects.requireNonNull(v); + toolProvider = null; + return this; + } + + Optional processBuilder() { + return Optional.ofNullable(processBuilder); + } + + Executor args(List v) { + args.addAll(v); + return this; + } + + Executor args(String... args) { + return args(List.of(args)); + } + + List args() { + return args; } Executor setQuiet(boolean v) { @@ -79,159 +179,207 @@ public final class Executor { return this; } - List getOutput() { - return output; - } - - Executor executeExpectSuccess() throws IOException { - int ret = execute(); - if (0 != ret) { - throw new IOException( - String.format("Command %s exited with %d code", - createLogMessage(pb, false), ret)); - } + Executor mapper(UnaryOperator v) { + mapper = v; return this; } - int execute() throws IOException { - output = null; + Optional> mapper() { + return Optional.ofNullable(mapper); + } - boolean needProcessOutput = outputConsumer != null || Log.isVerbose() || saveOutput; - Path outputFile = null; - if (needProcessOutput) { - pb.redirectErrorStream(true); - if (writeOutputToFile) { - outputFile = Files.createTempFile("jpackageOutputTempFile", ".tmp"); - pb.redirectOutput(outputFile.toFile()); + Executor copy() { + return new Executor(this); + } + + Result execute() throws IOException { + if (mapper != null) { + var mappedExecutor = Objects.requireNonNull(mapper.apply(this)); + if (mappedExecutor != this) { + return mappedExecutor.execute(); } + } + + var coc = commandOutputControl.copy(); + + final CommandOutputControl.Executable exec; + if (processBuilder != null) { + exec = coc.createExecutable(copyProcessBuilder()); + } else if (toolProvider != null) { + exec = coc.createExecutable(toolProvider, args.toArray(String[]::new)); } else { - // We are not going to read process output, so need to notify - // ProcessBuilder about this. Otherwise some processes might just - // hang up (`ldconfig -p`). - pb.redirectError(ProcessBuilder.Redirect.DISCARD); - pb.redirectOutput(ProcessBuilder.Redirect.DISCARD); + throw new IllegalStateException("No target to execute"); } - if (!quietCommand) { - Log.verbose(String.format("Running %s", createLogMessage(pb, true))); + PrintableOutputBuilder printableOutputBuilder; + if (dumpOutput()) { + printableOutputBuilder = new PrintableOutputBuilder(coc); + } else { + printableOutputBuilder = null; } - Process p = pb.start(); - - int code = 0; - if (writeOutputToFile) { - try { - code = waitForProcess(p); - } catch (InterruptedException ex) { - Log.verbose(ex); - throw new RuntimeException(ex); - } - } - - if (needProcessOutput) { - final List savedOutput; - Supplier> outputStream; - - if (writeOutputToFile) { - output = savedOutput = Files.readAllLines(outputFile); - Files.delete(outputFile); - outputStream = () -> { - if (savedOutput != null) { - return savedOutput.stream(); - } - return null; - }; - if (outputConsumer != null) { - outputConsumer.accept(outputStream.get()); - } - } else { - try (var br = new BufferedReader(new InputStreamReader( - p.getInputStream()))) { - - if ((outputConsumer != null || Log.isVerbose()) - || saveOutput) { - savedOutput = br.lines().toList(); - } else { - savedOutput = null; - } - output = savedOutput; - - outputStream = () -> { - if (savedOutput != null) { - return savedOutput.stream(); - } - return br.lines(); - }; - if (outputConsumer != null) { - outputConsumer.accept(outputStream.get()); - } - - if (savedOutput == null) { - // For some processes on Linux if the output stream - // of the process is opened but not consumed, the process - // would exit with code 141. - // It turned out that reading just a single line of process - // output fixes the problem, but let's process - // all of the output, just in case. - br.lines().forEach(x -> {}); - } - } - } + if (dumpOutput()) { + Log.verbose(String.format("Running %s", CommandLineFormat.DEFAULT.apply(List.of(commandLine().getFirst())))); } + Result result; try { - if (!writeOutputToFile) { - code = p.waitFor(); - } - if (!quietCommand) { - Log.verbose(pb.command(), getOutput(), code, IOUtils.getPID(p)); - } - return code; - } catch (InterruptedException ex) { - Log.verbose(ex); - throw new RuntimeException(ex); - } - } - - private int waitForProcess(Process p) throws InterruptedException { - if (timeout == INFINITE_TIMEOUT) { - return p.waitFor(); - } else { - if (p.waitFor(timeout, TimeUnit.SECONDS)) { - return p.exitValue(); + if (timeout == null) { + result = exec.execute(); } else { - Log.verbose(String.format("Command %s timeout after %d seconds", - createLogMessage(pb, false), timeout)); - p.destroy(); - return -1; + result = exec.execute(timeout.toMillis(), TimeUnit.MILLISECONDS); + } + } catch (InterruptedException ex) { + throw ExceptionBox.toUnchecked(ex); + } + + if (dumpOutput()) { + log(result, printableOutputBuilder.create()); + } + + return result; + } + + Result executeExpectSuccess() throws IOException { + return execute().expectExitCode(0); + } + + Result executeExpect(int mainExitCode, int... otherExitCodes) throws IOException { + return execute().expectExitCode(mainExitCode, otherExitCodes); + } + + RetryExecutor retry() { + return Globals.instance().objectFactory().retryExecutor(IOException.class) + .setExecutable(this::executeExpectSuccess); + } + + RetryExecutor retryOnKnownErrorMessage(String msg) { + Objects.requireNonNull(msg); + return saveOutput().retry().setExecutable(() -> { + // Execute it without exit code check. + var result = execute(); + if (result.stderr().stream().anyMatch(msg::equals)) { + throw result.unexpected(); + } + return result; + }); + } + + List commandLine() { + if (processBuilder != null) { + return Stream.of(processBuilder.command(), args).flatMap(Collection::stream).toList(); + } else if (toolProvider != null) { + return Stream.concat(Stream.of(toolProvider.name()), args.stream()).toList(); + } else { + throw new IllegalStateException("No target to execute"); + } + } + + private ProcessBuilder copyProcessBuilder() { + if (processBuilder == null) { + throw new IllegalStateException(); + } + + var copy = new ProcessBuilder(commandLine()); + copy.directory(processBuilder.directory()); + var env = copy.environment(); + env.clear(); + env.putAll(processBuilder.environment()); + + return copy; + } + + private boolean dumpOutput() { + return Log.isVerbose() && !quietCommand; + } + + private static void log(Result result, String printableOutput) throws IOException { + Objects.requireNonNull(result); + Objects.requireNonNull(printableOutput); + + Optional pid; + if (result.execAttrs() instanceof ProcessAttributes attrs) { + pid = attrs.pid(); + } else { + pid = Optional.empty(); + } + + var sb = new StringBuilder(); + sb.append("Command"); + pid.ifPresent(p -> { + sb.append(" [PID: ").append(p).append("]"); + }); + sb.append(":\n ").append(result.execAttrs()); + Log.verbose(sb.toString()); + + if (!printableOutput.isEmpty()) { + sb.delete(0, sb.length()); + sb.append("Output:"); + try (var lines = new BufferedReader(new StringReader(printableOutput)).lines()) { + lines.forEach(line -> { + sb.append("\n ").append(line); + }); + } + Log.verbose(sb.toString()); + } + + result.exitCode().ifPresentOrElse(exitCode -> { + Log.verbose("Returned: " + exitCode + "\n"); + }, () -> { + Log.verbose("Aborted: timed-out" + "\n"); + }); + } + + private static final class PrintableOutputBuilder { + + PrintableOutputBuilder(CommandOutputControl coc) { + coc.dumpOutput(true); + charset = coc.charset(); + if (coc.isBinaryOutput()) { + // Assume binary output goes into stdout and text error messages go into stderr, so keep them separated. + sinks = new ByteArrayOutputStream[2]; + sinks[0] = new ByteArrayOutputStream(); + sinks[1] = new ByteArrayOutputStream(); + coc.dumpStdout(new PrintStream(sinks[0], false, charset)) + .dumpStderr(new PrintStream(sinks[1], false, charset)); + } else { + sinks = new ByteArrayOutputStream[1]; + sinks[0] = new ByteArrayOutputStream(); + var ps = new PrintStream(sinks[0], false, charset); + // Redirect stderr in stdout. + coc.dumpStdout(ps).dumpStderr(ps); } } - } - static Executor of(String... cmdline) { - return new Executor().setCommandLine(cmdline); - } - - static Executor of(ProcessBuilder pb) { - return new Executor().setProcessBuilder(pb); - } - - private static String createLogMessage(ProcessBuilder pb, boolean quiet) { - StringBuilder sb = new StringBuilder(); - sb.append((quiet) ? pb.command().get(0) : pb.command()); - if (pb.directory() != null) { - sb.append(String.format(" in %s", pb.directory().getAbsolutePath())); + String create() { + if (isBinaryOutput()) { + // In case of binary output: + // - Convert binary stdout to text using ISO-8859-1 encoding and + // replace non-printable characters with the question mark symbol (?). + // - Convert binary stderr to text using designated encoding (assume stderr is always a character stream). + // - Merge text stdout and stderr into a single string; + // stderr first, stdout follows, with the aim to present user error messages first. + var sb = new StringBuilder(); + var stdout = sinks[0].toString(StandardCharsets.ISO_8859_1).replaceAll("[^\\p{Print}\\p{Space}]", "?"); + return sb.append(sinks[1].toString(charset)).append(stdout).toString(); + } else { + return sinks[0].toString(charset); + } } - return sb.toString(); + + private boolean isBinaryOutput() { + return sinks.length == 2; + } + + private final ByteArrayOutputStream sinks[]; + private final Charset charset; } - public static final int INFINITE_TIMEOUT = -1; - - private ProcessBuilder pb; - private boolean saveOutput; - private boolean writeOutputToFile; + private final CommandOutputControl commandOutputControl; private boolean quietCommand; - private long timeout = INFINITE_TIMEOUT; - private List output; - private Consumer> outputConsumer; + private final List args; + private ProcessBuilder processBuilder; + private ToolProvider toolProvider; + private Duration timeout; + private UnaryOperator mapper; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ExecutorFactory.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ExecutorFactory.java new file mode 100644 index 00000000000..ce703358b82 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ExecutorFactory.java @@ -0,0 +1,33 @@ +/* + * 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; + +@FunctionalInterface +interface ExecutorFactory { + + Executor executor(); + + static final ExecutorFactory DEFAULT = Executor::new; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java new file mode 100644 index 00000000000..c1b56b24e0a --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java @@ -0,0 +1,71 @@ +/* + * 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; + +import java.util.Optional; +import java.util.function.Supplier; + +public final class Globals { + + private Globals() { + } + + Globals objectFactory(ObjectFactory v) { + checkMutable(); + objectFactory = Optional.ofNullable(v).orElse(ObjectFactory.DEFAULT); + return this; + } + + ObjectFactory objectFactory() { + return objectFactory; + } + + Globals executorFactory(ExecutorFactory v) { + return objectFactory(ObjectFactory.build(objectFactory).executorFactory(v).create()); + } + + public static int main(Supplier mainBody) { + if (INSTANCE.isBound()) { + return mainBody.get(); + } else { + return ScopedValue.where(INSTANCE, new Globals()).call(mainBody::get); + } + } + + public static Globals instance() { + return INSTANCE.orElse(DEFAULT); + } + + private void checkMutable() { + if (this == DEFAULT) { + throw new UnsupportedOperationException("Can't modify immutable instance"); + } + } + + private ObjectFactory objectFactory = ObjectFactory.DEFAULT; + + private static final ScopedValue INSTANCE = ScopedValue.newInstance(); + private static final Globals DEFAULT = new Globals(); +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java index aac113d7777..08cf0c10982 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/IOUtils.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. * 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,9 @@ package jdk.jpackage.internal; import java.io.IOException; -import java.io.PrintStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.List; import jdk.jpackage.internal.model.JPackageException; /** @@ -50,46 +47,6 @@ final class IOUtils { StandardCopyOption.COPY_ATTRIBUTES); } - public static void exec(ProcessBuilder pb) - throws IOException { - exec(pb, false, null, false, Executor.INFINITE_TIMEOUT); - } - - // timeout in seconds. -1 will be return if process timeouts. - public static void exec(ProcessBuilder pb, long timeout) - throws IOException { - exec(pb, false, null, false, timeout); - } - - static void exec(ProcessBuilder pb, boolean testForPresenceOnly, - PrintStream consumer, boolean writeOutputToFile, long timeout) - throws IOException { - exec(pb, testForPresenceOnly, consumer, writeOutputToFile, - timeout, false); - } - - static void exec(ProcessBuilder pb, boolean testForPresenceOnly, - PrintStream consumer, boolean writeOutputToFile, - long timeout, boolean quiet) throws IOException { - List output = new ArrayList<>(); - Executor exec = Executor.of(pb) - .setWriteOutputToFile(writeOutputToFile) - .setTimeout(timeout) - .setQuiet(quiet) - .setOutputConsumer(lines -> { - lines.forEach(output::add); - if (consumer != null) { - output.forEach(consumer::println); - } - }); - - if (testForPresenceOnly) { - exec.execute(); - } else { - exec.executeExpectSuccess(); - } - } - static void writableOutputDir(Path outdir) { if (!Files.isDirectory(outdir)) { try { @@ -103,15 +60,4 @@ final class IOUtils { throw new JPackageException(I18N.format("error.cannot-write-to-output-dir", outdir.toAbsolutePath())); } } - - public static long getPID(Process p) { - try { - return p.pid(); - } catch (UnsupportedOperationException ex) { - Log.verbose(ex); // Just log exception and ignore it. This method - // is used for verbose output, so not a problem - // if unsupported. - return -1; - } - } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java index 37f166a4e7c..f960f8dc2bf 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/JLinkRuntimeBuilder.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 @@ -22,13 +22,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package jdk.jpackage.internal; + import static jdk.jpackage.internal.model.RuntimeBuilder.getDefaultModulePath; +import static jdk.jpackage.internal.util.function.ThrowingRunnable.toRunnable; import java.io.File; import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; @@ -50,7 +51,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.internal.module.ModulePath; import jdk.jpackage.internal.model.AppImageLayout; -import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherModularStartupInfo; import jdk.jpackage.internal.model.LauncherStartupInfo; import jdk.jpackage.internal.model.RuntimeBuilder; @@ -58,27 +58,15 @@ import jdk.jpackage.internal.model.RuntimeBuilder; final class JLinkRuntimeBuilder implements RuntimeBuilder { private JLinkRuntimeBuilder(List jlinkCmdLine) { - this.jlinkCmdLine = jlinkCmdLine; + this.jlinkCmdLine = Objects.requireNonNull(jlinkCmdLine); } @Override public void create(AppImageLayout appImageLayout) { - var args = new ArrayList(); - args.add("--output"); - args.add(appImageLayout.runtimeDirectory().toString()); - args.addAll(jlinkCmdLine); - - StringWriter writer = new StringWriter(); - PrintWriter pw = new PrintWriter(writer); - - int retVal = LazyLoad.JLINK_TOOL.run(pw, pw, args.toArray(String[]::new)); - String jlinkOut = writer.toString(); - - args.add(0, "jlink"); - Log.verbose(args, List.of(jlinkOut), retVal, -1); - if (retVal != 0) { - throw new JPackageException(I18N.format("error.jlink.failed", jlinkOut)); - } + toRunnable(Executor.of() + .toolProvider(LazyLoad.JLINK_TOOL) + .args("--output", appImageLayout.runtimeDirectory().toString()) + .args(jlinkCmdLine)::executeExpectSuccess).run(); } @Override diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java index b14b4ab22ca..0f51fa166f9 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, 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 @@ -28,7 +28,6 @@ package jdk.jpackage.internal; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.List; /** * Log @@ -105,29 +104,6 @@ public class Log { } } - public void verbose(List strings, - List output, int returnCode, long pid) { - if (verbose) { - StringBuilder sb = new StringBuilder(); - sb.append("Command [PID: "); - sb.append(pid); - sb.append("]:\n "); - - for (String s : strings) { - sb.append(" " + s); - } - verbose(sb.toString()); - if (output != null && !output.isEmpty()) { - sb = new StringBuilder("Output:"); - for (String s : output) { - sb.append("\n " + s); - } - verbose(sb.toString()); - } - verbose("Returned: " + returnCode + "\n"); - } - } - private String addTimestamp(String msg) { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); Date time = new Date(System.currentTimeMillis()); @@ -177,9 +153,4 @@ public class Log { public static void verbose(Throwable t) { instance.get().verbose(t); } - - public static void verbose(List strings, List out, - int ret, long pid) { - instance.get().verbose(strings, out, ret, pid); - } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ObjectFactory.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ObjectFactory.java new file mode 100644 index 00000000000..f1a83eb9eab --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ObjectFactory.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; + +import java.util.Objects; +import java.util.Optional; +import jdk.jpackage.internal.util.CompositeProxy; + +interface ObjectFactory extends ExecutorFactory, RetryExecutorFactory { + + static ObjectFactory.Builder build() { + return new Builder(); + } + + static ObjectFactory.Builder build(ObjectFactory from) { + return build().initFrom(from); + } + + static final class Builder { + private Builder() { + } + + ObjectFactory create() { + return CompositeProxy.build().invokeTunnel(CompositeProxyTunnel.INSTANCE).create( + ObjectFactory.class, + Optional.ofNullable(executorFactory).orElse(ExecutorFactory.DEFAULT), + Optional.ofNullable(retryExecutorFactory).orElse(RetryExecutorFactory.DEFAULT)); + } + + Builder initFrom(ObjectFactory of) { + Objects.requireNonNull(of); + return executorFactory(of).retryExecutorFactory(of); + } + + Builder executorFactory(ExecutorFactory v) { + executorFactory = v; + return this; + } + + Builder retryExecutorFactory(RetryExecutorFactory v) { + retryExecutorFactory = v; + return this; + } + + private ExecutorFactory executorFactory; + private RetryExecutorFactory retryExecutorFactory; + } + + static final ObjectFactory DEFAULT = build().create(); +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutor.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutor.java deleted file mode 100644 index f806217e8f9..00000000000 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutor.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2020, 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. 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.util.List; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public final class RetryExecutor { - public RetryExecutor() { - setMaxAttemptsCount(5); - setAttemptTimeoutMillis(2 * 1000); - setWriteOutputToFile(false); - } - - public RetryExecutor setMaxAttemptsCount(int v) { - attempts = v; - return this; - } - - public RetryExecutor setAttemptTimeoutMillis(int v) { - timeoutMillis = v; - return this; - } - - public RetryExecutor saveOutput(boolean v) { - saveOutput = v; - return this; - } - - public List getOutput() { - return output; - } - - public RetryExecutor setWriteOutputToFile(boolean v) { - writeOutputToFile = v; - return this; - } - - public RetryExecutor setExecutorInitializer(Consumer v) { - executorInitializer = v; - return this; - } - - public void abort() { - aborted = true; - } - - public boolean isAborted() { - return aborted; - } - - static RetryExecutor retryOnKnownErrorMessage(String v) { - RetryExecutor result = new RetryExecutor(); - return result.setExecutorInitializer(exec -> { - exec.setOutputConsumer(output -> { - if (!output.anyMatch(v::equals)) { - result.abort(); - } - }); - }); - } - - public void execute(String cmdline[]) throws IOException { - executeLoop(() -> - Executor.of(cmdline).setWriteOutputToFile(writeOutputToFile)); - } - - public void execute(ProcessBuilder pb) throws IOException { - executeLoop(() -> - Executor.of(pb).setWriteOutputToFile(writeOutputToFile)); - } - - private void executeLoop(Supplier execSupplier) throws IOException { - aborted = false; - for (;;) { - if (aborted) { - break; - } - - try { - Executor exec = execSupplier.get().saveOutput(saveOutput); - if (executorInitializer != null) { - executorInitializer.accept(exec); - } - exec.executeExpectSuccess(); - if (saveOutput) { - output = exec.getOutput(); - } - break; - } catch (IOException ex) { - if (aborted || (--attempts) <= 0) { - throw ex; - } - } - - try { - Thread.sleep(timeoutMillis); - } catch (InterruptedException ex) { - Log.verbose(ex); - throw new RuntimeException(ex); - } - } - } - - private Consumer executorInitializer; - private boolean aborted; - private int attempts; - private int timeoutMillis; - private boolean saveOutput; - private List output; - private boolean writeOutputToFile; -} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutorFactory.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutorFactory.java new file mode 100644 index 00000000000..3efb522abd4 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/RetryExecutorFactory.java @@ -0,0 +1,35 @@ +/* + * 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; + +import jdk.jpackage.internal.util.RetryExecutor; + +@FunctionalInterface +interface RetryExecutorFactory { + + RetryExecutor retryExecutor(Class exceptionType); + + static final RetryExecutorFactory DEFAULT = RetryExecutor::new; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/SystemEnvironment.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/SystemEnvironment.java index de98e97c922..5d9a1d6d147 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/SystemEnvironment.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/SystemEnvironment.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,5 +24,5 @@ */ package jdk.jpackage.internal; -public interface SystemEnvironment { +interface SystemEnvironment { } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ToolValidator.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ToolValidator.java index 13e87d5cfa6..9440aff3a53 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ToolValidator.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ToolValidator.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 @@ -121,32 +121,32 @@ final class ToolValidator { cmdline.addAll(args); } - boolean canUseTool[] = new boolean[1]; + boolean canUseTool = false; if (minimalVersion == null) { // No version check. - canUseTool[0] = true; + canUseTool = true; } - String[] version = new String[1]; + String version = null; try { - Executor.of(cmdline.toArray(String[]::new)).setQuiet(true).setOutputConsumer(lines -> { - if (versionParser != null && minimalVersion != null) { - version[0] = versionParser.apply(lines); - if (version[0] != null && minimalVersion.compareTo(version[0]) <= 0) { - canUseTool[0] = true; - } + var result = Executor.of(cmdline).setQuiet(true).saveOutput().execute(); + var lines = result.content(); + if (versionParser != null && minimalVersion != null) { + version = versionParser.apply(lines.stream()); + if (version != null && minimalVersion.compareTo(version) <= 0) { + canUseTool = true; } - }).execute(); + } } catch (IOException e) { return new ConfigException(I18N.format("error.tool-error", toolPath, e.getMessage()), null, e); } - if (canUseTool[0]) { + if (canUseTool) { // All good. Tool can be used. return null; } else if (toolOldVersionErrorHandler != null) { - return toolOldVersionErrorHandler.apply(toolPath, version[0]); + return toolOldVersionErrorHandler.apply(toolPath, version); } else { return new ConfigException( I18N.format("error.tool-old-version", toolPath, minimalVersion), diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java index 9feb5b40944..519958d9ff7 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.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 @@ -45,6 +45,7 @@ import java.util.function.Supplier; import java.util.spi.ToolProvider; import jdk.internal.opt.CommandLine; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.Globals; import jdk.jpackage.internal.Log; import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.JPackageException; @@ -56,7 +57,15 @@ import jdk.jpackage.internal.util.function.ExceptionBox; */ public final class Main { - public static final class Provider implements ToolProvider { + public record Provider(Supplier bundlingEnvSupplier) implements ToolProvider { + + public Provider { + Objects.requireNonNull(bundlingEnvSupplier); + } + + public Provider() { + this(Main::loadBundlingEnvironment); + } @Override public String name() { @@ -65,7 +74,7 @@ public final class Main { @Override public int run(PrintWriter out, PrintWriter err, String... args) { - return Main.run(out, err, args); + return Main.run(bundlingEnvSupplier, out, err, args); } @Override @@ -94,7 +103,23 @@ public final class Main { System.exit(run(out, err, args)); } - public static int run(PrintWriter out, PrintWriter err, String... args) { + static int run(PrintWriter out, PrintWriter err, String... args) { + return run(Main::loadBundlingEnvironment, out, err, args); + } + + static int run(Supplier bundlingEnvSupplier, PrintWriter out, PrintWriter err, String... args) { + return Globals.main(() -> { + return runWithGlobals(bundlingEnvSupplier, out, err, args); + }); + } + + private static int runWithGlobals( + Supplier bundlingEnvSupplier, + PrintWriter out, + PrintWriter err, + String... args) { + + Objects.requireNonNull(bundlingEnvSupplier); Objects.requireNonNull(args); for (String arg : args) { Objects.requireNonNull(arg); @@ -128,8 +153,7 @@ public final class Main { return preprocessStatus; } - final var bundlingEnv = ServiceLoader.load(CliBundlingEnvironment.class, - CliBundlingEnvironment.class.getClassLoader()).findFirst().orElseThrow(); + final var bundlingEnv = bundlingEnvSupplier.get(); final var parseResult = Utils.buildParser(OperatingSystem.current(), bundlingEnv).create().apply(mappedArgs.get()); @@ -285,4 +309,10 @@ public final class Main { private static String getVersion() { return System.getProperty("java.version"); } + + private static CliBundlingEnvironment loadBundlingEnvironment() { + return ServiceLoader.load( + CliBundlingEnvironment.class, + CliBundlingEnvironment.class.getClassLoader()).findFirst().orElseThrow(); + } } 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 c5ea583514f..e97bee79e6e 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 @@ -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 @@ -96,7 +96,6 @@ error.tool-not-found.advice=Please install "{0}" error.tool-old-version=Can not find "{0}" {1} or newer error.tool-old-version.advice=Please install "{0}" {1} or newer -error.jlink.failed=jlink failed with: {0} error.blocked.option=jlink option [{0}] is not permitted in --jlink-options error.no.name=Name not specified with --name and cannot infer one from app-image error.no.name.advice=Specify name with --name diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandLineFormat.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandLineFormat.java new file mode 100644 index 00000000000..4d64030876f --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandLineFormat.java @@ -0,0 +1,52 @@ +/* + * 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.util; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Formats command line arguments. + */ +public final class CommandLineFormat { + + public String format(List cmdline) { + return cmdline.stream().map(enquoter::applyTo).collect(Collectors.joining(" ")); + } + + public static CommandLineFormat platform() { + var format = new CommandLineFormat(); + format.enquoter = Enquoter.identity().setEnquotePredicate(Enquoter.QUOTE_IF_WHITESPACES).setQuoteChar('\''); + return format; + } + + private CommandLineFormat() { + } + + private Enquoter enquoter; + + public static final Function, String> DEFAULT = platform()::format; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java new file mode 100644 index 00000000000..e35439815b1 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java @@ -0,0 +1,1904 @@ +/* + * 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.util; + +import static java.util.stream.Collectors.joining; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.StringReader; +import java.io.UncheckedIOException; +import java.nio.charset.Charset; +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.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntPredicate; +import java.util.function.Predicate; +import java.util.spi.ToolProvider; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.internal.util.function.ThrowingConsumer; +import jdk.jpackage.internal.util.function.ThrowingRunnable; + +/** + * Runs commands and processes their stdout and stderr streams. + *

    + * A command is either a subprocess represented by {@link ProcessBuilder} or a + * tool provided by {@link ToolProvider}. + *

    + * A command is executed synchronously, and the result of its execution is + * stored in a {@link Result} instance which captures the exit code and any + * saved output streams. + *

    + * Depending on the configuration, it can save the entire output stream, only + * the first line, or not save the output at all. Stdout and stderr streams can + * be configured independently. + *

    + * Output streams can be treated as either byte streams or character streams. + * + *

    + * The table below shows how different parameter combinations affect the content + * written to streams returned by {@link #dumpStdout()} and + * {@link #dumpStderr()} for subsequently executed tools, regardless of whether + * their output streams are saved, or for subprocesses when the output streams + * are saved: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    discardStdout(false) and discardStderr(false)discardStdout(false) and discardStderr(true)discardStdout(true) and discardStderr(false)
    redirectStderr(true) and dumpOutput(true) + *

    + * dumpStdout(): STDOUT and STDERR interleaved + *

    + * dumpStderr(): unchanged

    + *

    + * dumpStdout(): STDOUT + *

    + * dumpStderr(): unchanged

    + *

    + * dumpStdout(): STDERR; + *

    + * dumpStderr(): unchanged

    redirectStderr(false) and dumpOutput(true) + *

    + * dumpStdout(): STDOUT + *

    + * dumpStderr(): STDERR

    + *

    + * dumpStdout(): STDOUT + *

    + * dumpStderr(): unchanged

    + *

    + * dumpStdout(): unchanged + *

    + * dumpStderr(): STDERR

    + * + *

    + * The table below shows how different parameter combinations affect the content + * written to the native file descriptors associated with {@link System#out} and + * {@link System#err} for subsequently executed subprocesses when the output + * streams are not saved: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    discardStdout(false) and discardStderr(false)discardStdout(false) and discardStderr(true)discardStdout(true) and discardStderr(false)
    redirectStderr(true) and dumpOutput(true) + *

    + * System.out: STDOUT and STDERR interleaved + *

    + * System.err: unchanged

    + *

    + * System.out: STDOUT + *

    + * System.err: unchanged

    + *

    + * System.out: STDERR; + *

    + * The command's STDERR will be written to the stream referenced by + * {@link #dumpStdout()} rather than to the underlying file descriptor + * associated with the Java process's STDOUT + *

    + * System.err: unchanged

    redirectStderr(false) and dumpOutput(true) + *

    + * System.out: STDOUT + *

    + * System.err: STDERR

    + *

    + * System.out: STDOUT + *

    + * System.err: unchanged

    + *

    + * System.out: unchanged + *

    + * System.err: STDERR

    + * + *

    + * The table below shows how different parameter combinations affect the + * properties of {@link Result} objects returned by + * {@link #execute(ProcessBuilder, long)} or + * {@link #execute(ToolProvider, long, String...)} when processing character + * streams: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    saveOutput(true)saveFirstLineOfOutput()
    redirectStderr(true) and discardStdout(false) and + * discardStderr(false) + *

    + * content(): STDOUT and STDERR interleaved + *

    + * findStdout(): {@code Optional.empty()} + *

    + * findStderr(): {@code Optional.empty()}

    + *

    + * content(): a single-item list containing the first line of interleaved STDOUT + * and STDERR if the command produced any output; otherwise, an empty list + *

    + * findStdout(): {@code Optional.empty()} + *

    + * findStderr(): {@code Optional.empty()}

    redirectStderr(false) and discardStdout(false) and + * discardStderr(false) + *

    + * content(): STDOUT followed by STDERR + *

    + * stdout(): STDOUT + *

    + * stderr(): STDERR

    + *

    + * content(): a list containing at most two items: the first line of STDOUT (if + * the command produced any), followed by the first line of STDERR (if the + * command produced any) + *

    + * stdout(): The first line of STDOUT (if the command produced any); otherwise + * an empty list + *

    + * findStderr(): The first line of STDERR (if the command produced any); + * otherwise an empty list + *

    redirectStderr(true) and discardStdout(false) and + * discardStderr(true) + *

    + * content(): STDOUT + *

    + * stdout(): The same as content() + *

    + * findStderr(): {@code Optional.empty()}

    + *

    + * content(): The first line of STDOUT (if the command produced any); otherwise + * an empty list + *

    + * stdout(): The same as content() + *

    + * findStderr(): {@code Optional.empty()}

    redirectStderr(false) and discardStdout(false) and + * discardStderr(true) + *

    + * content(): STDOUT + *

    + * stdout(): The same as content() + *

    + * stderr(): an empty list

    + *

    + * content(): The first line of STDOUT (if the command produced any); otherwise + * an empty list + *

    + * stdout(): The same as content() + *

    + * stderr(): an empty list

    redirectStderr(true) and discardStdout(true) and + * discardStderr(false) + *

    + * content(): STDERR + *

    + * stdout(): The same as content() + *

    + * findStderr(): {@code Optional.empty()}

    + *

    + * content(): The first line of STDERR (if the command produced any); otherwise + * an empty list + *

    + * stdout(): The same as content() + *

    + * findStderr(): {@code Optional.empty()}

    redirectStderr(false) and discardStdout(true) and + * discardStderr(false) + *

    + * content(): STDERR + *

    + * findStdout(): an empty list + *

    + * stderr(): The same as content()

    + *

    + * content(): The first line of STDERR (if the command produced any); otherwise + * an empty list + *

    + * findStdout(): an empty list + *

    + * stderr(): The same as content()

    + *

    + * The table below shows how different parameter combinations affect the + * properties of {@link Result} objects returned by + * {@link #execute(ProcessBuilder, long)} or + * {@link #execute(ToolProvider, long, String...)} when processing byte streams: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    saveOutput(true) or saveFirstLineOfOutput()
    redirectStderr(true) and discardStdout(false) and + * discardStderr(false) + *

    + * byteContent(): STDOUT and STDERR interleaved + *

    + * findByteStdout(): {@code Optional.empty()} + *

    + * findByteStderr(): {@code Optional.empty()}

    redirectStderr(false) and discardStdout(false) and + * discardStderr(false) + *

    + * byteContent(): STDOUT followed by STDERR + *

    + * byteStdout(): STDOUT + *

    + * byteStderr(): STDERR

    redirectStderr(true) and discardStdout(false) and + * discardStderr(true) + *

    + * byteContent(): STDOUT + *

    + * byteStdout(): The same as byteContent() + *

    + * findByteStderr(): {@code Optional.empty()}

    redirectStderr(false) and discardStdout(false) and + * discardStderr(true) + *

    + * byteContent(): STDOUT + *

    + * byteStdout(): The same as byteContent() + *

    + * byteStderr(): an empty array

    redirectStderr(true) and discardStdout(true) and + * discardStderr(false) + *

    + * byteContent(): STDERR + *

    + * byteStdout(): The same as byteContent() + *

    + * findByteStderr(): {@code Optional.empty()}

    redirectStderr(false) and discardStdout(true) and + * discardStderr(false) + *

    + * byteContent(): STDERR + *

    + * findByteStdout(): an empty array + *

    + * byteStderr(): The same as byteContent()

    + */ +public final class CommandOutputControl { + + public CommandOutputControl() { + outputStreamsControl = new OutputStreamsControl(); + } + + private CommandOutputControl(CommandOutputControl other) { + flags = other.flags; + outputStreamsControl = other.outputStreamsControl.copy(); + dumpStdout = other.dumpStdout; + dumpStderr = other.dumpStderr; + charset = other.charset; + processListener = other.processListener; + } + + /** + * Specifies whether the full output produced by commands subsequently executed + * by this object will be saved. + *

    + * If {@code v} is {@code true}, both stdout and stderr streams will be saved; + * otherwise, they will not be saved. + *

    + * This setting is mutually exclusive with {@link #saveFirstLineOfOutput()}. + * + * @param v {@code true} to save the full stdout and stderr streams; + * {@code false} otherwise + * @return this + */ + public CommandOutputControl saveOutput(boolean v) { + return setOutputControl(v, OutputControlOption.SAVE_ALL); + } + + /** + * Returns whether this object will save the complete output of commands + * subsequently executed. + * + * @return {@code true} if this object will save the full output of commands it + * executes subsequently; {@code false} otherwise + */ + public boolean isSaveOutput() { + return outputStreamsControl.stdout().saveAll(); + } + + /** + * Specifies whether the first line of the output, combined from the stdout and + * stderr streams of commands subsequently executed by this object, will be + * saved. + *

    + * This setting is mutually exclusive with {@link #saveOutput(boolean)}. + * + * @return this + */ + public CommandOutputControl saveFirstLineOfOutput() { + return setOutputControl(true, OutputControlOption.SAVE_FIRST_LINE); + } + + /** + * Returns whether this object will save the first line of the output of + * commands subsequently executed. + * + * @return {@code true} if this object will save the first line of the output of + * commands it executes subsequently; {@code false} otherwise + */ + public boolean isSaveFirstLineOfOutput() { + return outputStreamsControl.stdout().saveFirstLine(); + } + + /** + * Specifies whether this object will dump the output streams from + * subsequently executed commands into the streams returned by + * {@link #dumpStdout()} and {@link #dumpStdout()} methods respectively. + *

    + * If this object is configured to redirect stderr of subsequently executed + * commands into their stdout ({@code redirectStderr(true)}), their output + * streams will be dumped into the stream returned by {@code dumpStdout()} + * method. Otherwise, their stdout and stderr streams will be dumped into the + * stream returned by {@code dumpStdout()} and {@code dumpStderr()} methods + * respectively. + * + * @param v if output streams from subsequently executed commands will be + * dumped into streams returned by {@code dumpStdout()} and + * {@code dumpStderr()} methods respectively + * + * @return this + * + * @see #redirectStderr(boolean) + * @see #dumpStdout() + * @see #dumpStderr() + */ + public CommandOutputControl dumpOutput(boolean v) { + setFlag(Flag.DUMP, v); + return setOutputControl(v, OutputControlOption.DUMP); + } + + /** + * Returns the value passed in the last call of {@link #dumpOutput(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #dumpOutput(boolean)} + */ + public boolean isDumpOutput() { + return Flag.DUMP.isSet(flags); + } + + /** + * Specifies whether this object will treat output streams of subsequently + * executed commands as byte streams rather than character streams. + * + * @param v {@code true} if this object will treat the output streams of + * subsequently executed commands as byte streams, and {@code false} + * otherwise + * + * @return this + */ + public CommandOutputControl binaryOutput(boolean v) { + return setFlag(Flag.BINARY_OUTPUT, v); + } + + /** + * Returns the value passed in the last call of {@link #binaryOutput(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #binaryOutput(boolean)} + */ + public boolean isBinaryOutput() { + return Flag.BINARY_OUTPUT.isSet(flags); + } + + /** + * Sets character encoding that will be applied to the stdout and the stderr + * streams of commands (subprocesses and {@code ToolProvider}-s) subsequently + * executed by this object. The default encoding is {@code UTF-8}. + *

    + * The value will be ignored if this object is configured for byte output + * streams. + * + * @param v character encoding for output streams of subsequently executed + * commands + * + * @see #binaryOutput(boolean) + * + * @return this + */ + public CommandOutputControl charset(Charset v) { + charset = v; + return this; + } + + /** + * Returns the value passed in the last call of + * {@link #charset(Charset)} method on this object, or + * {@link StandardCharsets#UTF_8} if the method has not been called. + * + * @return the character encoding that will be applied to the stdout and stderr + * streams of commands subsequently executed by this object + */ + public Charset charset() { + return Optional.ofNullable(charset).orElse(StandardCharsets.UTF_8); + } + + /** + * Specifies whether the stderr stream will be redirected into the stdout stream + * for commands subsequently executed by this object. + * + * @see ProcessBuilder#redirectErrorStream(boolean) + * + * @param v {@code true} if the stderr stream of commands subsequently executed + * by this object will be redirected into the stdout stream; + * {@code false} otherwise + * + * @return this + */ + public CommandOutputControl redirectStderr(boolean v) { + return setFlag(Flag.REDIRECT_STDERR, v); + } + + /** + * Returns the value passed in the last call of {@link #redirectStderr(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #redirectStderr(boolean)} + */ + public boolean isRedirectStderr() { + return Flag.REDIRECT_STDERR.isSet(flags); + } + + /** + * Specifies whether stderr and stdout streams for subprocesses subsequently + * executed by this object will be stored in files. + *

    + * By default, if an output stream of a subprocess is configured for saving, + * this object will retrieve the content using {@link Process#getInputStream()} + * function for stdout and {@link Process#getErrorStream()} function for stderr. + * However, these functions don't always work correctly due to a + * JDK-8236825 bug + * still reproducible on macOS JDK26. The alternative way to get the content of + * output streams of a subprocess is to redirect them into files and read these + * files when the subprocess terminates. + *

    + * It will use {@code Files.createTempFile("jpackageOutputTempFile", ".tmp")} to + * create a file for each subprocess's output stream configured for saving. All + * created files will be automatically deleted at the exit of + * {@link #execute(ProcessBuilder, long)} method. + *

    + * Doesn't apply to executing {@code ToolProvider}-s. + *

    + * Storing output streams in files takes longer than managing them in memory and + * should be avoided if possible. + * + * @param v {@code true} if this object will use files to store saved output + * streams of subsequently executed subprocesses; {@code false} + * otherwise + * @return this + */ + public CommandOutputControl storeOutputInFiles(boolean v) { + return setFlag(Flag.STORE_OUTPUT_IN_FILES, v); + } + + /** + * Returns the value passed in the last call of {@link #storeOutputInFiles(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #storeOutputInFiles(boolean)} + */ + public boolean isStoreOutputInFiles() { + return Flag.STORE_OUTPUT_IN_FILES.isSet(flags); + } + + /** + * Specifies whether stdout streams from commands subsequently executed by this + * object will be discarded. + * + * @param v {@code true} if this object will discard stdout streams from + * commands subsequently executed by this object; {@code false} + * otherwise + * @return this + */ + public CommandOutputControl discardStdout(boolean v) { + setFlag(Flag.DISCARD_STDOUT, v); + outputStreamsControl.stdout().discard(v); + return this; + } + + /** + * Returns the value passed in the last call of {@link #discardStdout(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #discardStdout(boolean)} + */ + public boolean isDiscardStdout() { + return Flag.DISCARD_STDOUT.isSet(flags); + } + + /** + * Specifies whether stderr streams from commands subsequently executed by this + * object will be discarded. + * + * @param v {@code true} if this object will discard stderr streams from + * commands subsequently executed by this object; {@code false} + * otherwise + * @return this + */ + public CommandOutputControl discardStderr(boolean v) { + setFlag(Flag.DISCARD_STDERR, v); + outputStreamsControl.stderr().discard(v); + return this; + } + + /** + * Returns the value passed in the last call of {@link #discardStderr(boolean)} + * method on this object, or {@code false} if the method has not been called. + * + * @return the value passed in the last call of {@link #discardStderr(boolean)} + */ + public boolean isDiscardStderr() { + return Flag.DISCARD_STDERR.isSet(flags); + } + + /** + * Specifies the stream where stdout streams from commands subsequently executed + * by this object will be dumped. + *

    + * If the {@code null} is specified and this object configuration is equivalent + * to {@code dumpOutput(true).saveOutput(false).discardStdout(false)} the stdout + * streams from commands subsequently executed by this object will be written + * into the file descriptor associated with the {@code Systsem.out} stream. If + * you want them to be written into the {@code Systsem.out} object, pass the + * {@code Systsem.out} reference into this function. + * + * @param v the stream where stdout streams from commands subsequently executed + * by this object will be dumped; {@code null} permitted + * @return this + */ + public CommandOutputControl dumpStdout(PrintStream v) { + dumpStdout = v; + return this; + } + + /** + * Returns the value passed in the last call of {@link #dumpStdout(PrintStream)} + * method on this object, or {@link System#out} if the method has not been + * called. + * + * @return the stream where stdout streams from commands subsequently executed + * by this object will be dumped + */ + public PrintStream dumpStdout() { + return Optional.ofNullable(dumpStdout).orElse(System.out); + } + + /** + * Specifies the stream where stderr streams from commands subsequently executed + * by this object will be dumped. + *

    + * If the {@code null} is specified and this object configuration is equivalent + * to + * {@code dumpOutput(true).saveOutput(false).redirectStderr(false).discardStderr(false)} + * the stderr streams from commands subsequently executed by this object will be + * written into the file descriptor associated with the {@code Systsem.err} + * stream. If you want them to be written into the {@code Systsem.err} object, + * pass the {@code Systsem.err} reference into this function. + * + * @param v the stream where stderr streams from commands subsequently executed + * by this object will be dumped; {@code null} permitted + * @return this + */ + public CommandOutputControl dumpStderr(PrintStream v) { + dumpStderr = v; + return this; + } + + /** + * Returns the value passed in the last call of {@link #dumpStderr(PrintStream)} + * method on this object, or {@link System#err} if the method has not been + * called. + * + * @return the stream where stderr streams from commands subsequently executed + * by this object will be dumped + */ + public PrintStream dumpStderr() { + return Optional.ofNullable(dumpStderr).orElse(System.err); + } + + /** + * Sets the callback to be invoked when this object starts a subprocess from + * subsequent {@link #execute(ProcessBuilder, long)} calls. + * + *

    + * This object maintains a single callback. Calling this method replaces any + * previously set callback. + * + *

    + * The callback is invoked on the thread that calls + * {@link #execute(ProcessBuilder, long)} after the subprocess's output streams + * begin being pumped. + * + * @param v the callback for notifying a subprocess being started or + * {@code null} + * @return this + */ + public CommandOutputControl processListener(Consumer v) { + processListener = v; + return this; + } + + /** + * Returns an {@code Optional} wrapping the value passed in the last call of + * {@link #processListener(Consumer)} method on this object, or an empty + * {@code Optional} if the method has not been called or {@code null} was passed in the last call. + * + * @return an {@code Optional} wrapping the value passed in the last call of + * {@link #processListener(Consumer)} + */ + public Optional> processListener() { + return Optional.ofNullable(processListener); + } + + /** + * Returns a deep copy of this object. Changes to the copy will not affect the + * original. + * + * @return a deep copy of this object + */ + public CommandOutputControl copy() { + return new CommandOutputControl(this); + } + + public interface ExecutableAttributes { + List commandLine(); + } + + public sealed interface Executable { + + ExecutableAttributes attributes(); + + Result execute() throws IOException, InterruptedException; + + Result execute(long timeout, TimeUnit unit) throws IOException, InterruptedException; + } + + public record ProcessAttributes(Optional pid, List commandLine) implements ExecutableAttributes { + public ProcessAttributes { + Objects.requireNonNull(pid); + commandLine.forEach(Objects::requireNonNull); + } + + @Override + public String toString() { + return CommandLineFormat.DEFAULT.apply(commandLine()); + } + } + + public record ToolProviderAttributes(String name, List args) implements ExecutableAttributes { + public ToolProviderAttributes { + Objects.requireNonNull(name); + args.forEach(Objects::requireNonNull); + } + + @Override + public String toString() { + return CommandLineFormat.DEFAULT.apply(commandLine()); + } + + @Override + public List commandLine() { + return Stream.concat(Stream.of(name), args.stream()).toList(); + } + } + + public static ExecutableAttributes EMPTY_EXECUTABLE_ATTRIBUTES = new ExecutableAttributes() { + @Override + public String toString() { + return ""; + } + + @Override + public List commandLine() { + return List.of(); + } + }; + + public Executable createExecutable(ToolProvider tp, String... args) { + return new ToolProviderExecutable(tp, List.of(args), this); + } + + public Executable createExecutable(ProcessBuilder pb) { + return new ProcessExecutable(pb, this); + } + + public record Result( + Optional exitCode, + Optional>> output, + Optional> byteOutput, + ExecutableAttributes execAttrs) { + + public Result { + Objects.requireNonNull(exitCode); + Objects.requireNonNull(output); + Objects.requireNonNull(byteOutput); + Objects.requireNonNull(execAttrs); + } + + public Result(int exitCode) { + this(Optional.of(exitCode), Optional.empty(), Optional.empty(), EMPTY_EXECUTABLE_ATTRIBUTES); + } + + public int getExitCode() { + return exitCode.orElseThrow(() -> { + return new IllegalStateException("Exit code is unavailable for timed-out command"); + }); + } + + public Result expectExitCode(int main, int... other) throws UnexpectedExitCodeException { + return expectExitCode(v -> { + return IntStream.concat(IntStream.of(main), IntStream.of(other)).boxed().anyMatch(Predicate.isEqual(v)); + }); + } + + public Result expectExitCode(Collection expected) throws UnexpectedExitCodeException { + return expectExitCode(expected::contains); + } + + public Result expectExitCode(IntPredicate expected) throws UnexpectedExitCodeException { + if (!expected.test(getExitCode())) { + throw new UnexpectedExitCodeException(this); + } + return this; + } + + public UnexpectedResultException unexpected() { + return new UnexpectedResultException(this); + } + + public UnexpectedResultException unexpected(String message) { + return new UnexpectedResultException(this, message); + } + + public Optional> findContent() { + return output.flatMap(CommandOutput::combined); + } + + public Optional> findStdout() { + return output.flatMap(CommandOutput::stdout); + } + + public Optional> findStderr() { + return output.flatMap(CommandOutput::stderr); + } + + // For backward compatibility + public List getOutput() { + return content(); + } + + public List content() { + return findContent().orElseThrow(); + } + + public List stdout() { + return findStdout().orElseThrow(); + } + + public List stderr() { + return findStderr().orElseThrow(); + } + + public Optional findByteContent() { + return byteOutput.flatMap(CommandOutput::combined); + } + + public Optional findByteStdout() { + return byteOutput.flatMap(CommandOutput::stdout); + } + + public Optional findByteStderr() { + return byteOutput.flatMap(CommandOutput::stderr); + } + + public byte[] byteContent() { + return findByteContent().orElseThrow(); + } + + public byte[] byteStdout() { + return findByteStdout().orElseThrow(); + } + + public byte[] byteStderr() { + return findByteStderr().orElseThrow(); + } + + public Result toCharacterResult(Charset charset, boolean keepByteContent) throws IOException { + Objects.requireNonNull(charset); + + if (byteOutput.isEmpty()) { + return this; + } + + var theByteOutput = byteOutput.get(); + + try { + Optional>> out; + if (theByteOutput.content().isEmpty()) { + // The content is unavailable. + out = Optional.empty(); + } else if (theByteOutput.stdoutContentSize() == 0) { + // The content is available, but empty. + out = Optional.of(new StringListContent(List.of())); + } else if (theByteOutput.interleaved()) { + // STDOUT and STDERR streams are interleaved. + out = theByteOutput.combined().map(data -> { + return toStringList(data, charset); + }); + } else { + // Non-empty STDOUT not interleaved with STDERR. + out = findByteStdout().map(data -> { + return toStringList(data, charset); + }); + } + + var err = findByteStderr().map(data -> { + return toStringList(data, charset); + }); + + var newOutput = combine(out, err, theByteOutput.interleaved); + + return new Result(exitCode, Optional.of(newOutput), byteOutput.filter(_ -> keepByteContent), execAttrs); + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + } + + public Result copyWithExecutableAttributes(ExecutableAttributes execAttrs) { + return new Result(exitCode, output, byteOutput, Objects.requireNonNull(execAttrs)); + } + + private static StringListContent toStringList(byte[] data, Charset charset) { + try (var bufReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(data), charset))) { + return new StringListContent(bufReader.lines().toList()); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + } + + public static sealed class UnexpectedResultException extends IOException { + + private UnexpectedResultException(Result value, String message) { + super(Objects.requireNonNull(message)); + this.value = Objects.requireNonNull(value); + } + + private UnexpectedResultException(Result value) { + this(value, String.format("Unexpected result from executing the command %s", value.execAttrs())); + } + + public Result getResult() { + return value; + } + + private final transient Result value; + + private static final long serialVersionUID = 1L; + } + + public static final class UnexpectedExitCodeException extends UnexpectedResultException { + + public UnexpectedExitCodeException(Result value, String message) { + super(value, message); + } + + public UnexpectedExitCodeException(Result value) { + this(value, String.format("Unexpected exit code %d from executing the command %s", value.getExitCode(), value.execAttrs())); + } + + private static final long serialVersionUID = 1L; + } + + public String description() { + var tokens = outputStreamsControl.descriptionTokens(); + if (isBinaryOutput()) { + tokens.add("byte"); + } + if (redirectRetainedStderr()) { + tokens.add("interleave"); + } + return String.join("; ", tokens); + } + + private Result execute(ProcessBuilder pb, long timeoutMillis) + throws IOException, InterruptedException { + + Objects.requireNonNull(pb); + + var theCharset = charset(); + + configureProcessBuilder(pb); + + var csc = new CachingStreamsConfig(); + + var process = pb.start(); + + BiConsumer gobbler = (in, ps) -> { + try { + if (isBinaryOutput()) { + try (in) { + in.transferTo(ps); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } else { + try (var bufReader = new BufferedReader(new InputStreamReader(in, theCharset))) { + bufReader.lines().forEach(ps::println); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + } finally { + suppressIOException(ps::flush); + } + }; + + // Start fetching process output streams. + // Do it before waiting for the process termination to avoid deadlocks. + + final Optional> stdoutGobbler; + if (mustReadOutputStream(pb.redirectOutput())) { + stdoutGobbler = Optional.of(CompletableFuture.runAsync(() -> { + gobbler.accept(process.getInputStream(), csc.out()); + }, gobblerExecutor)); + } else { + stdoutGobbler = Optional.empty(); + } + + final Optional> stderrGobbler; + if (!pb.redirectErrorStream() && mustReadOutputStream(pb.redirectError())) { + stderrGobbler = Optional.of(CompletableFuture.runAsync(() -> { + gobbler.accept(process.getErrorStream(), csc.err()); + }, gobblerExecutor)); + } else { + stderrGobbler = Optional.empty(); + } + + processListener().ifPresent(c -> { + c.accept(process); + }); + + final Optional exitCode; + if (timeoutMillis < 0) { + exitCode = Optional.of(process.waitFor()); + } else if (!process.waitFor(timeoutMillis, TimeUnit.MILLISECONDS)) { + // Destroy the process and cancel the process output stream gobblers. + process.destroy(); + for (var g : List.of(stdoutGobbler, stderrGobbler)) { + g.ifPresent(future -> { + future.cancel(true); + }); + } + exitCode = Optional.empty(); + } else { + exitCode = Optional.of(process.exitValue()); + } + + try { + if (isStoreOutputInFiles()) { + var stdoutStorage = streamFileSink(pb.redirectOutput()); + var stderrStorage = streamFileSink(pb.redirectError()); + + Function toInputStream = path -> { + try { + return Files.newInputStream(path); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }; + + try { + stdoutStorage.map(toInputStream).ifPresent(in -> { + gobbler.accept(in, csc.out()); + }); + + stderrStorage.map(toInputStream).ifPresent(in -> { + gobbler.accept(in, csc.err()); + }); + } finally { + Consumer silentDeleter = path -> { + suppressIOException(Files::delete, path); + }; + + stdoutStorage.ifPresent(silentDeleter); + stderrStorage.ifPresent(silentDeleter); + } + } else { + stdoutGobbler.ifPresent(CommandOutputControl::join); + stderrGobbler.ifPresent(CommandOutputControl::join); + } + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + + return csc.createResult(exitCode, new ProcessAttributes(getPID(process), pb.command())); + } + + private Result execute(ToolProvider tp, long timeoutMillis, String... args) + throws IOException, InterruptedException { + + var csc = new CachingStreamsConfig(); + + Optional exitCode; + var out = csc.out(); + var err = csc.err(); + try { + if (timeoutMillis < 0) { + exitCode = Optional.of(tp.run(out, err, args)); + } else { + var future = new CompletableFuture>(); + + var workerThread = Thread.ofVirtual().start(() -> { + Optional result = Optional.empty(); + try { + result = Optional.of(tp.run(out, err, args)); + } catch (Exception ex) { + future.completeExceptionally(ex); + return; + } + future.complete(result); + }); + + try { + exitCode = future.get(timeoutMillis, TimeUnit.MILLISECONDS); + } catch (ExecutionException ex) { + // Rethrow the cause (ex.getCause()) as a RuntimeException. + // If `ex.getCause()` returns an Error, ExceptionBox.unbox() will throw it. + throw ExceptionBox.toUnchecked(ExceptionBox.unbox(ex.getCause())); + } catch (TimeoutException ex) { + workerThread.interrupt(); + exitCode = Optional.empty(); + } + } + } finally { + suppressIOException(out::flush); + suppressIOException(err::flush); + } + + return csc.createResult(exitCode, new ToolProviderAttributes(tp.name(), List.of(args))); + } + + private CommandOutputControl setOutputControl(boolean set, OutputControlOption v) { + outputStreamsControl.stdout().set(set, v); + outputStreamsControl.stderr().set(set, v); + return this; + } + + private CommandOutputControl setFlag(Flag flag, boolean v) { + flags = flag.set(flags, v); + return this; + } + + private Optional streamFileSink(ProcessBuilder.Redirect redirect) { + return Optional.of(redirect) + .filter(Predicate.isEqual(ProcessBuilder.Redirect.DISCARD).negate()) + .map(ProcessBuilder.Redirect::file) + .map(File::toPath); + } + + private void configureProcessBuilder(ProcessBuilder pb) throws IOException { + + var stdoutRedirect = outputStreamsControl.stdout().asProcessBuilderRedirect(); + var stderrRedirect = outputStreamsControl.stderr().asProcessBuilderRedirect(); + + if (!stdoutRedirect.equals(stderrRedirect) && Stream.of( + stdoutRedirect, + stderrRedirect + ).noneMatch(Predicate.isEqual(ProcessBuilder.Redirect.DISCARD)) && redirectRetainedStderr()) { + throw new IllegalStateException(String.format( + "Can't redirect stderr into stdout because they have different redirects: stdout=%s; stderr=%s", + stdoutRedirect, stderrRedirect)); + } + + pb.redirectErrorStream(redirectRetainedStderr()); + if (replaceStdoutWithStderr()) { + if (stderrRedirect.equals(ProcessBuilder.Redirect.INHERIT)) { + stderrRedirect = ProcessBuilder.Redirect.PIPE; + } + pb.redirectErrorStream(false); + } + + stdoutRedirect = mapRedirect(stdoutRedirect); + stderrRedirect = mapRedirect(stderrRedirect); + + if (dumpStdout != null && stdoutRedirect.equals(ProcessBuilder.Redirect.INHERIT)) { + stdoutRedirect = ProcessBuilder.Redirect.PIPE; + } + + if (dumpStderr != null && stderrRedirect.equals(ProcessBuilder.Redirect.INHERIT)) { + stderrRedirect = ProcessBuilder.Redirect.PIPE; + } + + pb.redirectOutput(stdoutRedirect); + pb.redirectError(stderrRedirect); + } + + private ProcessBuilder.Redirect mapRedirect(ProcessBuilder.Redirect redirect) throws IOException { + if (isStoreOutputInFiles() && redirect.equals(ProcessBuilder.Redirect.PIPE)) { + var sink = Files.createTempFile("jpackageOutputTempFile", ".tmp"); + return ProcessBuilder.Redirect.to(sink.toFile()); + } else { + return redirect; + } + } + + /** + * Returns {@code true} if STDERR is not discarded and will be redirected to STDOUT, and {@code false} otherwise. + */ + private boolean redirectRetainedStderr() { + return isRedirectStderr() && !outputStreamsControl.stderr().discard(); + } + + /** + * Returns {@code true} if STDERR will replace STDOUT, and {@code false} otherwise. + *

    + * STDERR will replace STDOUT if it is redirected and not discarded, and if STDOUT is discarded. + */ + private boolean replaceStdoutWithStderr() { + return redirectRetainedStderr() && outputStreamsControl.stdout().discard(); + } + + private static T join(CompletableFuture future, T cancelledValue) { + Objects.requireNonNull(future); + try { + return future.join(); + } catch (CancellationException ex) { + return cancelledValue; + } catch (CompletionException ex) { + switch (ExceptionBox.unbox(ex.getCause())) { + case IOException cause -> { + throw new UncheckedIOException(cause); + } + case UncheckedIOException cause -> { + throw cause; + } + case Exception cause -> { + throw ExceptionBox.toUnchecked(cause); + } + } + } + } + + private static void join(CompletableFuture future) { + join(future, null); + } + + private static boolean mustReadOutputStream(ProcessBuilder.Redirect redirect) { + return redirect.equals(ProcessBuilder.Redirect.PIPE); + } + + private static Optional> read(OutputControl outputControl, CachingPrintStream cps) throws IOException { + final var bufferAsString = cps.bufferContents(); + try (final var bufReader = new BufferedReader(new StringReader(bufferAsString.orElse("")))) { + if (outputControl.saveFirstLine()) { + return Optional.of(bufReader.lines().findFirst().map(List::of).orElseGet(List::of)); + } else if (outputControl.saveAll()) { + return Optional.of(bufReader.lines().toList()); + } else { + return Optional.empty(); + } + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + } + + private static Optional readBinary(OutputControl outputControl, CachingPrintStream cps) { + if (outputControl.save()) { + return cps.buf().map(ByteArrayOutputStream::toByteArray).or(() -> { + return Optional.of(new byte[0]); + }); + } else { + return Optional.empty(); + } + } + + private static CommandOutput combine( + Optional> out, + Optional> err, + boolean interleaved) { + + if (out.isEmpty() && err.isEmpty()) { + return CommandOutput.empty(); + } else if (out.isEmpty()) { + // This branch is unreachable because it is impossible to make it save stderr without saving stdout. + // If streams are configured for saving and stdout is discarded, + // its saved contents will be an Optional instance wrapping an empty content, not an empty Optional. + throw ExceptionBox.reachedUnreachable(); + } else if (err.isEmpty()) { + return new CommandOutput<>(out, Integer.MAX_VALUE, interleaved); + } else { + final var combined = out.get().append(err.get()); + return new CommandOutput<>(Optional.of(combined), out.orElseThrow().size(), interleaved); + } + } + + private static PrintStream nullPrintStream() { + return new PrintStream(OutputStream.nullOutputStream()); + } + + private sealed interface Content { + T data(); + int size(); + Content slice(int from, int to); + Content append(Content other); + } + + private record StringListContent(List data) implements Content> { + StringListContent { + Objects.requireNonNull(data); + } + + @Override + public int size() { + return data.size(); + } + + @Override + public StringListContent slice(int from, int to) { + return new StringListContent(data.subList(from, to)); + } + + @Override + public StringListContent append(Content> other) { + return new StringListContent(Stream.of(data, other.data()).flatMap(List::stream).toList()); + } + } + + private record ByteContent(byte[] data) implements Content { + ByteContent { + Objects.requireNonNull(data); + } + + @Override + public int size() { + return data.length; + } + + @Override + public ByteContent slice(int from, int to) { + return new ByteContent(Arrays.copyOfRange(data, from, to)); + } + + @Override + public ByteContent append(Content other) { + byte[] combined = new byte[size() + other.size()]; + System.arraycopy(data, 0, combined, 0, data.length); + System.arraycopy(other.data(), 0, combined, data.length, other.size()); + return new ByteContent(combined); + } + } + + private record OutputStreamsControl(OutputControl stdout, OutputControl stderr) { + OutputStreamsControl { + Objects.requireNonNull(stdout); + Objects.requireNonNull(stderr); + } + + OutputStreamsControl() { + this(new OutputControl(), new OutputControl()); + } + + OutputStreamsControl copy() { + return new OutputStreamsControl(stdout.copy(), stderr.copy()); + } + + List descriptionTokens() { + final List tokens = new ArrayList<>(); + if (stdout.save()) { // Save flags are the same for stdout and stderr, checking stdout is sufficient. + streamsLabel("save ", true).ifPresent(tokens::add); + } + if (stdout.dump() || stderr.dump()) { + streamsLabel("echo ", true).ifPresent(tokens::add); + } + streamsLabel("discard ", false).ifPresent(tokens::add); + if (tokens.isEmpty()) { + // Unreachable because there is always at least one token in the description. + throw ExceptionBox.reachedUnreachable(); + } else { + return tokens; + } + } + + private Optional streamsLabel(String prefix, boolean negate) { + Objects.requireNonNull(prefix); + final var str = Stream.of(stdoutLabel(negate), stderrLabel(negate)) + .filter(Optional::isPresent) + .map(Optional::orElseThrow) + .collect(joining("+")); + if (str.isEmpty()) { + return Optional.empty(); + } else { + return Optional.of(prefix + str); + } + } + + private Optional stdoutLabel(boolean negate) { + if ((stdout.discard() && !negate) || (!stdout.discard() && negate)) { + return Optional.of("out"); + } else { + return Optional.empty(); + } + } + + private Optional stderrLabel(boolean negate) { + if ((stderr.discard() && !negate) || (!stderr.discard() && negate)) { + return Optional.of("err"); + } else { + return Optional.empty(); + } + } + } + + private record CachingPrintStream(PrintStream ps, Optional buf) { + CachingPrintStream { + Objects.requireNonNull(ps); + Objects.requireNonNull(buf); + } + + Optional bufferContents() { + return buf.map(ByteArrayOutputStream::toString); + } + + static Builder build(Charset charset) { + return new Builder(charset); + } + + static final class Builder { + + private Builder(Charset charset) { + this.charset = Objects.requireNonNull(charset); + } + + Builder save(boolean v) { + save = v; + return this; + } + + Builder discard(boolean v) { + discard = v; + return this; + } + + Builder dumpStream(PrintStream v) { + dumpStream = v; + return this; + } + + Builder buffer(ByteArrayOutputStream v) { + externalBuffer = v; + return this; + } + + CachingPrintStream create() { + final Optional buf; + if (save && !discard) { + buf = Optional.ofNullable(externalBuffer).or(() -> { + return Optional.of(new ByteArrayOutputStream()); + }); + } else { + buf = Optional.empty(); + } + + final PrintStream ps; + if (buf.isPresent() && dumpStream != null) { + ps = new PrintStream(new TeeOutputStream(List.of(buf.get(), dumpStream)), true, dumpStream.charset()); + } else if (!discard) { + ps = buf.map(in -> { + return new PrintStream(in, false, charset); + }).or(() -> { + return Optional.ofNullable(dumpStream); + }).orElseGet(CommandOutputControl::nullPrintStream); + } else { + ps = nullPrintStream(); + } + + return new CachingPrintStream(ps, buf); + } + + private boolean save; + private boolean discard; + private PrintStream dumpStream; + private ByteArrayOutputStream externalBuffer; + private final Charset charset; + } + } + + private final class CachingStreamsConfig { + + CachingStreamsConfig() { + out = outputStreamsControl.stdout().buildCachingPrintStream(dumpStdout(), charset()).create(); + if (isRedirectStderr()) { + var builder = outputStreamsControl.stderr().buildCachingPrintStream(dumpStdout(), charset()); + out.buf().ifPresent(builder::buffer); + err = builder.create(); + } else { + err = outputStreamsControl.stderr().buildCachingPrintStream(dumpStderr(), charset()).create(); + } + } + + Result createResult(Optional exitCode, ExecutableAttributes execAttrs) throws IOException { + + CommandOutput> output; + CommandOutput byteOutput; + + CachingPrintStream effectiveOut; + if (out.buf().isEmpty() && isRedirectStderr()) { + effectiveOut = new CachingPrintStream(nullPrintStream(), err.buf()); + } else { + effectiveOut = out; + } + + if (isBinaryOutput()) { + Optional outContent, errContent; + if (isRedirectStderr()) { + outContent = readBinary(outputStreamsControl.stdout(), effectiveOut).map(ByteContent::new); + errContent = Optional.empty(); + } else { + outContent = readBinary(outputStreamsControl.stdout(), out).map(ByteContent::new); + errContent = readBinary(outputStreamsControl.stderr(), err).map(ByteContent::new); + } + + byteOutput = combine(outContent, errContent, redirectRetainedStderr()); + output = null; + } else { + Optional outContent, errContent; + if (isRedirectStderr()) { + outContent = read(outputStreamsControl.stdout(), effectiveOut).map(StringListContent::new); + errContent = Optional.empty(); + } else { + outContent = read(outputStreamsControl.stdout(), out).map(StringListContent::new); + errContent = read(outputStreamsControl.stderr(), err).map(StringListContent::new); + } + + output = combine(outContent, errContent, redirectRetainedStderr()); + byteOutput = null; + } + + return new Result(exitCode, Optional.ofNullable(output), Optional.ofNullable(byteOutput), execAttrs); + } + + PrintStream out() { + return out.ps(); + } + + PrintStream err() { + return err.ps(); + } + + private final CachingPrintStream out; + private final CachingPrintStream err; + } + + private static final class OutputControl { + + OutputControl() { + } + + private OutputControl(OutputControl other) { + dump = other.dump; + discard = other.discard; + save = other.save; + } + + boolean save() { + return save.isPresent(); + } + + boolean saveAll() { + return save.orElse(null) == OutputControlOption.SAVE_ALL; + } + + boolean saveFirstLine() { + return save.orElse(null) == OutputControlOption.SAVE_FIRST_LINE; + } + + boolean discard() { + return discard || (!dump && save.isEmpty()); + } + + boolean dump() { + return !discard && dump; + } + + OutputControl dump(boolean v) { + this.dump = v; + return this; + } + + OutputControl discard(boolean v) { + this.discard = v; + return this; + } + + OutputControl saveAll(boolean v) { + if (v) { + save = Optional.of(OutputControlOption.SAVE_ALL); + } else { + save = Optional.empty(); + } + return this; + } + + OutputControl saveFirstLine(boolean v) { + if (v) { + save = Optional.of(OutputControlOption.SAVE_FIRST_LINE); + } else { + save = Optional.empty(); + } + return this; + } + + OutputControl set(boolean set, OutputControlOption v) { + switch (v) { + case DUMP -> dump(set); + case SAVE_ALL -> saveAll(set); + case SAVE_FIRST_LINE -> saveFirstLine(set); + } + return this; + } + + OutputControl copy() { + return new OutputControl(this); + } + + ProcessBuilder.Redirect asProcessBuilderRedirect() { + if (discard()) { + return ProcessBuilder.Redirect.DISCARD; + } else if (dump && !save()) { + return ProcessBuilder.Redirect.INHERIT; + } else { + return ProcessBuilder.Redirect.PIPE; + } + } + + CachingPrintStream.Builder buildCachingPrintStream(PrintStream dumpStream, Charset charset) { + Objects.requireNonNull(dumpStream); + final var builder = CachingPrintStream.build(charset).save(save()).discard(discard()); + if (dump()) { + builder.dumpStream(dumpStream); + } + return builder; + } + + private boolean dump; + private boolean discard; + private Optional save = Optional.empty(); + } + + private record CommandOutput(Optional> content, int stdoutContentSize, boolean interleaved) { + + CommandOutput { + Objects.requireNonNull(content); + if (interleaved) { + stdoutContentSize = content.map(Content::size).orElse(-1); + } + } + + CommandOutput() { + this(Optional.empty(), 0, false); + } + + Optional combined() { + return content.map(Content::data); + } + + /** + * Returns non-empty {@code Optional} if stdout is available and stdout and stderr are not interleaved. + * @return stdout if it can be extracted from the combined output + */ + Optional stdout() { + if (withoutExtractableStdout()) { + return Optional.empty(); + } + + final var theContent = content.orElseThrow(); + if (stdoutContentSize == theContent.size()) { + return combined(); + } else { + return Optional.of(theContent.slice(0, Integer.min(stdoutContentSize, theContent.size())).data()); + } + } + + /** + * Returns non-empty {@code Optional} if stderr is available and stdout and stderr are not interleaved. + * @return stderr if it can be extracted from the combined output + */ + Optional stderr() { + if (withoutExtractableStderr()) { + return Optional.empty(); + } else if (stdoutContentSize <= 0) { + return combined(); + } else { + final var theContent = content.orElseThrow(); + return Optional.of(theContent.slice(stdoutContentSize, theContent.size()).data()); + } + } + + @SuppressWarnings("unchecked") + static CommandOutput empty() { + return (CommandOutput)EMPTY; + } + + private boolean withoutExtractableStdout() { + return interleaved || content.isEmpty() || stdoutContentSize < 0; + } + + private boolean withoutExtractableStderr() { + return interleaved || content.isEmpty() || stdoutContentSize > content.get().size(); + } + + private static final CommandOutput EMPTY = new CommandOutput<>(); + } + + private record ToolProviderExecutable(ToolProvider tp, List args, CommandOutputControl coc) implements Executable { + + ToolProviderExecutable { + Objects.requireNonNull(tp); + Objects.requireNonNull(args); + Objects.requireNonNull(coc); + } + + @Override + public Result execute() throws IOException, InterruptedException { + return coc.execute(tp, -1, args.toArray(String[]::new)); + } + + @Override + public Result execute(long timeout, TimeUnit unit) throws IOException, InterruptedException { + return coc.execute(tp, unit.toMillis(timeout), args.toArray(String[]::new)); + } + + @Override + public ExecutableAttributes attributes() { + return new ToolProviderAttributes(tp.name(), args); + } + } + + private record ProcessExecutable(ProcessBuilder pb, CommandOutputControl coc) implements Executable { + + ProcessExecutable { + Objects.requireNonNull(pb); + Objects.requireNonNull(coc); + } + + @Override + public Result execute() throws IOException, InterruptedException { + return coc.execute(pb, -1L); + } + + @Override + public Result execute(long timeout, TimeUnit unit) throws IOException, InterruptedException { + return coc.execute(pb, unit.toMillis(timeout)); + } + + @Override + public ExecutableAttributes attributes() { + return new ProcessAttributes(Optional.empty(), pb.command()); + } + } + + private static Optional getPID(Process p) { + try { + return Optional.of(p.pid()); + } catch (UnsupportedOperationException ex) { + return Optional.empty(); + } + } + + private static void suppressIOException(ThrowingRunnable r) { + try { + r.run(); + } catch (IOException ex) {} + } + + private static void suppressIOException(ThrowingConsumer c, T value) { + suppressIOException(() -> { + c.accept(value); + }); + } + + private int flags; + private final OutputStreamsControl outputStreamsControl; + private PrintStream dumpStdout; + private PrintStream dumpStderr; + private Charset charset; + private Consumer processListener; + + // Executor to run subprocess output stream gobblers. + // Output stream gobblers should start fetching output streams ASAP after the process starts. + // No pooling, no waiting. + // CompletableFuture#runAsync() method starts an output stream gobbler. + // If used with the default executor, it is known to make WiX3 light.exe create + // a locked msi file when multiple jpackage tool providers are executed asynchronously. + // The AsyncTest fails with cryptic java.nio.file.FileSystemException error: + // jtreg_open_test_jdk_tools_jpackage_share_AsyncTest_java\\tmp\\jdk.jpackage8108811639097525318\\msi\\Foo-1.0.msi: The process cannot access the file because it is being used by another process. + // The remedy for the problem is to use non-pooling executor to run subprocess output stream gobblers. + private final java.util.concurrent.Executor gobblerExecutor = Executors.newVirtualThreadPerTaskExecutor(); + + private enum OutputControlOption { + SAVE_ALL, SAVE_FIRST_LINE, DUMP + } + + private enum Flag { + DUMP (0x01), + REDIRECT_STDERR (0x02), + BINARY_OUTPUT (0x04), + STORE_OUTPUT_IN_FILES (0x08), + DISCARD_STDOUT (0x10), + DISCARD_STDERR (0x20), + ; + + Flag(int value) { + this.value = value; + } + + int set(int flags, boolean set) { + if (set) { + return flags | value; + } else { + return flags & ~value; + } + } + + boolean isSet(int flags) { + return (flags & value) != 0; + } + + private final int value; + } +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Enquoter.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Enquoter.java similarity index 77% rename from src/jdk.jpackage/share/classes/jdk/jpackage/internal/Enquoter.java rename to src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Enquoter.java index 51f97ad2cd6..d9ededcbadb 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Enquoter.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/Enquoter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, 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,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package jdk.jpackage.internal; +package jdk.jpackage.internal.util; import java.util.Optional; import java.util.function.BiConsumer; @@ -32,39 +32,43 @@ import java.util.regex.Pattern; /** * Add quotes to the given string in a configurable way. */ -final class Enquoter { +public final class Enquoter { private Enquoter() { setQuoteChar('"'); } - static Enquoter forPropertyValues() { + public static Enquoter identity() { + return new Enquoter(); + } + + public static Enquoter forPropertyValues() { return new Enquoter() .setEnquotePredicate(QUOTE_IF_WHITESPACES) .setEscaper(PREPEND_BACKSLASH); } - static Enquoter forShellLiterals() { + public static Enquoter forShellLiterals() { return forShellLiterals('\''); } - static Enquoter forShellLiterals(char quoteChar) { + public static Enquoter forShellLiterals(char quoteChar) { return new Enquoter() .setQuoteChar(quoteChar) .setEnquotePredicate(x -> true) .setEscaper(PREPEND_BACKSLASH); } - String applyTo(String v) { + public String applyTo(String v) { if (!needQuotes.test(v)) { return v; } else { var buf = new StringBuilder(); buf.appendCodePoint(beginQuoteChr); - Optional.of(escaper).ifPresentOrElse(op -> { + Optional.ofNullable(escaper).ifPresentOrElse(op -> { v.codePoints().forEachOrdered(chr -> { if (chr == beginQuoteChr || chr == endQuoteChr) { - escaper.accept(chr, buf); + op.accept(chr, buf); } else { buf.appendCodePoint(chr); } @@ -77,28 +81,23 @@ final class Enquoter { } } - Enquoter setQuoteChar(char chr) { + public Enquoter setQuoteChar(char chr) { beginQuoteChr = chr; endQuoteChr = chr; return this; } - Enquoter setEscaper(BiConsumer v) { + public Enquoter setEscaper(BiConsumer v) { escaper = v; return this; } - Enquoter setEnquotePredicate(Predicate v) { + public Enquoter setEnquotePredicate(Predicate v) { needQuotes = v; return this; } - private int beginQuoteChr; - private int endQuoteChr; - private BiConsumer escaper; - private Predicate needQuotes = str -> false; - - private static final Predicate QUOTE_IF_WHITESPACES = new Predicate() { + public static final Predicate QUOTE_IF_WHITESPACES = new Predicate() { @Override public boolean test(String t) { return pattern.matcher(t).find(); @@ -106,8 +105,13 @@ final class Enquoter { private final Pattern pattern = Pattern.compile("\\s"); }; - private static final BiConsumer PREPEND_BACKSLASH = (chr, buf) -> { + public static final BiConsumer PREPEND_BACKSLASH = (chr, buf) -> { buf.append('\\'); buf.appendCodePoint(chr); }; + + private int beginQuoteChr; + private int endQuoteChr; + private BiConsumer escaper; + private Predicate needQuotes = str -> false; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RetryExecutor.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RetryExecutor.java new file mode 100644 index 00000000000..c67373e8233 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RetryExecutor.java @@ -0,0 +1,194 @@ +/* + * 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 + * 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.util; + +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; + +import java.time.Duration; +import java.util.Iterator; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.function.Function; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.internal.util.function.ThrowingFunction; +import jdk.jpackage.internal.util.function.ThrowingSupplier; + +public class RetryExecutor { + + public RetryExecutor(Class exceptionType) { + this.exceptionType = Objects.requireNonNull(exceptionType); + setMaxAttemptsCount(5); + setAttemptTimeout(2, TimeUnit.SECONDS); + } + + final public Class exceptionType() { + return exceptionType; + } + + public RetryExecutor setExecutable(ThrowingFunction>, T, E> v) { + executable = v; + return this; + } + + final public RetryExecutor setExecutable(ThrowingSupplier v) { + if (v != null) { + setExecutable(_ -> { + return v.get(); + }); + } else { + executable = null; + } + return this; + } + + public RetryExecutor setMaxAttemptsCount(int v) { + attempts = v; + return this; + } + + final public RetryExecutor setAttemptTimeout(long v, TimeUnit unit) { + return setAttemptTimeout(Duration.of(v, unit.toChronoUnit())); + } + + public RetryExecutor setAttemptTimeout(Duration v) { + timeout = v; + return this; + } + + public RetryExecutor setExceptionMapper(Function v) { + toUnchecked = v; + return this; + } + + public RetryExecutor setSleepFunction(Consumer v) { + sleepFunction = v; + return this; + } + + final public RetryExecutor mutate(Consumer> mutator) { + mutator.accept(this); + return this; + } + + public T execute() throws E { + var curExecutable = executable(); + T result = null; + var attemptIter = new DefaultContext(); + while (attemptIter.hasNext()) { + attemptIter.next(); + try { + result = curExecutable.apply(attemptIter); + break; + } catch (Exception ex) { + if (!exceptionType.isInstance(ex)) { + throw ExceptionBox.toUnchecked(ex); + } else if (attemptIter.isLastAttempt()) { + // No more attempts left. This is fatal. + throw exceptionType.cast(ex); + } else { + curExecutable = executable(); + } + } + + sleep(); + } + + return result; + } + + final public T executeUnchecked() { + try { + return execute(); + } catch (Error | RuntimeException t) { + throw t; + } catch (Exception ex) { + if (exceptionType.isInstance(ex)) { + throw Optional.ofNullable(toUnchecked).orElse(ExceptionBox::toUnchecked).apply(exceptionType.cast(ex)); + } else { + // Unreachable unless it is a direct subclass of Throwable, + // which is not Error or Exception which should not happen. + throw ExceptionBox.reachedUnreachable(); + } + } + } + + public interface Context { + boolean isLastAttempt(); + int attempt(); + T executor(); + } + + private final class DefaultContext implements Context>, Iterator { + + @Override + public boolean isLastAttempt() { + return !hasNext(); + } + + @Override + public int attempt() { + return attempt; + } + + @Override + public boolean hasNext() { + return (attempts - attempt) > 1; + } + + @Override + public Void next() { + attempt++; + return null; + } + + @Override + public RetryExecutor executor() { + return RetryExecutor.this; + } + + private int attempt = -1; + } + + private ThrowingFunction>, T, E> executable() { + return Optional.ofNullable(executable).orElseThrow(() -> { + return new IllegalStateException("No executable"); + }); + } + + private void sleep() { + Optional.ofNullable(timeout).ifPresent(Optional.ofNullable(sleepFunction).orElseGet(() -> { + return toConsumer(Thread::sleep); + })); + } + + private final Class exceptionType; + private ThrowingFunction>, T, E> executable; + private int attempts; + private Duration timeout; + private Function toUnchecked; + private Consumer sleepFunction; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/TeeOutputStream.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/TeeOutputStream.java new file mode 100644 index 00000000000..db098eb2b48 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/TeeOutputStream.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. 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.util; + +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Objects; +import jdk.jpackage.internal.util.function.ThrowingConsumer; + +public final class TeeOutputStream extends OutputStream { + + public TeeOutputStream(Iterable items) { + items.forEach(Objects::requireNonNull); + this.items = items; + } + + @Override + public void write(int b) throws IOException { + for (final var item : items) { + item.write(b); + } + } + + @Override + public void write(byte[] b) throws IOException { + for (final var item : items) { + item.write(b); + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + for (final var item : items) { + item.write(b, off, len); + } + } + + @Override + public void flush() throws IOException { + forEach(Flushable::flush); + } + + @Override + public void close() throws IOException { + forEach(Closeable::close); + } + + private void forEach(ThrowingConsumer c) throws IOException { + IOException firstEx = null; + for (final var item : items) { + try { + c.accept(item); + } catch (IOException e) { + if (firstEx == null) { + firstEx = e; + } + } + } + if (firstEx != null) { + throw firstEx; + } + } + + private final Iterable items; +} diff --git a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java index e1e02ba7850..b196a5805a0 100644 --- a/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.java +++ b/src/jdk.jpackage/unix/classes/jdk/jpackage/internal/UnixLaunchersAsServices.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 @@ -24,8 +24,6 @@ */ package jdk.jpackage.internal; -import jdk.jpackage.internal.model.Launcher; -import jdk.jpackage.internal.model.Application; import java.io.IOException; import java.nio.file.Path; import java.util.Collections; @@ -36,6 +34,9 @@ import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.jpackage.internal.model.Application; +import jdk.jpackage.internal.model.Launcher; +import jdk.jpackage.internal.util.Enquoter; /** * Helper to install launchers as services for Unix installers. diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java index dc9891f154a..de52a222d7d 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinBundlingEnvironment.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,16 +31,17 @@ import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_WIN_EXE import static jdk.jpackage.internal.cli.StandardBundlingOperation.CREATE_WIN_MSI; import jdk.jpackage.internal.cli.Options; -import jdk.jpackage.internal.util.Result; public class WinBundlingEnvironment extends DefaultBundlingEnvironment { public WinBundlingEnvironment() { - super(build() - .defaultOperation(CREATE_WIN_EXE) - .bundler(CREATE_WIN_APP_IMAGE, WinBundlingEnvironment::createAppImage) - .bundler(CREATE_WIN_EXE, LazyLoad::sysEnv, WinBundlingEnvironment::createExePackage) - .bundler(CREATE_WIN_MSI, LazyLoad::sysEnv, WinBundlingEnvironment::createMsiPackage)); + super(build().mutate(builder -> { + var sysEnv = runOnce(WinSystemEnvironment::create); + + builder + .bundler(CREATE_WIN_EXE, sysEnv, WinBundlingEnvironment::createExePackage) + .bundler(CREATE_WIN_MSI, sysEnv, WinBundlingEnvironment::createMsiPackage); + }).defaultOperation(CREATE_WIN_EXE).bundler(CREATE_WIN_APP_IMAGE, WinBundlingEnvironment::createAppImage)); } private static void createMsiPackage(Options options, WinSystemEnvironment sysEnv) { @@ -98,12 +99,4 @@ public class WinBundlingEnvironment extends DefaultBundlingEnvironment { } } - private static final class LazyLoad { - - static Result sysEnv() { - return SYS_ENV; - } - - private static final Result SYS_ENV = WinSystemEnvironment.create(); - } } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java index e2ed175fbf3..c4f8610312a 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WixTool.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 @@ -233,10 +233,10 @@ public enum WixTool { // Detect FIPS mode var fips = false; try { - final var exec = Executor.of(toolPath.toString(), "-?").setQuiet(true).saveOutput(true); - final var exitCode = exec.execute(); + final var result = Executor.of(toolPath.toString(), "-?").setQuiet(true).saveOutput(true).execute(); + final var exitCode = result.getExitCode(); if (exitCode != 0 /* 308 */) { - final var output = exec.getOutput(); + final var output = result.getOutput(); if (!output.isEmpty() && output.get(0).contains("error CNDL0308")) { fips = true; } diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/ExecutorTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/ExecutorTest.java deleted file mode 100644 index 2b075e0f13c..00000000000 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/ExecutorTest.java +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * 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.test; - -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toSet; -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 java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.io.PrintWriter; -import java.io.UncheckedIOException; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.function.Consumer; -import java.util.spi.ToolProvider; -import java.util.stream.Stream; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -public class ExecutorTest extends JUnitAdapter { - - private record Command(List stdout, List stderr) { - Command { - stdout.forEach(Objects::requireNonNull); - stderr.forEach(Objects::requireNonNull); - } - - List asExecutable() { - final List commandline = new ArrayList<>(); - if (TKit.isWindows()) { - commandline.addAll(List.of("cmd", "/C")); - } else { - commandline.addAll(List.of("sh", "-c")); - } - commandline.add(Stream.concat(createEchoCommands(stdout), - createEchoCommands(stderr).map(v -> v + ">&2")).collect(joining(" && "))); - return commandline; - } - - private static Stream createEchoCommands(List lines) { - return lines.stream().map(line -> { - if (TKit.isWindows()) { - return "(echo " + line + ")"; - } else { - return "echo " + line; - } - }); - } - - ToolProvider asToolProvider() { - return new ToolProvider() { - - @Override - public int run(PrintWriter out, PrintWriter err, String... args) { - stdout.forEach(out::println); - stderr.forEach(err::println); - return 0; - } - - @Override - public String name() { - return "test"; - } - }; - } - } - - private enum OutputData { - EMPTY(List.of()), - ONE_LINE(List.of("Jupiter")), - MANY(List.of("Uranus", "Saturn", "Earth")); - - OutputData(List data) { - data.forEach(Objects::requireNonNull); - this.data = data; - } - - final List data; - } - - private record CommandSpec(OutputData stdout, OutputData stderr) { - CommandSpec { - Objects.requireNonNull(stdout); - Objects.requireNonNull(stderr); - } - - Command command() { - return new Command(stdout.data.stream().map(line -> { - return "stdout." + line; - }).toList(), stderr.data.stream().map(line -> { - return "stderr." + line; - }).toList()); - } - } - - public enum OutputControl { - DUMP(Executor::dumpOutput), - SAVE_ALL(Executor::saveOutput), - SAVE_FIRST_LINE(Executor::saveFirstLineOfOutput), - DISCARD_STDOUT(Executor::discardStdout), - DISCARD_STDERR(Executor::discardStderr), - ; - - OutputControl(Consumer configureExector) { - this.configureExector = Objects.requireNonNull(configureExector); - } - - Executor applyTo(Executor exec) { - configureExector.accept(exec); - return exec; - } - - static List> variants() { - final List> variants = new ArrayList<>(); - for (final var withDump : BOOLEAN_VALUES) { - variants.addAll(Stream.of( - Set.of(), - Set.of(SAVE_ALL), - Set.of(SAVE_FIRST_LINE), - Set.of(DISCARD_STDOUT), - Set.of(DISCARD_STDERR), - Set.of(SAVE_ALL, DISCARD_STDOUT), - Set.of(SAVE_FIRST_LINE, DISCARD_STDOUT), - Set.of(SAVE_ALL, DISCARD_STDERR), - Set.of(SAVE_FIRST_LINE, DISCARD_STDERR), - Set.of(SAVE_ALL, DISCARD_STDOUT, DISCARD_STDERR), - Set.of(SAVE_FIRST_LINE, DISCARD_STDOUT, DISCARD_STDERR) - ).map(v -> { - if (withDump) { - return Stream.concat(Stream.of(DUMP), v.stream()).collect(toSet()); - } else { - return v; - } - }).toList()); - } - return variants.stream().map(options -> { - return options.stream().filter(o -> { - return o.configureExector != NOP; - }).collect(toSet()); - }).distinct().toList(); - } - - private final Consumer configureExector; - - static final Set SAVE = Set.of(SAVE_ALL, SAVE_FIRST_LINE); - } - - public record OutputTestSpec(boolean toolProvider, Set outputControl, CommandSpec commandSpec) { - public OutputTestSpec { - outputControl.forEach(Objects::requireNonNull); - if (outputControl.containsAll(OutputControl.SAVE)) { - throw new IllegalArgumentException(); - } - Objects.requireNonNull(commandSpec); - } - - @Override - public String toString() { - final List tokens = new ArrayList<>(); - - if (toolProvider) { - tokens.add("tool-provider"); - } - - tokens.add("output=" + format(outputControl)); - tokens.add("command=" + commandSpec); - - return String.join(",", tokens.toArray(String[]::new)); - } - - void test() { - final var command = commandSpec.command(); - final var commandWithDiscardedStreams = discardStreams(command); - - final Executor.Result[] result = new Executor.Result[1]; - final var outputCapture = OutputCapture.captureOutput(() -> { - result[0] = createExecutor(command).executeWithoutExitCodeCheck(); - }); - - assertEquals(0, result[0].getExitCode()); - - // If we dump the subprocesses's output, and the command produced both STDOUT and STDERR, - // then the captured STDOUT may contain interleaved command's STDOUT and STDERR, - // not in sequential order (STDOUT followed by STDERR). - // In this case don't check the contents of the captured command's STDOUT. - if (toolProvider || outputCapture.outLines().isEmpty() || (command.stdout().isEmpty() || command.stderr().isEmpty())) { - assertEquals(expectedCapturedSystemOut(commandWithDiscardedStreams), outputCapture.outLines()); - } - assertEquals(expectedCapturedSystemErr(commandWithDiscardedStreams), outputCapture.errLines()); - - assertEquals(expectedResultStdout(commandWithDiscardedStreams), result[0].stdout().getOutput()); - assertEquals(expectedResultStderr(commandWithDiscardedStreams), result[0].stderr().getOutput()); - - if (!saveOutput()) { - assertNull(result[0].getOutput()); - } else { - assertNotNull(result[0].getOutput()); - final var allExpectedOutput = expectedCommandOutput(command); - assertEquals(allExpectedOutput.isEmpty(), result[0].getOutput().isEmpty()); - if (!allExpectedOutput.isEmpty()) { - if (outputControl.contains(OutputControl.SAVE_ALL)) { - assertEquals(allExpectedOutput, result[0].getOutput()); - } else if (outputControl.contains(OutputControl.SAVE_FIRST_LINE)) { - assertEquals(1, result[0].getOutput().size()); - assertEquals(allExpectedOutput.getFirst(), result[0].getFirstLineOfOutput()); - } else { - throw new UnsupportedOperationException(); - } - } - } - } - - private boolean dumpOutput() { - return outputControl.contains(OutputControl.DUMP); - } - - private boolean saveOutput() { - return !Collections.disjoint(outputControl, OutputControl.SAVE); - } - - private boolean discardStdout() { - return outputControl.contains(OutputControl.DISCARD_STDOUT); - } - - private boolean discardStderr() { - return outputControl.contains(OutputControl.DISCARD_STDERR); - } - - private static String format(Set outputControl) { - return outputControl.stream().map(OutputControl::name).sorted().collect(joining("+")); - } - - private List expectedCapturedSystemOut(Command command) { - if (!dumpOutput() || (!toolProvider && !saveOutput())) { - return List.of(); - } else if(saveOutput()) { - return Stream.concat(command.stdout().stream(), command.stderr().stream()).toList(); - } else { - return command.stdout(); - } - } - - private List expectedCapturedSystemErr(Command command) { - if (!dumpOutput() || (!toolProvider && !saveOutput())) { - return List.of(); - } else if(saveOutput()) { - return List.of(); - } else { - return command.stderr(); - } - } - - private List expectedResultStdout(Command command) { - return expectedResultStream(command.stdout()); - } - - private List expectedResultStderr(Command command) { - if (outputControl.contains(OutputControl.SAVE_FIRST_LINE) && !command.stdout().isEmpty()) { - return List.of(); - } - return expectedResultStream(command.stderr()); - } - - private List expectedResultStream(List commandOutput) { - Objects.requireNonNull(commandOutput); - if (outputControl.contains(OutputControl.SAVE_ALL)) { - return commandOutput; - } else if (outputControl.contains(OutputControl.SAVE_FIRST_LINE)) { - return commandOutput.stream().findFirst().map(List::of).orElseGet(List::of); - } else { - return null; - } - } - - private Command discardStreams(Command command) { - return new Command(discardStdout() ? List.of() : command.stdout(), discardStderr() ? List.of() : command.stderr()); - } - - private record OutputCapture(byte[] out, byte[] err, Charset outCharset, Charset errCharset) { - OutputCapture { - Objects.requireNonNull(out); - Objects.requireNonNull(err); - Objects.requireNonNull(outCharset); - Objects.requireNonNull(errCharset); - } - - List outLines() { - return toLines(out, outCharset); - } - - List errLines() { - return toLines(err, errCharset); - } - - private static List toLines(byte[] buf, Charset charset) { - try (var reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf), charset))) { - return reader.lines().filter(line -> { - return !line.contains("TRACE"); - }).toList(); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - - static OutputCapture captureOutput(Runnable runnable) { - final var captureOut = new ByteArrayOutputStream(); - final var captureErr = new ByteArrayOutputStream(); - - final var out = System.out; - final var err = System.err; - try { - final var outCharset = System.out.charset(); - final var errCharset = System.err.charset(); - System.setOut(new PrintStream(captureOut, true, outCharset)); - System.setErr(new PrintStream(captureErr, true, errCharset)); - runnable.run(); - return new OutputCapture(captureOut.toByteArray(), captureErr.toByteArray(), outCharset, errCharset); - } finally { - try { - System.setOut(out); - } finally { - System.setErr(err); - } - } - } - } - - private List expectedCommandOutput(Command command) { - command = discardStreams(command); - return Stream.of(command.stdout(), command.stderr()).flatMap(List::stream).toList(); - } - - private Executor createExecutor(Command command) { - final Executor exec; - if (toolProvider) { - exec = Executor.of(command.asToolProvider()); - } else { - exec = Executor.of(command.asExecutable()); - } - - outputControl.forEach(control -> control.applyTo(exec)); - - return exec; - } - } - - @ParameterizedTest - @MethodSource - public void testSavedOutput(OutputTestSpec spec) { - spec.test(); - } - - public static List testSavedOutput() { - List testCases = new ArrayList<>(); - for (final var toolProvider : BOOLEAN_VALUES) { - for (final var outputControl : OutputControl.variants()) { - for (final var stdoutContent : List.of(OutputData.values())) { - for (final var stderrContent : List.of(OutputData.values())) { - final var commandSpec = new CommandSpec(stdoutContent, stderrContent); - testCases.add(new OutputTestSpec(toolProvider, outputControl, commandSpec)); - } - } - } - } - return testCases; - } - - private static final List BOOLEAN_VALUES = List.of(Boolean.TRUE, Boolean.FALSE); - private static final Consumer NOP = exec -> {}; -} diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java index 16909d0eb40..3e169b9e184 100644 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.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 @@ -213,7 +213,7 @@ public class PackageTestTest extends JUnitAdapter { @Override public void accept(JPackageCommand cmd, Executor.Result result) { tick(); - jpackageExitCode = result.exitCode(); + jpackageExitCode = result.getExitCode(); } @Override @@ -371,8 +371,7 @@ public class PackageTestTest extends JUnitAdapter { } catch (IOException ex) { throw new UncheckedIOException(ex); } - return new Executor.Result(actualJPackageExitCode, - this::getPrintableCommandLine).assertExitCodeIs(expectedExitCode); + return new Executor.Result(actualJPackageExitCode).assertExitCodeIs(expectedExitCode); } }; }).setExpectedExitCode(expectedJPackageExitCode) diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java index ef118e525c5..5d3033e2e8c 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.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 @@ -22,18 +22,9 @@ */ package jdk.jpackage.test; -import static java.util.stream.Collectors.joining; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PrintStream; -import java.io.StringReader; import java.io.UncheckedIOException; -import java.io.Writer; +import java.nio.charset.Charset; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; @@ -43,15 +34,17 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; +import java.util.concurrent.TimeUnit; import java.util.function.Supplier; -import java.util.regex.Pattern; import java.util.spi.ToolProvider; -import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; -import jdk.jpackage.internal.util.function.ThrowingSupplier; +import jdk.jpackage.internal.util.CommandLineFormat; +import jdk.jpackage.internal.util.CommandOutputControl; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedExitCodeException; +import jdk.jpackage.internal.util.RetryExecutor; import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.internal.util.function.ThrowingSupplier; public final class Executor extends CommandArguments { @@ -69,8 +62,6 @@ public final class Executor extends CommandArguments { } public Executor() { - outputStreamsControl = new OutputStreamsControl(); - winEnglishOutput = false; } public Executor setExecutable(String v) { @@ -136,62 +127,31 @@ public final class Executor extends CommandArguments { return this; } - /** - * Configures this instance to save all stdout and stderr streams from the to be - * executed command. - *

    - * This function is mutually exclusive with {@link #saveFirstLineOfOutput()}. - * - * @return this - */ public Executor saveOutput() { return saveOutput(true); } - /** - * Configures if all stdout and stderr streams from the to be executed command - * should be saved. - *

    - * If v is true, the function call is equivalent to - * {@link #saveOutput()} call. If v is false, command - * output will not be saved. - * - * @parameter v if both stdout and stderr streams should be saved - * - * @return this - */ public Executor saveOutput(boolean v) { - return setOutputControl(v, OutputControlOption.SAVE_ALL); + commandOutputControl.saveOutput(v); + return this; } - /** - * Configures this instance to save the first line of a stream merged from - * stdout and stderr streams from the to be executed command. - *

    - * This function is mutually exclusive with {@link #saveOutput()}. - * - * @return this - */ public Executor saveFirstLineOfOutput() { - return setOutputControl(true, OutputControlOption.SAVE_FIRST_LINE); + commandOutputControl.saveFirstLineOfOutput(); + return this; } - /** - * Configures this instance to dump both stdout and stderr streams from the to - * be executed command into {@link System.out}. - * - * @return this - */ public Executor dumpOutput() { return dumpOutput(true); } public Executor dumpOutput(boolean v) { - return setOutputControl(v, OutputControlOption.DUMP); + commandOutputControl.dumpOutput(v); + return this; } public Executor discardStdout(boolean v) { - outputStreamsControl.stdout().discard(v); + commandOutputControl.discardStdout(v); return this; } @@ -200,7 +160,7 @@ public final class Executor extends CommandArguments { } public Executor discardStderr(boolean v) { - outputStreamsControl.stderr().discard(v); + commandOutputControl.discardStderr(v); return this; } @@ -208,45 +168,126 @@ public final class Executor extends CommandArguments { return discardStderr(true); } - public interface Output { - public List getOutput(); - - public default String getFirstLineOfOutput() { - return findFirstLineOfOutput().orElseThrow(); - } - - public default Optional findFirstLineOfOutput() { - return getOutput().stream().findFirst(); - } + public Executor binaryOutput(boolean v) { + commandOutputControl.binaryOutput(v); + return this; } - public record Result(int exitCode, CommandOutput output, Supplier cmdline) implements Output { + public Executor binaryOutput() { + return binaryOutput(true); + } + + public Executor charset(Charset v) { + commandOutputControl.charset(v); + return this; + } + + public Charset charset() { + return commandOutputControl.charset(); + } + + Executor storeOutputInFiles(boolean v) { + commandOutputControl.storeOutputInFiles(v); + return this; + } + + Executor storeOutputInFiles() { + return storeOutputInFiles(true); + } + + public record Result(CommandOutputControl.Result base) { public Result { - Objects.requireNonNull(output); - Objects.requireNonNull(cmdline); + Objects.requireNonNull(base); } - public Result(int exitCode, Supplier cmdline) { - this(exitCode, CommandOutput.EMPTY, cmdline); + public Result(int exitCode) { + this(new CommandOutputControl.Result(exitCode)); } - @Override public List getOutput() { - return output.lines().orElse(null); + return base.content(); } - public Output stdout() { - return createView(output.stdoutLines()); + public String getFirstLineOfOutput() { + return getOutput().getFirst(); } - public Output stderr() { - return createView(output.stderrLines()); + public List stdout() { + return base.stdout(); } - public Result assertExitCodeIs(int expectedExitCode) { - TKit.assertEquals(expectedExitCode, exitCode, String.format( - "Check command %s exited with %d code", - cmdline.get(), expectedExitCode)); + public List stderr() { + return base.stderr(); + } + + public Optional> findContent() { + return base.findContent(); + } + + public Optional> findStdout() { + return base.findStdout(); + } + + public Optional> findStderr() { + return base.findStderr(); + } + + public byte[] byteContent() { + return base.byteContent(); + } + + public byte[] byteStdout() { + return base.byteStdout(); + } + + public byte[] byteStderr() { + return base.byteStderr(); + } + + public Optional findByteContent() { + return base.findByteContent(); + } + + public Optional findByteStdout() { + return base.findByteStdout(); + } + + public Optional findByteStderr() { + return base.findByteStderr(); + } + + public Result toCharacterResult(Charset charset, boolean keepByteContent) { + try { + return new Result(base.toCharacterResult(charset, keepByteContent)); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + public Result assertExitCodeIs(int main, int... other) { + if (other.length != 0) { + return assertExitCodeIs(IntStream.concat(IntStream.of(main), IntStream.of(other)).boxed().toList()); + } else { + return assertExitCodeIs(List.of(main)); + } + } + + private Result assertExitCodeIs(List expectedExitCodes) { + Objects.requireNonNull(expectedExitCodes); + switch (expectedExitCodes.size()) { + case 0 -> { + throw new IllegalArgumentException(); + } case 1 -> { + long expectedExitCode = expectedExitCodes.getFirst(); + TKit.assertEquals(expectedExitCode, getExitCode(), String.format( + "Check command %s exited with %d code", + base.execAttrs(), expectedExitCode)); + } default -> { + TKit.assertTrue(expectedExitCodes.contains(getExitCode()), String.format( + "Check command %s exited with one of %s codes", + base.execAttrs(), expectedExitCodes.stream().sorted().toList())); + } + } return this; } @@ -255,16 +296,11 @@ public final class Executor extends CommandArguments { } public int getExitCode() { - return exitCode; + return base.getExitCode(); } - private static Output createView(Optional> lines) { - return new Output() { - @Override - public List getOutput() { - return lines.orElse(null); - } - }; + public String getPrintableCommandLine() { + return base.execAttrs().toString(); } } @@ -292,8 +328,8 @@ public final class Executor extends CommandArguments { }).get(); } - public Result execute(int expectedCode) { - return executeWithoutExitCodeCheck().assertExitCodeIs(expectedCode); + Result execute(int mainExitCode, int... otherExitCodes) { + return executeWithoutExitCodeCheck().assertExitCodeIs(mainExitCode, otherExitCodes); } public Result execute() { @@ -301,28 +337,36 @@ public final class Executor extends CommandArguments { } public String executeAndGetFirstLineOfOutput() { - return saveFirstLineOfOutput().execute().getFirstLineOfOutput(); + return saveFirstLineOfOutput().execute().getOutput().getFirst(); } public List executeAndGetOutput() { return saveOutput().execute().getOutput(); } - private static class BadResultException extends RuntimeException { - BadResultException(Result v) { - value = v; + private static class FailedAttemptException extends Exception { + FailedAttemptException(Exception cause) { + super(Objects.requireNonNull(cause)); } - Result getValue() { - return value; - } - - private final transient Result value; private static final long serialVersionUID = 1L; } + public RetryExecutor retryUntilExitCodeIs( + int mainExpectedExitCode, int... otherExpectedExitCodes) { + return new RetryExecutor(UnexpectedExitCodeException.class).setExecutable(() -> { + var result = executeWithoutExitCodeCheck(); + result.base().expectExitCode(mainExpectedExitCode, otherExpectedExitCodes); + return result; + }).setExceptionMapper((UnexpectedExitCodeException ex) -> { + createResult(ex.getResult()).assertExitCodeIs(mainExpectedExitCode, otherExpectedExitCodes); + // Unreachable, because the above `Result.assertExitCodeIs(...)` must throw. + throw ExceptionBox.reachedUnreachable(); + }); + } + /** - * Executes the configured command {@code max} at most times and waits for + * Executes the configured command at most {@code max} times and waits for * {@code wait} seconds between each execution until the command exits with * {@code expectedCode} exit code. * @@ -332,17 +376,10 @@ public final class Executor extends CommandArguments { * command */ public Result executeAndRepeatUntilExitCode(int expectedExitCode, int max, int wait) { - try { - return tryRunMultipleTimes(() -> { - Result result = executeWithoutExitCodeCheck(); - if (result.getExitCode() != expectedExitCode) { - throw new BadResultException(result); - } - return result; - }, max, wait).assertExitCodeIs(expectedExitCode); - } catch (BadResultException ex) { - return ex.getValue().assertExitCodeIs(expectedExitCode); - } + return retryUntilExitCodeIs(expectedExitCode) + .setAttemptTimeout(wait, TimeUnit.SECONDS) + .setMaxAttemptsCount(max) + .executeUnchecked(); } /** @@ -359,26 +396,16 @@ public final class Executor extends CommandArguments { * @param wait number of seconds to wait between executions of the */ public static T tryRunMultipleTimes(Supplier task, int max, int wait) { - RuntimeException lastException = null; - int count = 0; - - do { + return new RetryExecutor(FailedAttemptException.class).setExecutable(() -> { try { return task.get(); } catch (RuntimeException ex) { - lastException = ex; + throw new FailedAttemptException(ex); } + }).setExceptionMapper((FailedAttemptException ex) -> { + return (RuntimeException)ex.getCause(); + }).setAttemptTimeout(wait, TimeUnit.SECONDS).setMaxAttemptsCount(max).executeUnchecked(); - try { - Thread.sleep(wait * 1000); - } catch (InterruptedException ex) { - throw new RuntimeException(ex); - } - - count++; - } while (count < max); - - throw lastException; } public static void tryRunMultipleTimes(Runnable task, int max, int wait) { @@ -392,12 +419,6 @@ public final class Executor extends CommandArguments { return saveOutput().executeWithoutExitCodeCheck().getOutput(); } - private Executor setOutputControl(boolean set, OutputControlOption v) { - outputStreamsControl.stdout().set(set, v); - outputStreamsControl.stderr().set(set, v); - return this; - } - private Path executablePath() { if (directory == null || executable.isAbsolute() @@ -431,12 +452,8 @@ public final class Executor extends CommandArguments { builder.environment().put("TMP", winTmpDir); } - outputStreamsControl.applyTo(builder); - StringBuilder sb = new StringBuilder(getPrintableCommandLine()); - outputStreamsControl.describe().ifPresent(desc -> { - sb.append("; ").append(desc); - }); + sb.append("; ").append(commandOutputControl.description()); if (directory != null) { builder.directory(directory.toFile()); @@ -466,141 +483,31 @@ public final class Executor extends CommandArguments { }); } - trace("Execute " + sb.toString() + "..."); - Process process = builder.start(); - - var stdoutGobbler = CompletableFuture.>>supplyAsync(() -> { - try { - return processProcessStream(outputStreamsControl.stdout(), process.getInputStream()); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - }); - - var stderrGobbler = CompletableFuture.>>supplyAsync(() -> { - try { - return processProcessStream(outputStreamsControl.stderr(), process.getErrorStream()); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - }); - - final CommandOutput output; - - try { - output = combine(stdoutGobbler.join(), stderrGobbler.join()); - } catch (CompletionException ex) { - var cause = ex.getCause(); - switch (cause) { - case UncheckedIOException uioex -> { - throw uioex.getCause(); - } - default -> { - throw ExceptionBox.toUnchecked(ExceptionBox.unbox(cause)); - } - } - } - - final int exitCode = process.waitFor(); - trace("Done. Exit code: " + exitCode); - - return createResult(exitCode, output); + return execute(sb, commandOutputControl.createExecutable(builder)); } - private int runToolProvider(PrintStream out, PrintStream err) { + private Result runToolProvider() throws IOException, InterruptedException { final var sb = new StringBuilder(getPrintableCommandLine()); - outputStreamsControl.describe().ifPresent(desc -> { - sb.append("; ").append(desc); - }); - trace("Execute " + sb + "..."); - final int exitCode = toolProvider.run(out, err, args.toArray( - String[]::new)); - trace("Done. Exit code: " + exitCode); - return exitCode; + sb.append("; ").append(commandOutputControl.description()); + + return execute(sb, commandOutputControl.createExecutable(toolProvider, args.toArray(String[]::new))); } - private Result runToolProvider() throws IOException { - final var toolProviderStreamConfig = ToolProviderStreamConfig.create(outputStreamsControl); + private Result execute(StringBuilder traceMsg, CommandOutputControl.Executable exec) throws IOException, InterruptedException { + Objects.requireNonNull(traceMsg); - final var exitCode = runToolProvider(toolProviderStreamConfig); + trace("Execute " + traceMsg + "..."); - final var output = combine( - read(outputStreamsControl.stdout(), toolProviderStreamConfig.out()), - read(outputStreamsControl.stderr(), toolProviderStreamConfig.err())); - return createResult(exitCode, output); + var result = exec.execute(); + + trace("Done. Exit code: " + result.getExitCode()); + + return createResult(result); } - private int runToolProvider(ToolProviderStreamConfig cfg) throws IOException { - try { - return runToolProvider(cfg.out().ps(), cfg.err().ps()); - } finally { - cfg.out().ps().flush(); - cfg.err().ps().flush(); - } - } - - private static Optional> processProcessStream(OutputControl outputControl, InputStream in) throws IOException { - List outputLines = null; - try (final var bufReader = new BufferedReader(new InputStreamReader(in))) { - if (outputControl.dump() || outputControl.saveAll()) { - outputLines = bufReader.lines().toList(); - } else if (outputControl.saveFirstLine()) { - outputLines = Optional.ofNullable(bufReader.readLine()).map(List::of).orElseGet(List::of); - // Read all input, or the started process may exit with an error (cmd.exe does so). - bufReader.transferTo(Writer.nullWriter()); - } else { - // This should be empty input stream, fetch it anyway. - bufReader.transferTo(Writer.nullWriter()); - } - } finally { - if (outputControl.dump() && outputLines != null) { - outputLines.forEach(System.out::println); - if (outputControl.saveFirstLine()) { - outputLines = outputLines.stream().findFirst().map(List::of).orElseGet(List::of); - } - } - if (!outputControl.save()) { - outputLines = null; - } - } - return Optional.ofNullable(outputLines); - } - - private static Optional> read(OutputControl outputControl, CachingPrintStream cps) throws IOException { - final var bufferAsString = cps.bufferContents(); - try (final var bufReader = new BufferedReader(new StringReader(bufferAsString.orElse("")))) { - if (outputControl.saveFirstLine()) { - return Optional.of(bufReader.lines().findFirst().map(List::of).orElseGet(List::of)); - } else if (outputControl.saveAll()) { - return Optional.of(bufReader.lines().toList()); - } else if (bufferAsString.isPresent()) { - return Optional.of(List.of()); - } else { - return Optional.empty(); - } - } - } - - private CommandOutput combine(Optional> out, Optional> err) { - if (out.isEmpty() && err.isEmpty()) { - return new CommandOutput(); - } else if (out.isEmpty()) { - return new CommandOutput(err, -1); - } else if (err.isEmpty()) { - return new CommandOutput(out, Integer.MAX_VALUE); - } else { - final var combined = Stream.of(out, err).map(Optional::orElseThrow).flatMap(List::stream); - if (outputStreamsControl.stdout().saveFirstLine() && outputStreamsControl.stderr().saveFirstLine()) { - return new CommandOutput(Optional.of(combined.findFirst().map(List::of).orElseGet(List::of)), - Integer.min(1, out.orElseThrow().size())); - } else { - return new CommandOutput(Optional.of(combined.toList()), out.orElseThrow().size()); - } - } - } - - private Result createResult(int exitCode, CommandOutput output) { - return new Result(exitCode, output, this::getPrintableCommandLine); + private Result createResult(CommandOutputControl.Result baseResult) { + return new Result(baseResult.copyWithExecutableAttributes( + new ExecutableAttributes(baseResult.execAttrs(), getPrintableCommandLine()))); } public String getPrintableCommandLine() { @@ -618,359 +525,40 @@ public final class Executor extends CommandArguments { var cmdline = Stream.of(prefixCommandLineArgs(), List.of(exec), args).flatMap( List::stream).toList(); - return String.format(format, printCommandLine(cmdline), cmdline.size()); + return String.format(format, CommandLineFormat.DEFAULT.apply(cmdline), cmdline.size()); } - private static String printCommandLine(List cmdline) { - // Want command line printed in a way it can be easily copy/pasted - // to be executed manually - Pattern regex = Pattern.compile("\\s"); - return cmdline.stream().map( - v -> (v.isEmpty() || regex.matcher(v).find()) ? "\"" + v + "\"" : v).collect( - Collectors.joining(" ")); + private record ExecutableAttributes(CommandOutputControl.ExecutableAttributes base, String toStringValue) + implements CommandOutputControl.ExecutableAttributes { + + ExecutableAttributes { + Objects.requireNonNull(base); + if (toStringValue.isBlank()) { + throw new IllegalArgumentException(); + } + } + + @Override + public String toString() { + return toStringValue; + } + + @Override + public List commandLine() { + return base.commandLine(); + } } private static void trace(String msg) { TKit.trace(String.format("exec: %s", msg)); } - private static PrintStream nullPrintStream() { - return new PrintStream(OutputStream.nullOutputStream()); - } - - private record OutputStreamsControl(OutputControl stdout, OutputControl stderr) { - OutputStreamsControl { - Objects.requireNonNull(stdout); - Objects.requireNonNull(stderr); - } - - OutputStreamsControl() { - this(new OutputControl(), new OutputControl()); - } - - void applyTo(ProcessBuilder pb) { - pb.redirectOutput(stdout.asProcessBuilderRedirect()); - pb.redirectError(stderr.asProcessBuilderRedirect()); - } - - Optional describe() { - final List tokens = new ArrayList<>(); - if (stdout.save() || stderr.save()) { - streamsLabel("save ", true).ifPresent(tokens::add); - } - if (stdout.dump() || stderr.dump()) { - streamsLabel("inherit ", true).ifPresent(tokens::add); - } - streamsLabel("discard ", false).ifPresent(tokens::add); - if (tokens.isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(String.join("; ", tokens)); - } - } - - Optional streamsLabel(String prefix, boolean negate) { - Objects.requireNonNull(prefix); - final var str = Stream.of(stdoutLabel(negate), stderrLabel(negate)) - .filter(Optional::isPresent) - .map(Optional::orElseThrow) - .collect(joining("+")); - if (str.isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(prefix + str); - } - } - - private Optional stdoutLabel(boolean negate) { - if ((stdout.discard() && !negate) || (!stdout.discard() && negate)) { - return Optional.of("out"); - } else { - return Optional.empty(); - } - } - - private Optional stderrLabel(boolean negate) { - if ((stderr.discard() && !negate) || (!stderr.discard() && negate)) { - return Optional.of("err"); - } else { - return Optional.empty(); - } - } - } - - private record CachingPrintStream(PrintStream ps, Optional buf) { - CachingPrintStream { - Objects.requireNonNull(ps); - Objects.requireNonNull(buf); - } - - Optional bufferContents() { - return buf.map(ByteArrayOutputStream::toString); - } - - static Builder build() { - return new Builder(); - } - - static final class Builder { - - Builder save(boolean v) { - save = v; - return this; - } - - Builder discard(boolean v) { - discard = v; - return this; - } - - Builder dumpStream(PrintStream v) { - dumpStream = v; - return this; - } - - CachingPrintStream create() { - final Optional buf; - if (save && !discard) { - buf = Optional.of(new ByteArrayOutputStream()); - } else { - buf = Optional.empty(); - } - - final PrintStream ps; - if (buf.isPresent() && dumpStream != null) { - ps = new PrintStream(new TeeOutputStream(List.of(buf.orElseThrow(), dumpStream)), true, dumpStream.charset()); - } else if (!discard) { - ps = buf.map(PrintStream::new).or(() -> Optional.ofNullable(dumpStream)).orElseGet(Executor::nullPrintStream); - } else { - ps = nullPrintStream(); - } - - return new CachingPrintStream(ps, buf); - } - - private boolean save; - private boolean discard; - private PrintStream dumpStream; - } - } - - private record ToolProviderStreamConfig(CachingPrintStream out, CachingPrintStream err) { - ToolProviderStreamConfig { - Objects.requireNonNull(out); - Objects.requireNonNull(err); - } - - static ToolProviderStreamConfig create(OutputStreamsControl cfg) { - final var errCfgBuilder = cfg.stderr().buildCachingPrintStream(System.err); - if (cfg.stderr().dump() && cfg.stderr().save()) { - errCfgBuilder.dumpStream(System.out); - } - return new ToolProviderStreamConfig( - cfg.stdout().buildCachingPrintStream(System.out).create(), errCfgBuilder.create()); - } - } - - private static final class OutputControl { - - boolean save() { - return save.isPresent(); - } - - boolean saveAll() { - return save.orElse(null) == OutputControlOption.SAVE_ALL; - } - - boolean saveFirstLine() { - return save.orElse(null) == OutputControlOption.SAVE_FIRST_LINE; - } - - boolean discard() { - return discard || (!dump && save.isEmpty()); - } - - boolean dump() { - return !discard && dump; - } - - OutputControl dump(boolean v) { - this.dump = v; - return this; - } - - OutputControl discard(boolean v) { - this.discard = v; - return this; - } - - OutputControl saveAll(boolean v) { - if (v) { - save = Optional.of(OutputControlOption.SAVE_ALL); - } else { - save = Optional.empty(); - } - return this; - } - - OutputControl saveFirstLine(boolean v) { - if (v) { - save = Optional.of(OutputControlOption.SAVE_FIRST_LINE); - } else { - save = Optional.empty(); - } - return this; - } - - OutputControl set(boolean set, OutputControlOption v) { - switch (v) { - case DUMP -> dump(set); - case SAVE_ALL -> saveAll(set); - case SAVE_FIRST_LINE -> saveFirstLine(set); - } - return this; - } - - ProcessBuilder.Redirect asProcessBuilderRedirect() { - if (discard()) { - return ProcessBuilder.Redirect.DISCARD; - } else if (dump && !save()) { - return ProcessBuilder.Redirect.INHERIT; - } else { - return ProcessBuilder.Redirect.PIPE; - } - } - - CachingPrintStream.Builder buildCachingPrintStream(PrintStream dumpStream) { - Objects.requireNonNull(dumpStream); - final var builder = CachingPrintStream.build().save(save()).discard(discard()); - if (dump()) { - builder.dumpStream(dumpStream); - } - return builder; - } - - private boolean dump; - private boolean discard; - private Optional save = Optional.empty(); - } - - private static final class TeeOutputStream extends OutputStream { - - public TeeOutputStream(Iterable streams) { - streams.forEach(Objects::requireNonNull); - this.streams = streams; - } - - @Override - public void write(int b) throws IOException { - for (final var out : streams) { - out.write(b); - } - } - - @Override - public void write(byte[] b) throws IOException { - for (final var out : streams) { - out.write(b); - } - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - for (final var out : streams) { - out.write(b, off, len); - } - } - - @Override - public void flush() throws IOException { - forEach(OutputStream::flush); - } - - @Override - public void close() throws IOException { - forEach(OutputStream::close); - } - - private void forEach(OutputStreamConsumer c) throws IOException { - IOException firstEx = null; - for (final var out : streams) { - try { - c.accept(out); - } catch (IOException e) { - if (firstEx == null) { - firstEx = e; - } - } - } - if (firstEx != null) { - throw firstEx; - } - } - - @FunctionalInterface - private static interface OutputStreamConsumer { - void accept(OutputStream out) throws IOException; - } - - private final Iterable streams; - } - - private static final class CommandOutput { - CommandOutput(Optional> lines, int stdoutLineCount) { - this.lines = Objects.requireNonNull(lines); - this.stdoutLineCount = stdoutLineCount; - } - - CommandOutput() { - this(Optional.empty(), 0); - } - - Optional> lines() { - return lines; - } - - Optional> stdoutLines() { - if (lines.isEmpty() || stdoutLineCount < 0) { - return Optional.empty(); - } - - final var theLines = lines.orElseThrow(); - if (stdoutLineCount == theLines.size()) { - return lines; - } else { - return Optional.of(theLines.subList(0, Integer.min(stdoutLineCount, theLines.size()))); - } - } - - Optional> stderrLines() { - if (lines.isEmpty() || stdoutLineCount > lines.orElseThrow().size()) { - return Optional.empty(); - } else if (stdoutLineCount == 0) { - return lines; - } else { - final var theLines = lines.orElseThrow(); - return Optional.of(theLines.subList(stdoutLineCount, theLines.size())); - } - } - - private final Optional> lines; - private final int stdoutLineCount; - - static final CommandOutput EMPTY = new CommandOutput(); - } - private ToolProvider toolProvider; private Path executable; - private OutputStreamsControl outputStreamsControl; + private final CommandOutputControl commandOutputControl = new CommandOutputControl(); private Path directory; private Set removeEnvVars = new HashSet<>(); private Map setEnvVars = new HashMap<>(); private boolean winEnglishOutput; private String winTmpDir = null; - - private static enum OutputControlOption { - SAVE_ALL, SAVE_FIRST_LINE, DUMP - } } 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 38091cb3452..c5c4f87b097 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.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 @@ -52,6 +52,8 @@ import java.util.Optional; import java.util.OptionalInt; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -780,10 +782,9 @@ public class JPackageCommand extends CommandArguments { } /** - * Starts a new thread. In this thread calls - * {@link #useToolProviderByDefault(ToolProvider)} with the specified - * {@code jpackageToolProvider} and then calls {@code workload.run()}. Joins the - * thread. + * In a separate thread calls {@link #useToolProviderByDefault(ToolProvider)} + * with the specified {@code jpackageToolProvider} and then calls + * {@code workload.run()}. Joins the thread. *

    * The idea is to run the {@code workload} in the context of the specified * jpackage {@code ToolProvider} without altering the global variable holding @@ -794,13 +795,23 @@ public class JPackageCommand extends CommandArguments { * @param jpackageToolProvider jpackage {@code ToolProvider} * @param workload the workload to run */ - public static void withToolProvider(ToolProvider jpackageToolProvider, Runnable workload) { - Objects.requireNonNull(jpackageToolProvider); + public static void withToolProvider(Runnable workload, ToolProvider jpackageToolProvider) { Objects.requireNonNull(workload); - ThrowingRunnable.toRunnable(Thread.ofVirtual().start(() -> { + Objects.requireNonNull(jpackageToolProvider); + + CompletableFuture.runAsync(() -> { + var oldValue = defaultToolProvider.get(); useToolProviderByDefault(jpackageToolProvider); - workload.run(); - })::join).run(); + try { + workload.run(); + } finally { + defaultToolProvider.set(oldValue); + } + // Run the future in a new native thread. Don't run it in a virtual/pooled thread. + // Pooled and/or virtual threads are problematic when used with inheritable thread-local variables. + // TKit class depends on such a variable, which results in intermittent test failures + // if the default executor runs this future. + }, Executors.newThreadPerTaskExecutor(Thread.ofPlatform().factory())).join(); } public JPackageCommand useToolProvider(boolean v) { @@ -1022,7 +1033,7 @@ public class JPackageCommand extends CommandArguments { outputValidator.accept(result.getOutput().iterator()); } - if (result.exitCode() == 0 && expectedExitCode.isPresent()) { + if (result.getExitCode() == 0 && expectedExitCode.isPresent()) { verifyActions.run(); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java index 3b02a3f6a69..78562b2ed26 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.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 @@ -723,7 +723,9 @@ public final class LinuxHelper { private static Optional queryMimeTypeDefaultHandler(String mimeType) { return Executor.of("xdg-mime", "query", "default", mimeType) - .discardStderr().saveFirstLineOfOutput().execute().findFirstLineOfOutput(); + .discardStderr() + .saveFirstLineOfOutput() + .execute().getOutput().stream().findFirst(); } private static void verifyIconInScriptlet(Scriptlet scriptletType, diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java index 92d9fa0bd44..6a5be77457a 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.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 @@ -49,11 +49,11 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -66,10 +66,10 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; -import jdk.jpackage.internal.RetryExecutor; import jdk.jpackage.internal.util.FileUtils; import jdk.jpackage.internal.util.PListReader; import jdk.jpackage.internal.util.PathUtils; +import jdk.jpackage.internal.util.RetryExecutor; import jdk.jpackage.internal.util.XmlUtils; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.internal.util.function.ThrowingSupplier; @@ -90,38 +90,34 @@ public final class MacHelper { final var mountRoot = TKit.createTempDirectory("mountRoot"); // Explode DMG assuming this can require interaction, thus use `yes`. - String attachCMD[] = { - "sh", "-c", - String.join(" ", "yes", "|", "/usr/bin/hdiutil", "attach", - JPackageCommand.escapeAndJoin(cmd.outputBundle().toString()), - "-mountroot", PathUtils.normalizedAbsolutePathString(mountRoot), - "-nobrowse", "-plist")}; - RetryExecutor attachExecutor = new RetryExecutor(); - try { - // 10 times with 6 second delays. - attachExecutor.setMaxAttemptsCount(10) - .setAttemptTimeoutMillis(6000) - .setWriteOutputToFile(true) - .saveOutput(true) - .execute(attachCMD); - } catch (IOException ex) { - throw new RuntimeException(ex); - } + final var attachStdout = Executor.of("sh", "-c", String.join(" ", + "yes", + "|", + "/usr/bin/hdiutil", + "attach", + JPackageCommand.escapeAndJoin(cmd.outputBundle().toString()), + "-mountroot", PathUtils.normalizedAbsolutePathString(mountRoot), + "-nobrowse", + "-plist" + )).saveOutput().storeOutputInFiles().executeAndRepeatUntilExitCode(0, 10, 6).stdout(); - Path mountPoint = null; + final Path mountPoint; + + boolean mountPointInitialized = false; try { // One of "dict" items of "system-entities" array property should contain "mount-point" string property. - mountPoint = readPList(attachExecutor.getOutput()).queryArrayValue("system-entities", false).map(PListReader.class::cast).map(dict -> { - try { - return dict.queryValue("mount-point"); - } catch (NoSuchElementException ex) { - return (String)null; - } - }).filter(Objects::nonNull).map(Path::of).findFirst().orElseThrow(); + mountPoint = readPList(attachStdout).queryArrayValue("system-entities", false) + .map(PListReader.class::cast) + .map(dict -> { + return dict.findValue("mount-point"); + }) + .filter(Optional::isPresent).map(Optional::get) + .map(Path::of).findFirst().orElseThrow(); + mountPointInitialized = true; } finally { - if (mountPoint == null) { + if (!mountPointInitialized) { TKit.trace("Unexpected plist file missing `system-entities` array:"); - attachExecutor.getOutput().forEach(TKit::trace); + attachStdout.forEach(TKit::trace); TKit.trace("Done"); } } @@ -138,39 +134,27 @@ public final class MacHelper { ThrowingConsumer.toConsumer(consumer).accept(childPath); } } finally { - String detachCMD[] = { - "/usr/bin/hdiutil", - "detach", - "-verbose", - mountPoint.toAbsolutePath().toString()}; // "hdiutil detach" might not work right away due to resource busy error, so // repeat detach several times. - RetryExecutor detachExecutor = new RetryExecutor(); - // Image can get detach even if we got resource busy error, so stop - // trying to detach it if it is no longer attached. - final Path mp = mountPoint; - detachExecutor.setExecutorInitializer(exec -> { - if (!Files.exists(mp)) { - detachExecutor.abort(); + new RetryExecutor(RuntimeException.class).setExecutable(context -> { + var exec = Executor.of("/usr/bin/hdiutil", "detach").storeOutputInFiles(); + if (context.isLastAttempt()) { + // The last attempt, force detach. + exec.addArgument("-force"); } - }); - try { - // 10 times with 6 second delays. - detachExecutor.setMaxAttemptsCount(10) - .setAttemptTimeoutMillis(6000) - .setWriteOutputToFile(true) - .saveOutput(true) - .execute(detachCMD); - } catch (IOException ex) { - if (!detachExecutor.isAborted()) { - // Now force to detach if it still attached - if (Files.exists(mountPoint)) { - Executor.of("/usr/bin/hdiutil", "detach", - "-force", "-verbose") - .addArgument(mountPoint).execute(); - } + exec.addArgument(mountPoint); + + // The image can get detached even if we get a resource busy error, + // so execute the detach command without checking the exit code. + var result = exec.executeWithoutExitCodeCheck(); + + if (result.getExitCode() == 0 || !Files.exists(mountPoint)) { + // Detached successfully! + return null; + } else { + throw new RuntimeException(String.format("[%s] mount point still attached", mountPoint)); } - } + }).setMaxAttemptsCount(10).setAttemptTimeout(6, TimeUnit.SECONDS).execute(); } } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java index 15249c51887..70de6ba92af 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.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 @@ -1172,7 +1172,7 @@ public final class MacSign { "-c", certFile.normalize().toString(), "-k", keychain.name(), "-p", resolvedCertificateRequest.installed().type().verifyPolicy()).saveOutput(!quite).executeWithoutExitCodeCheck(); - if (result.exitCode() == 0) { + if (result.getExitCode() == 0) { return VerifyStatus.VERIFY_OK; } } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java index 1f37829791e..0ecfd4c3432 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.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 @@ -94,7 +94,7 @@ public final class MacSignVerify { public static Optional findEntitlements(Path path) { final var exec = Executor.of("/usr/bin/codesign", "-d", "--entitlements", "-", "--xml", path.toString()).saveOutput().dumpOutput(); final var result = exec.execute(); - var xml = result.stdout().getOutput(); + var xml = result.stdout(); if (xml.isEmpty()) { return Optional.empty(); } else { @@ -137,7 +137,7 @@ public final class MacSignVerify { public static Optional findSpctlSignOrigin(SpctlType type, Path path) { final var exec = Executor.of("/usr/sbin/spctl", "-vv", "--raw", "--assess", "--type", type.value(), path.toString()).saveOutput().discardStderr(); final var result = exec.executeWithoutExitCodeCheck(); - TKit.assertTrue(Set.of(0, 3).contains(result.exitCode()), + TKit.assertTrue(Set.of(0, 3).contains(result.getExitCode()), String.format("Check exit code of command %s is either 0 or 3", exec.getPrintableCommandLine())); return toSupplier(() -> { try { @@ -173,7 +173,7 @@ public final class MacSignVerify { } else if (result.getExitCode() == 1 && result.getFirstLineOfOutput().endsWith("code object is not signed at all")) { return Optional.empty(); } else { - reportUnexpectedCommandOutcome(exec.getPrintableCommandLine(), result); + reportUnexpectedCommandOutcome(result); return null; // Unreachable } } @@ -205,7 +205,7 @@ public final class MacSignVerify { TKit.trace("Try /usr/bin/codesign again with `sudo`"); assertSigned(path, true); } else { - reportUnexpectedCommandOutcome(exec.getPrintableCommandLine(), result); + reportUnexpectedCommandOutcome(result); } } @@ -264,13 +264,13 @@ public final class MacSignVerify { return signIdentities; } catch (Exception ex) { ex.printStackTrace(); - reportUnexpectedCommandOutcome(exec.getPrintableCommandLine(), result); + reportUnexpectedCommandOutcome(result); return null; // Unreachable } } else if (result.getExitCode() == 1 && result.getOutput().getLast().endsWith("Status: no signature")) { return List.of(); } else { - reportUnexpectedCommandOutcome(exec.getPrintableCommandLine(), result); + reportUnexpectedCommandOutcome(result); return null; // Unreachable } } @@ -282,14 +282,13 @@ public final class MacSignVerify { } } - private static void reportUnexpectedCommandOutcome(String printableCommandLine, Executor.Result result) { - Objects.requireNonNull(printableCommandLine); + private static void reportUnexpectedCommandOutcome(Executor.Result result) { Objects.requireNonNull(result); TKit.trace(String.format("Command %s exited with exit code %d and the following output:", - printableCommandLine, result.getExitCode())); + result.getPrintableCommandLine(), result.getExitCode())); result.getOutput().forEach(TKit::trace); TKit.trace("Done"); - TKit.assertUnexpected(String.format("Outcome of command %s", printableCommandLine)); + TKit.assertUnexpected(String.format("Outcome of command %s", result.getPrintableCommandLine())); } private static final Pattern SIGN_IDENTITY_NAME_REGEXP = Pattern.compile("^\\s+\\d+\\.\\s+(.*)$"); 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 72b5dbc578b..f9fbf285b49 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.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 @@ -81,16 +81,16 @@ public class WindowsHelper { msiLog.ifPresent(v -> misexec.clearArguments().addArguments(origArgs).addArgument("/L*v").addArgument(v)); result = misexec.executeWithoutExitCodeCheck(); - if (result.exitCode() == 1605) { + if (result.getExitCode() == 1605) { // ERROR_UNKNOWN_PRODUCT, attempt to uninstall not installed // package - return result.exitCode(); + return result.getExitCode(); } // The given Executor may either be of an msiexec command or an // unpack.bat script containing the msiexec command. In the later // case, when misexec returns 1618, the unpack.bat may return 1603 - if ((result.exitCode() == 1618) || (result.exitCode() == 1603 && isUnpack)) { + if ((result.getExitCode() == 1618) || (result.getExitCode() == 1603 && isUnpack)) { // Another installation is already in progress. // Wait a little and try again. Long timeout = 1000L * (attempt + 3); // from 3 to 10 seconds @@ -100,7 +100,7 @@ public class WindowsHelper { break; } - return result.exitCode(); + return result.getExitCode(); } static PackageHandlers createMsiPackageHandlers(boolean createMsiLog) { @@ -462,7 +462,7 @@ public class WindowsHelper { var status = Executor.of("reg", "query", keyPath, "/v", valueName) .saveOutput() .executeWithoutExitCodeCheck(); - if (status.exitCode() == 1) { + if (status.getExitCode() == 1) { // Should be the case of no such registry value or key String lookupString = "ERROR: The system was unable to find the specified registry key or value."; TKit.assertTextStream(lookupString) diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java new file mode 100644 index 00000000000..d9ab38e006a --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandAction.java @@ -0,0 +1,76 @@ +/* + * 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.test.mock; + +import java.io.PrintStream; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * An action. + */ +@FunctionalInterface +public interface CommandAction { + + public record Context(PrintStream out, PrintStream err, List args) { + + public Context { + Objects.requireNonNull(out); + Objects.requireNonNull(err); + args.forEach(Objects::requireNonNull); + } + + public Optional findOptionValue(String option) { + Objects.requireNonNull(option); + var idx = args.indexOf(option); + if (idx >= 0 && idx + 1 < args.size()) { + return Optional.of(args.get(idx + 1)); + } else { + return Optional.empty(); + } + } + + public String optionValue(String option) { + return findOptionValue(option).orElseThrow(() -> { + throw new MockIllegalStateException(String.format("No option %s", option)); + }); + } + + public MockIllegalStateException unexpectedArguments() { + return new MockIllegalStateException(String.format("Unexpected arguments: %s", args)); + } + } + + /** + * Runs the action in the given context. + * + * @param context the context + * @return an {@code Optional} wrapping the exit code, indicating it is the last + * action in the sequence or an empty {@code Optional} otherwise + * @throws Exception simulates a failure + * @throws MockIllegalStateException if error in internal mock logic occurred. + * E.g.: if the action was called unexpectedly + */ + Optional run(Context context) throws Exception, MockIllegalStateException; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpec.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpec.java new file mode 100644 index 00000000000..25814d84205 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpec.java @@ -0,0 +1,86 @@ +/* + * 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.test.mock; + +import java.util.Objects; +import java.util.Optional; +import jdk.jpackage.internal.util.function.ThrowingSupplier; +import jdk.jpackage.internal.util.function.ThrowingConsumer; +import jdk.jpackage.internal.util.function.ThrowingRunnable; + +/** + * Specification of a {@link CommandAction}. + *

    + * Comprised of a human-readable description and an associated action. + */ +public interface CommandActionSpec { + + String description(); + CommandAction action(); + + public static CommandActionSpec create(String description, CommandAction action) { + return new Internal.DefaultCommandActionSpec(description, action); + } + + public static CommandActionSpec create(String description, ThrowingSupplier action) { + Objects.requireNonNull(action); + return create(description, _ -> { + return Optional.of(action.get()); + }); + } + + public static CommandActionSpec create(String description, ThrowingRunnable action) { + Objects.requireNonNull(action); + return create(description, _ -> { + action.run(); + return Optional.empty(); + }); + } + + @SuppressWarnings("overloads") + public static CommandActionSpec create(String description, ThrowingConsumer action) { + Objects.requireNonNull(action); + return create(description, context -> { + action.accept(context); + return Optional.empty(); + }); + } + + final class Internal { + + private Internal() { + } + + private record DefaultCommandActionSpec(String description, CommandAction action) implements CommandActionSpec { + DefaultCommandActionSpec { + Objects.requireNonNull(description); + Objects.requireNonNull(action); + } + + @Override + public String toString() { + return description(); + } + } + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java new file mode 100644 index 00000000000..e89e458c02d --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java @@ -0,0 +1,185 @@ +/* + * 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.test.mock; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.function.ExceptionBox; + +/** + * A sequence of actions. + */ +public record CommandActionSpecs(List specs) { + + public CommandActionSpecs { + Objects.requireNonNull(specs); + } + + public CommandActionSpecs andThen(CommandActionSpecs other) { + return build().append(this).append(other).create(); + } + + public Stream actions() { + return specs.stream().map(CommandActionSpec::action); + } + + public CommandMock.Builder toCommandMockBuilder() { + return new CommandMock.Builder().mutate(builder -> { + builder.actions().append(this); + }); + } + + @Override + public String toString() { + return specs.toString(); + } + + public static Builder build() { + return new Builder(); + } + + public static final class Builder { + + public CommandActionSpecs create() { + return new CommandActionSpecs(List.copyOf(specs)); + } + + public Builder stdout(List content) { + Objects.requireNonNull(content); + return action(CommandActionSpec.create(String.format("%s>>1", content), context -> { + var out = context.out(); + content.forEach(out::println); + })); + } + + public Builder stdout(String... str) { + return stdout(List.of(str)); + } + + public Builder stderr(List content) { + Objects.requireNonNull(content); + return action(CommandActionSpec.create(String.format("%s>>2", content), context -> { + var err = context.err(); + content.forEach(err::println); + })); + } + + public Builder stderr(String... str) { + return stderr(List.of(str)); + } + + public Builder printToStdout(List content) { + Objects.requireNonNull(content); + return action(CommandActionSpec.create(String.format("%s(no-eol)>>1", content), context -> { + var out = context.out(); + content.forEach(out::print); + })); + } + + public Builder printToStdout(String... str) { + return printToStdout(List.of(str)); + } + + public Builder printToStderr(List content) { + Objects.requireNonNull(content); + return action(CommandActionSpec.create(String.format("%s(no-eol)>>2", content), context -> { + var err = context.err(); + content.forEach(err::print); + })); + } + + public Builder printToStderr(String... str) { + return printToStderr(List.of(str)); + } + + public Builder exit(int exitCode) { + return action(CommandActionSpec.create(String.format("exit(%d)", exitCode), () -> { + return exitCode; + })); + } + + public Builder exit() { + return exit(0); + } + + public Builder exit(CommandMockExit exit) { + switch (exit) { + case SUCCEED -> { + return exit(); + } + case EXIT_1 -> { + return exit(1); + } + case THROW_MOCK_IO_EXCEPTION -> { + return action(CommandActionSpec.create("", () -> { + throw new MockingToolProvider.RethrowableException(new MockIOException("Kaput!")); + })); + } + default -> { + throw ExceptionBox.reachedUnreachable(); + } + } + } + + public Builder mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + + public Builder append(Builder other) { + return append(other.specs); + } + + public Builder append(CommandActionSpecs other) { + return append(other.specs()); + } + + public Builder append(List other) { + specs.addAll(other); + return this; + } + + public Builder action(CommandActionSpec v) { + specs.add(Objects.requireNonNull(v)); + return this; + } + + public Builder copy() { + return new Builder().append(this); + } + + public CommandMock.Builder toCommandMockBuilder() { + return new CommandMock.Builder().mutate(builder -> { + builder.actions(this); + }); + } + + private final List specs = new ArrayList<>(); + } + + public static final CommandActionSpecs UNREACHABLE = new CommandActionSpecs(List.of()); +} + diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMock.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMock.java new file mode 100644 index 00000000000..bb3980fd4e0 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMock.java @@ -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. + */ +package jdk.jpackage.test.mock; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.stream.IntStream; + +/** + * Command mock. + */ +public sealed interface CommandMock permits ToolProviderCommandMock, VerbatimCommandMock, CompletableCommandMock { + + public static CommandMock ioerror(String name) { + return CommandActionSpecs.build() + .exit(CommandMockExit.THROW_MOCK_IO_EXCEPTION) + .toCommandMockBuilder().name(Objects.requireNonNull(name)).create(); + } + + public static CommandMock fail(String name) { + return CommandActionSpecs.build() + .exit(CommandMockExit.EXIT_1) + .toCommandMockBuilder().name(Objects.requireNonNull(name)).create(); + } + + public static CommandMock succeed(String name) { + return CommandActionSpecs.build() + .exit(CommandMockExit.SUCCEED) + .toCommandMockBuilder().name(Objects.requireNonNull(name)).create(); + } + + public static CommandMock unreachable() { + return MockingToolProvider.UNREACHABLE; + } + + public final class Builder { + + public ToolProviderCommandMock create() { + + var actionSpecs = Optional.ofNullable(scriptBuilder) + .map(CommandActionSpecs.Builder::create) + .orElse(CommandActionSpecs.UNREACHABLE); + if (actionSpecs.equals(CommandActionSpecs.UNREACHABLE)) { + return (ToolProviderCommandMock)unreachable(); + } + + var theName = Optional.ofNullable(name).orElse("mock"); + var script = actionSpecs.actions().toList(); + switch (repeat) { + case 0 -> { + return MockingToolProvider.create(theName, script); + } + case -1 -> { + return MockingToolProvider.createLoop(theName, script); + } + default -> { + var repeatedScript = IntStream.rangeClosed(0, repeat) + .mapToObj(i -> script) + .flatMap(List::stream) + .toList(); + return MockingToolProvider.create(theName, repeatedScript); + } + } + } + + public Builder name(String v) { + name = v; + return this; + } + + public Builder mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + + public Builder repeat(int v) { + repeat = Integer.max(-1, v); + return this; + } + + public Builder noRepeats() { + return repeat(0); + } + + public Builder repeatInfinitely() { + return repeat(-1); + } + + public Builder actions(CommandActionSpecs.Builder v) { + scriptBuilder = Optional.ofNullable(v).orElseGet(CommandActionSpecs::build); + return this; + } + + public CommandActionSpecs.Builder actions() { + if (scriptBuilder == null) { + scriptBuilder = CommandActionSpecs.build(); + } + return scriptBuilder; + } + + private String name; + private int repeat = -1; + private CommandActionSpecs.Builder scriptBuilder; + } + +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockExit.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockExit.java new file mode 100644 index 00000000000..26bc8ba757b --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockExit.java @@ -0,0 +1,60 @@ +/* + * 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.test.mock; + +import jdk.jpackage.internal.util.CommandOutputControl; + +public enum CommandMockExit { + /** + * Exit normally with "0" exit code. + */ + SUCCEED(true, true), + /** + * Exit normally with "1" exit code. + */ + EXIT_1(false, true), + /** + * Throw {@link MockIOException}. This simulates a situation when an I/O error + * occurs starting a subprocess with {@link ProcessBuilder#start()}. + * {@link CommandOutputControl.Executable#execute()} will handle I/O errors and + * let them out. + */ + THROW_MOCK_IO_EXCEPTION(false, false), + ; + + CommandMockExit(boolean succeed, boolean exitNormally) { + this.succeed = succeed; + this.exitNormally = exitNormally; + } + + public boolean succeed() { + return succeed; + } + + public boolean exitNormally() { + return exitNormally; + } + + private final boolean succeed; + private final boolean exitNormally; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockSpec.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockSpec.java new file mode 100644 index 00000000000..2572309e751 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandMockSpec.java @@ -0,0 +1,63 @@ +/* + * 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.test.mock; + +import java.nio.file.Path; +import java.util.Objects; + +/** + * Specification of a {@link CommandMock}. + */ +public record CommandMockSpec(Path name, Path mockName, CommandActionSpecs actions) { + + public CommandMockSpec { + Objects.requireNonNull(name); + Objects.requireNonNull(mockName); + Objects.requireNonNull(actions); + } + + public CommandMockSpec(Path name, CommandActionSpecs actions) { + this(name, Path.of(name.toString() + "-mock"), actions); + } + + public CommandMockSpec(String name, CommandActionSpecs actions) { + this(Path.of(name), actions); + } + + public CommandMockSpec(String name, String mockName, CommandActionSpecs actions) { + this(Path.of(name), Path.of(mockName), actions); + } + + public CommandMock.Builder toCommandMockBuilder() { + return actions.toCommandMockBuilder().name(mockName.toString()); + } + + public boolean isDefaultMockName() { + return (name.getFileName().toString() + "-mock").equals(mockName.getFileName().toString()); + } + + @Override + public String toString() { + return String.format("mock-of(%s)%s", name, actions); + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CompletableCommandMock.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CompletableCommandMock.java new file mode 100644 index 00000000000..c9441e038b3 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CompletableCommandMock.java @@ -0,0 +1,31 @@ +/* + * 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.test.mock; + +/** + * Command mock that runs a finite sequence of actions. + */ +public sealed interface CompletableCommandMock extends CommandMock permits ToolProviderCompletableCommandMock { + + boolean completed(); +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIOException.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIOException.java new file mode 100644 index 00000000000..3b299f05d3f --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIOException.java @@ -0,0 +1,39 @@ +/* + * 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.test.mock; + +import java.io.IOException; + +/** + * Simulates I/O error. + * + * @see CommandMockExit#THROW_MOCK_IO_EXCEPTION + */ +public final class MockIOException extends IOException { + + MockIOException(String msg) { + super(msg); + } + + private static final long serialVersionUID = 1L; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java new file mode 100644 index 00000000000..1817587364a --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockIllegalStateException.java @@ -0,0 +1,35 @@ +/* + * 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.test.mock; + +/** + * Indicates command mock internal error. + */ +public final class MockIllegalStateException extends IllegalStateException { + + public MockIllegalStateException(String msg) { + super(msg); + } + + private static final long serialVersionUID = 1L; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockingToolProvider.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockingToolProvider.java new file mode 100644 index 00000000000..f8c04cc3927 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/MockingToolProvider.java @@ -0,0 +1,164 @@ +/* + * 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.test.mock; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; +import jdk.jpackage.internal.util.function.ExceptionBox; + +/** + * A command simulator implementing {@code ToolProvider}. + *

    + * Iterates over actions and runs them. Each action is write to stdout/stderr, create a file, etc. + */ +abstract sealed class MockingToolProvider implements ToolProviderCommandMock { + + MockingToolProvider(String name, Iterator actionIter) { + this.name = Objects.requireNonNull(name); + this.actionIter = Objects.requireNonNull(actionIter); + } + + static ToolProviderCommandMock createLoop(String name, Iterable actions) { + return new MockingToolProvider.NonCompletable(name, actions); + } + + static MockingToolProvider create(String name, Iterable actions) { + return new MockingToolProvider.Completable(name, actions); + } + + public boolean completed() { + return !actionIter.hasNext(); + } + + @Override + public String name() { + return name; + } + + @Override + public int run(PrintStream out, PrintStream err, String... args) { + var context = new CommandAction.Context(out, err, List.of(args)); + try { + while (actionIter.hasNext()) { + var action = actionIter.next(); + var reply = action.run(context); + if (reply.isPresent()) { + return reply.get(); + } + } + } catch (RethrowableException ex) { + // Let the checked exception out. + throwAny(ex.getCause()); + // Unreachable + return 0; + } catch (Exception ex) { + throw ExceptionBox.toUnchecked(ex); + } + + // No more actions to execute, but still expect it to keep going. + throw new MockIllegalStateException("No more actions to execute"); + } + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + throw new UnsupportedOperationException(); + } + + static final class RethrowableException extends Exception { + + RethrowableException(Exception ex) { + super(Objects.requireNonNull(ex)); + } + + private static final long serialVersionUID = 1L; + } + + @SuppressWarnings("unchecked") + private static void throwAny(Throwable e) throws E { + throw (E)e; + } + + private static final class LoopIterator implements Iterator { + + LoopIterator(Iterable iterable) { + this.iterable = Objects.requireNonNull(iterable); + rewind(); + } + + @Override + public boolean hasNext() { + return iter != null; + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } else if (iter.hasNext()) { + return iter.next(); + } else { + rewind(); + if (!hasNext()) { + throw new NoSuchElementException(); + } else { + return iter.next(); + } + } + } + + private void rewind() { + iter = Objects.requireNonNull(iterable.iterator()); + if (!iter.hasNext()) { + iter = null; + } + } + + private final Iterable iterable; + private Iterator iter; + } + + static final class NonCompletable extends MockingToolProvider { + + NonCompletable(String name, Iterable actions) { + super(name, new LoopIterator<>(actions)); + } + + } + + static final class Completable extends MockingToolProvider implements ToolProviderCompletableCommandMock { + + Completable(String name, Iterable actions) { + super(name, actions.iterator()); + } + + } + + private final String name; + private final Iterator actionIter; + + static ToolProviderCommandMock UNREACHABLE = new MockingToolProvider.NonCompletable("", List.of()); +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/Script.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/Script.java new file mode 100644 index 00000000000..bc51d2b69f8 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/Script.java @@ -0,0 +1,297 @@ +/* + * 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.test.mock; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.IdentityWrapper; + +/** + * Script of command mocks. + */ +public interface Script { + + /** + * Returns a command mock for the given command line. + * + * @param cmdline the command line for which to look up a command mock + * + * @return a command mock matching the given command line + * @throws ScriptException if an internal script error occures + */ + CommandMock map(List cmdline) throws ScriptException; + + /** + * Returns command mocks registered with this object that have not completed yet. + * + * @See {@link CompletableCommandMock#completed()} + * + * @return the command mocks registered with this object that have not completed yet + */ + Collection incompleteMocks(); + + public static Builder build() { + return new Builder(); + } + + public static Predicate> cmdlinePredicate( + Predicate pred, + Function conv, + Function, Stream> toStream) { + + Objects.requireNonNull(pred); + Objects.requireNonNull(conv); + Objects.requireNonNull(toStream); + + return cmdline -> { + return toStream.apply(cmdline).map(conv).filter(pred).findFirst().isPresent(); + }; + } + + public static Predicate> cmdlineContains(String arg) { + return cmdlinePredicate(Predicate.isEqual(Objects.requireNonNull(arg)), x -> x, List::stream); + } + + public static Predicate> cmdlineContains(Path arg) { + return cmdlinePredicate(Predicate.isEqual(Objects.requireNonNull(arg)), Path::of, List::stream); + } + + public static Predicate> cmdlineStartsWith(String arg) { + return cmdlinePredicate(Predicate.isEqual(Objects.requireNonNull(arg)), x -> x, cmdline -> { + return cmdline.stream().limit(1); + }); + } + + public static Predicate> cmdlineStartsWith(Path arg) { + return cmdlinePredicate(Predicate.isEqual(Objects.requireNonNull(arg)), Path::of, cmdline -> { + return cmdline.stream().limit(1); + }); + } + + public final class ScriptException extends RuntimeException { + + ScriptException(RuntimeException cause) { + super(Objects.requireNonNull(cause)); + } + + ScriptException(String msg) { + super(Objects.requireNonNull(msg)); + } + + private static final long serialVersionUID = 1L; + } + + public final class Builder { + + public Script createSequence() { + return new SequenceScript(List.copyOf(instructions), completableMocks()); + } + + public Script createLoop() { + return new LoopScript(List.copyOf(instructions), completableMocks()); + } + + public Builder map(Predicate> pred, CommandMock mock) { + Objects.requireNonNull(pred); + Objects.requireNonNull(mock); + if (mock instanceof CompletableCommandMock completable) { + completableMocks.add(new IdentityWrapper<>(completable)); + } + instruction(cmdline -> { + if (pred.test(cmdline)) { + return new CommandMockResult(Optional.of(mock)); + } else { + return new CommandMockResult(Optional.empty()); + } + }); + return this; + } + + public Builder map(Predicate> pred, CommandMock.Builder mock) { + Optional.ofNullable(commandMockBuilderMutator).ifPresent(mock::mutate); + return map(pred, mock.create()); + } + + public Builder map(Predicate> pred, CommandMockSpec mock) { + return map(pred, mock.toCommandMockBuilder()); + } + + public Builder map(CommandMockSpec mock) { + return map(cmdlineStartsWith(mock.name()), mock.toCommandMockBuilder()); + } + + public Builder use(CommandMock mock) { + return map(_ -> true, mock); + } + + public Builder use(Predicate> pred, CommandMock.Builder mock) { + return map(_ -> true, mock); + } + + public Builder use(Predicate> pred, CommandMockSpec mock) { + return map(_ -> true, mock); + } + + public Builder branch(Predicate> pred, Script script) { + Objects.requireNonNull(pred); + Objects.requireNonNull(script); + instruction(cmdline -> { + if (pred.test(cmdline)) { + return new ScriptResult(script); + } else { + return new CommandMockResult(Optional.empty()); + } + }); + return this; + } + + public Builder commandMockBuilderMutator(Consumer v) { + commandMockBuilderMutator = v; + return this; + } + + public Builder mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + + private Builder instruction(Function, Result> instruction) { + instructions.add(Objects.requireNonNull(instruction)); + return this; + } + + private Collection completableMocks() { + return completableMocks.stream().map(IdentityWrapper::value).toList(); + } + + private static RuntimeException noMapping(List cmdline) { + return new ScriptException(String.format("Mapping for %s command line not found", cmdline)); + } + + private sealed interface Result { + } + + private record CommandMockResult(Optional value) implements Result { + CommandMockResult { + Objects.requireNonNull(value); + } + } + + private record ScriptResult(Script value) implements Result { + ScriptResult { + Objects.requireNonNull(value); + } + } + + private abstract static class AbstractScript implements Script { + + AbstractScript(Collection completableMocks) { + this.completableMocks = Objects.requireNonNull(completableMocks); + } + + @Override + public Collection incompleteMocks() { + return completableMocks.stream().filter(Predicate.not(CompletableCommandMock::completed)).toList(); + } + + private final Collection completableMocks; + } + + private static final class LoopScript extends AbstractScript { + + LoopScript(List, Result>> instructions, + Collection completableMocks) { + super(completableMocks); + this.instructions = Objects.requireNonNull(instructions); + } + + @Override + public CommandMock map(List cmdline) { + for (var instruction : instructions) { + switch (instruction.apply(cmdline)) { + case CommandMockResult result -> { + var mock = result.value(); + if (mock.isPresent()) { + return mock.get(); + } + } + case ScriptResult result -> { + return result.value().map(cmdline); + } + } + } + + throw noMapping(cmdline); + } + + private final List, Result>> instructions; + } + + private static final class SequenceScript extends AbstractScript { + + SequenceScript(List, Result>> instructions, + Collection completableMocks) { + super(completableMocks); + this.iter = instructions.iterator(); + } + + @Override + public CommandMock map(List cmdline) { + if (!iter.hasNext()) { + throw new ScriptException("No more mappings"); + } else { + switch (iter.next().apply(cmdline)) { + case CommandMockResult result -> { + var mock = result.value(); + if (mock.isPresent()) { + return mock.get(); + } + } + case ScriptResult result -> { + return result.value().map(cmdline); + } + } + } + + throw noMapping(cmdline); + } + + private final Iterator, Result>> iter; + } + + private Consumer commandMockBuilderMutator = CommandMock.Builder::noRepeats; + private final List, Result>> instructions = new ArrayList<>(); + private final Set> completableMocks = new HashSet<>(); + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpec.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpec.java new file mode 100644 index 00000000000..60e5723e9a7 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpec.java @@ -0,0 +1,179 @@ +/* + * 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.test.mock; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.IntStream; + +/** + * Specification of a {@link Script}. + */ +public record ScriptSpec(List items, boolean loop) { + + public ScriptSpec { + Objects.requireNonNull(items); + } + + @Override + public String toString() { + var sb = new StringBuilder(); + sb.append(items.toString()); + if (loop) { + // Append "Clockwise Gapped Circle Arrow" Unicode symbol. + sb.append('(').appendCodePoint(0x27F3).append(')'); + } + return sb.toString(); + } + + public Script create() { + var script = Script.build(); + items.forEach(item -> { + item.applyTo(script, loop); + }); + if (loop) { + return script.createLoop(); + } else { + return script.createSequence(); + } + } + + public Collection commandNames() { + return items.stream().map(Item::mockSpec).map(CommandMockSpec::name).distinct().toList(); + } + + private record Item(CommandMockSpec mockSpec, int repeatCount, boolean detailedDescription) { + + private Item { + Objects.requireNonNull(mockSpec); + if (repeatCount < 0) { + throw new IllegalArgumentException(); + } + } + + @Override + public String toString() { + var sb = new StringBuilder(); + if (detailedDescription) { + sb.append(mockSpec); + } else if (mockSpec.isDefaultMockName()) { + sb.append(mockSpec.name()); + } else { + sb.append(mockSpec.mockName()); + } + if (repeatCount > 0) { + sb.append('(').append(repeatCount + 1).append(')'); + } + return sb.toString(); + } + + void applyTo(Script.Builder script, boolean loopScript) { + var pred = Script.cmdlineStartsWith(mockSpec.name()); + + var mockBuilder = mockSpec.toCommandMockBuilder(); + if (loopScript) { + script.map(pred, mockBuilder.repeat(repeatCount).create()); + } else { + mockBuilder.repeat(0); + IntStream.rangeClosed(0, repeatCount).forEach(_ -> { + script.map(pred, mockBuilder.create()); + }); + } + } + + } + + public static Builder build() { + return new Builder(); + } + + public static final class Builder { + + private Builder() { + } + + public ScriptSpec create() { + return new ScriptSpec(List.copyOf(items), loop); + } + + public Builder loop(boolean v) { + loop = v; + return this; + } + + public Builder loop() { + return loop(true); + } + + public final class ItemBuilder { + + private ItemBuilder(CommandMockSpec mockSpec) { + this.mockSpec = Objects.requireNonNull(mockSpec); + } + + public Builder add() { + items.add(new Item(mockSpec, repeat, detailedDescription)); + return Builder.this; + } + + public ItemBuilder repeat(int v) { + if (repeat < 0) { + throw new IllegalArgumentException(); + } + repeat = v; + return this; + } + + public ItemBuilder detailedDescription(boolean v) { + detailedDescription = v; + return this; + } + + public ItemBuilder detailedDescription() { + return detailedDescription(true); + } + + private final CommandMockSpec mockSpec; + private int repeat; + private boolean detailedDescription; + } + + public Builder add(CommandMockSpec mockSpec) { + return build(mockSpec).add(); + } + + public Builder addLoop(CommandMockSpec mockSpec) { + return build(mockSpec).add(); + } + + public ItemBuilder build(CommandMockSpec mockSpec) { + return new ItemBuilder(mockSpec); + } + + private final List items = new ArrayList<>(); + private boolean loop; + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpecInDir.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpecInDir.java new file mode 100644 index 00000000000..8c59c777d96 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ScriptSpecInDir.java @@ -0,0 +1,66 @@ +/* + * 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.test.mock; + +import java.nio.file.Path; +import java.util.Objects; + +/** + * Specification of a {@link Script} bound to a specific directory. + */ +public class ScriptSpecInDir { + + public ScriptSpecInDir() { + } + + @Override + public String toString() { + return scriptSpec.toString(); + } + + public boolean isPathInDir(Path path) { + return path.startsWith(dir); + } + + public ScriptSpecInDir dir(Path v) { + dir = v; + return this; + } + + public ScriptSpecInDir scriptSpec(ScriptSpec v) { + scriptSpec = v; + return this; + } + + public ScriptSpec scriptSpec() { + Objects.requireNonNull(dir); + return Objects.requireNonNull(scriptSpec); + } + + public Script create() { + return scriptSpec().create(); + } + + private ScriptSpec scriptSpec; + private Path dir; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCommandMock.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCommandMock.java new file mode 100644 index 00000000000..ee9556277d4 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCommandMock.java @@ -0,0 +1,29 @@ +/* + * 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.test.mock; + +import java.util.spi.ToolProvider; + +public sealed interface ToolProviderCommandMock extends CommandMock, ToolProvider + permits ToolProviderCompletableCommandMock, MockingToolProvider { +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCompletableCommandMock.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCompletableCommandMock.java new file mode 100644 index 00000000000..907cedd38d7 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/ToolProviderCompletableCommandMock.java @@ -0,0 +1,27 @@ +/* + * 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.test.mock; + +public sealed interface ToolProviderCompletableCommandMock extends ToolProviderCommandMock, CompletableCommandMock + permits MockingToolProvider.Completable { +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/VerbatimCommandMock.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/VerbatimCommandMock.java new file mode 100644 index 00000000000..799caaa0ea9 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/VerbatimCommandMock.java @@ -0,0 +1,28 @@ +/* + * 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.test.mock; + +public enum VerbatimCommandMock implements CommandMock { + + INSTANCE +} diff --git a/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LibProvidersLookupTest.java b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LibProvidersLookupTest.java new file mode 100644 index 00000000000..41a9bdc647e --- /dev/null +++ b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LibProvidersLookupTest.java @@ -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. + */ + +package jdk.jpackage.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockExit; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +public class LibProvidersLookupTest { + + @ParameterizedTest + @EnumSource(value = CommandMockExit.class) + public void test_supported(CommandMockExit exit) { + + var ldd = CommandActionSpecs.build().exit(exit).toCommandMockBuilder().name("ldd-mock").create(); + + Globals.main(() -> { + Globals.instance().executorFactory(() -> { + return new Executor().mapper(executor -> { + return executor.copy().mapper(null).toolProvider(ldd); + }); + }); + + boolean actual = LibProvidersLookup.supported(); + assertEquals(exit.exitNormally(), actual); + + return 0; + }); + } +} diff --git a/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxPackageArchTest.java b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxPackageArchTest.java new file mode 100644 index 00000000000..baf03a32142 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxPackageArchTest.java @@ -0,0 +1,154 @@ +/* + * 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.model.StandardPackageType.LINUX_DEB; +import static jdk.jpackage.internal.model.StandardPackageType.LINUX_RPM; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import jdk.jpackage.internal.model.StandardPackageType; +import jdk.jpackage.internal.util.Result; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockExit; +import jdk.jpackage.test.mock.CommandMockSpec; +import jdk.jpackage.test.mock.Script; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class LinuxPackageArchTest { + + @ParameterizedTest + @MethodSource + public void test(Runnable test) { + test.run(); + } + + private static List test() { + var data = new ArrayList(); + + // "foo" stdout interleaved with "bar" stderr + var fooArch = CommandActionSpecs.build() + .printToStdout("f").printToStderr("b") + .printToStdout("o").printToStderr("a") + .printToStdout("o").printToStderr("r"); + + for (var exit : CommandMockExit.values()) { + var dpkg = fooArch.copy().printToStdout("-deb").exit(exit).create(); + + data.add(new DebTestSpec(dpkg, Optional.of("foo-deb").filter(_ -> { + return exit.succeed(); + }))); + } + + for (var rpmbuildExit : CommandMockExit.values()) { + var rpmbuild = fooArch.copy().printToStdout("-rpmbuild").exit(rpmbuildExit).create(); + for (var rpmExit : CommandMockExit.values()) { + var rpm = fooArch.copy().printToStdout("-rpm").exit(rpmExit).create(); + Optional expect; + if (rpmbuildExit.succeed()) { + expect = Optional.of("foo-rpmbuild"); + rpm = CommandActionSpecs.UNREACHABLE; + } else { + if (rpmExit.succeed()) { + expect = Optional.of("foo-rpm"); + } else { + expect = Optional.empty(); + } + } + + data.add(new RpmTestSpec(rpmbuild, rpm, expect)); + } + } + + return data; + } + + record RpmTestSpec(CommandActionSpecs rpmbuild, CommandActionSpecs rpm, Optional expect) implements Runnable { + + RpmTestSpec { + Objects.requireNonNull(rpm); + Objects.requireNonNull(rpmbuild); + Objects.requireNonNull(expect); + } + + @Override + public void run() { + + // Create an executor factory that will: + // - Substitute the "rpm" command with `rpm` mock. + // - Substitute the "rpmbuild" command with `rpmbuild` mock. + // - Throw if a command with the name other than "rpm" and "rpmbuild" is requested for execution. + + var script = Script.build() + // LinuxPackageArch must run the "rpmbuild" command first. Put its mapping at the first position. + .map(new CommandMockSpec("rpmbuild", rpmbuild)) + // LinuxPackageArch may optionally run the "rpm" command. Put its mapping after the "rpmbuild" command mapping. + .map(new CommandMockSpec("rpm", rpm)) + // Create a sequential script: after every Script#map() call, the script will advance the current mapping. + // This means each mapping in the script will be considered only once. + // If "rpm" and "rpmbuild" commands are executed in reverse order, the second Script#map() will throw. + .createSequence(); + + test(expect, LINUX_RPM, script); + } + } + + record DebTestSpec(CommandActionSpecs dpkg, Optional expect) implements Runnable { + + DebTestSpec { + Objects.requireNonNull(dpkg); + Objects.requireNonNull(expect); + } + + @Override + public void run() { + var script = Script.build().map(new CommandMockSpec("dpkg", dpkg)).createSequence(); + + test(expect, LINUX_DEB, script); + } + } + + private static void test(Optional expectedArch, StandardPackageType pkgType, Script script) { + + Globals.main(() -> { + + MockUtils.buildJPackage().script(script).applyToGlobals(); + + Result arch = LinuxPackageArch.create(pkgType); + + assertEquals(arch.hasValue(), expectedArch.isPresent()); + expectedArch.ifPresent(v -> { + assertEquals(v, arch.orElseThrow().value()); + }); + + assertEquals(List.of(), script.incompleteMocks()); + + return 0; + }); + } +} diff --git a/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxSystemEnvironmentTest.java b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxSystemEnvironmentTest.java new file mode 100644 index 00000000000..8ff958491b1 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/linux/jdk.jpackage/jdk/jpackage/internal/LinuxSystemEnvironmentTest.java @@ -0,0 +1,101 @@ +/* + * 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 org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import jdk.jpackage.internal.model.StandardPackageType; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockExit; +import jdk.jpackage.test.mock.CommandMockSpec; +import jdk.jpackage.test.mock.Script; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class LinuxSystemEnvironmentTest { + + @ParameterizedTest + @MethodSource + public void test_detectNativePackageType(DetectNativePackageTypeTestSpec test) { + test.run(); + } + + private static List test_detectNativePackageType() { + var data = new ArrayList(); + for (var rpmExit : CommandMockExit.values()) { + for (var debExit : CommandMockExit.values()) { + CommandActionSpecs deb = CommandActionSpecs.build().exit(debExit).create(); + CommandActionSpecs rpm; + Optional expected; + if (debExit.succeed()) { + expected = Optional.of(StandardPackageType.LINUX_DEB); + rpm = CommandActionSpecs.UNREACHABLE; + } else { + rpm = CommandActionSpecs.build().exit(rpmExit).create(); + if (rpmExit.succeed()) { + expected = Optional.of(StandardPackageType.LINUX_RPM); + } else { + expected = Optional.empty(); + } + } + data.add(new DetectNativePackageTypeTestSpec(expected, rpm, deb)); + } + } + return data; + } + + record DetectNativePackageTypeTestSpec(Optional expect, CommandActionSpecs rpm, CommandActionSpecs deb) { + + DetectNativePackageTypeTestSpec { + Objects.requireNonNull(expect); + Objects.requireNonNull(rpm); + Objects.requireNonNull(deb); + } + + void run() { + + var script = Script.build() + .map(new CommandMockSpec("rpm", rpm)) + .map(new CommandMockSpec("dpkg", deb)) + .createLoop(); + + Globals.main(() -> { + + MockUtils.buildJPackage().script(script).applyToGlobals(); + + var actual = LinuxSystemEnvironment.detectNativePackageType(); + + assertEquals(expect, actual); + + assertEquals(List.of(), script.incompleteMocks()); + + return 0; + }); + } + } +} diff --git a/test/jdk/tools/jpackage/junit/linux/junit.java b/test/jdk/tools/jpackage/junit/linux/junit.java index 214812e951e..0fd337c812c 100644 --- a/test/jdk/tools/jpackage/junit/linux/junit.java +++ b/test/jdk/tools/jpackage/junit/linux/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 @@ -30,3 +30,35 @@ * ../../share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java * @run junit jdk.jpackage/jdk.jpackage.internal.LinuxApplicationLayoutTest */ + +/* @test + * @summary Test LinuxSystemEnvironment + * @requires (os.family == "linux") + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.mock.* + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/LinuxSystemEnvironmentTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java + * @run junit jdk.jpackage/jdk.jpackage.internal.LinuxSystemEnvironmentTest + */ + +/* @test + * @summary Test LibProvidersLookup + * @requires (os.family == "linux") + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.mock.* + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/LibProvidersLookupTest.java + * @run junit jdk.jpackage/jdk.jpackage.internal.LibProvidersLookupTest + */ + +/* @test + * @summary Test LinuxPackageArch + * @requires (os.family == "linux") + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.mock.* + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/LinuxPackageArchTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java + * @run junit jdk.jpackage/jdk.jpackage.internal.LinuxPackageArchTest + */ diff --git a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java new file mode 100644 index 00000000000..e5da383142a --- /dev/null +++ b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgPackagerTest.java @@ -0,0 +1,420 @@ +/* + * 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.model.StandardPackageType.MAC_DMG; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.MacPackagingPipeline.MacBuildApplicationTaskID; +import jdk.jpackage.internal.PackagingPipeline.BuildApplicationTaskID; +import jdk.jpackage.internal.model.AppImageLayout; +import jdk.jpackage.internal.model.RuntimeBuilder; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedResultException; +import jdk.jpackage.internal.util.FileUtils; +import jdk.jpackage.internal.util.RetryExecutor; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.test.mock.CommandActionSpec; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockSpec; +import jdk.jpackage.test.mock.MockIllegalStateException; +import jdk.jpackage.test.mock.ScriptSpec; +import jdk.jpackage.test.mock.ScriptSpecInDir; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class MacDmgPackagerTest { + + /** + * Exercise branches in {@link MacDmgPackager#buildDMG()}. + */ + @ParameterizedTest + @MethodSource + public void test(DmgScript scriptSpec, @TempDir Path workDir) { + scriptSpec.run(workDir); + } + + private static List test() { + var data = new ArrayList(); + + var succeed = CommandActionSpecs.build().exit().create(); + var fail = CommandActionSpecs.build().exit(1).create(); + + // Test create + for (var createFullSucceed : List.of(true, false)) { + var dmgScript = new DmgScript(); + + var scriptBuilder = ScriptSpec.build(); + + if (createFullSucceed) { + // `hdiutil create -srcfolder` succeeds + scriptBuilder.add(new CommandMockSpec("hdiutil", "hdiutil-create", dmgScript.hdiutilCreate().exit().create())); + } else { + // `hdiutil create -srcfolder` fails + scriptBuilder.add(new CommandMockSpec("hdiutil", "hdiutil-create", fail)); + scriptBuilder.add(new CommandMockSpec("hdiutil", "hdiutil-create-empty", dmgScript.hdiutilCreateEmpty().exit().create())); + } + + scriptBuilder + // `hdiutil attach` succeeds + .add(new CommandMockSpec("hdiutil", "hdiutil-attach", succeed)) + // `osascript` succeeds + .add(new CommandMockSpec("osascript", succeed)) + // `hdiutil detach` succeeds + .add(new CommandMockSpec("hdiutil", "hdiutil-detach", dmgScript.hdiutilDetach().exit().create())) + // `hdiutil convert` succeeds + .add(new CommandMockSpec("hdiutil", "hdiutil-convert", dmgScript.hdiutilConvert().exit().create())); + + data.add(dmgScript.scriptSpec(scriptBuilder.create())); + } + + // Test detach + for (var detachResult : DetachResult.values()) { + var dmgScript = new DmgScript(); + + var scriptBuilder = ScriptSpec.build() + .add(new CommandMockSpec("hdiutil", "hdiutil-create", dmgScript.hdiutilCreate().exit().create())) + .add(new CommandMockSpec("hdiutil", "hdiutil-attach", succeed)) + .add(new CommandMockSpec("osascript", succeed)); + + switch (detachResult) { + case ALL_FAIL -> { + dmgScript.expect(UnexpectedResultException.class); + scriptBuilder.build(new CommandMockSpec("hdiutil", "hdiutil-detach", fail)).repeat(9).add(); + } + case LAST_SUCCEED -> { + scriptBuilder + .build(new CommandMockSpec("hdiutil", "hdiutil-detach", fail)).repeat(8).add() + .add(new CommandMockSpec("hdiutil", "hdiutil-detach", dmgScript.hdiutilDetach().exit().create())) + .add(new CommandMockSpec("hdiutil", "hdiutil-convert", dmgScript.hdiutilConvert().exit().create())); + } + case FIRST_SUCCEED_WITH_EXIT_1 -> { + scriptBuilder + .build(new CommandMockSpec("hdiutil", "hdiutil-detach", dmgScript.hdiutilDetach().exit(1).create())) + .detailedDescription().add() + .add(new CommandMockSpec("hdiutil", "hdiutil-convert", dmgScript.hdiutilConvert().exit().create())); + } + case FIRST_SUCCEED_MOUNT_POINT_REMAINS -> { + scriptBuilder + .build(new CommandMockSpec("hdiutil", "hdiutil-detach", dmgScript.hdiutilDetach(false).exit().create())) + .detailedDescription().add() + .add(new CommandMockSpec("hdiutil", "hdiutil-convert", dmgScript.hdiutilConvert().exit().create())); + } + } + + data.add(dmgScript.scriptSpec(scriptBuilder.create())); + } + + return data; + } + + private enum DetachResult { + ALL_FAIL, + LAST_SUCCEED, + // The first `hdiutil detach` attempt exits with exit code "1" but deletes the mounted directory. + FIRST_SUCCEED_WITH_EXIT_1, + // The first `hdiutil detach` attempt exits with exit code "0" and the mounted directory stays undeleted. + FIRST_SUCCEED_MOUNT_POINT_REMAINS, + ; + } + + private static MacDmgSystemEnvironment createSysEnv(ScriptSpec scriptSpec) { + return new MacDmgSystemEnvironment( + Path.of("hdiutil"), + Path.of("osascript"), + Stream.of("SetFile").map(Path::of).filter(scriptSpec.commandNames()::contains).findFirst() + ); + } + + private static RuntimeBuilder createRuntimeBuilder() { + return new RuntimeBuilder() { + @Override + public void create(AppImageLayout appImageLayout) { + throw new UnsupportedOperationException(); + } + }; + } + + private static void runPackagingMock(Path workDir, MacDmgSystemEnvironment sysEnv) { + + var app = new ApplicationBuilder() + .appImageLayout(MacPackagingPipeline.APPLICATION_LAYOUT) + .runtimeBuilder(createRuntimeBuilder()) + .name("foo") + .create(); + + var macApp = new MacApplicationBuilder(app).create(); + + var macDmgPkg = new MacDmgPackageBuilder(new MacPackageBuilder(new PackageBuilder(macApp, MAC_DMG))).create(); + + var buildEnv = new BuildEnvBuilder(workDir.resolve("build-root")).appImageDirFor(macDmgPkg).create(); + + var packager = new MacDmgPackager(buildEnv, macDmgPkg, workDir, sysEnv); + + var pipelineBuilder = MacPackagingPipeline.build(Optional.of(packager.pkg())); + packager.accept(pipelineBuilder); + + // Disable actions of tasks filling an application image. + Stream.concat( + Stream.of(BuildApplicationTaskID.values()), + Stream.of(MacBuildApplicationTaskID.values()) + ).forEach(taskId -> { + pipelineBuilder.task(taskId).noaction().add(); + }); + + var contentMock = new ContentMock(); + + // Fill application image with content mock. + pipelineBuilder.task(BuildApplicationTaskID.CONTENT).applicationAction(env -> { + contentMock.create(env.resolvedLayout().contentDirectory()); + }).add(); + + pipelineBuilder.create().execute(buildEnv, packager.pkg(), packager.outputDir()); + + var outputDmg = packager.outputDir().resolve(packager.pkg().packageFileNameWithSuffix()); + + contentMock.verifyStoredInFile(outputDmg); + } + + private static final class DmgScript extends ScriptSpecInDir { + + @Override + public String toString() { + var sb = new StringBuilder(); + sb.append(super.toString()); + Optional.ofNullable(expectedErrorType).ifPresent(type -> { + sb.append("; ").append(type.getCanonicalName()); + }); + return sb.toString(); + } + + @Override + public DmgScript scriptSpec(ScriptSpec v) { + super.scriptSpec(v); + return this; + } + + DmgScript expect(Class v) { + expectedErrorType = v; + return this; + } + + void run(Path workDir) { + + var script = dir(Objects.requireNonNull(workDir)).create(); + + ExecutorFactory executorFactory = MockUtils.buildJPackage() + .script(script).listener(System.out::println).createExecutorFactory(); + + var objectFactory = ObjectFactory.build() + .executorFactory(executorFactory) + .retryExecutorFactory(new RetryExecutorFactory() { + @Override + public RetryExecutor retryExecutor(Class exceptionType) { + return RetryExecutorFactory.DEFAULT.retryExecutor(exceptionType).setSleepFunction(_ -> { + // Don't "sleep" to make the test run faster. + }); + } + }) + .create(); + + Globals.main(() -> { + Globals.instance().objectFactory(objectFactory); + if (expectedErrorType == null) { + runPackagingMock(workDir, createSysEnv(scriptSpec())); + } else { + var ex = assertThrows(Exception.class, () -> { + runPackagingMock(workDir, createSysEnv(scriptSpec())); + }); + var cause = ExceptionBox.unbox(ex); + assertEquals(expectedErrorType, cause.getClass()); + } + return 0; + }); + + assertEquals(List.of(), script.incompleteMocks()); + } + + CommandActionSpecs.Builder hdiutilCreate(boolean empty) { + CommandActionSpec action = CommandActionSpec.create("create", context -> { + var dstDmg = Path.of(context.optionValue("-ov")); + assertTrue(isPathInDir(dstDmg)); + + var volumeName = context.optionValue("-volname"); + + if (empty) { + createDmg(new CreateDmgResult(dstDmg, volumeName, Optional.empty())); + Files.createFile(dstDmg); + } else { + var srcDir = Path.of(context.optionValue("-srcfolder")); + assertTrue(isPathInDir(srcDir)); + + createDmg(new CreateDmgResult(dstDmg, volumeName, Optional.of(srcDir))); + + try (var walk = Files.walk(srcDir)) { + var paths = walk.map(srcDir::relativize).map(Path::toString).toList(); + Files.write(dstDmg, paths); + } + } + }); + return CommandActionSpecs.build().action(action); + } + + CommandActionSpecs.Builder hdiutilCreate() { + return hdiutilCreate(false); + } + + CommandActionSpecs.Builder hdiutilCreateEmpty() { + return hdiutilCreate(true); + } + + CommandActionSpecs.Builder hdiutilDetach() { + return hdiutilDetach(true); + } + + CommandActionSpecs.Builder hdiutilDetach(boolean deleteMountPoint) { + var sb = new StringBuilder(); + sb.append("detach"); + if (!deleteMountPoint) { + sb.append("(rm-mount-point)"); + } + CommandActionSpec action = CommandActionSpec.create(sb.toString(), context -> { + var mountPoint = Path.of(context.args().getLast()); + assertTrue(isPathInDir(mountPoint)); + + try (var walk = Files.walk(mountPoint)) { + var dstDmg = dmg().dmg(); + var paths = Stream.concat( + walk.map(mountPoint::relativize), + Files.readAllLines(dstDmg).stream().filter(Predicate.not(String::isEmpty)).map(Path::of) + ).sorted().map(Path::toString).toList(); + Files.write(dstDmg, paths); + } + + if (deleteMountPoint) { + FileUtils.deleteRecursive(mountPoint); + } + }); + return CommandActionSpecs.build().action(action); + } + + CommandActionSpecs.Builder hdiutilConvert() { + CommandActionSpec action = CommandActionSpec.create("convert", context -> { + var srcDmg = Path.of(context.args().get(1)); + assertTrue(isPathInDir(srcDmg)); + + var dstDmg = Path.of(context.args().getLast()); + assertTrue(isPathInDir(dstDmg)); + + Files.copy(srcDmg, dstDmg); + }); + return CommandActionSpecs.build().action(action); + } + + private void createDmg(CreateDmgResult v) { + if (dmg != null) { + throw new MockIllegalStateException("The DMG already set"); + } else { + dmg = Objects.requireNonNull(v); + } + } + + private CreateDmgResult dmg() { + if (dmg == null) { + throw new MockIllegalStateException("The DMG not set"); + } else { + return dmg; + } + } + + private record CreateDmgResult(Path dmg, String volumeName, Optional srcFolder) { + CreateDmgResult { + Objects.requireNonNull(dmg); + Objects.requireNonNull(volumeName); + Objects.requireNonNull(srcFolder); + } + } + + private CreateDmgResult dmg; + private Class expectedErrorType; + } + + private static final class ContentMock { + + void create(Path dir) throws IOException { + Files.createDirectories(dir.resolve("foo/bar")); + Files.writeString(dir.resolve("foo/bar/buz"), "Hello!"); + if (!OperatingSystem.isWindows()) { + Files.createSymbolicLink(dir.resolve("symlink"), Path.of("foo")); + } + } + + void verifyStoredInFile(Path file) { + try { + var expectedPaths = Stream.of( + Stream.of(Path.of("")), + DMG_ICON_FILES.stream(), + Stream.of( + Stream.of("foo/bar/buz"), + Stream.of("symlink").filter(_ -> { + return !OperatingSystem.isWindows(); + }) + ).flatMap(x -> x).map(Path::of).map(Path.of("foo.app/Contents")::resolve) + ).flatMap(x -> x).mapMulti(EXPAND_PATH).sorted().distinct().toList(); + var actualPaths = Files.readAllLines(file).stream().map(Path::of).toList(); + assertEquals(expectedPaths, actualPaths); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + } + + private final static BiConsumer> EXPAND_PATH = (path, sink) -> { + do { + sink.accept(path); + path = path.getParent(); + } while (path != null); + }; + + private final static List DMG_ICON_FILES = Stream.of( + ".VolumeIcon.icns", + ".background/background.tiff" + ).map(Path::of).collect(Collectors.toUnmodifiableList()); +} diff --git a/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgSystemEnvironmentTest.java b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgSystemEnvironmentTest.java new file mode 100644 index 00000000000..de2b07e86a6 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/macosx/jdk.jpackage/jdk/jpackage/internal/MacDmgSystemEnvironmentTest.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. + */ + +package jdk.jpackage.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import jdk.jpackage.internal.util.RetryExecutor; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockExit; +import jdk.jpackage.test.mock.CommandMockSpec; +import jdk.jpackage.test.mock.Script; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class MacDmgSystemEnvironmentTest { + + @ParameterizedTest + @MethodSource + void test_findSetFileUtility(FindSetFileUtilityTestSpec test) { + test.run(); + } + + private static List test_findSetFileUtility() { + var data = new ArrayList(); + + var succeed = CommandActionSpecs.build().exit().create(); + + for (var failureCause : List.of(CommandMockExit.EXIT_1, CommandMockExit.THROW_MOCK_IO_EXCEPTION)) { + + var fail = CommandActionSpecs.build().exit(failureCause).create(); + + for (var i = 0; i != MacDmgSystemEnvironment.SETFILE_KNOWN_PATHS.size(); i++) { + + var expected = MacDmgSystemEnvironment.SETFILE_KNOWN_PATHS.get(i); + + var mocks = new ArrayList(); + + MacDmgSystemEnvironment.SETFILE_KNOWN_PATHS.subList(0, i).stream().map(failureSetFilePath -> { + return new CommandMockSpec(failureSetFilePath, fail); + }).forEach(mocks::add); + + mocks.add(new CommandMockSpec(expected, succeed)); + + data.add(new FindSetFileUtilityTestSpec(Optional.of(expected), mocks)); + } + + var lastMocks = data.getLast().mockSpecs(); + var lastSucceedMock = lastMocks.getLast(); + var lastFailMock = new CommandMockSpec(lastSucceedMock.name(), lastSucceedMock.mockName(), fail); + + var mocks = new ArrayList<>(lastMocks); + mocks.set(mocks.size() - 1, lastFailMock); + + for (var xcrunOutout : List., Boolean>>of( + // Use the path to the command of the current process + // as an output mock for the /usr/bin/xcrun command. + // MacDmgSystemEnvironment.findSetFileUtility() reads the command output + // and checks whether it is an executable file, + // so the hardcoded value is not an option for the output mock. + Map.entry(Optional.of(ProcessHandle.current().info().command().orElseThrow()), true), + // "/usr/bin/xcrun" outputs a path to non-executable file. + Map.entry(Optional.of("/dev/null"), false), + // "/usr/bin/xcrun" outputs '\0' making subsequent Path.of("\0") fail. + Map.entry(Optional.of("\0"), false), + // "/usr/bin/xcrun" doesn't output anything. + Map.entry(Optional.empty(), false) + )) { + + + mocks.add(new CommandMockSpec("/usr/bin/xcrun", CommandActionSpecs.build().mutate(builder -> { + xcrunOutout.getKey().ifPresent(builder::stdout); + }).exit(CommandMockExit.SUCCEED).create())); + + Optional expected; + if (xcrunOutout.getValue()) { + expected = xcrunOutout.getKey(); + } else { + expected = Optional.empty(); + } + + data.add(new FindSetFileUtilityTestSpec(expected.map(Path::of), List.copyOf(mocks))); + + mocks.removeLast(); + } + + // The last test case: "/usr/bin/xcrun" fails + mocks.add(new CommandMockSpec("/usr/bin/xcrun", fail)); + data.add(new FindSetFileUtilityTestSpec(Optional.empty(), mocks)); + } + + return data; + } + + record FindSetFileUtilityTestSpec(Optional expected, List mockSpecs) { + + FindSetFileUtilityTestSpec { + Objects.requireNonNull(expected); + Objects.requireNonNull(mockSpecs); + } + + @Override + public String toString() { + var tokens = new ArrayList(); + expected.ifPresent(v -> { + tokens.add(String.format("expect=%s", v)); + }); + tokens.add(mockSpecs.toString()); + return tokens.stream().collect(Collectors.joining(", ")); + } + + void run() { + + var script = Script.build().mutate(builder -> { + mockSpecs.forEach(builder::map); + }).createSequence(); + + Globals.main(() -> { + MockUtils.buildJPackage().script(script).applyToGlobals(); + + var actual = MacDmgSystemEnvironment.findSetFileUtility(); + + assertEquals(expected, actual); + assertEquals(List.of(), script.incompleteMocks()); + + return 0; + }); + } + } +} diff --git a/test/jdk/tools/jpackage/junit/macosx/junit.java b/test/jdk/tools/jpackage/junit/macosx/junit.java index 1549aaa6cd7..c7fd2bc5f8d 100644 --- a/test/jdk/tools/jpackage/junit/macosx/junit.java +++ b/test/jdk/tools/jpackage/junit/macosx/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 @@ -30,3 +30,25 @@ * ../../share/jdk.jpackage/jdk/jpackage/internal/model/ApplicationLayoutTest.java * @run junit jdk.jpackage/jdk.jpackage.internal.MacApplicationLayoutTest */ + +/* @test + * @summary Test MacDmgSystemEnvironmentTest + * @requires (os.family == "mac") + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.mock.* + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/MacDmgSystemEnvironmentTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java + * @run junit jdk.jpackage/jdk.jpackage.internal.MacDmgSystemEnvironmentTest + */ + +/* @test + * @summary Test MacDmgPackagerTest + * @requires (os.family == "mac") + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.mock.* + * @compile/module=jdk.jpackage -Xlint:all -Werror + * jdk/jpackage/internal/MacDmgPackagerTest.java + * ../../share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java + * @run junit jdk.jpackage/jdk.jpackage.internal.MacDmgPackagerTest + */ diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java index 35ab1fbec5e..1a14330fe6e 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.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,15 +22,42 @@ */ package jdk.jpackage.internal; + +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; +import static jdk.jpackage.test.mock.CommandMock.ioerror; +import static jdk.jpackage.test.mock.CommandMock.succeed; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.IOException; +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.Optional; +import java.util.Set; +import java.util.spi.ToolProvider; +import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.cli.StandardBundlingOperation; +import jdk.jpackage.internal.model.AppImagePackageType; import jdk.jpackage.internal.model.BundlingOperationDescriptor; +import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.model.StandardPackageType; +import jdk.jpackage.test.Annotations; +import jdk.jpackage.test.HelloApp; +import jdk.jpackage.test.JUnitAdapter; +import jdk.jpackage.test.JavaAppDesc; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMock; +import jdk.jpackage.test.mock.CommandMockExit; +import jdk.jpackage.test.mock.Script; import org.junit.jupiter.api.Test; -public class DefaultBundlingEnvironmentTest { +public class DefaultBundlingEnvironmentTest extends JUnitAdapter { @Test void testDefaultBundlingOperation() { @@ -55,4 +82,200 @@ public class DefaultBundlingEnvironmentTest { assertEquals(descriptor, env.defaultOperation().orElseThrow()); assertEquals(1, executed[0]); } + + /** + * Tests that commands executed to initialize the system environment are + * executed only once. + * @throws IOException + */ + @Annotations.Test + @Annotations.ParameterSupplier + public void testInitializedOnce(StandardBundlingOperation op) throws IOException { + + List> executedCommands = Collections.synchronizedList(new ArrayList<>()); + + var script = createMockScript(op); + + ToolProvider jpackage = MockUtils.buildJPackage() + .os(op.os()) + .script(script) + .listener(executedCommands::add).create(); + + var inputDir = TKit.createTempDirectory("input"); + var appDesc = JavaAppDesc.parse(null); + HelloApp.createBundle(appDesc, inputDir); + + // + // The command line should fail as the main class name is not specified and it is not set in the main jar. + // + // Run native packaging twice. + // It can execute commands required to configure the system environment in the first iteration. + // It must not execute a single command in the second iteration. + // + // Run app image packaging once. + // It must not execute a single command because app image packaging should not require native commands (Unless + // it is macOS where it will sign the app image with an ad hoc signature + // using the codesign tool. But: #1 - it is not a variable part of the system environment; + // #2 - jpackage should bail out earlier). + // + + final var type = op.packageTypeValue(); + final int iterationCount; + if (op.packageType() instanceof AppImagePackageType) { + iterationCount = 1; + } else { + iterationCount = 2; + } + + for (var i = 0; i != iterationCount; i++) { + var result = new Executor().toolProvider(jpackage).saveOutput().args( + "--type=" + type, + "--input", inputDir.toString(), + "--main-jar", appDesc.jarFileName()).execute(); + + assertEquals(1, result.getExitCode()); + + // Assert it bailed out with the expected error. + assertEquals(List.of( + I18N.format("message.error-header", I18N.format("error.no-main-class-with-main-jar", appDesc.jarFileName())), + I18N.format("message.advice-header", I18N.format("error.no-main-class-with-main-jar.advice", appDesc.jarFileName())) + ), result.stderr()); + + TKit.trace("The list of executed commands:"); + executedCommands.forEach(cmdline -> { + TKit.trace(" " + cmdline); + }); + TKit.trace("Done"); + + if (i == 0) { + executedCommands.clear(); + } + } + + assertEquals(List.of(), executedCommands); + assertEquals(List.of(), script.incompleteMocks()); + } + + public static List testInitializedOnce() { + return StandardBundlingOperation.ofPlatform(OperatingSystem.current()) + .filter(StandardBundlingOperation::isCreateBundle).map(v -> { + return new Object[] {v}; + }).toList(); + } + + private static Script createMockScript(StandardBundlingOperation op) { + + if (op.packageType() instanceof AppImagePackageType) { + return Script.build().createSequence(); + } + + switch (op.os()) { + case WINDOWS -> { + return createWinMockScript(); + } + case LINUX -> { + return createLinuxMockScript(op.packageType()); + } + case MACOS -> { + return createMacMockScript(); + } + default -> { + throw new AssertionError(); + } + } + } + + private static Script createWinMockScript() { + + // Make "candle.exe" and "light.exe" always fail. + var candle = ioerror("candle-mock"); + var light = ioerror("light-mock"); + + // Make the "wix.exe" functional. + var wix = CommandActionSpecs.build() + .stdout("5.0.2+aa65968c") + .exit(CommandMockExit.SUCCEED) + .toCommandMockBuilder().name("wix-mock").create(); + + var script = Script.build() + .map(Script.cmdlineStartsWith("candle.exe"), candle) + .map(Script.cmdlineStartsWith("light.exe"), light) + .map(Script.cmdlineStartsWith("wix.exe"), wix) + .createLoop(); + + return script; + } + + private static Script createMacMockScript() { + + @SuppressWarnings("unchecked") + var setfilePaths = (List)toSupplier(() -> { + return Class.forName(String.join(".", + DefaultBundlingEnvironmentTest.class.getPackageName(), + "MacDmgSystemEnvironment" + )).getDeclaredField("SETFILE_KNOWN_PATHS").get(null); + }).get(); + + var script = Script.build(); + + for (var setfilePath: setfilePaths) { + script.map(Script.cmdlineStartsWith(setfilePath), ioerror(setfilePath.toString() + "-mock")); + } + + script.map(Script.cmdlineStartsWith("/usr/bin/xcrun"), succeed("/usr/bin/xcrun-mock")); + + return script.createLoop(); + } + + private static Script createLinuxMockScript(PackageType pkgType) { + + final Map mocks = new HashMap<>(); + + var script = Script.build(); + + final Set debCommandNames = Set.of("dpkg", "dpkg-deb", "fakeroot"); + final Set rpmCommandNames = Set.of("rpm", "rpmbuild"); + + final Set succeedCommandNames; + switch (pkgType) { + case StandardPackageType.LINUX_DEB -> { + succeedCommandNames = debCommandNames; + // Simulate "dpkg --print-architecture". + var dpkg = CommandActionSpecs.build() + .stdout("foo-arch") + .exit(CommandMockExit.SUCCEED) + .toCommandMockBuilder().name("dpkg-mock").create(); + mocks.put("dpkg", dpkg); + } + case StandardPackageType.LINUX_RPM -> { + succeedCommandNames = rpmCommandNames; + // Simulate "rpmbuild --version" prints the minimal acceptable version. + var rpmbuild = CommandActionSpecs.build() + .stdout("RPM version 4.10") + .exit(CommandMockExit.SUCCEED) + .toCommandMockBuilder().name("rpmbuild-mock").create(); + mocks.put("rpmbuild", rpmbuild); + } + default -> { + throw new IllegalArgumentException(); + } + } + + script.map(Script.cmdlineStartsWith("ldd"), succeed("ldd-mock")); + + for (var commandName : succeedCommandNames) { + if (!mocks.containsKey(commandName)) { + mocks.put(commandName, succeed(commandName + "-mock")); + } + } + + Stream.of(debCommandNames, rpmCommandNames).flatMap(Set::stream).forEach(commandName -> { + var mock = Optional.ofNullable(mocks.get(commandName)).orElseGet(() -> { + return ioerror(commandName + "-mock"); + }); + script.map(Script.cmdlineStartsWith(commandName), mock); + }); + + return script.createLoop(); + } } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/ExecutorTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/ExecutorTest.java new file mode 100644 index 00000000000..551ef15e991 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/ExecutorTest.java @@ -0,0 +1,165 @@ +/* + * 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 org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedResultException; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.CommandMockExit; +import jdk.jpackage.test.mock.CompletableCommandMock; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class ExecutorTest { + + @ParameterizedTest + @MethodSource + public void test_retryOnKnownErrorMessage(RetryOnKnownErrorMessageTestSpec test) { + test.run(); + } + + private static Stream test_retryOnKnownErrorMessage() { + var data = new ArrayList(); + + final var subject = "French fries"; + + Supplier build = () -> { + return RetryOnKnownErrorMessageTestSpec.build().subject(subject); + }; + + for (var exit : Stream.of(CommandMockExit.values()).filter(CommandMockExit::exitNormally).toList()) { + // These should succeed as there is no "French fries" in stderr. + Stream.of( + build.get().mock(CommandActionSpecs.build().stderr("Coleslaw").exit(exit)), + build.get().mock(CommandActionSpecs.build().stdout(subject).exit(exit)), + build.get() + // Fail in the first attempt (triggering text in the stderr) + .mock(CommandActionSpecs.build().stderr(subject).exit()) + // Fail in the second attempt (same reason) + .repeatLastMoc() + // Pass in the next attempt (no triggering text in the stderr) + .mock(CommandActionSpecs.build().stderr("Coleslaw").exit(exit)), + build.get() + // Fail in the first attempt (triggering text in the stderr) + .mock(CommandActionSpecs.build().stderr(subject)) + // Fail in the second attempt (error running the command) + .mock(CommandActionSpecs.build().exit(CommandMockExit.THROW_MOCK_IO_EXCEPTION)) + // Pass in the next attempt (no triggering text in the stderr) + .mock(CommandActionSpecs.build().exit(exit)) + ).map(RetryOnKnownErrorMessageTestSpec.Builder::success).forEach(data::add); + } + + // These should fail as there is "French fries" in stderr. + data.addAll(List.of( + // Try once and fail. + build.get().mock(CommandActionSpecs.build().stderr(subject).exit()), + // Try twice and fail. + build.get().mock(CommandActionSpecs.build().stderr(subject).exit()).repeatLastMoc() + )); + + return data.stream().map(RetryOnKnownErrorMessageTestSpec.Builder::create); + } + + record RetryOnKnownErrorMessageTestSpec(List mockSpecs, String subject, boolean success) { + + RetryOnKnownErrorMessageTestSpec { + Objects.requireNonNull(mockSpecs); + Objects.requireNonNull(subject); + + if (mockSpecs.isEmpty()) { + throw new IllegalArgumentException(); + } + } + + void run() { + var mock = mockSpecs.stream() + .reduce(CommandActionSpecs::andThen) + .orElseThrow().toCommandMockBuilder() + // Ensure attempts to run the command more times than expected will fail. + .noRepeats().create(); + + var retry = new Executor().toolProvider(mock).retryOnKnownErrorMessage(subject) + .setAttemptTimeout(null) + .setMaxAttemptsCount(mockSpecs.size()); + + if (success) { + assertDoesNotThrow(retry::execute); + } else { + assertThrowsExactly(UnexpectedResultException.class, retry::execute); + } + + assertTrue(((CompletableCommandMock)mock).completed()); + } + + static Builder build() { + return new Builder(); + } + + static final class Builder { + + RetryOnKnownErrorMessageTestSpec create() { + return new RetryOnKnownErrorMessageTestSpec(mockSpecs, subject, success); + } + + public Builder mock(CommandActionSpecs v) { + mockSpecs.add(Objects.requireNonNull(v)); + return this; + } + + public Builder mock(CommandActionSpecs.Builder v) { + return mock(v.create()); + } + + public Builder repeatLastMoc() { + return mock(mockSpecs.getLast()); + } + + public Builder subject(String v) { + subject = v; + return this; + } + + public Builder success(boolean v) { + success = v; + return this; + } + + public Builder success() { + return success(true); + } + + private final List mockSpecs = new ArrayList<>(); + private String subject; + private boolean success; + } + } +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java new file mode 100644 index 00000000000..e88077a6c9d --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/MockUtils.java @@ -0,0 +1,235 @@ +/* + * 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.util.function.ThrowingSupplier.toSupplier; + +import java.io.PrintWriter; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; +import java.util.spi.ToolProvider; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.cli.CliBundlingEnvironment; +import jdk.jpackage.internal.cli.Main; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.test.mock.Script; +import jdk.jpackage.test.mock.ToolProviderCommandMock; +import jdk.jpackage.test.mock.VerbatimCommandMock; + +/** + * Bridges "jdk.jpackage.internal" and "jdk.jpackage.test.mock" packages. + */ +public final class MockUtils { + + private MockUtils() { + } + + public static JPackageToolProviderBuilder buildJPackage() { + return new JPackageToolProviderBuilder(); + } + + public static final class JPackageToolProviderBuilder { + + public ToolProvider create() { + return createJPackageToolProvider(os(), createObjectFactory()); + } + + public Consumer createGlobalsMutator() { + var objectFactory = createObjectFactory(); + return globals -> { + globals.objectFactory(objectFactory); + }; + } + + public void applyToGlobals() { + createGlobalsMutator().accept(Globals.instance()); + } + + ExecutorFactory createExecutorFactory() { + var commandMocksExecutorFactory = Optional.ofNullable(script).map(MockUtils::withCommandMocks).map(mapper -> { + return mapper.apply(ExecutorFactory.DEFAULT); + }).orElse(ExecutorFactory.DEFAULT); + + var recordingExecutorFactory = Optional.ofNullable(listener).map(MockUtils::withCommandListener).map(mapper -> { + return mapper.apply(commandMocksExecutorFactory); + }).orElse(commandMocksExecutorFactory); + + return recordingExecutorFactory; + } + + ObjectFactory createObjectFactory() { + var executorFactory = createExecutorFactory(); + if (executorFactory == ExecutorFactory.DEFAULT) { + return ObjectFactory.DEFAULT; + } else { + return ObjectFactory.build().executorFactory(executorFactory).create(); + } + } + + public JPackageToolProviderBuilder listener(Consumer> v) { + listener = v; + return this; + } + + public JPackageToolProviderBuilder script(Script v) { + script = v; + return this; + } + + public JPackageToolProviderBuilder os(OperatingSystem v) { + os = v; + return this; + } + + private OperatingSystem os() { + return Optional.ofNullable(os).orElseGet(OperatingSystem::current); + } + + private Consumer> listener; + private OperatingSystem os; + private Script script; + } + + public static ToolProvider createJPackageToolProvider(OperatingSystem os, Script script) { + return buildJPackage() + .os(Objects.requireNonNull(os)) + .script(Objects.requireNonNull(script)) + .create(); + } + + public static ToolProvider createJPackageToolProvider(Script script) { + return createJPackageToolProvider(OperatingSystem.current(), script); + } + + private static UnaryOperator withCommandListener(Consumer> listener) { + Objects.requireNonNull(listener); + return executorFactory -> { + Objects.requireNonNull(executorFactory); + return () -> { + var executor = executorFactory.executor(); + + Optional> oldMapper = executor.mapper(); + + UnaryOperator newMapper = exec -> { + listener.accept(exec.commandLine()); + return exec; + }; + + return executor.mapper(oldMapper.map(newMapper::compose).orElse(newMapper)::apply); + }; + }; + } + + private static UnaryOperator withCommandMocks(Script script) { + return executorFactory -> { + Objects.requireNonNull(executorFactory); + return () -> { + var executor = executorFactory.executor(); + + Optional> oldMapper = executor.mapper(); + + UnaryOperator newMapper = exec -> { + var commandLine = exec.commandLine(); + var mock = Objects.requireNonNull(script.map(commandLine)); + switch (mock) { + case VerbatimCommandMock.INSTANCE -> { + // No mock for this command line. + return exec; + } + case ToolProviderCommandMock tp -> { + // Create a copy of the executor with the old mapper to prevent further recursion. + var copy = exec.copy().mapper(oldMapper.orElse(null)); + copy.toolProvider(tp); + copy.args().clear(); + copy.args(commandLine.subList(1, commandLine.size())); + return copy; + } + default -> { + // Unreachable because there are no other cases for this switch. + throw ExceptionBox.reachedUnreachable(); + } + } + }; + + return executor.mapper(oldMapper.map(newMapper::compose).orElse(newMapper)::apply); + }; + }; + } + + public static CliBundlingEnvironment createBundlingEnvironment(OperatingSystem os) { + Objects.requireNonNull(os); + + String bundlingEnvironmentClassName; + switch (os) { + case WINDOWS -> { + bundlingEnvironmentClassName = "WinBundlingEnvironment"; + } + case LINUX -> { + bundlingEnvironmentClassName = "LinuxBundlingEnvironment"; + } + case MACOS -> { + bundlingEnvironmentClassName = "MacBundlingEnvironment"; + } + default -> { + throw new IllegalArgumentException(); + } + } + + return toSupplier(() -> { + var ctor = Class.forName(String.join(".", + DefaultBundlingEnvironment.class.getPackageName(), + bundlingEnvironmentClassName + )).getConstructor(); + return (CliBundlingEnvironment)ctor.newInstance(); + }).get(); + } + + static ToolProvider createJPackageToolProvider(OperatingSystem os, ObjectFactory of) { + Objects.requireNonNull(os); + Objects.requireNonNull(of); + + var impl = new Main.Provider(DefaultBundlingEnvironment.runOnce(() -> { + return createBundlingEnvironment(os); + })); + + return new ToolProvider() { + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + return Globals.main(() -> { + Globals.instance().objectFactory(of); + return impl.run(out, err, args); + }); + } + + @Override + public String name() { + return impl.name(); + } + }; + } +} 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 0e7545bd83d..07757211927 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 @@ -4,7 +4,6 @@ ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--app-version, 1.]; errors=[mess ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--app-version, 1.b.3]; errors=[message.error-header+[error.version-string-invalid-component, 1.b.3, b.3]]) ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--app-version, ]; errors=[message.error-header+[error.version-string-empty]]) ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--jlink-options, --add-modules]; errors=[message.error-header+[error.blocked.option, --add-modules]]) -ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--jlink-options, --foo]; errors=[message.error-header+[error.jlink.failed, Error: unknown option: --foo]]) ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--jlink-options, --module-path]; errors=[message.error-header+[error.blocked.option, --module-path]]) ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--jlink-options, --output]; errors=[message.error-header+[error.blocked.option, --output]]) ErrorTest.test(IMAGE; app-desc=Hello; args-add=[--main-jar, non-existent.jar]; errors=[message.error-header+[error.main-jar-does-not-exist, non-existent.jar]]) diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java index a85b5015e73..e15d5130d43 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java @@ -200,13 +200,13 @@ public class OptionsValidationFailTest { Stream.of("--jpt-run=ErrorTest") ).flatMap(x -> x).toArray(String[]::new)).map(dynamicTest -> { return DynamicTest.dynamicTest(dynamicTest.getDisplayName(), () -> { - JPackageCommand.withToolProvider(jpackageToolProviderMock, () -> { + JPackageCommand.withToolProvider(() -> { try { dynamicTest.getExecutable().execute(); } catch (Throwable t) { throw ExceptionBox.toUnchecked(ExceptionBox.unbox(t)); } - }); + }, jpackageToolProviderMock); }); }); } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java new file mode 100644 index 00000000000..f1d8c142eb9 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java @@ -0,0 +1,1846 @@ +/* + * 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.util; + +import static java.util.stream.Collectors.joining; +import static jdk.jpackage.internal.util.CommandOutputControlTestUtils.isInterleave; +import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; +import static jdk.jpackage.internal.util.function.ThrowingRunnable.toRunnable; +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; +import static jdk.jpackage.test.JUnitUtils.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.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringReader; +import java.io.UncheckedIOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.HexFormat; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.spi.ToolProvider; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.util.function.ExceptionBox; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIf; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.EnabledIf; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.EnumSource.Mode; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +public class CommandOutputControlTest { + + @DisabledIf("cherryPickSavedOutputTestCases") + @ParameterizedTest + @MethodSource + public void testSavedOutput(OutputTestSpec spec) { + spec.test(); + } + + /** + * Runs cherry-picked {@link OutputTestSpec} test cases. + *

    + * This test method is mutual exclusive with + * {@link #testSavedOutput(OutputTestSpec)} and is aimed for debugging + * {@code OutputTestSpec} test cases. + *

    + * It is disabled by default. To enable it, manually edit {@link #testSomeSavedOutput()}. + * + * @see #testSomeSavedOutput() + * + * @param spec the test case + */ + @EnabledIf("cherryPickSavedOutputTestCases") + @ParameterizedTest + @MethodSource + public void testSomeSavedOutput(OutputTestSpec spec) { + System.out.println(spec); + spec.test(); + } + + @ParameterizedTest + @MethodSource + public void testDumpStreams(OutputTestSpec spec) { + spec.test(); + } + + @ParameterizedTest + @MethodSource + public void testCharset(CharsetTestSpec spec) throws IOException, InterruptedException { + spec.test(); + } + + @ParameterizedTest + @MethodSource + public void test_description(CommandOutputControlSpec spec) { + // This test is mostly for coverage. + var desc = spec.create().description(); + assertFalse(desc.isBlank()); + } + + @Test + public void test_copy() { + var orig = new CommandOutputControl(); + var copy = orig.copy(); + assertNotSame(orig, copy); + } + + @ParameterizedTest + @EnumSource(names = "SAVE_NOTHING", mode = Mode.EXCLUDE) + public void test_flag(OutputControl flag) { + var coc = new CommandOutputControl(); + assertFalse(flag.get(coc)); + flag.set(coc); + assertTrue(flag.get(coc)); + if (flag.canUnset()) { + flag.unset(coc); + assertFalse(flag.get(coc)); + } + } + + @ParameterizedTest + @MethodSource + public void test_mutual_exclusive_flags(List controls) { + if (controls.isEmpty()) { + throw new IllegalArgumentException(); + } + + var coc = new CommandOutputControl(); + for (var c : controls) { + c.set(coc); + } + + for (var c : controls.subList(0, controls.size() - 1)) { + assertFalse(c.get(coc)); + } + assertTrue(controls.getLast().get(coc)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_ExecutableAttributes(boolean toolProvider) { + var coc = new CommandOutputControl(); + CommandOutputControl.Executable exec; + if (toolProvider) { + exec = coc.createExecutable(new ToolProvider() { + + @Override + public String name() { + return "runme"; + } + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + fail("Should never be called"); + return 0; + } + + }, "--foo", "--baz=10"); + } else { + exec = coc.createExecutable(new ProcessBuilder("runme", "--foo", "--baz=10")); + } + + assertEquals("runme --foo --baz=10", exec.attributes().toString()); + } + + @Test + public void test_Result_no_args_ctor() { + var result = new CommandOutputControl.Result(7); + assertFalse(result.findContent().isPresent()); + assertFalse(result.findStdout().isPresent()); + assertFalse(result.findStderr().isPresent()); + assertEquals(7, result.getExitCode()); + assertSame(Objects.requireNonNull(CommandOutputControl.EMPTY_EXECUTABLE_ATTRIBUTES), result.execAttrs()); + } + + @Test + public void test_Result_expectExitCode() throws IOException { + var result = new CommandOutputControl.Result(7); + + assertSame(result, result.expectExitCode(7)); + assertSame(result, result.expectExitCode(7, 2)); + assertSame(result, result.expectExitCode(2, 7)); + + assertSame(result, result.expectExitCode(List.of(7))); + assertSame(result, result.expectExitCode(Set.of(7, 2))); + assertSame(result, result.expectExitCode(List.of(2, 7))); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_Result_expectExitCode_negative(boolean collection) { + var result = new CommandOutputControl.Result(3); + + var ex = assertThrowsExactly(CommandOutputControl.UnexpectedExitCodeException.class, () -> { + if (collection) { + result.expectExitCode(List.of(17, 12)); + } else { + result.expectExitCode(17, 12); + } + }); + + assertNull(ex.getCause()); + assertSame(result, ex.getResult()); + assertEquals("Unexpected exit code 3 from executing the command ", ex.getMessage()); + } + + @ParameterizedTest + @MethodSource + public void test_Result_toCharacterResult(ToCharacterResultTestSpec spec) throws IOException, InterruptedException { + spec.test(); + } + + @Test + public void test_Result_toCharacterResult_nop() throws IOException, InterruptedException { + + var charset = StandardCharsets.UTF_8; + + var emptyResult = new CommandOutputControl.Result(7); + assertSame(emptyResult, emptyResult.toCharacterResult(charset, true)); + assertSame(emptyResult, emptyResult.toCharacterResult(charset, false)); + + var coc = new CommandOutputControl().saveOutput(true); + + var result = coc.createExecutable(new Command(List.of("foo"), List.of()).asToolProvider()).execute(); + + assertSame(result, result.toCharacterResult(charset, true)); + assertSame(result, result.toCharacterResult(charset, false)); + } + + @Test + public void test_Result_toCharacterResult_copyWithExecutableAttributes() { + + var empty = new CommandOutputControl.Result(0); + + var execAttrs = new CommandOutputControl.ExecutableAttributes() { + @Override + public String toString() { + return "foo"; + } + + @Override + public List commandLine() { + return List.of(); + } + }; + + var copy = empty.copyWithExecutableAttributes(execAttrs); + + assertSame(empty.exitCode(), copy.exitCode()); + assertSame(empty.output(), copy.output()); + assertSame(empty.byteOutput(), copy.byteOutput()); + assertSame(execAttrs, copy.execAttrs()); + } + + @ParameterizedTest + @EnumSource(ExecutableType.class) + public void test_timeout_expires(ExecutableType mode) throws InterruptedException, IOException { + + final var toolProvider = (mode == ExecutableType.TOOL_PROVIDER); + final var storeOutputInFiles = (mode == ExecutableType.PROCESS_BUILDER_WITH_STREAMS_IN_FILES); + + var actions = List.of( + CommandAction.echoStdout("The quick brown fox jumps"), + CommandAction.sleep(5), + CommandAction.echoStdout("over the lazy dog") + ); + + var coc = new CommandOutputControl().saveOutput(true).dumpOutput(true).storeOutputInFiles(storeOutputInFiles); + + CommandOutputControl.Executable exec; + + InterruptibleToolProvider tp; + + if (toolProvider) { + tp = new InterruptibleToolProvider(Command.createToolProvider(actions)); + exec = coc.createExecutable(tp); + } else { + var cmdline = Command.createShellCommandLine(actions); + tp = null; + exec = coc.createExecutable(new ProcessBuilder(cmdline)); + } + + var result = exec.execute(1, TimeUnit.SECONDS); + assertFalse(result.exitCode().isPresent()); + + var getExitCodeEx = assertThrowsExactly(IllegalStateException.class, result::getExitCode); + assertEquals(("Exit code is unavailable for timed-out command"), getExitCodeEx.getMessage()); + + // We want to check that the saved output contains only the text emitted before the "sleep" action. + // It works for a subprocess, but in the case of a ToolProvider, sometimes the timing is such + // that it gets interrupted before having written anything to the stdout, and the saved output is empty. + // This happens when the test case is executed together with other test cases + // and never when it is executed individually. + if (!toolProvider || !result.content().isEmpty()) { + assertEquals(List.of("The quick brown fox jumps"), result.content()); + } + + if (toolProvider) { + assertTrue(tp.interrupted()); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_timeout(boolean toolProvider) throws InterruptedException, IOException { + + var actions = List.of( + CommandAction.echoStdout("Sphinx of black quartz,"), + CommandAction.echoStdout("judge my vow") + ); + + var coc = new CommandOutputControl().saveOutput(true).dumpOutput(true); + + CommandOutputControl.Executable exec; + + if (toolProvider) { + var tp = Command.createToolProvider(actions); + exec = coc.createExecutable(tp); + } else { + var cmdline = Command.createShellCommandLine(actions); + exec = coc.createExecutable(new ProcessBuilder(cmdline)); + } + + var result = exec.execute(10, TimeUnit.SECONDS); + assertTrue(result.exitCode().isPresent()); + assertEquals(List.of("Sphinx of black quartz,", "judge my vow"), result.content()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_passthrough_exceptions(boolean withTimeout) throws IOException { + + var expected = new RuntimeException("Kaput!"); + + var exec = new CommandOutputControl().createExecutable(new ToolProvider() { + + @Override + public String name() { + return "foo"; + } + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + throw expected; + } + }); + + var actual = assertThrowsExactly(expected.getClass(), () -> { + if (withTimeout) { + exec.execute(10, TimeUnit.SECONDS); + } else { + exec.execute(); + } + }); + + assertSame(expected, actual); + } + + @Test + public void test_externally_terminated() throws InterruptedException, IOException { + var cmdline = Command.createShellCommandLine(List.of( + CommandAction.echoStderr("The five boxing wizards"), + CommandAction.sleep(10), + CommandAction.echoStderr("jump quickly") + )); + + var processDestroyer = Slot.>createEmpty(); + + var coc = new CommandOutputControl().saveOutput(true).dumpOutput(true).processListener(process -> { + // Once we are notified the process has been started, schedule its destruction. + // Give it a second to warm up and print some output and then destroy it. + processDestroyer.set(CompletableFuture.runAsync(toRunnable(() -> { + Thread.sleep(Duration.ofSeconds(1)); + // On Windows, CommandAction#sleep is implemented with the "ping" command. + // By some reason, when the parent "cmd" process is destroyed, + // the child "ping" command stays alive, and the test waits when it completes, + // making it last for at least 10 seconds. + // To optimize the test work time, destroy the entire subprocess tree. + // Even though this is essential on Windows keep this logic on all platforms for simplicity. + var descendants = List.of(); + try (var descendantsStream = process.descendants()) { + descendants = descendantsStream.toList(); + } finally { + process.destroyForcibly(); + } + descendants.forEach(ProcessHandle::destroyForcibly); + }))); + }); + var exec = coc.createExecutable(new ProcessBuilder(cmdline)); + + var result = exec.execute(); + assertNotEquals(0, result.getExitCode()); + assertEquals(List.of("The five boxing wizards"), result.content()); + processDestroyer.get().join(); + } + + @DisabledOnOs(value = OS.MAC, disabledReason = "Closing a stream doesn't consistently cause a trouble as it should") + @ParameterizedTest + @EnumSource(OutputStreams.class) + public void test_close_streams(OutputStreams action) throws InterruptedException, IOException { + var cmdline = Command.createShellCommandLine(List.of( + CommandAction.echoStdout("Hello stdout"), + CommandAction.echoStderr("Bye stderr") + )); + + var coc = new CommandOutputControl().saveOutput(true).dumpOutput(true).processListener(toConsumer(process -> { + // Close process output stream(s). This should make corresponding stream gobbler(s) throw IOException. + switch (action) { + case STDOUT -> { + process.getInputStream().close(); + } + case STDERR -> { + process.getErrorStream().close(); + } + case STDOUT_AND_STDERR -> { + process.getInputStream().close(); + process.getErrorStream().close(); + } + } + })); + var exec = coc.createExecutable(new ProcessBuilder(cmdline)); + + var ex = assertThrows(IOException.class, exec::execute); + System.out.println("test_close_streams: " + action); + ex.printStackTrace(System.out); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_interleaved(boolean customDumpStreams) throws IOException, InterruptedException { + var cmdline = Command.createShellCommandLine(List.of( + CommandAction.echoStdout("Eat some more"), + CommandAction.echoStderr("of these"), + CommandAction.echoStdout("soft French pastries"), + CommandAction.echoStderr("and drink some tea") + )); + + var coc = new CommandOutputControl(); + var exec = coc.createExecutable(new ProcessBuilder(cmdline)); + + coc.saveOutput(true).dumpOutput(true); + + CommandOutputControl.Result result; + + if (customDumpStreams) { + // Execute the command so that its stdout and stderr are dumped to the same sink. + var sink = new ByteArrayOutputStream(); + var ps = new PrintStream(sink); + + coc.dumpStdout(ps).dumpStderr(ps); + + result = exec.execute(); + + var commandStdout = List.of("Eat some more", "soft French pastries"); + var commandStderr = List.of("of these", "and drink some tea"); + + var sinkContent = toStringList(sink.toByteArray(), StandardCharsets.US_ASCII); + + if (!isInterleave(sinkContent, commandStdout, commandStderr)) { + fail(String.format("Unexpected combined output=%s; stdout=%s; stderr=%s", + sinkContent, commandStdout, commandStderr)); + } + + // CommandOutputControl was not configured to redirect stderr in stdout, + // hence the output is ordered: stdout goes first, stderr follows. + assertEquals(Stream.of(commandStdout, commandStderr).flatMap(List::stream).toList(), result.content()); + + // Saved stdout an stderr can be accessed individually. + assertEquals(commandStdout, result.stdout()); + assertEquals(commandStderr, result.stderr()); + } else { + // Execute the command so that its stdout and stderr are dumped into System.out. + coc.redirectStderr(true); + result = exec.execute(); + + // CommandOutputControl was configured to redirect stderr in stdout, + // hence the output is interleaved. + assertEquals(List.of("Eat some more", "of these", "soft French pastries", "and drink some tea"), result.content()); + + // Saved stdout an stderr can NOT be accessed individually because they are interleaved. + assertTrue(result.findStdout().isEmpty()); + assertTrue(result.findStderr().isEmpty()); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true}) + public void stressTest(boolean binaryOutput, @TempDir Path workDir) throws Exception { + + // Execute multiple subprocesses asynchronously. + // Each subprocess writes a few chunks of data each larger than the default buffer size (8192 bytes) + + final var chunkCount = 5; + final var subprocessCount = 100; + final var subprocessExecutor = Executors.newVirtualThreadPerTaskExecutor(); + + final var md = MessageDigest.getInstance("MD5"); + + var cmdline = Command.createShellCommandLine(IntStream.range(0, chunkCount).mapToObj(chunk -> { + byte[] bytes = new byte[10 * 1024]; // 10K to exceed the default BufferedOutputStream's buffer size of 8192. + new Random().nextBytes(bytes); + md.update(bytes); + var path = workDir.resolve(Integer.toString(chunk)); + try { + Files.write(path, bytes); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + return path; + }).map(CommandAction::cat).toList()); + + final var digest = HexFormat.of().formatHex(md.digest()); + + // Schedule to start every subprocess in a separate virtual thread. + // Start and suspend threads, waiting until all scheduled threads have started. + // After all scheduled threads start, resume them. + // This should result in starting all scheduled subprocesses simultaneously. + + var readyLatch = new CountDownLatch(subprocessCount); + var startLatch = new CountDownLatch(1); + + var futures = IntStream.range(0, subprocessCount).mapToObj(_ -> { + return CompletableFuture.supplyAsync(toSupplier(() -> { + + var exec = new CommandOutputControl() + .saveOutput(true) + .binaryOutput(binaryOutput) + .createExecutable(new ProcessBuilder(cmdline)); + + readyLatch.countDown(); + startLatch.await(); + + var result = exec.execute(); + + var localMd = MessageDigest.getInstance("MD5"); + localMd.update(result.byteContent()); + + return HexFormat.of().formatHex(localMd.digest()); + + }), subprocessExecutor); + }).toList(); + + readyLatch.await(); + startLatch.countDown(); + + CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); + + futures.forEach(future -> { + var actualDigest = future.join(); + assertEquals(digest, actualDigest); + }); + } + + public enum OutputStreams { + STDOUT, + STDERR, + STDOUT_AND_STDERR + } + + private static List test_description() { + List testCases = new ArrayList<>(); + testCases.add(new CommandOutputControlSpec(Set.of())); + for (var outputControl : OutputControl.variants()) { + testCases.add(new CommandOutputControlSpec(outputControl)); + } + return testCases; + } + + private static List> test_mutual_exclusive_flags() { + List> data = new ArrayList<>(); + + var flags = List.of(OutputControl.SAVE_ALL, OutputControl.SAVE_FIRST_LINE, OutputControl.SAVE_NOTHING); + + List seq = new ArrayList<>(); + for (var _1 : flags) { + seq.add(_1); + var flags2 = flags.stream().filter(Predicate.isEqual(_1).negate()).toList(); + for (var _2 : flags2) { + seq.add(_2); + var flags3 = flags2.stream().filter(Predicate.isEqual(_2).negate()).toList(); + for (var _3 : flags3) { + seq.add(_3); + data.add(List.copyOf(seq)); + seq.removeLast(); + } + seq.removeLast(); + } + seq.removeLast(); + } + + return data; + } + + public record ToCharacterResultTestSpec(OutputTestSpec execSpec, boolean keepByteContent) { + + public ToCharacterResultTestSpec { + Objects.requireNonNull(execSpec); + } + + @Override + public String toString() { + final List tokens = new ArrayList<>(); + + tokens.add(execSpec.toString()); + if (keepByteContent) { + tokens.add("keepByteContent"); + } + + return String.join(", ", tokens.toArray(String[]::new)); + } + + void test() throws IOException, InterruptedException { + var coc = execSpec.cocSpec().create(); + + var command = execSpec.commandSpec().command().asToolProvider(); + + var expected = coc.binaryOutput(false).createExecutable(command).execute(); + + var byteResult = coc.binaryOutput(true).createExecutable(command).execute(); + + var actual = byteResult.toCharacterResult(coc.charset(), keepByteContent); + + CommandOutputControl.Result expectedByteContent; + if (keepByteContent) { + expectedByteContent = byteResult; + } else { + expectedByteContent = expected; + } + + assertArrayEquals(expectedByteContent.findByteContent().orElse(null), actual.findByteContent().orElse(null)); + assertArrayEquals(expectedByteContent.findByteStdout().orElse(null), actual.findByteStdout().orElse(null)); + assertArrayEquals(expectedByteContent.findByteStderr().orElse(null), actual.findByteStderr().orElse(null)); + + assertEquals(expected.findContent(), actual.findContent()); + assertEquals(expected.findStdout(), actual.findStdout()); + assertEquals(expected.findStderr(), actual.findStderr()); + + assertSame(byteResult.execAttrs(), actual.execAttrs()); + assertEquals(expected.exitCode(), actual.exitCode()); + } + } + + private static Stream test_Result_toCharacterResult() { + List testCases = new ArrayList<>(); + + var skip = Set.of(OutputControl.BINARY_OUTPUT, OutputControl.DUMP, OutputControl.SAVE_FIRST_LINE); + + for (var outputControl : OutputControl.variants().stream().filter(spec -> { + return !skip.stream().anyMatch(spec::contains); + }).toList()) { + for (var stdoutContent : List.of(OutputData.EMPTY, OutputData.MANY)) { + for (var stderrContent : List.of(OutputData.EMPTY, OutputData.MANY)) { + var commandSpec = new CommandSpec(stdoutContent, stderrContent); + testCases.add(new OutputTestSpec(false, new CommandOutputControlSpec(outputControl), commandSpec)); + } + } + } + + return testCases.stream().flatMap(execSpec -> { + return Stream.of(true, false).map(keepByteContent -> { + return new ToCharacterResultTestSpec(execSpec, keepByteContent); + }); + }); + } + + private static boolean cherryPickSavedOutputTestCases() { + return !testSomeSavedOutput().isEmpty(); + } + + /** + * Returns test cases for {@link #testSomeSavedOutput(OutputTestSpec)}. + *

    + * Aimed to simplify debugging of {@link #OutputTestSpec} test cases. + *

    + * The total number of {@code #OutputTestSpec} test cases is ~1500. When some + * fail and need debugging, it is a waste of time to run them all. This method + * allows running only selected test cases. It works this way: + *

      + *
    • Run CommandOutputControlTest test. + *
    • If some {@linke #testSavedOutput(OutputTestSpec)} invocations fail, + * capture their IDs (test case ID is an index starting from 1). + *
    • Replace "/* 10, 67, 456 */" comment in the body of this method with + * the captured test case IDs. + *
    • Rerun CommandOutputControlTest test. This time, it will run + * {@link #testSomeSavedOutput(OutputTestSpec)} method instead of + * {@link #testSavedOutput(OutputTestSpec)} with the list of the captured test + * case IDs. + *
    + */ + private static List testSomeSavedOutput() { + var testIds = List.of(/* 10, 67, 456 */); + if (testIds.isEmpty()) { + return List.of(); + } else { + var allTestCases = testSavedOutput(); + return testIds.stream().map(testId -> { + return allTestCases.get(testId - 1); + }).toList(); + } + } + + private static List testSavedOutput() { + List testCases = new ArrayList<>(); + for (final var executableType : List.of(ExecutableType.values())) { + for (var outputControl : OutputControl.variants()) { + for (final var stdoutContent : List.of(OutputData.values())) { + for (final var stderrContent : List.of(OutputData.values())) { + + if (outputControl.contains(OutputControl.BINARY_OUTPUT) + && (stdoutContent == OutputData.ONE_LINE || stderrContent == OutputData.ONE_LINE)) { + // Skip a test case if it runs a command writing + // a single line in stdout or stderr, and handles command output as a byte stream. + // It duplicates test cases that write multiple lines in stdout or stderr. + continue; + } + + final var commandSpec = new CommandSpec(stdoutContent, stderrContent); + boolean toolProvider; + switch (executableType) { + case PROCESS_BUILDER -> { + toolProvider = false; + } + case PROCESS_BUILDER_WITH_STREAMS_IN_FILES -> { + outputControl = new SetBuilder() + .add(outputControl) + .add(OutputControl.STORE_STREAMS_IN_FILES) + .create(); + toolProvider = false; + } + case TOOL_PROVIDER -> { + toolProvider = true; + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + testCases.add(new OutputTestSpec( + toolProvider, + new CommandOutputControlSpec(outputControl), + commandSpec)); + } + } + } + } + return testCases; + } + + private static List testDumpStreams() { + List testCases = new ArrayList<>(); + final var commandSpec = new CommandSpec(OutputData.MANY, OutputData.MANY); + for (var discardStdout : withAndWithout(OutputControl.DISCARD_STDOUT)) { + for (var discardStderr : withAndWithout(OutputControl.DISCARD_STDERR)) { + for (var redirectStderr : withAndWithout(OutputControl.REDIRECT_STDERR)) { + for (var binaryOutput : withAndWithout(OutputControl.BINARY_OUTPUT)) { + for (var dumpStdout : withAndWithout(OutputControl.DUMP_STDOUT_IN_SYSTEM_OUT)) { + for (var dumpStderr : withAndWithout(OutputControl.DUMP_STDERR_IN_SYSTEM_ERR)) { + + if (dumpStderr.isEmpty() && dumpStdout.isEmpty()) { + // Output dumping disabled + continue; + } + + if (discardStderr.isPresent() && discardStdout.isPresent()) { + // Output dumping enabled, but all stream discarded + continue; + } + + if (dumpStderr.isPresent() == discardStderr.isPresent() && dumpStdout.isEmpty()) { + // Stderr dumping enabled but discarded, stdout dumping disabled + continue; + } + + if (dumpStdout.isPresent() == discardStdout.isPresent() && dumpStderr.isEmpty()) { + // Stdout dumping enabled but discarded, stderr dumping disabled + continue; + } + + final var outputControl = new HashSet(); + outputControl.add(OutputControl.DUMP); + discardStdout.ifPresent(outputControl::add); + discardStderr.ifPresent(outputControl::add); + redirectStderr.ifPresent(outputControl::add); + binaryOutput.ifPresent(outputControl::add); + dumpStdout.ifPresent(outputControl::add); + dumpStderr.ifPresent(outputControl::add); + + testCases.add(new OutputTestSpec( + false, + new CommandOutputControlSpec(outputControl), + commandSpec)); + } + } + } + } + } + } + return testCases; + } + + private static List testCharset() { + List testCases = new ArrayList<>(); + + for (boolean toolProvider : BOOLEAN_VALUES) { + for (var redirectStderr : withAndWithout(OutputControl.REDIRECT_STDERR)) { + for (var charset : withAndWithout(OutputControl.CHARSET_UTF16LE)) { + var stdoutSink = new CharsetTestSpec.DumpOutputSink(StandardCharsets.US_ASCII, OutputStreams.STDOUT); + var stderrSink = new CharsetTestSpec.DumpOutputSink(StandardCharsets.UTF_32LE, OutputStreams.STDERR); + var outputControl = new HashSet(); + redirectStderr.ifPresent(outputControl::add); + charset.ifPresent(outputControl::add); + outputControl.add(stdoutSink); + outputControl.add(stderrSink); + testCases.add(new CharsetTestSpec(toolProvider, new CommandOutputControlSpec(outputControl))); + } + } + } + + return testCases; + } + + private enum ExecutableType { + TOOL_PROVIDER, + PROCESS_BUILDER, + PROCESS_BUILDER_WITH_STREAMS_IN_FILES, + ; + } + + private sealed interface CommandAction { + static SleepCommandAction sleep(int seconds) { + return new SleepCommandAction(seconds); + } + + static EchoCommandAction echoStdout(String str) { + return new EchoCommandAction(str, false); + } + + static EchoCommandAction echoStderr(String str) { + return new EchoCommandAction(str, true); + } + + static WriteCommandAction writeStdout(byte[] binary) { + return new WriteCommandAction(binary, false); + } + + static WriteCommandAction writeStderr(byte[] binary) { + return new WriteCommandAction(binary, true); + } + + static CatCommandAction cat(Path file) { + return new CatCommandAction(file); + } + } + + private record EchoCommandAction(String value, boolean stderr) implements CommandAction { + EchoCommandAction { + Objects.requireNonNull(value); + } + } + + private record WriteCommandAction(byte[] value, boolean stderr) implements CommandAction { + WriteCommandAction { + Objects.requireNonNull(value); + } + } + + private record CatCommandAction(Path file) implements CommandAction { + CatCommandAction { + Objects.requireNonNull(file); + } + } + + private record SleepCommandAction(int seconds) implements CommandAction { + SleepCommandAction { + if (seconds < 0) { + throw new IllegalArgumentException(); + } + } + } + + private record Command(List stdout, List stderr) { + Command { + stdout.forEach(Objects::requireNonNull); + stderr.forEach(Objects::requireNonNull); + } + + List asExecutable() { + return createShellCommandLine(actions()); + } + + ToolProvider asToolProvider() { + return createToolProvider(actions()); + } + + // + // Type of shell for which to create a command line. + // On Unix it is always the "sh". + // On Windows, it is "cmd" by default and "powershell" when a command needs to write binary data to output stream(s). + // Extra complexity on Windows is because "powershell" is times slower than "cmd", + // and the latter doesn't support binary output. + // + private enum ShellType { + SH(OperatingSystem.LINUX, OperatingSystem.MACOS), + CMD(OperatingSystem.WINDOWS), + POWERSHELL(OperatingSystem.WINDOWS), + ; + + ShellType(OperatingSystem... os) { + if (os.length == 0) { + throw new IllegalArgumentException(); + } + this.os = Set.of(os); + } + + boolean isSupportedOnCurrentOS() { + return os.contains(OperatingSystem.current()); + } + + private final Set os; + } + + private List actions() { + return Stream.concat( + stdout.stream().map(CommandAction::echoStdout), + stderr.stream().map(CommandAction::echoStderr) + ).toList(); + } + + static List createShellCommandLine(List actions) { + final var shellType = detectShellType(actions); + final List commandline = new ArrayList<>(); + final String commandSeparator; + switch (shellType) { + case SH -> { + commandline.addAll(List.of("sh", "-c")); + commandSeparator = " && "; + } + case CMD -> { + commandline.addAll(List.of("cmd", "/C")); + commandSeparator = " && "; + } + case POWERSHELL -> { + commandline.addAll(List.of("powershell", "-NoProfile", "-Command")); + commandSeparator = "; "; + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + commandline.add(actions.stream().map(action -> { + return Command.toString(action, shellType); + }).collect(joining(commandSeparator))); + return commandline; + } + + static ToolProvider createToolProvider(List actions) { + var copiedActions = List.copyOf(actions); + return new ToolProvider() { + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + throw new UnsupportedOperationException(); + } + + @Override + public int run(PrintStream out, PrintStream err, String... args) { + for (var action : copiedActions) { + switch (action) { + case EchoCommandAction echo -> { + if (echo.stderr()) { + err.println(echo.value()); + } else { + out.println(echo.value()); + } + } + case WriteCommandAction write -> { + try { + if (write.stderr()) { + err.write(write.value()); + } else { + out.write(write.value()); + } + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + case SleepCommandAction sleep -> { + toRunnable(() -> { + synchronized (this) { + var millis = Duration.ofSeconds(sleep.seconds()).toMillis(); + this.wait(millis); + } + }).run(); + } + case CatCommandAction _ -> { + // Not used, no point to implement. + throw new UnsupportedOperationException(); + } + } + } + return 0; + } + + @Override + public String name() { + return "test"; + } + }; + } + + private static ShellType detectShellType(List actions) { + var supportedShellTypes = Stream.of(ShellType.values()) + .filter(ShellType::isSupportedOnCurrentOS) + .collect(Collectors.toCollection(HashSet::new)); + for (var action : actions) { + if (action instanceof WriteCommandAction) { + supportedShellTypes.remove(ShellType.CMD); + } + } + return supportedShellTypes.stream() + .sorted(Comparator.comparingInt(Enum::ordinal)) + .findFirst().orElseThrow(); + } + + private static String toString(CommandAction action, ShellType shellType) { + switch (action) { + case EchoCommandAction a -> { + return toString(a, shellType); + } + case WriteCommandAction a -> { + return toString(a, shellType); + } + case SleepCommandAction a -> { + return toString(a, shellType); + } + case CatCommandAction a -> { + return toString(a, shellType); + } + } + } + + private static String toString(EchoCommandAction echo, ShellType shellType) { + String str; + switch (shellType) { + case SH -> { + str = "echo " + echo.value(); + if (echo.stderr()) { + str += ">&2"; + } + } + case CMD -> { + str = "(echo " + echo.value() + ")"; + if (echo.stderr()) { + str += ">&2"; + } + } + case POWERSHELL -> { + str = String.format("[Console]::%s.WriteLine(\\\"%s\\\")", + echo.stderr() ? "Error" : "Out", echo.value()); + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + return str; + } + + private static String toString(WriteCommandAction write, ShellType shellType) { + String str; + switch (shellType) { + case SH -> { + // Convert byte[] to octal string to make it work with POSIX printf. + // POSIX printf doesn't recognize hex strings, so can't use handy HexFormat. + var sb = new StringBuilder(); + sb.append("printf "); + for (var b : write.value()) { + sb.append("\\\\").append(Integer.toOctalString(b & 0xFF)); + } + if (write.stderr()) { + sb.append(">&2"); + } + str = sb.toString(); + } + case CMD -> { + throw new UnsupportedOperationException("Can't output binary data with 'cmd'"); + } + case POWERSHELL -> { + var base64 = Base64.getEncoder().encodeToString(write.value()); + str = String.format( + "$base64 = '%s'; " + + "$bytes = [Convert]::FromBase64String($base64); " + + "[Console]::%s().Write($bytes, 0, $bytes.Length)", + base64, write.stderr() ? "OpenStandardError" : "OpenStandardOutput"); + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + return str; + } + + private static String toString(SleepCommandAction sleep, ShellType shellType) { + switch (shellType) { + case SH -> { + return "sleep " + sleep.seconds(); + } + case CMD -> { + // The standard way to sleep in "cmd" is to use the "ping" command. + // It sends packets every second. + // To wait N seconds, it should send N+1 packets. + // The "timeout" command works only in a console. + return String.format("(ping -n %d localhost > nul)", sleep.seconds() + 1); + } + case POWERSHELL -> { + return "Start-Sleep -Seconds " + sleep.seconds(); + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + } + + private static String toString(CatCommandAction cat, ShellType shellType) { + switch (shellType) { + case SH -> { + return "cat " + cat.file(); + } + case CMD -> { + return "type " + cat.file(); + } + case POWERSHELL -> { + // Not used, no point to implement. + throw new UnsupportedOperationException(); + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + } + + } + + private enum OutputData { + EMPTY(List.of()), + ONE_LINE(List.of("Jupiter")), + MANY(List.of("Uranus", "Saturn", "Earth")); + + OutputData(List data) { + data.forEach(Objects::requireNonNull); + this.data = data; + } + + final List data; + } + + private record CommandSpec(OutputData stdout, OutputData stderr) { + CommandSpec { + Objects.requireNonNull(stdout); + Objects.requireNonNull(stderr); + } + + @Override + public String toString() { + return String.format("[stdout=%s, stderr=%s]", stdout, stderr); + } + + Command command() { + return new Command(stdout.data.stream().map(line -> { + return "stdout." + line; + }).toList(), stderr.data.stream().map(line -> { + return "stderr." + line; + }).toList()); + } + } + + public interface CommandOutputControlMutator { + String name(); + void mutate(CommandOutputControl coc); + + static Function> addToSet(Set set) { + return m -> { + return new SetBuilder().add(set).add(m).create(); + }; + } + } + + public enum OutputControl implements CommandOutputControlMutator { + DUMP(CommandOutputControl::dumpOutput, CommandOutputControl::isDumpOutput), + SAVE_ALL(CommandOutputControl::saveOutput, CommandOutputControl::isSaveOutput), + SAVE_FIRST_LINE(CommandOutputControl::saveFirstLineOfOutput, CommandOutputControl::isSaveFirstLineOfOutput), + SAVE_NOTHING(coc -> { + coc.saveOutput(false); + }, coc -> { + return !coc.isSaveOutput() && !coc.isSaveFirstLineOfOutput(); + }), + DISCARD_STDOUT(CommandOutputControl::discardStdout, CommandOutputControl::isDiscardStdout), + DISCARD_STDERR(CommandOutputControl::discardStderr, CommandOutputControl::isDiscardStderr), + REDIRECT_STDERR(CommandOutputControl::redirectStderr, CommandOutputControl::isRedirectStderr), + STORE_STREAMS_IN_FILES(CommandOutputControl::storeOutputInFiles, CommandOutputControl::isStoreOutputInFiles), + BINARY_OUTPUT(CommandOutputControl::binaryOutput, CommandOutputControl::isBinaryOutput), + DUMP_STDOUT_IN_SYSTEM_OUT(coc -> { + coc.dumpStdout(new PrintStreamWrapper(System.out)); + }, coc -> { + return coc.dumpStdout() instanceof PrintStreamWrapper; + }), + DUMP_STDERR_IN_SYSTEM_ERR(coc -> { + coc.dumpStderr(new PrintStreamWrapper(System.err)); + }, coc -> { + return coc.dumpStderr() instanceof PrintStreamWrapper; + }), + CHARSET_UTF16LE(coc -> { + coc.charset(StandardCharsets.UTF_16LE); + }, coc -> { + return coc.charset() == StandardCharsets.UTF_16LE; + }), + ; + + OutputControl(Consumer setter, Function getter) { + this.setter = Objects.requireNonNull(setter); + this.unsetter = null; + this.getter = Objects.requireNonNull(getter); + } + + OutputControl(BiConsumer setter, Function getter) { + Objects.requireNonNull(setter); + this.setter = coc -> { + setter.accept(coc, true); + }; + this.unsetter = coc -> { + setter.accept(coc, false); + }; + this.getter = Objects.requireNonNull(getter); + } + + @Override + public void mutate(CommandOutputControl coc) { + set(coc); + } + + CommandOutputControl set(CommandOutputControl coc) { + setter.accept(coc); + return coc; + } + + CommandOutputControl unset(CommandOutputControl coc) { + Objects.requireNonNull(unsetter).accept(coc); + return coc; + } + + boolean canUnset() { + return unsetter != null; + } + + boolean get(CommandOutputControl coc) { + return getter.apply(coc); + } + + static List> variants() { + final List> variants = new ArrayList<>(); + for (final var binaryOutput : withAndWithout(BINARY_OUTPUT)) { + for (final var redirectStderr : withAndWithout(REDIRECT_STDERR)) { + for (final var withDump : withAndWithout(DUMP)) { + variants.addAll(Stream.of( + Set.of(), + Set.of(SAVE_ALL), + Set.of(SAVE_FIRST_LINE), + Set.of(DISCARD_STDOUT), + Set.of(DISCARD_STDERR), + Set.of(SAVE_ALL, DISCARD_STDOUT), + Set.of(SAVE_FIRST_LINE, DISCARD_STDOUT), + Set.of(SAVE_ALL, DISCARD_STDERR), + Set.of(SAVE_FIRST_LINE, DISCARD_STDERR), + Set.of(SAVE_ALL, DISCARD_STDOUT, DISCARD_STDERR), + Set.of(SAVE_FIRST_LINE, DISCARD_STDOUT, DISCARD_STDERR) + ).map(v -> { + return withDump.map(CommandOutputControlMutator.addToSet(v)).orElse(v); + }).map(v -> { + return redirectStderr.filter(_ -> { + return !v.containsAll(List.of(DISCARD_STDOUT, DISCARD_STDERR)); + }).map(CommandOutputControlMutator.addToSet(v)).orElse(v); + }).map(v -> { + return binaryOutput.map(CommandOutputControlMutator.addToSet(v)).orElse(v); + }).toList()); + } + } + } + return variants.stream().distinct().toList(); + } + + private static final class PrintStreamWrapper extends PrintStream { + PrintStreamWrapper(PrintStream out) { + super(out, true); + } + } + + private final Consumer setter; + private final Consumer unsetter; + private final Function getter; + + static final Set SAVE = Set.of(SAVE_ALL, SAVE_FIRST_LINE); + } + + public record CommandOutputControlSpec(Set outputControl) { + public CommandOutputControlSpec { + outputControl.forEach(Objects::requireNonNull); + if (outputControl.containsAll(OutputControl.SAVE)) { + throw new IllegalArgumentException(); + } + } + + @Override + public String toString() { + return outputControl.stream().map(CommandOutputControlMutator::name).sorted().collect(joining("+")); + } + + boolean contains(OutputControl v) { + return outputControl.contains(Objects.requireNonNull(v)); + } + + boolean dumpOutput() { + return contains(OutputControl.DUMP); + } + + boolean saveOutput() { + return !Collections.disjoint(outputControl, OutputControl.SAVE); + } + + boolean discardStdout() { + return contains(OutputControl.DISCARD_STDOUT); + } + + boolean discardStderr() { + return contains(OutputControl.DISCARD_STDERR); + } + + boolean redirectStderr() { + return contains(OutputControl.REDIRECT_STDERR); + } + + CommandOutputControl create() { + final CommandOutputControl coc = new CommandOutputControl(); + outputControl.forEach(control -> control.mutate(coc)); + return coc; + } + } + + public record OutputTestSpec(boolean toolProvider, CommandOutputControlSpec cocSpec, CommandSpec commandSpec) { + public OutputTestSpec { + Objects.requireNonNull(cocSpec); + Objects.requireNonNull(commandSpec); + } + + @Override + public String toString() { + final List tokens = new ArrayList<>(); + + if (toolProvider) { + tokens.add("tool-provider"); + } + + tokens.add("output=" + cocSpec.toString()); + tokens.add("command=" + commandSpec); + + return String.join(", ", tokens.toArray(String[]::new)); + } + + void test() { + final var command = commandSpec.command(); + + final Slot result = Slot.createEmpty(); + final var dumpCapture = DumpCapture.captureDump(toRunnable(() -> { + result.set(createExecutable(command).execute()); + })); + + assertEquals(0, result.get().getExitCode()); + + verifyDump(dumpCapture, command); + if (contains(OutputControl.BINARY_OUTPUT)) { + verifyByteResultContent(result.get(), command, StandardCharsets.UTF_8); + } else { + verifyResultContent(result.get(), command); + } + } + + private boolean contains(OutputControl v) { + return cocSpec.contains(v); + } + + private boolean dumpOutput() { + return cocSpec.dumpOutput(); + } + + private boolean saveOutput() { + return cocSpec.saveOutput(); + } + + private boolean discardStdout() { + return cocSpec.discardStdout(); + } + + private boolean discardStderr() { + return cocSpec.discardStderr(); + } + + private boolean redirectStderr() { + return cocSpec.redirectStderr(); + } + + private boolean replaceStdoutWithStderr() { + return redirectStderr() && discardStdout() && !discardStderr(); + } + + private boolean stdoutInherited() { + if (toolProvider || saveOutput() || replaceStdoutWithStderr()) { + return false; + } + return dumpOutput() && !discardStdout() && !contains(OutputControl.DUMP_STDOUT_IN_SYSTEM_OUT); + } + + private boolean stderrInherited() { + if (toolProvider || saveOutput() || redirectStderr()) { + return false; + } + return dumpOutput() && !discardStderr() && !contains(OutputControl.DUMP_STDERR_IN_SYSTEM_ERR); + } + + private void verifyDump(DumpCapture dumpCapture, Command command) { + if (!dumpOutput()) { + assertEquals(List.of(), dumpCapture.outLines()); + assertEquals(List.of(), dumpCapture.errLines()); + return; + } + + if (replaceStdoutWithStderr()) { + // STDERR replaces STDOUT + assertEquals(command.stderr(), dumpCapture.outLines()); + assertEquals(List.of(), dumpCapture.errLines()); + return; + } + + verifyDumpedStdout(dumpCapture, command); + verifyDumpedStderr(dumpCapture, command); + } + + private void verifyDumpedStdout(DumpCapture dumpCapture, Command command) { + if (stdoutInherited()) { + // A subprocess wrote its STDOUT into a file descriptor associated + // with the Java process's STDOUT, not into System.out. Can't capture it. + assertEquals(List.of(), dumpCapture.outLines()); + return; + } + + if (redirectStderr() && !discardStderr()) { + // Interleaved STDOUT and STDERR + if (!isInterleave(dumpCapture.outLines(), command.stdout(), command.stderr())) { + fail(String.format("Unexpected combined output=%s; stdout=%s; stderr=%s", + dumpCapture.outLines(), command.stdout(), command.stderr())); + } + } else if (discardStdout()) { + assertEquals(List.of(), dumpCapture.outLines()); + } else { + assertEquals(command.stdout(), dumpCapture.outLines()); + } + } + + private void verifyDumpedStderr(DumpCapture dumpCapture, Command command) { + if (stderrInherited()) { + // A subprocess wrote its STDERR into a file descriptor associated + // with the Java process's STDERR, not into System.err. Can't capture it. + assertEquals(List.of(), dumpCapture.errLines()); + return; + } + + if (redirectStderr() || discardStderr()) { + assertEquals(List.of(), dumpCapture.errLines()); + } else { + assertEquals(command.stderr(), dumpCapture.errLines()); + } + } + + private void verifyResultContent(CommandOutputControl.Result result, Command command) { + Objects.requireNonNull(result); + Objects.requireNonNull(command); + + assertTrue(result.findByteContent().isEmpty()); + assertTrue(result.findByteStdout().isEmpty()); + assertTrue(result.findByteStderr().isEmpty()); + + if (!saveOutput()) { + assertTrue(result.findContent().isEmpty()); + assertTrue(result.findStdout().isEmpty()); + assertTrue(result.findStderr().isEmpty()); + return; + } + + assertTrue(result.findContent().isPresent()); + + command = filterSavedStreams(command); + + var content = result.content(); + + if (contains(OutputControl.SAVE_FIRST_LINE)) { + assertTrue(content.size() <= 2, String.format("The number of saved lines must be less than or equal to two. Actual: %d", result.content().size())); + } + + if (!redirectStderr()) { + var stdout = result.stdout(); + var stderr = result.stderr(); + + assertEquals(command.stdout(), stdout); + assertEquals(command.stderr(), stderr); + assertEquals(Stream.of( + stdout, + stderr + ).flatMap(List::stream).toList(), content); + } else { + assertEquals(discardStderr(), result.findStdout().isPresent()); + assertTrue(result.findStderr().isEmpty()); + if (contains(OutputControl.SAVE_FIRST_LINE)) { + assertTrue(List.of(command.stdout(), command.stderr()).contains(result.content()), + String.format("Saved content %s is either %s or %s", + content, command.stdout(), command.stderr())); + } else if (contains(OutputControl.SAVE_ALL)) { + if (!isInterleave(content, command.stdout(), command.stderr())) { + fail(String.format("Unexpected combined saved content=%s; stdout=%s; stderr=%s", + content, command.stdout(), command.stderr())); + } + } else { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + } + + private void verifyByteResultContent(CommandOutputControl.Result result, Command command, Charset charset) { + Objects.requireNonNull(result); + Objects.requireNonNull(command); + Objects.requireNonNull(charset); + + assertTrue(result.findContent().isEmpty()); + assertTrue(result.findStdout().isEmpty()); + assertTrue(result.findStderr().isEmpty()); + + if (!saveOutput()) { + assertTrue(result.findByteContent().isEmpty()); + assertTrue(result.findByteStdout().isEmpty()); + assertTrue(result.findByteStderr().isEmpty()); + return; + } + + assertTrue(result.findByteContent().isPresent()); + + command = filterSavedStreams(command); + + if (!redirectStderr()) { + assertEquals(command.stdout(), toStringList(result.byteStdout(), charset)); + assertEquals(command.stderr(), toStringList(result.byteStderr(), charset)); + assertEquals(Stream.of( + command.stdout(), + command.stderr() + ).flatMap(List::stream).toList(), toStringList(result.byteContent(), charset)); + } else { + assertEquals(discardStderr(), result.findByteStdout().isPresent()); + assertTrue(result.findByteStderr().isEmpty()); + + var combined = toStringList(result.byteContent(), charset); + if (!isInterleave(combined, command.stdout(), command.stderr())) { + fail(String.format("Unexpected combined saved content=%s; stdout=%s; stderr=%s", + combined, command.stdout(), command.stderr())); + } + } + } + + private List expectedSavedStream(List commandOutput) { + Objects.requireNonNull(commandOutput); + if (contains(OutputControl.SAVE_ALL) || (contains(OutputControl.SAVE_FIRST_LINE) && contains(OutputControl.BINARY_OUTPUT))) { + return commandOutput; + } else if (contains(OutputControl.SAVE_FIRST_LINE)) { + return commandOutput.stream().findFirst().map(List::of).orElseGet(List::of); + } else { + throw new IllegalStateException(); + } + } + + private Command filterSavedStreams(Command command) { + return new Command( + (discardStdout() ? List.of() : expectedSavedStream(command.stdout())), + (discardStderr() ? List.of() : expectedSavedStream(command.stderr()))); + } + + private record DumpCapture(byte[] out, byte[] err, Charset outCharset, Charset errCharset) { + DumpCapture { + Objects.requireNonNull(out); + Objects.requireNonNull(err); + Objects.requireNonNull(outCharset); + Objects.requireNonNull(errCharset); + } + + List outLines() { + return toStringList(out, outCharset); + } + + List errLines() { + return toStringList(err, errCharset); + } + + static DumpCapture captureDump(Runnable runnable) { + final var captureOut = new ByteArrayOutputStream(); + final var captureErr = new ByteArrayOutputStream(); + + final var out = System.out; + final var err = System.err; + try { + final var outCharset = System.out.charset(); + final var errCharset = System.err.charset(); + System.setOut(new PrintStream(captureOut, true, outCharset)); + System.setErr(new PrintStream(captureErr, true, errCharset)); + runnable.run(); + return new DumpCapture(captureOut.toByteArray(), captureErr.toByteArray(), outCharset, errCharset); + } finally { + try { + System.setOut(out); + } finally { + System.setErr(err); + } + } + } + } + + private CommandOutputControl.Executable createExecutable(Command command) { + final var coc = cocSpec.create(); + if (toolProvider) { + return coc.createExecutable(command.asToolProvider()); + } else { + return coc.createExecutable(new ProcessBuilder(command.asExecutable())); + } + } + } + + record CharsetTestSpec(boolean toolProvider, CommandOutputControlSpec cocSpec) { + + void test() throws IOException, InterruptedException { + if (cocSpec.outputControl().stream().noneMatch(DumpOutputSink.class::isInstance)) { + throw new IllegalArgumentException(); + } + + final var expectedString = "veni-vidi-vici"; + + var coc = cocSpec.create().dumpOutput(true); + + CommandOutputControl.Executable exec; + if (toolProvider) { + var tp = Command.createToolProvider(Stream.of(expectedString).mapMulti((str, sink) -> { + sink.accept(CommandAction.echoStdout(str)); + sink.accept(CommandAction.echoStderr(str)); + }).toList()); + exec = coc.createExecutable(tp); + } else { + var cmdline = Command.createShellCommandLine(Stream.of(expectedString).map(str -> { + return (str + System.lineSeparator()).getBytes(coc.charset()); + }).mapMulti((bytes, sink) -> { + sink.accept(CommandAction.writeStdout(bytes)); + sink.accept(CommandAction.writeStderr(bytes)); + }).toList()); + exec = coc.createExecutable(new ProcessBuilder(cmdline)); + } + + exec.execute(); + + for (var outputContolMutator : cocSpec.outputControl()) { + if (outputContolMutator instanceof DumpOutputSink sink) { + var actual = sink.lines(); + List expected; + if (cocSpec.redirectStderr()) { + switch (sink.streams()) { + case STDERR -> { + expected = List.of(); + } + default -> { + expected = List.of(expectedString, expectedString); + } + } + } else { + expected = List.of(expectedString); + } + assertEquals(expected, actual); + } + } + + } + + record DumpOutputSink(Charset charset, ByteArrayOutputStream buffer, OutputStreams streams) implements CommandOutputControlMutator { + DumpOutputSink { + Objects.requireNonNull(charset); + Objects.requireNonNull(buffer); + Objects.requireNonNull(streams); + } + + DumpOutputSink(Charset charset, OutputStreams streams) { + this(charset, new ByteArrayOutputStream(), streams); + } + + List lines() { + var str = buffer.toString(charset); + return new BufferedReader(new StringReader(str)).lines().toList(); + } + + @Override + public String name() { + return String.format("DUMP-%s-%s", streams, charset.name()); + } + + @Override + public void mutate(CommandOutputControl coc) { + var ps = new PrintStream(buffer, false, charset); + switch (streams) { + case STDOUT -> { + coc.dumpStdout(ps); + } + case STDERR -> { + coc.dumpStderr(ps); + } + case STDOUT_AND_STDERR -> { + // Easy to implement, but not used. + throw new IllegalArgumentException(); + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + } + } + } + + private final static class InterruptibleToolProvider implements ToolProvider { + + InterruptibleToolProvider(ToolProvider impl) { + this.impl = impl; + } + + @Override + public String name() { + return impl.name(); + } + + @Override + public int run(PrintStream out, PrintStream err, String... args) { + return run(_ -> { + return impl.run(out, err, args); + }, args); + } + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + return run(_ -> { + return impl.run(out, err, args); + }, args); + } + + boolean interrupted() { + return interrupted.join(); + } + + private int run(Function workload, String... args) { + boolean interruptedValue = false; + try { + return workload.apply(args); + } catch (ExceptionBox ex) { + if (ex.getCause() instanceof InterruptedException) { + interruptedValue = true; + return 1; + } else { + throw ex; + } + } finally { + interrupted.complete(interruptedValue); + } + } + + private final ToolProvider impl; + private final CompletableFuture interrupted = new CompletableFuture<>(); + } + + private static List toStringList(byte[] data, Charset charset) { + try (var bufReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(data), charset))) { + return bufReader.lines().toList(); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static List> withAndWithout (T value) { + return List.of(Optional.empty(), Optional.of(value)); + } + + private static final List BOOLEAN_VALUES = List.of(true, false); +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTestUtils.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTestUtils.java new file mode 100644 index 00000000000..dec0c5d6b0a --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTestUtils.java @@ -0,0 +1,168 @@ +/* + * 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.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class CommandOutputControlTestUtils { + + @ParameterizedTest + @MethodSource + public void test_isInterleave(TestSpec test) { + test.run(); + } + + private static Stream test_isInterleave() { + var data = new ArrayList(); + + data.addAll(List.of( + interleaved("Toaday", "Today", "a"), + interleaved("Todanaay", "Today", "ana"), + interleaved("aaaababaaa", "aaaba", "aabaa"), + interleaved("xxxxxxxxxxxyxxyx", "xxxxxxxy", "xxxxxyxx"), + interleaved("xyxxxxyxxxxxxxxx", "yxxxxxxx", "xxxyxxxx"), + interleaved("xxxxxxxyxxxxyxxx", "xxxyxxxx", "xxxxxxyx"), + interleaved("cbdddcdaadacdbddbdcdddccdabbadba", "cdddaaddbcdcdbab", "bdcadcbddddcabda"), + interleaved("ddbdcacddddbddbdbddadcaaccdcabab", "dbccdddbbddacdaa", "ddaddbdddacaccbb"), + interleaved("adccbacbacaacddadddcdbbddbbddddd", "acbcaacddddbdbdd", "dcabcadadcbdbddd"), + interleaved("abdbdabdaacdcdbddddadbbccddcddac", "addbaccbdddbcdda", "bbadaddddabcdcdc"), + interleaved("cdaacbddaabdddbddbddbddadbacccdc", "dabdadddbddabccc", "cacdabdbddbddacd"), + notInterleaved("Toady", "Today", "a"), + notInterleaved("", "Today", "a") + )); + + data.addAll(generateTestData("abcdefghijklmnopqrstuvwxyz", 10)); + data.addAll(generateTestData("xxxxxxxy", 8)); + data.addAll(generateTestData("aaabbbcccddddddd", 50)); + + return data.stream().flatMap(test -> { + return Stream.of(test, test.flip()); + }); + } + + private static List generateTestData(String src, int iteration) { + + var srcCodePoints = new ArrayList(); + src.codePoints().mapToObj(Integer::valueOf).forEach(srcCodePoints::add); + + var data = new ArrayList(); + + Function, String> toString = codePoints -> { + var arr = codePoints.stream().mapToInt(Integer::intValue).toArray(); + return new String(arr, 0, arr.length); + }; + + for (int i = 0; i < 10; i++) { + Collections.shuffle(srcCodePoints); + var a = List.copyOf(srcCodePoints); + + Collections.shuffle(srcCodePoints); + var b = List.copyOf(srcCodePoints); + + var zip = new int[srcCodePoints.size() * 2]; + for (int codePointIdx = 0; codePointIdx != a.size(); codePointIdx++) { + var dstIdx = codePointIdx * 2; + zip[dstIdx] = a.get(codePointIdx); + zip[dstIdx + 1] = b.get(codePointIdx); + } + + data.add(interleaved(toString.apply(Arrays.stream(zip).boxed().toList()), toString.apply(a), toString.apply(b))); + } + + return data; + } + + public record TestSpec(String combined, String a, String b, boolean expected) { + + public TestSpec { + Objects.requireNonNull(combined); + Objects.requireNonNull(a); + Objects.requireNonNull(b); + } + + TestSpec flip() { + return new TestSpec(combined, b, a, expected); + } + + void run() { + assertEquals(expected, isInterleave( + combined.chars().mapToObj(Integer::valueOf).toList(), + a.chars().mapToObj(Integer::valueOf).toList(), + b.chars().mapToObj(Integer::valueOf).toList()), + String.format("combined: %s; a=%s; b=%s", combined, a, b)); + } + } + + private static TestSpec interleaved(String combined, String a, String b) { + return new TestSpec(combined, a, b, true); + } + + private static TestSpec notInterleaved(String combined, String a, String b) { + return new TestSpec(combined, a, b, false); + } + + // Solves the standard "Find if a string C is an interleave of strings A and B." + // problem but use containers instead of strings. + static boolean isInterleave(List combined, List a, List b) { + + if (a.size() + b.size() != combined.size()) { + return false; + } + + final var n = a.size(); + final var m = b.size(); + + var prev = new boolean[m + 1]; + final var cur = new boolean[m + 1]; + + prev[0] = true; + + for (int j = 1; j <= m; j++) { + prev[j] = prev[j - 1] && Objects.equals(b.get(j - 1), combined.get(j - 1)); + } + + for (int i = 1; i <= n; i++) { + cur[0] = prev[0] && Objects.equals(a.get(i - 1), combined.get(i - 1)); + + for (int j = 1; j <= m; j++) { + int k = i + j; + cur[j] = (prev[j] && Objects.equals(a.get(i - 1), combined.get(k - 1))) + || (cur[j - 1] && Objects.equals(b.get(j - 1), combined.get(k - 1))); + } + + prev = cur.clone(); + } + + return prev[m]; + } +} diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/EnquoterTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/EnquoterTest.java similarity index 57% rename from test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/EnquoterTest.java rename to test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/EnquoterTest.java index 95b0e54a0cd..8dc44f3f2fd 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/EnquoterTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/EnquoterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, 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 @@ -21,11 +21,13 @@ * questions. */ -package jdk.jpackage.internal; +package jdk.jpackage.internal.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -45,27 +47,41 @@ public class EnquoterTest { assertEquals(expected, actual); } - private static Stream testForShellLiterals() { + @ParameterizedTest + @MethodSource + public void testIdentity(String input) { + var actual = Enquoter.identity().applyTo(input); + assertEquals(input, actual); + } + + @ParameterizedTest + @MethodSource("testIdentity") + public void testNoEscaper(String input) { + var actual = Enquoter.identity().setEnquotePredicate(_ -> true).applyTo(input); + assertEquals('"' + input + '"', actual); + } + + private static Stream testForShellLiterals() { return Stream.of( - makeArguments("''", ""), - makeArguments("'foo'", "foo"), - makeArguments("' foo '", " foo "), - makeArguments("'foo bar'", "foo bar"), - makeArguments("'foo\\' bar'", "foo' bar") + Arguments.of("''", ""), + Arguments.of("'foo'", "foo"), + Arguments.of("' foo '", " foo "), + Arguments.of("'foo bar'", "foo bar"), + Arguments.of("'foo\\' bar'", "foo' bar") ); } - private static Stream testForPropertyValues() { + private static Stream testForPropertyValues() { return Stream.of( - makeArguments("", ""), - makeArguments("foo", "foo"), - makeArguments("\" foo \"", " foo "), - makeArguments("\"foo bar\"", "foo bar"), - makeArguments("\"foo' bar\"", "foo' bar") + Arguments.of("", ""), + Arguments.of("foo", "foo"), + Arguments.of("\" foo \"", " foo "), + Arguments.of("\"foo bar\"", "foo bar"), + Arguments.of("\"foo' bar\"", "foo' bar") ); } - static org.junit.jupiter.params.provider.Arguments makeArguments(Object ... args) { - return org.junit.jupiter.params.provider.Arguments.of(args); + private static Stream testIdentity() { + return Stream.of("", "foo", " foo ", "foo bar", "foo' bar"); } } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RetryExecutorTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RetryExecutorTest.java new file mode 100644 index 00000000000..abb0363da80 --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RetryExecutorTest.java @@ -0,0 +1,331 @@ +/* + * 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.util; + +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.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.time.Duration; +import java.util.Objects; +import jdk.jpackage.internal.util.RetryExecutor.Context; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.internal.util.function.ThrowingFunction; +import jdk.jpackage.internal.util.function.ThrowingSupplier; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class RetryExecutorTest { + + @Test + public void test_defaults() { + + var executor = new AttemptCounter(context -> { + throw new AttemptFailedException(); + }); + + var defaultTimeout = Duration.ofSeconds(2); + var defaultAttemptCount = 5; + + var timeout = Slot.createEmpty(); + + assertThrowsExactly(AttemptFailedException.class, new RetryExecutor(Exception.class) + .setExecutable(executor) + .setSleepFunction(t -> { + assertEquals(defaultTimeout, t); + timeout.set(t); + return; + })::execute); + + assertEquals(defaultTimeout, timeout.get()); + assertEquals(defaultAttemptCount, executor.count()); + } + + @ParameterizedTest + @ValueSource(ints = {0, 1, 2, 3, -4}) + public void test_N_attempts_fail(int maxAttemptsCount) throws AttemptFailedException { + + var retry = new RetryExecutor(AttemptFailedException.class) + .setMaxAttemptsCount(maxAttemptsCount) + .setAttemptTimeout(null) + .setExecutable(context -> { + if (context.attempt() == (maxAttemptsCount - 1)) { + assertTrue(context.isLastAttempt()); + } else { + assertFalse(context.isLastAttempt()); + } + throw new AttemptFailedException("Attempt: " + context.attempt()); + }); + + if (maxAttemptsCount <= 0) { + assertNull(retry.execute()); + } else { + var ex = assertThrowsExactly(AttemptFailedException.class, retry::execute); + assertEquals("Attempt: " + (maxAttemptsCount - 1), ex.getMessage()); + } + } + + @ParameterizedTest + @ValueSource(ints = {1, 2, 3}) + public void test_N_attempts_last_succeed(int maxAttemptsCount) throws AttemptFailedException { + test_N_attempts_M_succeed(maxAttemptsCount, maxAttemptsCount - 1, false); + } + + @ParameterizedTest + @ValueSource(ints = {2, 3}) + public void test_N_attempts_first_succeed(int maxAttemptsCount) throws AttemptFailedException { + test_N_attempts_M_succeed(maxAttemptsCount, 0, false); + } + + @Test + public void test_N_attempts_2nd_succeed() throws AttemptFailedException { + test_N_attempts_M_succeed(4, 1, false); + } + + @Test + public void test_N_attempts_2nd_succeed_unchecked() throws AttemptFailedException { + test_N_attempts_M_succeed(4, 1, true); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_null_executor(boolean dynamic) { + var retry = new RetryExecutor(AttemptFailedException.class) + .setAttemptTimeout(null).setMaxAttemptsCount(1000); + + if (dynamic) { + int maxAttemptsCount = 3; + var executor = new AttemptCounter(context -> { + assertTrue(context.attempt() <= (maxAttemptsCount - 1)); + if (context.attempt() == (maxAttemptsCount - 1)) { + context.executor().setExecutable((ThrowingSupplier)null); + } + throw new AttemptFailedException("foo"); + }); + + retry.setExecutable(executor); + + var ex = assertThrowsExactly(IllegalStateException.class, retry::execute); + assertEquals("No executable", ex.getMessage()); + assertEquals(3, executor.count()); + } else { + var ex = assertThrowsExactly(IllegalStateException.class, retry::execute); + assertEquals("No executable", ex.getMessage()); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_unexpected_exception(boolean executeUnchecked) { + var cause = new UnsupportedOperationException("foo"); + + var executor = new AttemptCounter(context -> { + assertEquals(0, context.attempt()); + throw cause; + }); + + var retry = new RetryExecutor(IOException.class).setExecutable(executor) + .setMaxAttemptsCount(10).setAttemptTimeout(null); + + UnsupportedOperationException ex; + if (executeUnchecked) { + ex = assertThrowsExactly(UnsupportedOperationException.class, retry::executeUnchecked); + } else { + ex = assertThrowsExactly(UnsupportedOperationException.class, retry::execute); + } + assertSame(cause, ex); + assertEquals(1, executor.count()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_dynamic(boolean abort) { + int maxAttemptsCount = 4; + + var secondExecutor = new AttemptCounter(context -> { + throw new AttemptFailedException("bar"); + }); + + var firstExecutor = new AttemptCounter(context -> { + assertTrue(context.attempt() <= (maxAttemptsCount - 1)); + if (context.attempt() == (maxAttemptsCount - 1)) { + if (abort) { + context.executor().setMaxAttemptsCount(maxAttemptsCount); + } else { + // Let it go two more times. + context.executor().setMaxAttemptsCount(maxAttemptsCount + 2); + } + context.executor().setExecutable(secondExecutor); + } + throw new AttemptFailedException("foo"); + }); + + var retry = new RetryExecutor(AttemptFailedException.class) + .setExecutable(firstExecutor) + .setMaxAttemptsCount(1000000) + .setAttemptTimeout(null); + + var ex = assertThrowsExactly(AttemptFailedException.class, retry::execute); + if (abort) { + assertEquals("foo", ex.getMessage()); + assertEquals(0, secondExecutor.count()); + } else { + assertEquals("bar", ex.getMessage()); + assertEquals(2, secondExecutor.count()); + } + assertEquals(maxAttemptsCount, firstExecutor.count()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_supplier_executor(boolean isNull) throws Exception { + var retry = new RetryExecutor(Exception.class).setMaxAttemptsCount(1); + if (isNull) { + retry.setExecutable((ThrowingSupplier)null); + var ex = assertThrowsExactly(IllegalStateException.class, retry::execute); + assertEquals("No executable", ex.getMessage()); + } else { + retry.setExecutable(() -> "Hello"); + assertEquals("Hello", retry.execute()); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_executeUnchecked_fail(boolean withExceptionMapper) throws AttemptFailedException { + var retry = new RetryExecutor(AttemptFailedException.class).setExecutable(() -> { + throw new AttemptFailedException("kaput!"); + }).setMaxAttemptsCount(1); + + Class expectedExceptionType; + if (withExceptionMapper) { + retry.setExceptionMapper((AttemptFailedException ex) -> { + assertEquals("kaput!", ex.getMessage()); + return new UncheckedAttemptFailedException(ex); + }); + expectedExceptionType = UncheckedAttemptFailedException.class; + } else { + expectedExceptionType = ExceptionBox.class; + } + + var ex = assertThrowsExactly(expectedExceptionType, retry::executeUnchecked); + assertEquals(AttemptFailedException.class, ex.getCause().getClass()); + assertEquals("kaput!", ex.getCause().getMessage()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_setSleepFunction(boolean withTimeout) { + + var timeout = Slot.createEmpty(); + + assertDoesNotThrow(new RetryExecutor(AttemptFailedException.class) + .setMaxAttemptsCount(2) + .mutate(retry -> { + if (withTimeout) { + retry.setAttemptTimeout(Duration.ofDays(100)); + } else { + retry.setAttemptTimeout(null); + } + }) + .setExecutable(context -> { + if (context.isLastAttempt()) { + return null; + } else { + throw new AttemptFailedException(); + } + }) + .setSleepFunction(timeout::set)::execute); + + assertEquals(withTimeout, timeout.find().isPresent()); + if (withTimeout) { + assertEquals(Duration.ofDays(100), timeout.get()); + } + } + + private static void test_N_attempts_M_succeed(int maxAttempts, int failedAttempts, boolean unchecked) throws AttemptFailedException { + + var countingExecutor = new AttemptCounter(context -> { + if (context.attempt() == failedAttempts) { + return "You made it!"; + } else { + throw new AttemptFailedException(); + } + }); + + var retry = new RetryExecutor(AttemptFailedException.class) + .setMaxAttemptsCount(maxAttempts) + .setAttemptTimeout(null) + .setExecutable(countingExecutor); + + assertEquals("You made it!", unchecked ? retry.execute() : retry.executeUnchecked()); + assertEquals(failedAttempts, countingExecutor.count() - 1); + } + + private static final class AttemptCounter implements ThrowingFunction>, T, E> { + + AttemptCounter(ThrowingFunction>, T, E> impl) { + this.impl = Objects.requireNonNull(impl); + } + + @Override + public T apply(Context> context) throws E { + counter++; + return impl.apply(context); + } + + int count() { + return counter; + } + + private int counter; + private final ThrowingFunction>, T, E> impl; + } + + private static final class AttemptFailedException extends Exception { + + AttemptFailedException(String msg) { + super(msg); + } + + AttemptFailedException() { + } + + private static final long serialVersionUID = 1L; + } + + private static final class UncheckedAttemptFailedException extends RuntimeException { + + UncheckedAttemptFailedException(AttemptFailedException ex) { + super(ex); + } + + private static final long serialVersionUID = 1L; + } +} diff --git a/test/jdk/tools/jpackage/share/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java index bc12577fa20..ca1189a191e 100644 --- a/test/jdk/tools/jpackage/share/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -441,10 +441,7 @@ public final class ErrorTest { .error("error.no-module-in-path", "com.foo.bar"), // non-existing argument file testSpec().noAppDesc().notype().addArgs("@foo") - .error("ERR_CannotParseOptions", "foo"), - // invalid jlink option - testSpec().addArgs("--jlink-options", "--foo") - .error("error.jlink.failed", "Error: unknown option: --foo") + .error("ERR_CannotParseOptions", "foo") ).map(TestSpec.Builder::create).toList()); // --main-jar and --module-name diff --git a/test/jdk/tools/jpackage/share/PostImageScriptTest.java b/test/jdk/tools/jpackage/share/PostImageScriptTest.java index 655658f036d..81cdfffbf46 100644 --- a/test/jdk/tools/jpackage/share/PostImageScriptTest.java +++ b/test/jdk/tools/jpackage/share/PostImageScriptTest.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 @@ -229,7 +229,7 @@ public class PostImageScriptTest { cmd.saveConsoleOutput(true); }).addBundleVerifier((cmd, result) -> { - final var imageDir = result.stdout().getOutput().stream().map(String::stripLeading).filter(str -> { + final var imageDir = result.stdout().stream().map(String::stripLeading).filter(str -> { return str.startsWith(imageDirOutputPrefix); }).map(str -> { return str.substring(imageDirOutputPrefix.length()); From 805866bbf680f44219e5c634eb9726e1c5dea690 Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Fri, 9 Jan 2026 22:42:53 +0000 Subject: [PATCH 049/204] 8372040: Remove Prefetch header vs inline header separation Reviewed-by: kbarrett, stefank --- .../aix_ppc/prefetch_aix_ppc.inline.hpp | 5 +-- .../prefetch_bsd_aarch64.inline.hpp | 5 +-- .../bsd_x86/prefetch_bsd_x86.inline.hpp | 5 +-- .../bsd_zero/prefetch_bsd_zero.inline.hpp | 4 +- .../prefetch_linux_aarch64.inline.hpp | 5 +-- .../linux_arm/prefetch_linux_arm.inline.hpp | 4 +- .../linux_ppc/prefetch_linux_ppc.inline.hpp | 5 +-- .../prefetch_linux_riscv.inline.hpp | 4 +- .../linux_s390/prefetch_linux_s390.inline.hpp | 4 +- .../linux_x86/prefetch_linux_x86.inline.hpp | 5 +-- .../linux_zero/prefetch_linux_zero.inline.hpp | 4 +- .../prefetch_windows_aarch64.inline.hpp | 5 +-- .../prefetch_windows_x86.inline.hpp | 4 +- .../gc/g1/g1YoungGCPostEvacuateTasks.cpp | 4 +- src/hotspot/share/gc/serial/cardTableRS.cpp | 3 +- src/hotspot/share/gc/serial/generation.hpp | 3 +- src/hotspot/share/runtime/prefetch.hpp | 45 ------------------- src/hotspot/share/runtime/prefetch.inline.hpp | 21 +++++++-- 18 files changed, 49 insertions(+), 86 deletions(-) delete mode 100644 src/hotspot/share/runtime/prefetch.hpp diff --git a/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp b/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp index 1f9917acae7..c741335b5f0 100644 --- a/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp +++ b/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp @@ -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. * Copyright (c) 2012, 2013 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,8 +26,7 @@ #ifndef OS_CPU_AIX_PPC_PREFETCH_AIX_PPC_INLINE_HPP #define OS_CPU_AIX_PPC_PREFETCH_AIX_PPC_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read(const void *loc, intx interval) { #if !defined(USE_XLC_BUILTINS) diff --git a/src/hotspot/os_cpu/bsd_aarch64/prefetch_bsd_aarch64.inline.hpp b/src/hotspot/os_cpu/bsd_aarch64/prefetch_bsd_aarch64.inline.hpp index 1bcbdcaf90d..185f9b54144 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/prefetch_bsd_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/bsd_aarch64/prefetch_bsd_aarch64.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -27,8 +27,7 @@ #ifndef OS_CPU_BSD_AARCH64_PREFETCH_BSD_AARCH64_INLINE_HPP #define OS_CPU_BSD_AARCH64_PREFETCH_BSD_AARCH64_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { if (interval >= 0) diff --git a/src/hotspot/os_cpu/bsd_x86/prefetch_bsd_x86.inline.hpp b/src/hotspot/os_cpu/bsd_x86/prefetch_bsd_x86.inline.hpp index 52cc405e211..464740920a1 100644 --- a/src/hotspot/os_cpu/bsd_x86/prefetch_bsd_x86.inline.hpp +++ b/src/hotspot/os_cpu/bsd_x86/prefetch_bsd_x86.inline.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. * 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 @@ #ifndef OS_CPU_BSD_X86_PREFETCH_BSD_X86_INLINE_HPP #define OS_CPU_BSD_X86_PREFETCH_BSD_X86_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { __asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval)); diff --git a/src/hotspot/os_cpu/bsd_zero/prefetch_bsd_zero.inline.hpp b/src/hotspot/os_cpu/bsd_zero/prefetch_bsd_zero.inline.hpp index 220d6a08f68..5edf3744719 100644 --- a/src/hotspot/os_cpu/bsd_zero/prefetch_bsd_zero.inline.hpp +++ b/src/hotspot/os_cpu/bsd_zero/prefetch_bsd_zero.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,7 +26,7 @@ #ifndef OS_CPU_BSD_ZERO_PREFETCH_BSD_ZERO_INLINE_HPP #define OS_CPU_BSD_ZERO_PREFETCH_BSD_ZERO_INLINE_HPP -#include "runtime/prefetch.hpp" +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read(const void* loc, intx interval) { } diff --git a/src/hotspot/os_cpu/linux_aarch64/prefetch_linux_aarch64.inline.hpp b/src/hotspot/os_cpu/linux_aarch64/prefetch_linux_aarch64.inline.hpp index 168a680a404..580e9f4ffa2 100644 --- a/src/hotspot/os_cpu/linux_aarch64/prefetch_linux_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/prefetch_linux_aarch64.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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. * @@ -26,8 +26,7 @@ #ifndef OS_CPU_LINUX_AARCH64_PREFETCH_LINUX_AARCH64_INLINE_HPP #define OS_CPU_LINUX_AARCH64_PREFETCH_LINUX_AARCH64_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { if (interval >= 0) diff --git a/src/hotspot/os_cpu/linux_arm/prefetch_linux_arm.inline.hpp b/src/hotspot/os_cpu/linux_arm/prefetch_linux_arm.inline.hpp index dfbab55d9b9..a536c17fbe3 100644 --- a/src/hotspot/os_cpu/linux_arm/prefetch_linux_arm.inline.hpp +++ b/src/hotspot/os_cpu/linux_arm/prefetch_linux_arm.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2021, 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 @@ -25,7 +25,7 @@ #ifndef OS_CPU_LINUX_ARM_PREFETCH_LINUX_ARM_INLINE_HPP #define OS_CPU_LINUX_ARM_PREFETCH_LINUX_ARM_INLINE_HPP -#include "runtime/prefetch.hpp" +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { #if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_5TE__) diff --git a/src/hotspot/os_cpu/linux_ppc/prefetch_linux_ppc.inline.hpp b/src/hotspot/os_cpu/linux_ppc/prefetch_linux_ppc.inline.hpp index 12c65e6bf00..c33624a07de 100644 --- a/src/hotspot/os_cpu/linux_ppc/prefetch_linux_ppc.inline.hpp +++ b/src/hotspot/os_cpu/linux_ppc/prefetch_linux_ppc.inline.hpp @@ -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. * Copyright (c) 2012, 2013 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,8 +26,7 @@ #ifndef OS_CPU_LINUX_PPC_PREFETCH_LINUX_PPC_INLINE_HPP #define OS_CPU_LINUX_PPC_PREFETCH_LINUX_PPC_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read(const void *loc, intx interval) { __asm__ __volatile__ ( diff --git a/src/hotspot/os_cpu/linux_riscv/prefetch_linux_riscv.inline.hpp b/src/hotspot/os_cpu/linux_riscv/prefetch_linux_riscv.inline.hpp index a2dd79544c1..c17054e8a0c 100644 --- a/src/hotspot/os_cpu/linux_riscv/prefetch_linux_riscv.inline.hpp +++ b/src/hotspot/os_cpu/linux_riscv/prefetch_linux_riscv.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. 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. * @@ -26,7 +26,7 @@ #ifndef OS_CPU_LINUX_RISCV_VM_PREFETCH_LINUX_RISCV_INLINE_HPP #define OS_CPU_LINUX_RISCV_VM_PREFETCH_LINUX_RISCV_INLINE_HPP -#include "runtime/prefetch.hpp" +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { if (interval >= 0 && UseZicbop) { diff --git a/src/hotspot/os_cpu/linux_s390/prefetch_linux_s390.inline.hpp b/src/hotspot/os_cpu/linux_s390/prefetch_linux_s390.inline.hpp index ee55d01886f..56038714a9a 100644 --- a/src/hotspot/os_cpu/linux_s390/prefetch_linux_s390.inline.hpp +++ b/src/hotspot/os_cpu/linux_s390/prefetch_linux_s390.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,7 +26,7 @@ #ifndef OS_CPU_LINUX_S390_PREFETCH_LINUX_S390_INLINE_HPP #define OS_CPU_LINUX_S390_PREFETCH_LINUX_S390_INLINE_HPP -#include "runtime/prefetch.hpp" +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read(const void* loc, intx interval) { // No prefetch instructions on z/Architecture -> implement trivially. diff --git a/src/hotspot/os_cpu/linux_x86/prefetch_linux_x86.inline.hpp b/src/hotspot/os_cpu/linux_x86/prefetch_linux_x86.inline.hpp index bb4302e1ddb..aadd21bca40 100644 --- a/src/hotspot/os_cpu/linux_x86/prefetch_linux_x86.inline.hpp +++ b/src/hotspot/os_cpu/linux_x86/prefetch_linux_x86.inline.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. * 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 @@ #ifndef OS_CPU_LINUX_X86_PREFETCH_LINUX_X86_INLINE_HPP #define OS_CPU_LINUX_X86_PREFETCH_LINUX_X86_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { __asm__ ("prefetcht0 (%0,%1,1)" : : "r" (loc), "r" (interval)); diff --git a/src/hotspot/os_cpu/linux_zero/prefetch_linux_zero.inline.hpp b/src/hotspot/os_cpu/linux_zero/prefetch_linux_zero.inline.hpp index a510fa87834..3a34c311acd 100644 --- a/src/hotspot/os_cpu/linux_zero/prefetch_linux_zero.inline.hpp +++ b/src/hotspot/os_cpu/linux_zero/prefetch_linux_zero.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,7 +26,7 @@ #ifndef OS_CPU_LINUX_ZERO_PREFETCH_LINUX_ZERO_INLINE_HPP #define OS_CPU_LINUX_ZERO_PREFETCH_LINUX_ZERO_INLINE_HPP -#include "runtime/prefetch.hpp" +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read(const void* loc, intx interval) { } diff --git a/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp b/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp index df301ade92d..a360ee342be 100644 --- a/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/prefetch_windows_aarch64.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Microsoft Corporation. All rights reserved. + * Copyright (c) 2020, 2026, Microsoft Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,7 @@ #ifndef OS_CPU_WINDOWS_AARCH64_PREFETCH_WINDOWS_AARCH64_INLINE_HPP #define OS_CPU_WINDOWS_AARCH64_PREFETCH_WINDOWS_AARCH64_INLINE_HPP -#include "runtime/prefetch.hpp" - +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) { } diff --git a/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp b/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp index 996625cb0ad..645fbe99a22 100644 --- a/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp +++ b/src/hotspot/os_cpu/windows_x86/prefetch_windows_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, 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,7 +25,7 @@ #ifndef OS_CPU_WINDOWS_X86_PREFETCH_WINDOWS_X86_INLINE_HPP #define OS_CPU_WINDOWS_X86_PREFETCH_WINDOWS_X86_INLINE_HPP -#include "runtime/prefetch.hpp" +// Included in runtime/prefetch.inline.hpp inline void Prefetch::read (const void *loc, intx interval) {} inline void Prefetch::write(void *loc, intx interval) {} diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index d875d9c8b8a..ec5d2393d8c 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.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 @@ -46,7 +46,7 @@ #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/prefetch.hpp" +#include "runtime/prefetch.inline.hpp" #include "runtime/threads.hpp" #include "runtime/threadSMR.hpp" #include "utilities/bitMap.inline.hpp" diff --git a/src/hotspot/share/gc/serial/cardTableRS.cpp b/src/hotspot/share/gc/serial/cardTableRS.cpp index 80985424a62..a53ab066387 100644 --- a/src/hotspot/share/gc/serial/cardTableRS.cpp +++ b/src/hotspot/share/gc/serial/cardTableRS.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 @@ -28,6 +28,7 @@ #include "gc/serial/serialHeap.inline.hpp" #include "gc/shared/space.hpp" #include "memory/iterator.inline.hpp" +#include "runtime/prefetch.inline.hpp" #include "utilities/align.hpp" void CardTableRS::scan_old_to_young_refs(TenuredGeneration* tg, HeapWord* saved_top) { diff --git a/src/hotspot/share/gc/serial/generation.hpp b/src/hotspot/share/gc/serial/generation.hpp index 8c9da3b42b7..ddfd4028a7d 100644 --- a/src/hotspot/share/gc/serial/generation.hpp +++ b/src/hotspot/share/gc/serial/generation.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 @@ -34,7 +34,6 @@ #include "memory/virtualspace.hpp" #include "runtime/mutex.hpp" #include "runtime/perfData.hpp" -#include "runtime/prefetch.inline.hpp" // A Generation models a heap area for similarly-aged objects. // It will contain one ore more spaces holding the actual objects. diff --git a/src/hotspot/share/runtime/prefetch.hpp b/src/hotspot/share/runtime/prefetch.hpp deleted file mode 100644 index 601337c14af..00000000000 --- a/src/hotspot/share/runtime/prefetch.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. - * 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_RUNTIME_PREFETCH_HPP -#define SHARE_RUNTIME_PREFETCH_HPP - -#include "memory/allStatic.hpp" - -// If calls to prefetch methods are in a loop, the loop should be cloned -// such that if Prefetch{Scan,Copy}Interval and/or PrefetchFieldInterval -// say not to do prefetching, these methods aren't called. At the very -// least, they take up a memory issue slot. They should be implemented -// as inline assembly code: doing an actual call isn't worth the cost. - -class Prefetch : AllStatic { - public: - // Prefetch anticipating read; must not fault, semantically a no-op - static void read(const void* loc, intx interval); - - // Prefetch anticipating write; must not fault, semantically a no-op - static void write(void* loc, intx interval); -}; - -#endif // SHARE_RUNTIME_PREFETCH_HPP diff --git a/src/hotspot/share/runtime/prefetch.inline.hpp b/src/hotspot/share/runtime/prefetch.inline.hpp index 4cc1d93f613..18630f71a62 100644 --- a/src/hotspot/share/runtime/prefetch.inline.hpp +++ b/src/hotspot/share/runtime/prefetch.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, 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,9 +25,24 @@ #ifndef SHARE_RUNTIME_PREFETCH_INLINE_HPP #define SHARE_RUNTIME_PREFETCH_INLINE_HPP -#include "runtime/prefetch.hpp" - +#include "memory/allStatic.hpp" #include "utilities/macros.hpp" + +// If calls to prefetch methods are in a loop, the loop should be cloned +// such that if Prefetch{Scan,Copy}Interval and/or PrefetchFieldInterval +// say not to do prefetching, these methods aren't called. At the very +// least, they take up a memory issue slot. They should be implemented +// as inline assembly code: doing an actual call isn't worth the cost. + +class Prefetch : AllStatic { + public: + // Prefetch anticipating read; must not fault, semantically a no-op + static void read(const void* loc, intx interval); + + // Prefetch anticipating write; must not fault, semantically a no-op + static void write(void* loc, intx interval); +}; + #include OS_CPU_HEADER_INLINE(prefetch) #endif // SHARE_RUNTIME_PREFETCH_INLINE_HPP From 74faf033127ab3a5e28be75b91e662c589f81084 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 9 Jan 2026 23:36:19 +0000 Subject: [PATCH 050/204] 8374819: jpackage and jpackage tests leave some I/O streams unclosed Reviewed-by: almatvee --- .../internal/AppImageInfoPListFile.java | 6 ++--- .../jdk/jpackage/internal/AppImageFile.java | 23 ++++++++++++++++--- .../jpackage/internal/util/PListReader.java | 5 ++-- .../jdk/jpackage/test/AppImageFile.java | 5 ++-- .../jdk/jpackage/test/LauncherVerifier.java | 11 +++++---- .../jpackage/macosx/HostArchPkgTest.java | 5 ++-- .../jpackage/windows/WinLongVersionTest.java | 7 +++--- 7 files changed, 37 insertions(+), 25 deletions(-) diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageInfoPListFile.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageInfoPListFile.java index 4787d1297bb..602e147a970 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageInfoPListFile.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageInfoPListFile.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,8 +24,6 @@ */ package jdk.jpackage.internal; -import static jdk.jpackage.internal.util.XmlUtils.initDocumentBuilder; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -50,7 +48,7 @@ record AppImageInfoPListFile(String bundleIdentifier, String bundleName, String static AppImageInfoPListFile loadFromInfoPList(Path infoPListFile) throws IOException, InvalidPlistFileException, SAXException { - final var plistReader = new PListReader(initDocumentBuilder().parse(Files.newInputStream(infoPListFile))); + final var plistReader = new PListReader(Files.readAllBytes(infoPListFile)); try { return new AppImageInfoPListFile( diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java index 5f473b554be..75ead9d08ad 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/AppImageFile.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 @@ -28,11 +28,12 @@ import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toUnmodifiableMap; import static java.util.stream.Collectors.toUnmodifiableSet; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.APP_VERSION; -import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_AS_SERVICE; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.DESCRIPTION; +import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_AS_SERVICE; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_NAME; import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.NoSuchFileException; @@ -162,7 +163,23 @@ final class AppImageFile { final var relativeAppImageFilePath = appImageDir.relativize(appImageFilePath); try { - final Document doc = XmlUtils.initDocumentBuilder().parse(Files.newInputStream(appImageFilePath)); + // + // Use javax.xml.parsers.DocumentBuilder#parse(java.io.InputStream). + // Don't use javax.xml.parsers.DocumentBuilder#parse(java.io.File) as this will introduce + // dependency on how the XML parser reports filesystem I/O errors. + // E.g.: the default JDK XML parser throws java.io.FileNotFoundException if the supplied + // directory is not found and throws org.xml.sax.SAXParseException if the supplied file is a directory. + // Another DOM XML parser (a different version of Xerces?) may behave differently. + // + // The use of javax.xml.parsers.DocumentBuilder#parse(java.io.InputStream) eliminates + // differences in how XML parsers handle file system I/O errors. + // Filesystem I/O is delegated to java.nio.file.Files#readAllBytes(java.nio.file.Path), + // XML parser deals with the byte stream in memory and the error handling code + // doesn't depend on how XML parser reports filesystem I/O errors because + // it reads data from memory, not from the filesystem. + // + final Document doc = XmlUtils.initDocumentBuilder().parse( + new ByteArrayInputStream(Files.readAllBytes(appImageFilePath))); final XPath xPath = XPathFactory.newInstance().newXPath(); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PListReader.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PListReader.java index 2c693939dab..4c85358d424 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PListReader.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/PListReader.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 @@ -35,7 +35,6 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; -import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.dom.DOMSource; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; @@ -311,7 +310,7 @@ public final class PListReader { } } - public PListReader(byte[] xmlData) throws ParserConfigurationException, SAXException, IOException { + public PListReader(byte[] xmlData) throws SAXException, IOException { this(XmlUtils.initDocumentBuilder().parse(new ByteArrayInputStream(xmlData))); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java index 94f5b0a52b4..1c6c0ce4447 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.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 @@ -28,7 +28,6 @@ import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; @@ -132,7 +131,7 @@ public record AppImageFile(String mainLauncherName, Optional mainLaunche public static AppImageFile load(Path appImageDir) { return toSupplier(() -> { Document doc = XmlUtils.initDocumentBuilder().parse( - Files.newInputStream(getPathInAppImage(appImageDir))); + getPathInAppImage(appImageDir).toFile()); XPath xPath = XPathFactory.newInstance().newXPath(); 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 15d96311d98..f9fcfb905af 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LauncherVerifier.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 @@ -39,7 +39,6 @@ import java.util.function.BiFunction; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; -import javax.xml.parsers.ParserConfigurationException; import jdk.jpackage.internal.resources.ResourceLocator; import jdk.jpackage.internal.util.PListReader; import jdk.jpackage.internal.util.function.ThrowingBiConsumer; @@ -367,7 +366,7 @@ public final class LauncherVerifier { } } - private void verifyMacEntitlements(JPackageCommand cmd) throws ParserConfigurationException, SAXException, IOException { + private void verifyMacEntitlements(JPackageCommand cmd) throws SAXException, IOException { Path launcherPath = cmd.appLauncherPath(name); var entitlements = MacSignVerify.findEntitlements(launcherPath); @@ -457,8 +456,10 @@ public final class LauncherVerifier { private static final class DefaultEntitlements { private static Map loadFromResources(String resourceName) { return ThrowingSupplier.toSupplier(() -> { - var bytes = ResourceLocator.class.getResourceAsStream(resourceName).readAllBytes(); - return new PListReader(bytes).toMap(true); + try (var in = ResourceLocator.class.getResourceAsStream(resourceName)) { + var bytes = in.readAllBytes(); + return new PListReader(bytes).toMap(true); + } }).get(); } diff --git a/test/jdk/tools/jpackage/macosx/HostArchPkgTest.java b/test/jdk/tools/jpackage/macosx/HostArchPkgTest.java index 7498043c14f..7aebff7cb00 100644 --- a/test/jdk/tools/jpackage/macosx/HostArchPkgTest.java +++ b/test/jdk/tools/jpackage/macosx/HostArchPkgTest.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 @@ -64,8 +64,7 @@ public class HostArchPkgTest { dbf.setFeature("http://apache.org/xml/features/" + "nonvalidating/load-external-dtd", false); DocumentBuilder b = dbf.newDocumentBuilder(); - org.w3c.dom.Document doc - = b.parse(Files.newInputStream(distributionFile)); + org.w3c.dom.Document doc = b.parse(distributionFile.toFile()); XPath xPath = XPathFactory.newInstance().newXPath(); diff --git a/test/jdk/tools/jpackage/windows/WinLongVersionTest.java b/test/jdk/tools/jpackage/windows/WinLongVersionTest.java index 2ee76bcaa15..13650b2dfd3 100644 --- a/test/jdk/tools/jpackage/windows/WinLongVersionTest.java +++ b/test/jdk/tools/jpackage/windows/WinLongVersionTest.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 @@ -180,9 +180,8 @@ public class WinLongVersionTest { cmd.setFakeRuntime(); // Create package without Upgrade table - Document doc = XmlUtils.initDocumentBuilder().parse( - Files.newInputStream(TKit.SRC_ROOT.resolve( - "windows/classes/jdk/jpackage/internal/resources/main.wxs"))); + Document doc = XmlUtils.initDocumentBuilder().parse(TKit.SRC_ROOT.resolve( + "windows/classes/jdk/jpackage/internal/resources/main.wxs").toFile()); XPath xPath = XPathFactory.newInstance().newXPath(); NodeList nodes = (NodeList) xPath.evaluate("/Wix/Product/Upgrade", doc, XPathConstants.NODESET); From a726e834b6d3674f0d573d8a0df6eb00464b825b Mon Sep 17 00:00:00 2001 From: John Jiang Date: Sat, 10 Jan 2026 00:52:34 +0000 Subject: [PATCH 051/204] 8373231: ECDSAOperations::toAffinePoint is redundant Reviewed-by: mullan --- .../classes/sun/security/ec/ECDSAOperations.java | 12 ++---------- test/jdk/sun/security/ec/ECDSAPrimitive.java | 4 ++-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ec/ECDSAOperations.java b/src/java.base/share/classes/sun/security/ec/ECDSAOperations.java index f58d7d8f2d7..7badcf42d9c 100644 --- a/src/java.base/share/classes/sun/security/ec/ECDSAOperations.java +++ b/src/java.base/share/classes/sun/security/ec/ECDSAOperations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,7 +68,7 @@ public class ECDSAOperations { public ECDSAOperations(ECOperations ecOps, ECPoint basePoint) { this.ecOps = ecOps; - this.basePoint = toAffinePoint(basePoint, ecOps.getField()); + this.basePoint = AffinePoint.fromECPoint(basePoint, ecOps.getField()); } public ECOperations getEcOperations() { @@ -79,14 +79,6 @@ public class ECDSAOperations { return ecOps.multiply(basePoint, scalar).asAffine(); } - public static AffinePoint toAffinePoint(ECPoint point, - IntegerFieldModuloP field) { - - ImmutableIntegerModuloP affineX = field.getElement(point.getAffineX()); - ImmutableIntegerModuloP affineY = field.getElement(point.getAffineY()); - return new AffinePoint(affineX, affineY); - } - public static Optional forParameters(ECParameterSpec ecParams) { Optional curveOps = diff --git a/test/jdk/sun/security/ec/ECDSAPrimitive.java b/test/jdk/sun/security/ec/ECDSAPrimitive.java index 71e2e30044b..b8901a784ff 100644 --- a/test/jdk/sun/security/ec/ECDSAPrimitive.java +++ b/test/jdk/sun/security/ec/ECDSAPrimitive.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -249,7 +249,7 @@ public class ECDSAPrimitive { byte[] u1Bytes = u1.asByteArray(length); byte[] u2Bytes = u2.asByteArray(length); - AffinePoint publicKeyPoint = ECDSAOperations.toAffinePoint(publicKey, + AffinePoint publicKeyPoint = AffinePoint.fromECPoint(publicKey, ecOps.getField()); MutablePoint R = ecOps.multiply(publicKeyPoint, u2Bytes); AffinePoint a1 = ops.basePointMultiply(u1Bytes); From 0537a3fae9bd55ab8b7279da7d3ee4b5ce5bc492 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Sat, 10 Jan 2026 01:55:00 +0000 Subject: [PATCH 052/204] 8374922: Build failure after JDK-8372040 Reviewed-by: smarks --- src/hotspot/share/gc/serial/serialHeap.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index 08d730bf877..8eafdfdcc82 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.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 @@ -70,6 +70,7 @@ #include "runtime/init.hpp" #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" +#include "runtime/prefetch.inline.hpp" #include "runtime/threads.hpp" #include "runtime/vmThread.hpp" #include "services/memoryManager.hpp" From 657d5f77f4985304995ee44fc2ae1643504de8df Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Sat, 10 Jan 2026 02:17:37 +0000 Subject: [PATCH 053/204] 8374754: jtreg failure handler - replace inline javascript and inline event handlers with same origin javascript files Reviewed-by: erikj --- .../jdk/test/failurehandler/HtmlPage.java | 106 +++++++++++++++++- .../jdk/test/failurehandler/HtmlSection.java | 48 ++------ .../jtreg/GatherDiagnosticInfoObserver.java | 10 +- .../GatherProcessInfoTimeoutHandler.java | 19 +--- 4 files changed, 121 insertions(+), 62 deletions(-) diff --git a/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlPage.java b/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlPage.java index d8fd13fdd7c..af28135e673 100644 --- a/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlPage.java +++ b/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlPage.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 @@ -23,19 +23,50 @@ package jdk.test.failurehandler; +import java.io.FileWriter; +import java.io.IOException; import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Objects; public class HtmlPage implements AutoCloseable { + static final String STYLE_SHEET_FILENAME = "failure-handler-style.css"; + static final String SCRIPT_FILENAME = "failure-handler-script.js"; + private final PrintWriter writer; private final HtmlSection rootSection; - public HtmlPage(PrintWriter writer) { - Objects.requireNonNull(writer, "writer cannot be null"); - this.writer = writer; + /** + * Constructs a {@code HtmlPage} + * + * @param dir The directory into which the HTML file and related resources will be created + * @param htmlFileName The HTML file name + * @param append if {@code true} then the content will be appended to the file represented + * by the {@code htmlFileName}, else the {@code htmlFileName} will be overwritten + * with the new content + * @throws IllegalArgumentException if {@code dir} is not a directory or if the + * {@code htmlFileName} is {@linkplain String#isBlank() blank} + * @throws IOException if there is an error constructing file resource(s) for this HTML page + */ + public HtmlPage(final Path dir, final String htmlFileName, final boolean append) + throws IOException { + Objects.requireNonNull(dir, "directory cannot be null"); + Objects.requireNonNull(htmlFileName, "HTML file name cannot be null"); + if (!Files.isDirectory(dir)) { + throw new IllegalArgumentException(dir + " is not a directory"); + } + if (htmlFileName.isBlank()) { + throw new IllegalArgumentException("HTML file name cannot be blank"); + } + final FileWriter fileWriter = new FileWriter(dir.resolve(htmlFileName).toFile(), append); + this.writer = new PrintWriter(fileWriter, true); + createScriptFile(dir); + createStyleSheetFile(dir); rootSection = new HtmlSection(writer); } + @Override public void close() { writer.close(); @@ -44,4 +75,71 @@ public class HtmlPage implements AutoCloseable { public HtmlSection getRootSection() { return rootSection; } + + private static void createStyleSheetFile(final Path destDir) throws IOException { + final Path styleSheet = destDir.resolve(STYLE_SHEET_FILENAME); + if (Files.exists(styleSheet)) { + return; + } + final String content = """ + div { display:none;} + """; + Files.writeString(styleSheet, content); + } + + private static void createScriptFile(final Path destDir) throws IOException { + final Path script = destDir.resolve(SCRIPT_FILENAME); + if (Files.exists(script)) { + return; + } + final String content = """ + function doShow(e) { + while (e != null) { + if (e.tagName == 'DIV') { + e.style.display = 'block'; + } + e = e.parentNode; + } + } + + function showHandler(event) { + elementId = this.dataset.show; + elementToShow = document.getElementById(elementId); + doShow(elementToShow); + } + + function toggleHandler(event) { + toggleElementId = this.dataset.toggle; + elementToToggle = document.getElementById(toggleElementId); + d = elementToToggle.style.display; + if (d == 'block') { + elementToToggle.style.display = 'none'; + } else { + doShow(elementToToggle); + } + } + + function bodyLoadHandler() { + const index = location.href.indexOf("#"); + if (index != -1) { + doShow(document.getElementById(location.href.substring(index + 1))); + } + // elements that require the "toggleHandler" function to be registered + // as an event handler for the onclick event + const requiringToggleHandler = document.querySelectorAll("[data-toggle]"); + for (const e of requiringToggleHandler) { + e.addEventListener("click", toggleHandler); + } + // elements that require the "showHandler" function to be registered + // as an event handler for the onclick event + const requiringShowHandler = document.querySelectorAll("[data-show]"); + for (const e of requiringShowHandler) { + e.addEventListener("click", showHandler); + } + } + // register a onload event handler + window.addEventListener("DOMContentLoaded", bodyLoadHandler); + """; + Files.writeString(script, content); + } } diff --git a/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlSection.java b/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlSection.java index 141caf450bd..53ffab95a1e 100644 --- a/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlSection.java +++ b/test/failure_handler/src/share/classes/jdk/test/failurehandler/HtmlSection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, 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 @@ -57,41 +57,15 @@ public class HtmlSection { if (rootSection == null) { this.rootSection = this; this.pw.println(""); - this.pw.println("\n" - + "\n" - + "\n" - + ""); - this.pw.println(""); + this.pw.println(""); + this.pw.println( + ""); + this.pw.println( + ""); + this.pw.println(""); + + this.pw.println(""); } else { this.rootSection = rootSection; this.pw.print("
      "); @@ -146,7 +120,7 @@ public class HtmlSection { } else if (child != null) { path = String.format("%s.%s", path, child); } - pw.printf("%2$s%n", + pw.printf("%2$s%n", path, name); } @@ -188,7 +162,7 @@ public class HtmlSection { : String.format("%s.%s", parent.id, name), name, rootSection); this.parent = parent; - pw.printf("
    • %2$s
      ",
      +            pw.printf("
    • %2$s
      ",
                           id, name);
               }
       
      diff --git a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java
      index 96c5f4f2858..e63e55888d7 100644
      --- a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java
      +++ b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2015, 2021, 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
      @@ -105,9 +105,7 @@ public class GatherDiagnosticInfoObserver implements Harness.Observer {
       
           private void gatherCoreInfo(Path workDir, String name, Path core, PrintWriter log,
                                      CoreInfoGatherer gatherer) {
      -        File output = workDir.resolve(CORES_OUTPUT).toFile();
      -        try (HtmlPage html = new HtmlPage(new PrintWriter(
      -                new FileWriter(output, true), true))) {
      +        try (HtmlPage html = new HtmlPage(workDir, CORES_OUTPUT, true)) {
                   try (ElapsedTimePrinter timePrinter
                                = new ElapsedTimePrinter(new Stopwatch(), name, log)) {
                       gatherer.gatherCoreInfo(html.getRootSection(), core);
      @@ -121,9 +119,7 @@ public class GatherDiagnosticInfoObserver implements Harness.Observer {
       
           private void gatherEnvInfo(Path workDir, String name, PrintWriter log,
                                      EnvironmentInfoGatherer gatherer) {
      -        File output = workDir.resolve(ENVIRONMENT_OUTPUT).toFile();
      -        try (HtmlPage html = new HtmlPage(new PrintWriter(
      -                new FileWriter(output, true), true))) {
      +        try (HtmlPage html = new HtmlPage(workDir, ENVIRONMENT_OUTPUT, true)) {
                   try (ElapsedTimePrinter timePrinter
                                = new ElapsedTimePrinter(new Stopwatch(), name, log)) {
                       gatherer.gatherEnvironmentInfo(html.getRootSection());
      diff --git a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherProcessInfoTimeoutHandler.java b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherProcessInfoTimeoutHandler.java
      index 63ed7e5f2c7..c1f1ddfb69f 100644
      --- a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherProcessInfoTimeoutHandler.java
      +++ b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherProcessInfoTimeoutHandler.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
      @@ -69,16 +69,7 @@ public class GatherProcessInfoTimeoutHandler extends TimeoutHandler {
               }
               try {
                   actionsLog.printf("%s ---%n", name);
      -
      -            File output = workDir.resolve(OUTPUT_FILENAME).toFile();
      -            try {
      -                PrintWriter pw = new PrintWriter(new FileWriter(output, true), true);
      -                runGatherer(name, workDir, actionsLog, pw, pid);
      -            } catch (IOException e) {
      -                actionsLog.printf("IOException: cannot open output file[%s] : %s",
      -                        output, e.getMessage());
      -                e.printStackTrace(actionsLog);
      -            }
      +            runGatherer(name, actionsLog, pid);
               } finally {
                   actionsLog.printf("--- %s%n", name);
                   // don't close jtreg log
      @@ -90,9 +81,9 @@ public class GatherProcessInfoTimeoutHandler extends TimeoutHandler {
               }
           }
       
      -    private void runGatherer(String name, Path workDir, PrintWriter log,
      -                             PrintWriter out, long pid) {
      -        try (HtmlPage html = new HtmlPage(out)) {
      +    private void runGatherer(String name, PrintWriter log, long pid) {
      +        Path workDir = outputDir.toPath();
      +        try (HtmlPage html = new HtmlPage(workDir, OUTPUT_FILENAME, true)) {
                   ProcessInfoGatherer gatherer = new GathererFactory(
                           OS.current().family,
                           workDir, log, testJdk.toPath()).getProcessInfoGatherer();
      
      From 12894a870a3c8d1da13a885cc006458ae9475b6e Mon Sep 17 00:00:00 2001
      From: Serguei Spitsyn 
      Date: Sat, 10 Jan 2026 11:10:06 +0000
      Subject: [PATCH 054/204] 8373643: Test
       serviceability/jvmti/vthread/ThreadListStackTracesTest/ThreadListStackTracesTest.java
       still failing
      
      Reviewed-by: lmesnik
      ---
       .../ThreadListStackTracesTest.java                   | 12 +++++++-----
       1 file changed, 7 insertions(+), 5 deletions(-)
      
      diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadListStackTracesTest/ThreadListStackTracesTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadListStackTracesTest/ThreadListStackTracesTest.java
      index 079f65620d8..2a5c9bea111 100644
      --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadListStackTracesTest/ThreadListStackTracesTest.java
      +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadListStackTracesTest/ThreadListStackTracesTest.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
      @@ -45,8 +45,9 @@ abstract class TestTask implements Runnable {
           }
       
           public void ensureReadyAndWaiting(Thread vt, Thread.State expState, ReentrantLock rlock) {
      +        sleep(50); // reliability: wait for a potential class loading to complete
               // wait while the thread is not ready or thread state is unexpected
      -        while (!threadReady || (vt.getState() != expState) || !rlock.hasQueuedThreads()) {
      +        while (!threadReady || (vt.getState() != expState) || !rlock.hasQueuedThread(vt)) {
                   sleep(1);
               }
           }
      @@ -125,11 +126,12 @@ public class ThreadListStackTracesTest {
               int jvmtiExpState = (expState == Thread.State.WAITING) ?
                                   JVMTI_THREAD_STATE_WAITING :
                                   JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER;
      +        Thread.State state = vt.getState();
       
      -        System.out.printf("State: expected: %s single: %x multi: %x\n",
      -                          vt.getState(), singleState, multiState);
      +        System.out.printf("State: expected: %s, vt.getState(): %s, jvmtiExpState: %x single: %x multi: %x\n",
      +                          expState, state, jvmtiExpState, singleState, multiState);
       
      -        if (vt.getState() != expState) {
      +        if (state != expState) {
                   failed("Java thread state is wrong");
               }
               if ((singleState & jvmtiExpState) == 0) {
      
      From 659b53fe33eaa531bca1951a26f357b51902311e Mon Sep 17 00:00:00 2001
      From: Alexey Semenyuk 
      Date: Sat, 10 Jan 2026 15:04:16 +0000
      Subject: [PATCH 055/204] 8374923: runtime/cds/ServiceLoaderTest.java fails
       with mismatch between cds and non-cds
      
      Reviewed-by: almatvee
      ---
       .../classes/jdk/jpackage/internal/cli/Main.java | 17 +++++++++++------
       1 file changed, 11 insertions(+), 6 deletions(-)
      
      diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java
      index 519958d9ff7..31be2bb33c5 100644
      --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java
      +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java
      @@ -64,7 +64,7 @@ public final class Main {
               }
       
               public Provider() {
      -            this(Main::loadBundlingEnvironment);
      +            this(DefaultBundlingEnvironmentLoader.INSTANCE);
               }
       
               @Override
      @@ -104,7 +104,7 @@ public final class Main {
           }
       
           static int run(PrintWriter out, PrintWriter err, String... args) {
      -        return run(Main::loadBundlingEnvironment, out, err, args);
      +        return run(DefaultBundlingEnvironmentLoader.INSTANCE, out, err, args);
           }
       
           static int run(Supplier bundlingEnvSupplier, PrintWriter out, PrintWriter err, String... args) {
      @@ -310,9 +310,14 @@ public final class Main {
               return System.getProperty("java.version");
           }
       
      -    private static CliBundlingEnvironment loadBundlingEnvironment() {
      -        return ServiceLoader.load(
      -                CliBundlingEnvironment.class,
      -                CliBundlingEnvironment.class.getClassLoader()).findFirst().orElseThrow();
      +    private enum DefaultBundlingEnvironmentLoader implements Supplier {
      +        INSTANCE;
      +
      +        @Override
      +        public CliBundlingEnvironment get() {
      +            return ServiceLoader.load(
      +                    CliBundlingEnvironment.class,
      +                    CliBundlingEnvironment.class.getClassLoader()).findFirst().orElseThrow();
      +        }
           }
       }
      
      From 336894857bfc9f610da55e6180dd7b668bf67752 Mon Sep 17 00:00:00 2001
      From: Aleksey Shipilev 
      Date: Sun, 11 Jan 2026 20:37:04 +0000
      Subject: [PATCH 056/204] 8374878: Add Atomic::compare_set
      
      Reviewed-by: kbarrett, stefank
      ---
       src/hotspot/share/gc/shared/oopStorage.cpp    |  2 +-
       src/hotspot/share/gc/shared/pretouchTask.cpp  |  2 +-
       src/hotspot/share/gc/shared/taskqueue.hpp     |  6 ++--
       .../share/gc/shared/taskqueue.inline.hpp      |  7 ++---
       src/hotspot/share/runtime/atomic.hpp          | 13 +++++++++
       .../utilities/concurrentHashTable.inline.hpp  |  4 +--
       .../share/utilities/waitBarrier_generic.cpp   |  6 ++--
       test/hotspot/gtest/runtime/test_atomic.cpp    | 29 +++++++++++++++++++
       8 files changed, 55 insertions(+), 14 deletions(-)
      
      diff --git a/src/hotspot/share/gc/shared/oopStorage.cpp b/src/hotspot/share/gc/shared/oopStorage.cpp
      index a1cc3ffa553..21e63f6fc32 100644
      --- a/src/hotspot/share/gc/shared/oopStorage.cpp
      +++ b/src/hotspot/share/gc/shared/oopStorage.cpp
      @@ -700,7 +700,7 @@ void OopStorage::Block::release_entries(uintx releasing, OopStorage* owner) {
           // then someone else has made such a claim and the deferred update has not
           // yet been processed and will include our change, so we don't need to do
           // anything further.
      -    if (_deferred_updates_next.compare_exchange(nullptr, this) == nullptr) {
      +    if (_deferred_updates_next.compare_set(nullptr, this)) {
             // Successfully claimed.  Push, with self-loop for end-of-list.
             Block* head = owner->_deferred_updates.load_relaxed();
             while (true) {
      diff --git a/src/hotspot/share/gc/shared/pretouchTask.cpp b/src/hotspot/share/gc/shared/pretouchTask.cpp
      index c999c98ea99..58a3a2693ed 100644
      --- a/src/hotspot/share/gc/shared/pretouchTask.cpp
      +++ b/src/hotspot/share/gc/shared/pretouchTask.cpp
      @@ -56,7 +56,7 @@ void PretouchTask::work(uint worker_id) {
           char* cur_end = cur_start + MIN2(_chunk_size, pointer_delta(_end_addr, cur_start, 1));
           if (cur_start >= cur_end) {
             break;
      -    } else if (cur_start == _cur_addr.compare_exchange(cur_start, cur_end)) {
      +    } else if (_cur_addr.compare_set(cur_start, cur_end)) {
             os::pretouch_memory(cur_start, cur_end, _page_size);
           } // Else attempt to claim chunk failed, so try again.
         }
      diff --git a/src/hotspot/share/gc/shared/taskqueue.hpp b/src/hotspot/share/gc/shared/taskqueue.hpp
      index 3a751852ab6..4334773a4e9 100644
      --- a/src/hotspot/share/gc/shared/taskqueue.hpp
      +++ b/src/hotspot/share/gc/shared/taskqueue.hpp
      @@ -183,8 +183,8 @@ protected:
           _age.store_relaxed(new_age);
         }
       
      -  Age cmpxchg_age(Age old_age, Age new_age) {
      -    return _age.compare_exchange(old_age, new_age);
      +  bool par_set_age(Age old_age, Age new_age) {
      +    return _age.compare_set(old_age, new_age);
         }
       
         idx_t age_top_relaxed() const {
      @@ -345,7 +345,7 @@ protected:
       
         using TaskQueueSuper::age_relaxed;
         using TaskQueueSuper::set_age_relaxed;
      -  using TaskQueueSuper::cmpxchg_age;
      +  using TaskQueueSuper::par_set_age;
         using TaskQueueSuper::age_top_relaxed;
       
         using TaskQueueSuper::increment_index;
      diff --git a/src/hotspot/share/gc/shared/taskqueue.inline.hpp b/src/hotspot/share/gc/shared/taskqueue.inline.hpp
      index f115d94740b..55851495a5f 100644
      --- a/src/hotspot/share/gc/shared/taskqueue.inline.hpp
      +++ b/src/hotspot/share/gc/shared/taskqueue.inline.hpp
      @@ -170,8 +170,7 @@ bool GenericTaskQueue::pop_local_slow(uint localBot, Age oldAge) {
         if (localBot == oldAge.top()) {
           // No competing pop_global has yet incremented "top"; we'll try to
           // install new_age, thus claiming the element.
      -    Age tempAge = cmpxchg_age(oldAge, newAge);
      -    if (tempAge == oldAge) {
      +    if (par_set_age(oldAge, newAge)) {
             // We win.
             assert_not_underflow(localBot, age_top_relaxed());
             TASKQUEUE_STATS_ONLY(stats.record_pop_slow());
      @@ -283,12 +282,12 @@ typename GenericTaskQueue::PopResult GenericTaskQueue::pop_g
         idx_t new_top = increment_index(oldAge.top());
         idx_t new_tag = oldAge.tag() + ((new_top == 0) ? 1 : 0);
         Age newAge(new_top, new_tag);
      -  Age resAge = cmpxchg_age(oldAge, newAge);
      +  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 resAge == oldAge ? PopResult::Success : PopResult::Contended;
      +  return result ? PopResult::Success : PopResult::Contended;
       }
       
       inline int randomParkAndMiller(int *seed0) {
      diff --git a/src/hotspot/share/runtime/atomic.hpp b/src/hotspot/share/runtime/atomic.hpp
      index 02e9f82cfb6..f708e9c18ca 100644
      --- a/src/hotspot/share/runtime/atomic.hpp
      +++ b/src/hotspot/share/runtime/atomic.hpp
      @@ -75,6 +75,7 @@
       //     v.release_store(x) -> void
       //     v.release_store_fence(x) -> void
       //     v.compare_exchange(x, y [, o]) -> T
      +//     v.compare_set(x, y [, o]) -> bool
       //     v.exchange(x [, o]) -> T
       //
       // (2) All atomic types are default constructible.
      @@ -267,6 +268,11 @@ public:
           return AtomicAccess::cmpxchg(value_ptr(), compare_value, new_value, order);
         }
       
      +  bool compare_set(T compare_value, T new_value,
      +                   atomic_memory_order order = memory_order_conservative) {
      +    return compare_exchange(compare_value, new_value, order) == compare_value;
      +  }
      +
         T exchange(T new_value,
                    atomic_memory_order order = memory_order_conservative) {
           return AtomicAccess::xchg(this->value_ptr(), new_value, order);
      @@ -479,6 +485,13 @@ public:
                                                  order));
         }
       
      +  bool compare_set(T compare_value, T new_value,
      +                   atomic_memory_order order = memory_order_conservative) {
      +    return _value.compare_set(decay(compare_value),
      +                              decay(new_value),
      +                              order);
      +  }
      +
         T exchange(T new_value, atomic_memory_order order = memory_order_conservative) {
           return recover(_value.exchange(decay(new_value), order));
         }
      diff --git a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp
      index 31b451ba38a..62d2dd29dab 100644
      --- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp
      +++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp
      @@ -157,7 +157,7 @@ inline bool ConcurrentHashTable::
         if (is_locked()) {
           return false;
         }
      -  if (_first.compare_exchange(expect, node) == expect) {
      +  if (_first.compare_set(expect, node)) {
           return true;
         }
         return false;
      @@ -172,7 +172,7 @@ inline bool ConcurrentHashTable::
         }
         // We will expect a clean first pointer.
         Node* tmp = first();
      -  if (_first.compare_exchange(tmp, set_state(tmp, STATE_LOCK_BIT)) == tmp) {
      +  if (_first.compare_set(tmp, set_state(tmp, STATE_LOCK_BIT))) {
           return true;
         }
         return false;
      diff --git a/src/hotspot/share/utilities/waitBarrier_generic.cpp b/src/hotspot/share/utilities/waitBarrier_generic.cpp
      index b268b10c757..0892feab699 100644
      --- a/src/hotspot/share/utilities/waitBarrier_generic.cpp
      +++ b/src/hotspot/share/utilities/waitBarrier_generic.cpp
      @@ -181,7 +181,7 @@ void GenericWaitBarrier::Cell::disarm(int32_t expected_tag) {
                  tag, waiters);
       
           int64_t new_state = encode(0, waiters);
      -    if (_state.compare_exchange(state, new_state) == state) {
      +    if (_state.compare_set(state, new_state)) {
             // Successfully disarmed.
             break;
           }
      @@ -218,7 +218,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) {
                  tag, waiters);
       
           int64_t new_state = encode(tag, waiters + 1);
      -    if (_state.compare_exchange(state, new_state) == state) {
      +    if (_state.compare_set(state, new_state)) {
             // Success! Proceed to wait.
             break;
           }
      @@ -247,7 +247,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) {
                  tag, waiters);
       
           int64_t new_state = encode(tag, waiters - 1);
      -    if (_state.compare_exchange(state, new_state) == state) {
      +    if (_state.compare_set(state, new_state)) {
             // Success!
             break;
           }
      diff --git a/test/hotspot/gtest/runtime/test_atomic.cpp b/test/hotspot/gtest/runtime/test_atomic.cpp
      index b37c14d41a7..753dde0ca57 100644
      --- a/test/hotspot/gtest/runtime/test_atomic.cpp
      +++ b/test/hotspot/gtest/runtime/test_atomic.cpp
      @@ -162,6 +162,35 @@ TEST_VM(AtomicIntegerTest, cmpxchg_int64) {
         Support().test();
       }
       
      +template
      +struct AtomicIntegerCmpsetTestSupport {
      +  Atomic _test_value;
      +
      +  AtomicIntegerCmpsetTestSupport() : _test_value{} {}
      +
      +  void test() {
      +    T zero = 0;
      +    T five = 5;
      +    T ten = 10;
      +    _test_value.store_relaxed(zero);
      +    EXPECT_FALSE(_test_value.compare_set(five, ten));
      +    EXPECT_EQ(zero, _test_value.load_relaxed());
      +    EXPECT_TRUE(_test_value.compare_set(zero, ten));
      +    EXPECT_EQ(ten, _test_value.load_relaxed());
      +  }
      +};
      +
      +TEST_VM(AtomicIntegerTest, cmpset_int32) {
      +  using Support = AtomicIntegerCmpsetTestSupport;
      +  Support().test();
      +}
      +
      +TEST_VM(AtomicIntegerTest, cmpset_int64) {
      +  // Check if 64-bit atomics are available on the machine.
      +  using Support = AtomicIntegerCmpsetTestSupport;
      +  Support().test();
      +}
      +
       struct AtomicXchgAndCmpxchg1ByteStressSupport {
         char _default_val;
         int  _base;
      
      From 669977f7c4b58ab4901a340906262ab907b3ffb6 Mon Sep 17 00:00:00 2001
      From: Trevor Bond 
      Date: Mon, 12 Jan 2026 07:05:52 +0000
      Subject: [PATCH 057/204] 8341272: Factory to create wide iinc instruction with
       small arguments
      
      Reviewed-by: liach, asotona
      ---
       .../instruction/IncrementInstruction.java     | 35 ++++++++++++++++++-
       .../classfile/impl/AbstractInstruction.java   |  6 ++--
       .../classfile/impl/BytecodeHelpers.java       |  7 ++++
       .../classfile/InstructionValidationTest.java  | 15 ++++++++
       4 files changed, 58 insertions(+), 5 deletions(-)
      
      diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/IncrementInstruction.java b/src/java.base/share/classes/java/lang/classfile/instruction/IncrementInstruction.java
      index 352f83f529c..4054a06c3e2 100644
      --- a/src/java.base/share/classes/java/lang/classfile/instruction/IncrementInstruction.java
      +++ b/src/java.base/share/classes/java/lang/classfile/instruction/IncrementInstruction.java
      @@ -31,6 +31,8 @@ import java.lang.classfile.Instruction;
       import java.lang.classfile.Opcode;
       
       import jdk.internal.classfile.impl.AbstractInstruction;
      +import jdk.internal.classfile.impl.BytecodeHelpers;
      +import jdk.internal.classfile.impl.Util;
       
       /**
        * Models a local variable increment instruction in the {@code code} array of a
      @@ -82,6 +84,37 @@ public sealed interface IncrementInstruction extends Instruction
            * @throws IllegalArgumentException if {@code slot} or {@code constant} is out of range
            */
           static IncrementInstruction of(int slot, int constant) {
      -        return new AbstractInstruction.UnboundIncrementInstruction(slot, constant);
      +        var opcode = BytecodeHelpers.validateAndIsWideIinc(slot, constant) ? Opcode.IINC_W: Opcode.IINC;
      +        return new AbstractInstruction.UnboundIncrementInstruction(opcode, slot, constant);
      +    }
      +
      +    /**
      +     * {@return an increment instruction}
      +     * 

      + * {@code slot} must be {@link java.lang.classfile##u1 u1} and + * {@code constant} must be within {@code [-128, 127]} for + * {@link Opcode#IINC iinc}, or {@code slot} must be + * {@link java.lang.classfile##u2 u2} and {@code constant} must be + * within {@code [-32768, 32767]} for {@link Opcode#IINC_W wide iinc}. + * + * @apiNote + * The explicit {@code op} argument allows creating {@code wide} or + * regular increment instructions when {@code slot} and + * {@code constant} can be encoded with more optimized + * increment instructions. + * + * @param op the opcode for the specific type of increment instruction, + * which must be of kind {@link Opcode.Kind#INCREMENT} + * @param slot the local variable slot to increment + * @param constant the increment constant + * @throws IllegalArgumentException if the opcode kind is not + * {@link Opcode.Kind#INCREMENT} or {@code slot} or + * {@code constant} is out of range + * @since 27 + */ + static IncrementInstruction of(Opcode op, int slot, int constant) { + Util.checkKind(op, Opcode.Kind.INCREMENT); + BytecodeHelpers.validateIncrement(op, slot, constant); + return new AbstractInstruction.UnboundIncrementInstruction(op, slot, constant); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java index 2197ac81e37..177103917de 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java @@ -832,10 +832,8 @@ public abstract sealed class AbstractInstruction final int slot; final int constant; - public UnboundIncrementInstruction(int slot, int constant) { - super(BytecodeHelpers.validateAndIsWideIinc(slot, constant) - ? Opcode.IINC_W - : Opcode.IINC); + public UnboundIncrementInstruction(Opcode op, int slot, int constant) { + super(op); this.slot = slot; this.constant = constant; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java index a51728eb3e9..50c6f8b131c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java @@ -450,6 +450,13 @@ public class BytecodeHelpers { return ret; } + public static void validateIncrement(Opcode opcode, int slot, int constant) { + if (validateAndIsWideIinc(slot, constant) && opcode != Opcode.IINC_W) { + throw new IllegalArgumentException( + "IINC: operands require wide encoding for %s".formatted(opcode)); + } + } + public static void validateRet(Opcode opcode, int slot) { if (opcode == Opcode.RET && (slot & ~0xFF) == 0 || opcode == Opcode.RET_W && (slot & ~0xFFFF) == 0) diff --git a/test/jdk/jdk/classfile/InstructionValidationTest.java b/test/jdk/jdk/classfile/InstructionValidationTest.java index f02c7a9c78c..190cbffca55 100644 --- a/test/jdk/jdk/classfile/InstructionValidationTest.java +++ b/test/jdk/jdk/classfile/InstructionValidationTest.java @@ -195,6 +195,7 @@ class InstructionValidationTest { ensureFailFast(i, cob -> cob.iinc(i, 1)); } check(fails, () -> IncrementInstruction.of(i, 1)); + check(fails, () -> IncrementInstruction.of(IINC_W, i, 1)); check(fails, () -> DiscontinuedInstruction.RetInstruction.of(i)); check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET_W, i)); check(fails, () -> LocalVariable.of(i, "test", CD_Object, dummyLabel, dummyLabel)); @@ -208,6 +209,7 @@ class InstructionValidationTest { check(fails, () -> LoadInstruction.of(u1Op, i)); for (var u1Op : List.of(ASTORE, ISTORE, LSTORE, FSTORE, DSTORE)) check(fails, () -> StoreInstruction.of(u1Op, i)); + check(fails, () -> IncrementInstruction.of(IINC, i, 1)); check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET, i)); } @@ -250,6 +252,13 @@ class InstructionValidationTest { IncrementInstruction.of(0, 2); IncrementInstruction.of(0, Short.MAX_VALUE); IncrementInstruction.of(0, Short.MIN_VALUE); + IncrementInstruction.of(IINC, 0, 2); + IncrementInstruction.of(IINC, 0, Byte.MIN_VALUE); + IncrementInstruction.of(IINC, 0, Byte.MAX_VALUE); + IncrementInstruction.of(IINC_W, 0, 2); + IncrementInstruction.of(IINC_W, 0, Short.MIN_VALUE); + IncrementInstruction.of(IINC_W, 0, Short.MAX_VALUE); + for (int i : new int[] {Short.MIN_VALUE - 1, Short.MAX_VALUE + 1}) { assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(0, i)); TestUtil.runCodeHandler(cob -> { @@ -257,6 +266,12 @@ class InstructionValidationTest { cob.return_(); }); } + for (int i : new int[] {Byte.MIN_VALUE - 1, Byte.MAX_VALUE + 1}) { + assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(IINC, 0, i)); + } + for (int i : new int[] {Short.MIN_VALUE - 1, Short.MAX_VALUE + 1}) { + assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(IINC_W, 0, i)); + } } @Test From 7cf7f01fb339bf3c5b81d946be8afa71ec267e42 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Mon, 12 Jan 2026 07:46:25 +0000 Subject: [PATCH 058/204] 8374875: Improve perfMemory warning about 'Insufficient space for shared memory file' Reviewed-by: lucy, mdoerr, clanger --- src/hotspot/os/posix/perfMemory_posix.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp index 39bfc72a486..08a19270943 100644 --- a/src/hotspot/os/posix/perfMemory_posix.cpp +++ b/src/hotspot/os/posix/perfMemory_posix.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2021 SAP SE. All rights reserved. + * Copyright (c) 2001, 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 @@ -946,7 +946,7 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size if (result == -1 ) break; if (!os::write(fd, &zero_int, 1)) { if (errno == ENOSPC) { - warning("Insufficient space for shared memory file:\n %s\nTry using the -Djava.io.tmpdir= option to select an alternate temp location.\n", filename); + warning("Insufficient space for shared memory file: %s/%s\n", dirname, filename); } result = OS_ERR; break; From 49040462f3d2761435cded1bd8898d0c6b16fc02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Maillard?= Date: Mon, 12 Jan 2026 07:59:37 +0000 Subject: [PATCH 059/204] 8372302: C2: IGVN verification fails because ModXNode::Ideal creates unused intermediate nodes Reviewed-by: epeter, qamai --- src/hotspot/share/opto/divnode.cpp | 17 +++--- .../igvn/TestModIdealCreatesUselessNode.java | 56 +++++++++++++++++++ 2 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/igvn/TestModIdealCreatesUselessNode.java diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index db4fedbba3b..ed72d8a11cf 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -1112,8 +1112,6 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) { if( !ti->is_con() ) return nullptr; jint con = ti->get_con(); - Node *hook = new Node(1); - // First, special check for modulo 2^k-1 if( con >= 0 && con < max_jint && is_power_of_2(con+1) ) { uint k = exact_log2(con+1); // Extract k @@ -1129,7 +1127,9 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) { Node *x = in(1); // Value being mod'd Node *divisor = in(2); // Also is mask - hook->init_req(0, x); // Add a use to x to prevent him from dying + // Add a use to x to prevent it from dying + Node* hook = new Node(1); + hook->init_req(0, x); // Generate code to reduce X rapidly to nearly 2^k-1. for( int i = 0; i < trip_count; i++ ) { Node *xl = phase->transform( new AndINode(x,divisor) ); @@ -1185,6 +1185,7 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) { } // Save in(1) so that it cannot be changed or deleted + Node* hook = new Node(1); hook->init_req(0, in(1)); // Divide using the transform from DivI to MulL @@ -1407,8 +1408,6 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) { if( !tl->is_con() ) return nullptr; jlong con = tl->get_con(); - Node *hook = new Node(1); - // Expand mod if(con >= 0 && con < max_jlong && is_power_of_2(con + 1)) { uint k = log2i_exact(con + 1); // Extract k @@ -1426,13 +1425,15 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node *x = in(1); // Value being mod'd Node *divisor = in(2); // Also is mask - hook->init_req(0, x); // Add a use to x to prevent him from dying + // Add a use to x to prevent it from dying + Node* hook = new Node(1); + hook->init_req(0, x); // Generate code to reduce X rapidly to nearly 2^k-1. for( int i = 0; i < trip_count; i++ ) { Node *xl = phase->transform( new AndLNode(x,divisor) ); Node *xh = phase->transform( new RShiftLNode(x,phase->intcon(k)) ); // Must be signed x = phase->transform( new AddLNode(xh,xl) ); - hook->set_req(0, x); // Add a use to x to prevent him from dying + hook->set_req(0, x); // Add a use to x to prevent it from dying } // Generate sign-fixup code. Was original value positive? @@ -1482,6 +1483,8 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) { } // Save in(1) so that it cannot be changed or deleted + // Add a use to x to prevent him from dying + Node* hook = new Node(1); hook->init_req(0, in(1)); // Divide using the transform from DivL to MulL diff --git a/test/hotspot/jtreg/compiler/c2/igvn/TestModIdealCreatesUselessNode.java b/test/hotspot/jtreg/compiler/c2/igvn/TestModIdealCreatesUselessNode.java new file mode 100644 index 00000000000..4d70ee92a92 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/igvn/TestModIdealCreatesUselessNode.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.c2.igvn; + +/* + * @test + * @bug 8372302 + * @summary ModINode::Ideal and ModLNode::Ideal use an intermediate "hook" node + * to keep stuff alive between phase->transform(...) calls. In some cases, + * this node is not properly deleted before returning, causing failure + * in the verification because the node count has changed. This test + * ensures that the intermediate node gets destroyed before returning. + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions + * -Xcomp -XX:-TieredCompilation + * -XX:CompileCommand=compileonly,${test.main.class}::test* + * -XX:VerifyIterativeGVN=1110 + * ${test.main.class} + * @run main ${test.main.class} + * + */ + +public class TestModIdealCreatesUselessNode { + static int test0(int x) { + return x % Integer.MIN_VALUE; + } + + static long test1(long x) { + return x % Long.MIN_VALUE; + } + + public static void main(String[] args) { + test0(0); + test1(0L); + } +} From 133a023e8e1ec1c555265a92eb0fcb4965f0b162 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Mon, 12 Jan 2026 08:04:14 +0000 Subject: [PATCH 060/204] 8374471: Check bin and lib folder of JDK image for unwanted files Reviewed-by: erikj, clanger --- test/jdk/build/CheckFiles.java | 152 +++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 test/jdk/build/CheckFiles.java diff --git a/test/jdk/build/CheckFiles.java b/test/jdk/build/CheckFiles.java new file mode 100644 index 00000000000..412e66ebf01 --- /dev/null +++ b/test/jdk/build/CheckFiles.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2026 SAP SE. 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. + */ + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; + +import jdk.test.lib.Platform; + +/* + * @test + * @summary Check for unwanted file (types/extensions) in the jdk image + * @library /test/lib + * @requires !vm.debug + * @run main CheckFiles + */ +public class CheckFiles { + + // Set this property on command line to scan an alternate dir or file: + // JTREG=JAVA_OPTIONS=-Djdk.test.build.CheckFiles.dir=/path/to/dir + public static final String DIR_PROPERTY = "jdk.test.build.CheckFiles.dir"; + + public static void main(String[] args) throws Exception { + String jdkPathString = System.getProperty("test.jdk"); + Path jdkHome = Paths.get(jdkPathString); + + Path mainDirToScan = jdkHome; + String overrideDir = System.getProperty(DIR_PROPERTY); + if (overrideDir != null) { + mainDirToScan = Paths.get(overrideDir); + } + + System.out.println("Main directory to scan:" + mainDirToScan); + Path binDir = mainDirToScan.resolve("bin"); + Path libDir = mainDirToScan.resolve("lib"); + + System.out.println("Bin directory to scan:" + binDir); + ArrayList allowedEndingsBinDir = new ArrayList<>(); + // UNIX - no extensions are allowed; Windows : .dll, .exe, .pdb, .jsa + if (Platform.isWindows()) { + allowedEndingsBinDir.add(".dll"); + allowedEndingsBinDir.add(".exe"); + allowedEndingsBinDir.add(".pdb"); + allowedEndingsBinDir.add(".jsa"); + } + boolean binDirRes = scanFiles(binDir, allowedEndingsBinDir); + + System.out.println("Lib directory to scan:" + libDir); + ArrayList allowedEndingsLibDir = new ArrayList<>(); + allowedEndingsLibDir.add(".jfc"); // jfr config files + allowedEndingsLibDir.add("cacerts"); + allowedEndingsLibDir.add("blocked.certs"); + allowedEndingsLibDir.add("public_suffix_list.dat"); + allowedEndingsLibDir.add("classlist"); + allowedEndingsLibDir.add("fontconfig.bfc"); + allowedEndingsLibDir.add("fontconfig.properties.src"); + allowedEndingsLibDir.add("ct.sym"); + allowedEndingsLibDir.add("jrt-fs.jar"); + allowedEndingsLibDir.add("jvm.cfg"); + allowedEndingsLibDir.add("modules"); + allowedEndingsLibDir.add("psfontj2d.properties"); + allowedEndingsLibDir.add("psfont.properties.ja"); + allowedEndingsLibDir.add("src.zip"); + allowedEndingsLibDir.add("tzdb.dat"); + if (Platform.isWindows()) { + allowedEndingsLibDir.add(".lib"); + allowedEndingsLibDir.add("tzmappings"); + } else { + allowedEndingsLibDir.add("jexec"); + allowedEndingsLibDir.add("jspawnhelper"); + allowedEndingsLibDir.add(".jsa"); + if (Platform.isOSX()) { + allowedEndingsLibDir.add("shaders.metallib"); + allowedEndingsLibDir.add(".dylib"); + } else { + allowedEndingsLibDir.add(".so"); + } + if (Platform.isAix()) { + allowedEndingsLibDir.add("tzmappings"); + } + } + boolean libDirRes = scanFiles(libDir, allowedEndingsLibDir); + + if (!binDirRes) { + throw new Error("bin dir scan failed"); + } + + if (!libDirRes) { + throw new Error("lib dir scan failed"); + } + } + + private static boolean scanFiles(Path root, ArrayList allowedEndings) throws IOException { + AtomicBoolean badFileFound = new AtomicBoolean(false); + + Files.walkFileTree(root, new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + String fullFileName = file.toString(); + String fileName = file.getFileName().toString(); + System.out.println(" visiting file:" + fullFileName); + checkFile(fileName, allowedEndings); + return super.visitFile(file, attrs); + } + + private void checkFile(String name, ArrayList allowedEndings) { + if (allowedEndings.isEmpty()) { // no file extensions allowed + int lastDot = name.lastIndexOf('.'); + if (lastDot > 0) { + System.out.println(" --> ERROR this file is not allowed:" + name); + badFileFound.set(true); + } + } else { + boolean allowed = allowedEndings.stream().anyMatch(name::endsWith); + if (! allowed) { + System.out.println(" --> ERROR this file is not allowed:" + name); + badFileFound.set(true); + } + } + } + }); + + return !badFileFound.get(); + } +} From fb13abef44d535ebc4535921fd4eb0f285030465 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 12 Jan 2026 08:26:10 +0000 Subject: [PATCH 061/204] 8374743: G1 starts a concurrent mark when allocating humongous objects during initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Erik Österlund Reviewed-by: eosterlund, iwalulya, sjohanss, shade --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 061241c24e2..2ad5a26d5e6 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.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 @@ -686,7 +686,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { // the check before we do the actual allocation. The reason for doing it // before the allocation is that we avoid having to keep track of the newly // allocated memory while we do a GC. - if (policy()->need_to_start_conc_mark("concurrent humongous allocation", + // Only try that if we can actually perform a GC. + if (is_init_completed() && policy()->need_to_start_conc_mark("concurrent humongous allocation", word_size)) { try_collect(word_size, GCCause::_g1_humongous_allocation, collection_counters(this)); } From d0aae04d61c90698ab5a01b4389dc6932de63cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6len?= Date: Mon, 12 Jan 2026 11:01:12 +0000 Subject: [PATCH 062/204] 8325108: POSIX map_memory_to_file calls release_memory unnecessarily Reviewed-by: dholmes, coleenp --- src/hotspot/os/posix/os_posix.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp index 4cae7d359e4..5412e2bc92d 100644 --- a/src/hotspot/os/posix/os_posix.cpp +++ b/src/hotspot/os/posix/os_posix.cpp @@ -458,12 +458,10 @@ char* os::map_memory_to_file(char* base, size_t size, int fd) { warning("Failed mmap to file. (%s)", os::strerror(errno)); return nullptr; } - if (base != nullptr && addr != base) { - if (!os::release_memory(addr, size)) { - warning("Could not release memory on unsuccessful file mapping"); - } - return nullptr; - } + + // The requested address should be the same as the returned address when using MAP_FIXED + // as per POSIX. + assert(base == nullptr || addr == base, "base should equal addr when using MAP_FIXED"); return addr; } From 2fbe47559e9ba45306bd08c3636647f865a75abd Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 12 Jan 2026 11:18:28 +0000 Subject: [PATCH 063/204] 8374785: Template Library: need to tag Float16.copySign as having non-deterministic result because of multiple NaNs with different sign bits Reviewed-by: thartmann, qamai --- .../compiler/lib/template_framework/library/Operations.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 cb0e94c9fe8..1fe05cc2b6c 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -274,6 +274,7 @@ public final class Operations { ops.add(Expression.make(BOOLEANS, "Boolean.logicalXor(", BOOLEANS, ", ", BOOLEANS, ")")); // TODO: Math and other classes. + // Note: Math.copySign is non-deterministic because of NaN having encoding with sign bit set and unset. // Make sure the list is not modifiable. return List.copyOf(ops); @@ -294,7 +295,8 @@ public final class Operations { ops.add(Expression.make(INTS, "Float16.compare(", FLOAT16, ",", FLOAT16, ")")); addComparisonOperations(ops, "Float16.compare", FLOAT16); ops.add(Expression.make(INTS, "(", FLOAT16, ").compareTo(", FLOAT16, ")")); - ops.add(Expression.make(FLOAT16, "Float16.copySign(", FLOAT16, ",", FLOAT16, ")")); + // Note: There are NaN encodings with bit set or unset. + ops.add(Expression.make(FLOAT16, "Float16.copySign(", FLOAT16, ",", FLOAT16, ")", WITH_NONDETERMINISTIC_RESULT)); ops.add(Expression.make(FLOAT16, "Float16.divide(", FLOAT16, ",", FLOAT16, ")")); ops.add(Expression.make(BOOLEANS, "", FLOAT16, ".equals(", FLOAT16, ")")); // Note: there are multiple NaN values with different bit representations. From 556bddfd9439d1bad698ab5134317ce263a36b04 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Mon, 12 Jan 2026 11:30:43 +0000 Subject: [PATCH 064/204] 8372321: TestBackToBackSensitive fails intermittently after JDK-8365972 Reviewed-by: mgronlun --- .../runtime/TestBackToBackSensitive.java | 59 +++++++++++-------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/test/jdk/jdk/jfr/event/runtime/TestBackToBackSensitive.java b/test/jdk/jdk/jfr/event/runtime/TestBackToBackSensitive.java index 147caee82ea..fbef91e73aa 100644 --- a/test/jdk/jdk/jfr/event/runtime/TestBackToBackSensitive.java +++ b/test/jdk/jdk/jfr/event/runtime/TestBackToBackSensitive.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 @@ -34,8 +34,9 @@ import jdk.jfr.Configuration; import jdk.jfr.Event; import jdk.jfr.Recording; import jdk.jfr.StackTrace; +import jdk.jfr.consumer.EventStream; import jdk.jfr.consumer.RecordedClassLoader; -import jdk.jfr.consumer.RecordingStream; +import jdk.test.lib.jfr.TestClassLoader; /** * @test @@ -52,28 +53,17 @@ public class TestBackToBackSensitive { static class FillEvent extends Event { String message; } + public static Object OBJECT; public static void main(String... arg) throws Exception { - Set threadDumps = Collections.synchronizedSet(new LinkedHashSet<>()); - Set classLoaderStatistics = Collections.synchronizedSet(new LinkedHashSet<>()); - Set physicalMemory = Collections.synchronizedSet(new LinkedHashSet<>()); - + TestClassLoader loader = new TestClassLoader(); + Class clazz = loader.loadClass(TestBackToBackSensitive.class.getName()); + String classLoaderName = loader.getClass().getName(); + OBJECT = clazz.getDeclaredConstructor().newInstance(); Configuration configuration = Configuration.getConfiguration("default"); - try (RecordingStream r1 = new RecordingStream(configuration)) { - r1.setMaxSize(Long.MAX_VALUE); - r1.onEvent("jdk.ThreadDump", e -> threadDumps.add(e.getStartTime())); - r1.onEvent("jdk.ClassLoaderStatistics", e -> { - RecordedClassLoader cl = e.getValue("classLoader"); - if (cl != null) { - if (cl.getType().getName().contains("PlatformClassLoader")) { - classLoaderStatistics.add(e.getStartTime()); - System.out.println("Class loader" + e); - } - } - }); - r1.onEvent("jdk.PhysicalMemory", e -> physicalMemory.add(e.getStartTime())); + try (Recording r1 = new Recording(configuration)) { // Start chunk 1 - r1.startAsync(); + r1.start(); try (Recording r2 = new Recording()) { // Start chunk 2 r2.start(); @@ -86,6 +76,25 @@ public class TestBackToBackSensitive { f.commit(); } r1.stop(); + Path file = Path.of("file.jfr"); + r1.dump(file); + Set threadDumps = new LinkedHashSet<>(); + Set classLoaderStatistics = new LinkedHashSet<>(); + Set physicalMemory = new LinkedHashSet<>(); + try (EventStream es = EventStream.openFile(file)) { + es.onEvent("jdk.ThreadDump", e -> threadDumps.add(e.getStartTime())); + es.onEvent("jdk.ClassLoaderStatistics", e -> { + RecordedClassLoader cl = e.getValue("classLoader"); + if (cl != null) { + if (cl.getType().getName().equals(classLoaderName)) { + classLoaderStatistics.add(e.getStartTime()); + System.out.println("Class loader" + e); + } + } + }); + es.onEvent("jdk.PhysicalMemory", e -> physicalMemory.add(e.getStartTime())); + es.start(); + } long chunkFiles = filesInRepository(); System.out.println("Number of chunk files: " + chunkFiles); // When jdk.PhysicalMemory is expected to be emitted: @@ -93,15 +102,15 @@ public class TestBackToBackSensitive { // Chunk 2: begin, end // Chunk 3: begin, end // Chunk 4: begin, end - assertCount(r1, "jdk.PhysicalMemory", physicalMemory, 2 * chunkFiles); + assertCount("jdk.PhysicalMemory", physicalMemory, 2 * chunkFiles); // When jdk.ClassLoaderStatistics and jdk.ThreadThreadDump are expected to be // emitted: // Chunk 1: begin, end // Chunk 2: begin, end // Chunk 3: end // Chunk 4: end - assertCount(r1, "jdk.ThreadDump", threadDumps, 2 + 2 + (chunkFiles - 2)); - assertCount(r1, "jdk.ClassLoaderStatistics", classLoaderStatistics, 2 + 2 + (chunkFiles - 2)); + assertCount("jdk.ThreadDump", threadDumps, 2 + 2 + (chunkFiles - 2)); + assertCount("jdk.ClassLoaderStatistics", classLoaderStatistics, 2 + 2 + (chunkFiles - 2)); } } @@ -110,15 +119,13 @@ public class TestBackToBackSensitive { return Files.list(repository).filter(p -> p.toString().endsWith(".jfr")).count(); } - private static void assertCount(RecordingStream stream, String eventName, Set timestamps, long expected) throws Exception { + private static void assertCount(String eventName, Set timestamps, long expected) throws Exception { System.out.println("Timestamps for " + eventName + ":"); for (Instant timestamp : timestamps) { System.out.println(timestamp); } int count = timestamps.size(); if (count != expected) { - System.out.println("Dumping failure file."); - stream.dump(Path.of("failure.jfr")); throw new Exception("Expected " + expected + " timestamps for event " + eventName + ", but got " + count); } } From d433ce52360994be5a88a0bcbf39cbb741b435ec Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 12 Jan 2026 15:22:42 +0000 Subject: [PATCH 065/204] 8369564: Provide a MemorySegment API to read strings with known lengths Co-authored-by: Per Minborg Reviewed-by: jvernee, mcimadamore --- .../share/classes/java/lang/String.java | 15 +- .../share/classes/java/lang/System.java | 8 +- .../java/lang/foreign/MemorySegment.java | 88 +++++++- .../java/lang/foreign/SegmentAllocator.java | 54 ++++- .../jdk/internal/access/JavaLangAccess.java | 4 +- .../foreign/AbstractMemorySegmentImpl.java | 17 ++ .../jdk/internal/foreign/StringSupport.java | 71 +++--- test/jdk/java/foreign/TestStringEncoding.java | 205 +++++++++++++++++- .../java/lang/foreign/FromJavaStringTest.java | 94 ++++++++ .../java/lang/foreign/ToJavaStringTest.java | 22 +- 10 files changed, 523 insertions(+), 55 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/lang/foreign/FromJavaStringTest.java diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index d3eda052740..1ac15e3a8b2 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -2045,19 +2045,26 @@ public final class String return encode(Charset.defaultCharset(), coder(), value); } - boolean bytesCompatible(Charset charset) { + boolean bytesCompatible(Charset charset, int srcIndex, int numChars) { if (isLatin1()) { if (charset == ISO_8859_1.INSTANCE) { return true; // ok, same encoding } else if (charset == UTF_8.INSTANCE || charset == US_ASCII.INSTANCE) { - return !StringCoding.hasNegatives(value, 0, value.length); // ok, if ASCII-compatible + return !StringCoding.hasNegatives(value, srcIndex, numChars); // ok, if ASCII-compatible } } return false; } - void copyToSegmentRaw(MemorySegment segment, long offset) { - MemorySegment.copy(value, 0, segment, ValueLayout.JAVA_BYTE, offset, value.length); + void copyToSegmentRaw(MemorySegment segment, long offset, int srcIndex, int srcLength) { + if (!isLatin1()) { + // This method is intended to be used together with bytesCompatible, which currently only supports + // latin1 strings. In the future, bytesCompatible could be updated to handle more cases, like + // UTF-16 strings (when the platform and charset endianness match, and the String doesn’t contain + // unpaired surrogates). If that happens, copyToSegmentRaw should also be updated. + throw new IllegalStateException("This string does not support copyToSegmentRaw"); + } + MemorySegment.copy(value, srcIndex, segment, ValueLayout.JAVA_BYTE, offset, srcLength); } /** diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index f3a57c34165..cfe09c61375 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2331,13 +2331,13 @@ public final class System { } @Override - public void copyToSegmentRaw(String string, MemorySegment segment, long offset) { - string.copyToSegmentRaw(segment, offset); + public void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength) { + string.copyToSegmentRaw(segment, offset, srcIndex, srcLength); } @Override - public boolean bytesCompatible(String string, Charset charset) { - return string.bytesCompatible(charset); + public boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars) { + return string.bytesCompatible(charset, srcIndex, numChars); } }); } diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java index 196f44d1abe..2b931a7d2e0 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -1296,12 +1296,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * over the decoding process is required. *

      * Getting a string from a segment with a known byte offset and - * known byte length can be done like so: - * {@snippet lang=java : - * byte[] bytes = new byte[length]; - * MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, length); - * return new String(bytes, charset); - * } + * known byte length can be done using {@link #getString(long, Charset, long)}. * * @param offset offset in bytes (relative to this segment address) at which this * access operation will occur @@ -1328,6 +1323,40 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { */ String getString(long offset, Charset charset); + /** + * Reads a string from this segment at the given offset, using the provided length + * and charset. + *

      + * This method always replaces malformed-input and unmappable-character + * sequences with this charset's default replacement string. The {@link + * java.nio.charset.CharsetDecoder} class should be used when more control + * over the decoding process is required. + *

      + * If the string contains any {@code '\0'} characters, they will be read as well. + * This differs from {@link #getString(long, Charset)}, which will only read up + * to the first {@code '\0'}, resulting in truncation for string data that contains + * the {@code '\0'} character. + * + * @param offset offset in bytes (relative to this segment address) at which this + * access operation will occur + * @param charset the charset used to {@linkplain Charset#newDecoder() decode} the + * string bytes + * @param byteLength length, in bytes, of the region of memory to read and decode into + * a string + * @return a Java string constructed from the bytes read from the given starting + * address up to the given length + * @throws IllegalArgumentException if the size of the string is greater than the + * largest string supported by the platform + * @throws IndexOutOfBoundsException if {@code offset < 0} + * @throws IndexOutOfBoundsException if {@code offset > byteSize() - byteLength} + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with + * this segment is not {@linkplain Scope#isAlive() alive} + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code isAccessibleBy(T) == false} + * @throws IllegalArgumentException if {@code byteLength < 0} + */ + String getString(long offset, Charset charset, long byteLength); + /** * Writes the given string into this segment at the given offset, converting it to * a null-terminated byte sequence using the {@linkplain StandardCharsets#UTF_8 UTF-8} @@ -1366,7 +1395,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * If the given string contains any {@code '\0'} characters, they will be * copied as well. This means that, depending on the method used to read * the string, such as {@link MemorySegment#getString(long)}, the string - * will appear truncated when read again. + * will appear truncated when read again. The string can be read without + * truncation using {@link #getString(long, Charset, long)}. * * @param offset offset in bytes (relative to this segment address) at which this * access operation will occur, the final address of this write @@ -2606,6 +2636,50 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { elementCount); } + /** + * Copies the byte sequence of the given string encoded using the provided charset + * to the destination segment. + *

      + * This method always replaces malformed-input and unmappable-character + * sequences with this charset's default replacement string. The {@link + * java.nio.charset.CharsetDecoder} class should be used when more control + * over the decoding process is required. + *

      + * If the given string contains any {@code '\0'} characters, they will be + * copied as well. This means that, depending on the method used to read + * the string, such as {@link MemorySegment#getString(long)}, the string + * will appear truncated when read again. The string can be read without + * truncation using {@link #getString(long, Charset, long)}. + * + * @param src the Java string to be written into the destination segment + * @param dstEncoding the charset used to {@linkplain Charset#newEncoder() encode} + * the string bytes. + * @param srcIndex the starting character index of the source string + * @param dst the destination segment + * @param dstOffset the starting offset, in bytes, of the destination segment + * @param numChars the number of characters to be copied + * @throws IllegalStateException if the {@linkplain #scope() scope} associated with + * {@code dst} is not {@linkplain Scope#isAlive() alive} + * @throws WrongThreadException if this method is called from a thread {@code T}, + * such that {@code dst.isAccessibleBy(T) == false} + * @throws IndexOutOfBoundsException if either {@code srcIndex}, {@code numChars}, or {@code dstOffset} + * are {@code < 0} + * @throws IndexOutOfBoundsException if {@code srcIndex > src.length() - numChars} + * @throws IllegalArgumentException if {@code dst} is {@linkplain #isReadOnly() read-only} + * @throws IndexOutOfBoundsException if {@code dstOffset > dstSegment.byteSize() - B} where {@code B} is the size, + * in bytes, of the substring of {@code src} encoded using the given charset + * @return the number of copied bytes. + */ + @ForceInline + static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars) { + Objects.requireNonNull(src); + Objects.requireNonNull(dstEncoding); + Objects.requireNonNull(dst); + Objects.checkFromIndexSize(srcIndex, numChars, src.length()); + + return AbstractMemorySegmentImpl.copy(src, dstEncoding, srcIndex, dst, dstOffset, numChars); + } + /** * Finds and returns the relative offset, in bytes, of the first mismatch between the * source and the destination segments. More specifically, the bytes at offset diff --git a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java index 1297406dcf1..5b213af544f 100644 --- a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java +++ b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java @@ -111,7 +111,8 @@ public interface SegmentAllocator { * If the given string contains any {@code '\0'} characters, they will be * copied as well. This means that, depending on the method used to read * the string, such as {@link MemorySegment#getString(long)}, the string - * will appear truncated when read again. + * will appear truncated when read again. The string can be read without + * truncation using {@link MemorySegment#getString(long, Charset, long)}. * * @param str the Java string to be converted into a C string * @param charset the charset used to {@linkplain Charset#newEncoder() encode} the @@ -137,10 +138,10 @@ public interface SegmentAllocator { int termCharSize = StringSupport.CharsetKind.of(charset).terminatorCharSize(); MemorySegment segment; int length; - if (StringSupport.bytesCompatible(str, charset)) { + if (StringSupport.bytesCompatible(str, charset, 0, str.length())) { length = str.length(); segment = allocateNoInit((long) length + termCharSize); - StringSupport.copyToSegmentRaw(str, segment, 0); + StringSupport.copyToSegmentRaw(str, segment, 0, 0, str.length()); } else { byte[] bytes = str.getBytes(charset); length = bytes.length; @@ -153,6 +154,53 @@ public interface SegmentAllocator { return segment; } + /** + * Encodes a Java string using the provided charset and stores the resulting + * byte array into a memory segment. + *

      + * This method always replaces malformed-input and unmappable-character + * sequences with this charset's default replacement byte array. The + * {@link java.nio.charset.CharsetEncoder} class should be used when more + * control over the encoding process is required. + *

      + * If the given string contains any {@code '\0'} characters, they will be + * copied as well. This means that, depending on the method used to read + * the string, such as {@link MemorySegment#getString(long)}, the string + * will appear truncated when read again. The string can be read without + * truncation using {@link MemorySegment#getString(long, Charset, long)}. + * + * @param str the Java string to be encoded + * @param charset the charset used to {@linkplain Charset#newEncoder() encode} the + * string bytes + * @param srcIndex the starting index of the source string + * @param numChars the number of characters to be copied + * @return a new native segment containing the encoded string + * @throws IndexOutOfBoundsException if either {@code srcIndex} or {@code numChars} are {@code < 0} + * @throws IndexOutOfBoundsException if {@code srcIndex > str.length() - numChars} + * + * @implSpec The default implementation for this method copies the contents of the + * provided Java string into a new memory segment obtained by calling + * {@code this.allocate(B)}, where {@code B} is the size, in bytes, of + * the string encoded using the provided charset + * (e.g. {@code str.getBytes(charset).length}); + */ + @ForceInline + default MemorySegment allocateFrom(String str, Charset charset, int srcIndex, int numChars) { + Objects.requireNonNull(charset); + Objects.requireNonNull(str); + Objects.checkFromIndexSize(srcIndex, numChars, str.length()); + MemorySegment segment; + if (StringSupport.bytesCompatible(str, charset, srcIndex, numChars)) { + segment = allocateNoInit(numChars); + StringSupport.copyToSegmentRaw(str, segment, 0, srcIndex, numChars); + } else { + byte[] bytes = str.substring(srcIndex, srcIndex + numChars).getBytes(charset); + segment = allocateNoInit(bytes.length); + MemorySegment.copy(bytes, 0, segment, ValueLayout.JAVA_BYTE, 0, bytes.length); + } + return segment; + } + /** * {@return a new memory segment initialized with the provided byte value} *

      diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index c5c45ca3553..b2a224f5917 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -634,10 +634,10 @@ public interface JavaLangAccess { /** * Copy the string bytes to an existing segment, avoiding intermediate copies. */ - void copyToSegmentRaw(String string, MemorySegment segment, long offset); + void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength); /** * Are the string bytes compatible with the given charset? */ - boolean bytesCompatible(String string, Charset charset); + boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars); } diff --git a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java index a0c8a0a5a4f..f75d67adbbb 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -551,6 +551,13 @@ public abstract sealed class AbstractMemorySegmentImpl unsafeGetOffset() == that.unsafeGetOffset(); } + @Override + public String getString(long offset, Charset charset, long byteLength) { + Utils.checkNonNegativeArgument(byteLength, "byteLength"); + Objects.requireNonNull(charset); + return StringSupport.read(this, offset, charset, byteLength); + } + @Override public int hashCode() { return Objects.hash( @@ -702,6 +709,16 @@ public abstract sealed class AbstractMemorySegmentImpl } } + @ForceInline + public static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars) { + Objects.requireNonNull(src); + Objects.requireNonNull(dstEncoding); + Objects.requireNonNull(dst); + + AbstractMemorySegmentImpl destImpl = (AbstractMemorySegmentImpl)dst; + return StringSupport.copyBytes(src, destImpl, dstEncoding, dstOffset, srcIndex, numChars); + } + // accessors @ForceInline diff --git a/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java b/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java index 208c6d54aab..b9f528969f0 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java +++ b/src/java.base/share/classes/jdk/internal/foreign/StringSupport.java @@ -30,11 +30,14 @@ import jdk.internal.access.SharedSecrets; import jdk.internal.misc.ScopedMemoryAccess; import jdk.internal.util.Architecture; import jdk.internal.util.ArraysSupport; +import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.ForceInline; import java.lang.foreign.MemorySegment; +import java.lang.reflect.Array; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; +import java.util.Objects; import static java.lang.foreign.ValueLayout.*; @@ -58,6 +61,27 @@ public final class StringSupport { }; } + @ForceInline + public static String read(AbstractMemorySegmentImpl segment, long offset, Charset charset, long length) { + return readBytes(segment, offset, charset, length); + } + + @ForceInline + public static String readBytes(AbstractMemorySegmentImpl segment, long offset, Charset charset, long length) { + if (length > Integer.MAX_VALUE) { + throw new IllegalArgumentException("Required length exceeds implementation limit"); + } + final int lengthBytes = (int) length; + final byte[] bytes = new byte[lengthBytes]; + MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, lengthBytes); + try { + return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset); + } catch (CharacterCodingException _) { + // use replacement characters for malformed input + return new String(bytes, charset); + } + } + @ForceInline public static void write(AbstractMemorySegmentImpl segment, long offset, Charset charset, String string) { switch (CharsetKind.of(charset)) { @@ -70,14 +94,7 @@ public final class StringSupport { @ForceInline private static String readByte(AbstractMemorySegmentImpl segment, long offset, Charset charset) { final int len = strlenByte(segment, offset, segment.byteSize()); - final byte[] bytes = new byte[len]; - MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len); - try { - return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset); - } catch (CharacterCodingException _) { - // use replacement characters for malformed input - return new String(bytes, charset); - } + return readBytes(segment, offset, charset, len); } @ForceInline @@ -89,14 +106,7 @@ public final class StringSupport { @ForceInline private static String readShort(AbstractMemorySegmentImpl segment, long offset, Charset charset) { int len = strlenShort(segment, offset, segment.byteSize()); - byte[] bytes = new byte[len]; - MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len); - try { - return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset); - } catch (CharacterCodingException _) { - // use replacement characters for malformed input - return new String(bytes, charset); - } + return readBytes(segment, offset, charset, len); } @ForceInline @@ -108,14 +118,7 @@ public final class StringSupport { @ForceInline private static String readInt(AbstractMemorySegmentImpl segment, long offset, Charset charset) { int len = strlenInt(segment, offset, segment.byteSize()); - byte[] bytes = new byte[len]; - MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len); - try { - return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset); - } catch (CharacterCodingException _) { - // use replacement characters for malformed input - return new String(bytes, charset); - } + return readBytes(segment, offset, charset, len); } @ForceInline @@ -345,22 +348,26 @@ public final class StringSupport { } } - public static boolean bytesCompatible(String string, Charset charset) { - return JAVA_LANG_ACCESS.bytesCompatible(string, charset); + public static boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars) { + return JAVA_LANG_ACCESS.bytesCompatible(string, charset, srcIndex, numChars); } public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset) { - if (bytesCompatible(string, charset)) { - copyToSegmentRaw(string, segment, offset); - return string.length(); + return copyBytes(string, segment, charset, offset, 0, string.length()); + } + + public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset, int srcIndex, int numChars) { + if (bytesCompatible(string, charset, srcIndex, numChars)) { + copyToSegmentRaw(string, segment, offset, srcIndex, numChars); + return numChars; } else { - byte[] bytes = string.getBytes(charset); + byte[] bytes = string.substring(srcIndex, srcIndex + numChars).getBytes(charset); MemorySegment.copy(bytes, 0, segment, JAVA_BYTE, offset, bytes.length); return bytes.length; } } - public static void copyToSegmentRaw(String string, MemorySegment segment, long offset) { - JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset); + public static void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength) { + JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset, srcIndex, srcLength); } } diff --git a/test/jdk/java/foreign/TestStringEncoding.java b/test/jdk/java/foreign/TestStringEncoding.java index 94732943b9d..e9e47420a68 100644 --- a/test/jdk/java/foreign/TestStringEncoding.java +++ b/test/jdk/java/foreign/TestStringEncoding.java @@ -37,6 +37,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; +import java.util.Set; import java.util.function.UnaryOperator; import jdk.internal.foreign.AbstractMemorySegmentImpl; @@ -102,6 +103,140 @@ public class TestStringEncoding { } } + @Test(dataProvider = "strings") + public void testStringsLength(String testString) { + if (!testString.isEmpty()) { + for (Charset charset : Charset.availableCharsets().values()) { + if (charset.canEncode()) { + for (Arena arena : arenas()) { + try (arena) { + MemorySegment text = arena.allocateFrom(testString, charset, 0, testString.length()); + long length = text.byteSize(); + assertEquals(length, testString.getBytes(charset).length); + String roundTrip = text.getString(0, charset, length); + if (charset.newEncoder().canEncode(testString)) { + assertEquals(roundTrip, testString); + } + } + } + } + } + } + } + + @Test(dataProvider = "strings") + public void testStringsCopy(String testString) { + if (!testString.isEmpty()) { + for (Charset charset : Charset.availableCharsets().values()) { + if (charset.canEncode()) { + for (Arena arena : arenas()) { + try (arena) { + byte[] bytes = testString.getBytes(charset); + MemorySegment text = arena.allocate(JAVA_BYTE, bytes.length); + MemorySegment.copy(testString, charset, 0, text, 0, testString.length()); + String roundTrip = text.getString(0, charset, bytes.length); + if (charset.newEncoder().canEncode(testString)) { + assertEquals(roundTrip, testString); + } + } + } + } + } + } + } + + @Test + public void testStringsLengthNegative() { + try (Arena arena = Arena.ofConfined()) { + var segment = arena.allocateFrom("abc"); + assertThrows(IllegalArgumentException.class, () -> segment.getString(1, StandardCharsets.UTF_8, -1)); + } + } + + @Test + public void testCopyThrows() { + try (Arena arena = Arena.ofConfined()) { + String testString = "abc"; + String testString_notBytesCompatible = "snowman \u26C4"; + MemorySegment text = arena.allocate(JAVA_BYTE, 3); + MemorySegment text_notBytesCompatible = arena.allocate(JAVA_BYTE, + testString_notBytesCompatible.getBytes(StandardCharsets.UTF_8).length); + MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, 0, testString.length()); + MemorySegment.copy(testString_notBytesCompatible, StandardCharsets.UTF_8, 0, + text_notBytesCompatible, 0, + testString_notBytesCompatible.length()); + // srcIndex < 0 + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, -1, text, 0, testString.length())); + // dstOffset < 0 + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, -1, testString.length())); + // numChars < 0 + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, 0, -1)); + // srcIndex + numChars > length + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, 1, text, 0, testString.length())); + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, 0, testString.length() + 1)); + // dstOffset > byteSize() - B + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, 1, testString.length())); + // srcIndex + numChars overflows + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString, StandardCharsets.UTF_8, Integer.MAX_VALUE, text, 0, Integer.MAX_VALUE + 3)); + assertThrows(IndexOutOfBoundsException.class, () -> + MemorySegment.copy(testString_notBytesCompatible, StandardCharsets.UTF_8, Integer.MAX_VALUE, text, 0, Integer.MAX_VALUE + 3)); + } + } + + @Test + public void testAllocateFromThrows() { + try (Arena arena = Arena.ofConfined()) { + String testString = "abc"; + String testString_notBytesCompatible = "snowman \u26C4"; + arena.allocateFrom(testString, StandardCharsets.UTF_8, 0, testString.length()); + arena.allocateFrom(testString, StandardCharsets.UTF_8, 2, 1); + // srcIndex < 0 + assertThrows(IndexOutOfBoundsException.class, () -> + arena.allocateFrom(testString, StandardCharsets.UTF_8, -1, testString.length())); + // numChars < 0 + assertThrows(IndexOutOfBoundsException.class, () -> + arena.allocateFrom(testString, StandardCharsets.UTF_8, 0, -1)); + // srcIndex + numChars > length + assertThrows(IndexOutOfBoundsException.class, () -> + arena.allocateFrom(testString, StandardCharsets.UTF_8, 0, testString.length() + 1)); + assertThrows(IndexOutOfBoundsException.class, () -> + arena.allocateFrom(testString, StandardCharsets.UTF_8, 1, testString.length())); + // srcIndex + numChars overflows + assertThrows(IndexOutOfBoundsException.class, () -> + arena.allocateFrom(testString, StandardCharsets.UTF_8, 3, Integer.MAX_VALUE)); + assertThrows(IndexOutOfBoundsException.class, () -> arena.allocateFrom( + testString_notBytesCompatible, StandardCharsets.UTF_8, 3, Integer.MAX_VALUE)); + } + } + + @Test + public void testGetStringThrows() { + try (Arena arena = Arena.ofConfined()) { + String testString = "abc"; + MemorySegment text = arena.allocateFrom(testString, StandardCharsets.UTF_8, 0, testString.length()); + text.getString(0, StandardCharsets.UTF_8, 3); + // unsupported string size + assertThrows(IllegalArgumentException.class, () -> + text.getString(0, StandardCharsets.UTF_8, Integer.MAX_VALUE + 1L)); + // offset < 0 + assertThrows(IndexOutOfBoundsException.class, () -> + text.getString(-1, StandardCharsets.UTF_8, 3)); + // offset > byteSize() - length + assertThrows(IndexOutOfBoundsException.class, () -> + text.getString(1, StandardCharsets.UTF_8, 3)); + // length < 0 + assertThrows(IllegalArgumentException.class, () -> + text.getString(0, StandardCharsets.UTF_8, -1)); + } + } + @Test(dataProvider = "strings") public void testStringsHeap(String testString) { for (Charset charset : singleByteCharsets()) { @@ -221,6 +356,74 @@ public class TestStringEncoding { } } + @Test(dataProvider = "strings") + public void testSubstringGetString(String testString) { + if (testString.length() < 3 || !containsOnlyRegularCharacters(testString)) { + return; + } + for (var charset : singleByteCharsets()) { + for (var arena: arenas()) { + try (arena) { + MemorySegment text = arena.allocateFrom(testString, charset, 0, testString.length()); + for (int srcIndex = 0; srcIndex <= testString.length(); srcIndex++) { + for (int numChars = 0; numChars <= testString.length() - srcIndex; numChars++) { + // this test assumes single-byte charsets + String roundTrip = text.getString(srcIndex, charset, numChars); + String substring = testString.substring(srcIndex, srcIndex + numChars); + assertEquals(roundTrip, substring); + } + } + } + } + } + } + + @Test(dataProvider = "strings") + public void testSubstringAllocate(String testString) { + if (testString.length() < 3 || !containsOnlyRegularCharacters(testString)) { + return; + } + for (var charset : singleByteCharsets()) { + for (var arena: arenas()) { + try (arena) { + for (int srcIndex = 0; srcIndex <= testString.length(); srcIndex++) { + for (int numChars = 0; numChars <= testString.length() - srcIndex; numChars++) { + MemorySegment text = arena.allocateFrom(testString, charset, srcIndex, numChars); + String substring = testString.substring(srcIndex, srcIndex + numChars); + assertEquals(text.byteSize(), substring.getBytes(charset).length); + String roundTrip = text.getString(0, charset, text.byteSize()); + assertEquals(roundTrip, substring); + } + } + } + } + } + } + + @Test(dataProvider = "strings") + public void testSubstringCopy(String testString) { + if (testString.length() < 3 || !containsOnlyRegularCharacters(testString)) { + return; + } + for (var charset : singleByteCharsets()) { + for (var arena: arenas()) { + try (arena) { + for (int srcIndex = 0; srcIndex <= testString.length(); srcIndex++) { + for (int numChars = 0; numChars <= testString.length() - srcIndex; numChars++) { + String substring = testString.substring(srcIndex, srcIndex + numChars); + long length = substring.getBytes(charset).length; + MemorySegment text = arena.allocate(JAVA_BYTE, length); + long copied = MemorySegment.copy(testString, charset, srcIndex, text, 0, numChars); + String roundTrip = text.getString(0, charset, length); + assertEquals(roundTrip, substring); + assertEquals(copied, length); + } + } + } + } + } + } + private static final MemoryLayout CHAR_POINTER = ADDRESS .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE)); private static final Linker LINKER = Linker.nativeLinker(); @@ -402,7 +605,7 @@ public class TestStringEncoding { {""}, {"X"}, {"12345"}, - {"yen \u00A5"}, + {"section \u00A7"}, {"snowman \u26C4"}, {"rainbow \uD83C\uDF08"}, {"0"}, diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/FromJavaStringTest.java b/test/micro/org/openjdk/bench/java/lang/foreign/FromJavaStringTest.java new file mode 100644 index 00000000000..ba559b52344 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/foreign/FromJavaStringTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang.foreign; + +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.nio.charset.StandardCharsets.UTF_8; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +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 java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(value = 3) +public class FromJavaStringTest { + + private String str; + private MemorySegment strSegment; + private int lengthBytes; + + @Param({"5", "20", "100", "200", "451"}) + int size; + + @Setup + public void setup() { + var arena = Arena.ofAuto(); + while (LOREM.length() < size) { + LOREM += LOREM; + } + str = LOREM.substring(0, size); + strSegment = arena.allocateFrom(str); + lengthBytes = str.getBytes(UTF_8).length; + } + + @Benchmark + public void segment_setString() { + strSegment.setString(0, str, UTF_8); + } + + @Benchmark + public void segment_copyStringRaw() { + MemorySegment.copy(str, UTF_8, 0, strSegment, 0, str.length()); + } + + @Benchmark + public void segment_copyStringBytes() { + byte[] bytes = str.getBytes(UTF_8); + MemorySegment.copy(bytes, 0, strSegment, JAVA_BYTE, 0, bytes.length); + } + + static String LOREM = + """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip + ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu + fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt + mollit anim id est laborum. + """; +} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java b/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java index 901f4c7097f..c3e8f3aaca4 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/ToJavaStringTest.java @@ -22,6 +22,9 @@ */ package org.openjdk.bench.java.lang.foreign; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.nio.charset.StandardCharsets.UTF_8; + import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -47,6 +50,7 @@ import java.util.concurrent.TimeUnit; public class ToJavaStringTest { private MemorySegment strSegment; + private int length; @Param({"5", "20", "100", "200", "451"}) int size; @@ -61,19 +65,33 @@ public class ToJavaStringTest { while (LOREM.length() < size) { LOREM += LOREM; } - strSegment = arena.allocateFrom(LOREM.substring(0, size)); + var s = LOREM.substring(0, size); + strSegment = arena.allocateFrom(s); + length = s.getBytes(UTF_8).length; } @Benchmark - public String panama_readString() { + public String segment_getString() { return strSegment.getString(0); } + @Benchmark + public String segment_getStringLength() { + return strSegment.getString(0, UTF_8, length); + } + @Benchmark public String jni_readString() { return readString(strSegment.address()); } + @Benchmark + public String segment_copyStringBytes() { + byte[] bytes = new byte[length]; + MemorySegment.copy(strSegment, JAVA_BYTE, 0, bytes, 0, length); + return new String(bytes, UTF_8); + } + static native String readString(long addr); static String LOREM = """ From 9a2592f8d2177f1480758e94faf9b986c7bba681 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Mon, 12 Jan 2026 19:41:21 +0000 Subject: [PATCH 066/204] 8374953: Add note on about implicit state when comparing TypeMirrors Reviewed-by: attila, vromero, jlahoda --- .../classes/javax/lang/model/type/TypeMirror.java | 11 ++++++++++- .../share/classes/javax/lang/model/util/Types.java | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java b/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java index 5bd205a6c4b..facdbe405dd 100644 --- a/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java +++ b/src/java.compiler/share/classes/javax/lang/model/type/TypeMirror.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, 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 @@ -120,6 +120,15 @@ public interface TypeMirror extends AnnotatedConstruct { * The results of {@code t1.equals(t2)} and * {@code Types.isSameType(t1, t2)} may differ. * + * @apiNote The identity of a {@code TypeMirror} involves implicit + * state not directly accessible from its methods, including state + * about the presence of unrelated types. {@code TypeMirror} + * objects created by different implementations of these + * interfaces should not be expected to compare as equal + * even if "the same" type is being modeled; this is + * analogous to the inequality of {@code Class} objects for the + * same class file loaded through different class loaders. + * * @param obj the object to be compared with this type * @return {@code true} if the specified object is equal to this one */ diff --git a/src/java.compiler/share/classes/javax/lang/model/util/Types.java b/src/java.compiler/share/classes/javax/lang/model/util/Types.java index 951b56ed214..e7212a7e0be 100644 --- a/src/java.compiler/share/classes/javax/lang/model/util/Types.java +++ b/src/java.compiler/share/classes/javax/lang/model/util/Types.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 @@ -106,6 +106,15 @@ public interface Types { * {@code TypeMirror} objects can have different annotations and * still be considered the same. * + * @apiNote The identity of a {@code TypeMirror} involves implicit + * state not directly accessible from its methods, including state + * about the presence of unrelated types. {@code TypeMirror} + * objects created by different implementations of these + * interfaces should not be expected to compare as equal + * even if "the same" type is being modeled; this is + * analogous to the inequality of {@code Class} objects for the + * same class file loaded through different class loaders. + * * @param t1 the first type * @param t2 the second type * @return {@code true} if and only if the two types are the same From 15b7a4252b8d3595b7bc409e20d4c617e89240e8 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Mon, 12 Jan 2026 23:36:26 +0000 Subject: [PATCH 067/204] 8373819: Genshen: Control thread can miss allocation failure notification (redux) Reviewed-by: kdnilsen, ysr --- .../shenandoahGenerationalControlThread.cpp | 106 +++++++++++------- .../shenandoahGenerationalControlThread.hpp | 11 +- .../shenandoah/shenandoahGenerationalHeap.hpp | 4 +- .../shenandoah/shenandoahRegulatorThread.cpp | 7 ++ 4 files changed, 78 insertions(+), 50 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index ece4150f577..018b4898a19 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -61,7 +61,12 @@ ShenandoahGenerationalControlThread::ShenandoahGenerationalControlThread() : void ShenandoahGenerationalControlThread::run_service() { + // This is the only instance of request. It is important that request.generation + // does not change between a concurrent cycle failure and the start of a degenerated + // cycle. We initialize it with the young generation to handle the pathological case + // where the very first cycle is degenerated (some tests exercise this path). ShenandoahGCRequest request; + request.generation = _heap->young_generation(); while (!should_terminate()) { // Figure out if we have pending requests. @@ -77,12 +82,10 @@ void ShenandoahGenerationalControlThread::run_service() { // If the cycle was cancelled, continue the next iteration to deal with it. Otherwise, // if there was no other cycle requested, cleanup and wait for the next request. - if (!_heap->cancelled_gc()) { - MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); - if (_requested_gc_cause == GCCause::_no_gc) { - set_gc_mode(ml, none); - ml.wait(); - } + MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); + if (_requested_gc_cause == GCCause::_no_gc) { + set_gc_mode(ml, none); + ml.wait(); } } @@ -96,8 +99,7 @@ void ShenandoahGenerationalControlThread::stop_service() { log_debug(gc, thread)("Stopping control thread"); MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); _heap->cancel_gc(GCCause::_shenandoah_stop_vm); - _requested_gc_cause = GCCause::_shenandoah_stop_vm; - notify_cancellation(ml, GCCause::_shenandoah_stop_vm); + notify_control_thread(ml, GCCause::_shenandoah_stop_vm); // We can't wait here because it may interfere with the active cycle's ability // to reach a safepoint (this runs on a java thread). } @@ -105,29 +107,39 @@ void ShenandoahGenerationalControlThread::stop_service() { void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest& request) { // Hold the lock while we read request cause and generation MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); - if (_heap->cancelled_gc()) { - // The previous request was cancelled. Either it was cancelled for an allocation - // failure (degenerated cycle), or old marking was cancelled to run a young collection. - // In either case, the correct generation for the next cycle can be determined by - // the cancellation cause. - request.cause = _heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc); - if (request.cause == GCCause::_shenandoah_concurrent_gc) { + + log_debug(gc, thread)("cancelled cause: %s, requested cause: %s", + GCCause::to_string(_heap->cancelled_cause()), GCCause::to_string(_requested_gc_cause)); + + request.cause = _requested_gc_cause; + if (ShenandoahCollectorPolicy::is_allocation_failure(request.cause)) { + if (_degen_point == ShenandoahGC::_degenerated_unset) { request.generation = _heap->young_generation(); + _degen_point = ShenandoahGC::_degenerated_outside_cycle; + } else { + assert(request.generation != nullptr, "Must know which generation to use for degenerated cycle"); } } else { - request.cause = _requested_gc_cause; + 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. + _heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc); + } request.generation = _requested_generation; - - // Only clear these if we made a request from them. In the case of a cancelled gc, - // we do not want to inadvertently lose this pending request. - _requested_gc_cause = GCCause::_no_gc; - _requested_generation = nullptr; } + log_debug(gc, thread)("request.cause: %s, request.generation: %s", + GCCause::to_string(request.cause), request.generation == nullptr ? "None" : request.generation->name()); + + _requested_gc_cause = GCCause::_no_gc; + _requested_generation = nullptr; + if (request.cause == GCCause::_no_gc || request.cause == GCCause::_shenandoah_stop_vm) { return; } + assert(request.generation != nullptr, "request.generation cannot be null, cause is: %s", GCCause::to_string(request.cause)); GCMode mode; if (ShenandoahCollectorPolicy::is_allocation_failure(request.cause)) { mode = prepare_for_allocation_failure_gc(request); @@ -140,11 +152,9 @@ void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest& } ShenandoahGenerationalControlThread::GCMode ShenandoahGenerationalControlThread::prepare_for_allocation_failure_gc(ShenandoahGCRequest &request) { - - if (_degen_point == ShenandoahGC::_degenerated_unset) { - _degen_point = ShenandoahGC::_degenerated_outside_cycle; - request.generation = _heap->young_generation(); - } else if (request.generation->is_old()) { + // Important: not all paths update the request.generation. This is intentional. + // A degenerated cycle must use the same generation carried over from the previous request. + if (request.generation->is_old()) { // This means we degenerated during the young bootstrap for the old generation // cycle. The following degenerated cycle should therefore also be young. request.generation = _heap->young_generation(); @@ -588,6 +598,8 @@ bool ShenandoahGenerationalControlThread::check_cancellation_or_degen(Shenandoah if (ShenandoahCollectorPolicy::is_allocation_failure(_heap->cancelled_cause())) { assert(_degen_point == ShenandoahGC::_degenerated_unset, "Should not be set yet: %s", ShenandoahGC::degen_point_to_string(_degen_point)); + MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); + _requested_gc_cause = _heap->cancelled_cause(); _degen_point = point; log_debug(gc, thread)("Cancellation detected:, reason: %s, degen point: %s", GCCause::to_string(_heap->cancelled_cause()), @@ -633,9 +645,7 @@ void ShenandoahGenerationalControlThread::service_stw_degenerated_cycle(const Sh void ShenandoahGenerationalControlThread::request_gc(GCCause::Cause cause) { if (ShenandoahCollectorPolicy::is_allocation_failure(cause)) { - // GC should already be cancelled. Here we are just notifying the control thread to - // wake up and handle the cancellation request, so we don't need to set _requested_gc_cause. - notify_cancellation(cause); + notify_control_thread(cause); } else if (ShenandoahCollectorPolicy::should_handle_requested_gc(cause)) { handle_requested_gc(cause); } @@ -653,7 +663,8 @@ bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenera MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); if (gc_mode() == servicing_old) { if (!preempt_old_marking(generation)) { - log_debug(gc, thread)("Cannot start young, old collection is not preemptible"); + // Global should be able to cause old collection to be abandoned + log_debug(gc, thread)("Cannot start %s, old collection is not preemptible", generation->name()); return false; } @@ -661,7 +672,7 @@ bool ShenandoahGenerationalControlThread::request_concurrent_gc(ShenandoahGenera log_info(gc)("Preempting old generation mark to allow %s GC", generation->name()); while (gc_mode() == servicing_old) { ShenandoahHeap::heap()->cancel_gc(GCCause::_shenandoah_concurrent_gc); - notify_cancellation(ml, GCCause::_shenandoah_concurrent_gc); + notify_control_thread(ml, GCCause::_shenandoah_concurrent_gc, generation); ml.wait(); } return true; @@ -695,21 +706,34 @@ void ShenandoahGenerationalControlThread::notify_control_thread(GCCause::Cause c void ShenandoahGenerationalControlThread::notify_control_thread(MonitorLocker& ml, GCCause::Cause cause, ShenandoahGeneration* generation) { assert(_control_lock.is_locked(), "Request lock must be held here"); - log_debug(gc, thread)("Notify control (%s): %s, %s", gc_mode_name(gc_mode()), GCCause::to_string(cause), generation->name()); - _requested_gc_cause = cause; - _requested_generation = generation; - ml.notify(); + if (ShenandoahCollectorPolicy::is_allocation_failure(_requested_gc_cause)) { + // We have already observed a request to handle an allocation failure. We cannot allow + // another request (System.gc or regulator) to subvert the degenerated cycle. + log_debug(gc, thread)("Not overwriting gc cause %s with %s", GCCause::to_string(_requested_gc_cause), GCCause::to_string(cause)); + } else { + log_debug(gc, thread)("Notify control (%s): %s, %s", gc_mode_name(gc_mode()), GCCause::to_string(cause), generation->name()); + _requested_gc_cause = cause; + _requested_generation = generation; + ml.notify(); + } } -void ShenandoahGenerationalControlThread::notify_cancellation(GCCause::Cause cause) { +void ShenandoahGenerationalControlThread::notify_control_thread(GCCause::Cause cause) { MonitorLocker ml(&_control_lock, Mutex::_no_safepoint_check_flag); - notify_cancellation(ml, cause); + notify_control_thread(ml, cause); } -void ShenandoahGenerationalControlThread::notify_cancellation(MonitorLocker& ml, GCCause::Cause cause) { - assert(_heap->cancelled_gc(), "GC should already be cancelled"); - log_debug(gc,thread)("Notify control (%s): %s", gc_mode_name(gc_mode()), GCCause::to_string(cause)); - ml.notify(); +void ShenandoahGenerationalControlThread::notify_control_thread(MonitorLocker& ml, GCCause::Cause cause) { + assert(_control_lock.is_locked(), "Request lock must be held here"); + if (ShenandoahCollectorPolicy::is_allocation_failure(_requested_gc_cause)) { + // We have already observed a request to handle an allocation failure. We cannot allow + // another request (System.gc or regulator) to subvert the degenerated cycle. + log_debug(gc, thread)("Not overwriting gc cause %s with %s", GCCause::to_string(_requested_gc_cause), GCCause::to_string(cause)); + } else { + log_debug(gc, thread)("Notify control (%s): %s", gc_mode_name(gc_mode()), GCCause::to_string(cause)); + _requested_gc_cause = cause; + ml.notify(); + } } bool ShenandoahGenerationalControlThread::preempt_old_marking(ShenandoahGeneration* generation) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp index b7dbedd5e84..13e69d25268 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.hpp @@ -135,16 +135,13 @@ private: // Return printable name for the given gc mode. static const char* gc_mode_name(GCMode mode); - // Takes the request lock and updates the requested cause and generation, then notifies the control thread. - // The overloaded variant should be used when the _control_lock is already held. + // These notify the control thread after updating _requested_gc_cause and (optionally) _requested_generation. + // Updating the requested generation is not necessary for allocation failures nor when stopping the thread. + void notify_control_thread(GCCause::Cause cause); + void notify_control_thread(MonitorLocker& ml, GCCause::Cause cause); void notify_control_thread(GCCause::Cause cause, ShenandoahGeneration* generation); void notify_control_thread(MonitorLocker& ml, GCCause::Cause cause, ShenandoahGeneration* generation); - // Notifies the control thread, but does not update the requested cause or generation. - // The overloaded variant should be used when the _control_lock is already held. - void notify_cancellation(GCCause::Cause cause); - void notify_cancellation(MonitorLocker& ml, GCCause::Cause cause); - // Configure the heap to age objects and regions if the aging period has elapsed. void maybe_set_aging_cycle(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp index 736026916f7..a2ae4a68cd0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp @@ -44,13 +44,13 @@ public: void post_initialize_heuristics() override; static ShenandoahGenerationalHeap* heap() { - assert(ShenandoahCardBarrier, "Should have card barrier to use genenrational heap"); + assert(ShenandoahCardBarrier, "Should have card barrier to use generational heap"); CollectedHeap* heap = Universe::heap(); return cast(heap); } static ShenandoahGenerationalHeap* cast(CollectedHeap* heap) { - assert(ShenandoahCardBarrier, "Should have card barrier to use genenrational heap"); + assert(ShenandoahCardBarrier, "Should have card barrier to use generational heap"); return checked_cast(heap); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp index 964b6f0a10a..ec4b7c7217c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRegulatorThread.cpp @@ -149,6 +149,13 @@ bool ShenandoahRegulatorThread::start_global_cycle() const { bool ShenandoahRegulatorThread::request_concurrent_gc(ShenandoahGeneration* generation) const { double now = os::elapsedTime(); + + // This call may find the control thread waiting on workers which have suspended + // to allow a safepoint to run. If this regulator thread does not yield, the safepoint + // will not run. The worker threads won't progress, the control thread won't progress, + // and the regulator thread may never yield. Therefore, we leave the suspendible + // thread set before making this call. + SuspendibleThreadSetLeaver leaver; bool accepted = _control_thread->request_concurrent_gc(generation); if (LogTarget(Debug, gc, thread)::is_enabled() && accepted) { double wait_time = os::elapsedTime() - now; From e89c1290ca8b3e07bef12f4c0465c3e83389fef4 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Tue, 13 Jan 2026 01:29:20 +0000 Subject: [PATCH 068/204] 8374181: failure_handler: The cores.html file is formatted incorrectly and so hides the core dump information Reviewed-by: erikj --- .../jtreg/GatherDiagnosticInfoObserver.java | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java index e63e55888d7..32dbedb5159 100644 --- a/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java +++ b/test/failure_handler/src/share/classes/jdk/test/failurehandler/jtreg/GatherDiagnosticInfoObserver.java @@ -29,13 +29,14 @@ import com.sun.javatest.TestResult; import com.sun.javatest.regtest.config.RegressionParameters; import jdk.test.failurehandler.*; -import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Stream; /** * The jtreg test execution observer, which gathers info about @@ -85,11 +86,15 @@ public class GatherDiagnosticInfoObserver implements Harness.Observer { testJdk, compileJdk); gatherEnvInfo(workDir, name, log, gathererFactory.getEnvironmentInfoGatherer()); - Files.walk(workDir) - .filter(Files::isRegularFile) - .filter(f -> (f.getFileName().toString().contains("core") || f.getFileName().toString().contains("mdmp"))) - .forEach(core -> gatherCoreInfo(workDir, name, - core, log, gathererFactory.getCoreInfoGatherer())); + // generate a cores.html file after parsing the core dump files (if any) + List coreFiles; + try (Stream paths = Files.walk(workDir)) { + coreFiles = paths.filter(Files::isRegularFile) + .filter(f -> (f.getFileName().toString().contains("core") + || f.getFileName().toString().contains("mdmp"))) + .toList(); + } + gatherCoreInfo(workDir, name, coreFiles, log, gathererFactory.getCoreInfoGatherer()); } catch (Throwable e) { log.printf("ERROR: exception in observer %s:", name); e.printStackTrace(log); @@ -103,16 +108,22 @@ public class GatherDiagnosticInfoObserver implements Harness.Observer { } } - private void gatherCoreInfo(Path workDir, String name, Path core, PrintWriter log, - CoreInfoGatherer gatherer) { + private void gatherCoreInfo(Path workDir, String name, List coreFiles, + PrintWriter log, CoreInfoGatherer gatherer) { + if (coreFiles.isEmpty()) { + return; + } try (HtmlPage html = new HtmlPage(workDir, CORES_OUTPUT, true)) { try (ElapsedTimePrinter timePrinter = new ElapsedTimePrinter(new Stopwatch(), name, log)) { - gatherer.gatherCoreInfo(html.getRootSection(), core); + // gather information from the contents of each core file + for (Path coreFile : coreFiles) { + gatherer.gatherCoreInfo(html.getRootSection(), coreFile); + } } } catch (Throwable e) { - log.printf("ERROR: exception in observer on getting environment " - + "information %s:", name); + log.printf("ERROR: exception in %s observer while gathering information from" + + " core dump file", name); e.printStackTrace(log); } } From 0b9d4c02e39191e9dba721115f422e28ee5b9869 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Tue, 13 Jan 2026 04:29:12 +0000 Subject: [PATCH 069/204] 4765299: componentResized() not always called with nested JSplitPanes Reviewed-by: tr, kizune --- .../swing/plaf/basic/BasicSplitPaneUI.java | 4 +- .../JSplitPane/TestSplitPaneCompResize.java | 173 ++++++++++++++++++ 2 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 test/jdk/javax/swing/JSplitPane/TestSplitPaneCompResize.java diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneUI.java index 5ff68f78d38..270181f4600 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicSplitPaneUI.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 @@ -1405,7 +1405,7 @@ public class BasicSplitPaneUI extends SplitPaneUI // If the splitpane has a zero size then no op out of here. // If we execute this function now, we're going to cause ourselves // much grief. - if (containerSize.height <= 0 || containerSize.width <= 0 ) { + if (containerSize.height <= 0 && containerSize.width <= 0 ) { lastSplitPaneSize = 0; return; } diff --git a/test/jdk/javax/swing/JSplitPane/TestSplitPaneCompResize.java b/test/jdk/javax/swing/JSplitPane/TestSplitPaneCompResize.java new file mode 100644 index 00000000000..07d9b77f90e --- /dev/null +++ b/test/jdk/javax/swing/JSplitPane/TestSplitPaneCompResize.java @@ -0,0 +1,173 @@ +/* + * 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 4765299 + * @key headful + * @summary Verifies componentResized() is called with nested JSplitPanes + * @run main TestSplitPaneCompResize + */ + +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.InputEvent; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JPanel; +import javax.swing.ListSelectionModel; +import javax.swing.plaf.basic.BasicSplitPaneDivider; +import javax.swing.plaf.basic.BasicSplitPaneUI; +import javax.swing.SwingUtilities; + +public class TestSplitPaneCompResize { + + private static JFrame frame; + private JSplitPane outer; + private static JButton leftOneTouchButton; + private static volatile Point leftBtnPos; + private static volatile boolean resized; + + public TestSplitPaneCompResize() { + + // set up a simple list embedded inside a scroll pane + String[] listItems = {"Item1", "Item2"}; + JList list = new JList<>(listItems); + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + list.setSelectedIndex(0); + + JScrollPane comp = new JScrollPane(list); + JSplitPane inner = new JSplitPane(JSplitPane.VERTICAL_SPLIT, + comp, new JPanel()); + JPanel rightPanel = new JPanel(); + + outer = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + inner, rightPanel); + outer.setDividerLocation(150); + + //Provide minimum sizes for the two components in the split pane + Dimension minimumSize = new Dimension(100, 50); + comp.setMinimumSize(minimumSize); + inner.setMinimumSize(minimumSize); + rightPanel.setMinimumSize(minimumSize); + + //Provide a preferred size for the split pane + outer.setPreferredSize(new Dimension(400, 200)); + inner.addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + System.out.println("inner resized"); + } + }); + comp.addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + resized = true; + System.out.println("comp resized"); + } + }); + } + + public JSplitPane getSplitPane() { + return outer; + } + + + public static void main(String[] s) throws Exception { + Robot robot = new Robot(); + try { + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame("SplitPaneDemo"); + + TestSplitPaneCompResize sp = new TestSplitPaneCompResize(); + JSplitPane jsp = sp.getSplitPane(); + frame.getContentPane().add(jsp); + jsp.setUI(new MySplitPaneUI()); + jsp.setOneTouchExpandable(true); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + }); + + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + leftBtnPos = leftOneTouchButton.getLocationOnScreen(); + leftBtnPos.x += leftOneTouchButton.getWidth() / 2; + leftBtnPos.y += leftOneTouchButton.getHeight() / 2; + }); + + resized = false; + robot.mouseMove(leftBtnPos.x, leftBtnPos.y); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(1000); + + if (!resized) { + throw new RuntimeException("ComponentResized not called"); + } + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + + static class MySplitPaneUI extends BasicSplitPaneUI { + + public MySplitPaneUI() { + super(); + } + + public BasicSplitPaneDivider createDefaultDivider() { + return new MySplitPaneDivider(this); + } + } + + static class MySplitPaneDivider extends BasicSplitPaneDivider { + + public MySplitPaneDivider(BasicSplitPaneUI ui) { + super(ui); + } + + protected JButton createLeftOneTouchButton() { + leftOneTouchButton = super.createLeftOneTouchButton(); + return leftOneTouchButton; + } + + protected JButton createRightOneTouchButton() { + JButton rightOneTouchButton = super.createRightOneTouchButton(); + return rightOneTouchButton; + } + } +} From f4ebf9585f63177584d8c48838ef793407ebce12 Mon Sep 17 00:00:00 2001 From: David Holmes Date: Tue, 13 Jan 2026 06:02:01 +0000 Subject: [PATCH 070/204] 8370314: Update signals_posix with new Linux signal codes Reviewed-by: shade, jwaters --- src/hotspot/os/posix/signals_posix.cpp | 40 +++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index 85babea5603..203e13a46ac 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.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 @@ -951,6 +951,32 @@ struct enum_sigcode_desc_t { const char* s_desc; }; +#if defined(LINUX) +// Additional kernel si_code definitions that are only exported by +// more recent glibc distributions, so we have to hard-code the values. +#ifndef BUS_MCEERR_AR // glibc 2.17 +#define BUS_MCEERR_AR 4 +#define BUS_MCEERR_AO 5 +#endif + +#ifndef SEGV_PKUERR // glibc 2.27 +#define SEGV_PKUERR 4 +#endif + +#ifndef SYS_SECCOMP // glibc 2.28 +#define SYS_SECCOMP 1 +#endif + +#ifndef TRAP_BRANCH // glibc 2.30 +#define TRAP_BRANCH 3 +#endif + +#ifndef TRAP_HWBKPT // not glibc version specific - gdb related +#define TRAP_HWBKPT 4 +#endif + +#endif // LINUX + static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t* out) { const struct { @@ -976,6 +1002,7 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t { SIGSEGV, SEGV_ACCERR, "SEGV_ACCERR", "Invalid permissions for mapped object." }, #if defined(LINUX) { SIGSEGV, SEGV_BNDERR, "SEGV_BNDERR", "Failed address bound checks." }, + { SIGSEGV, SEGV_PKUERR, "SEGV_PKUERR", "Protection key checking failure." }, #endif #if defined(AIX) // no explanation found what keyerr would be @@ -984,8 +1011,18 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t { SIGBUS, BUS_ADRALN, "BUS_ADRALN", "Invalid address alignment." }, { SIGBUS, BUS_ADRERR, "BUS_ADRERR", "Nonexistent physical address." }, { SIGBUS, BUS_OBJERR, "BUS_OBJERR", "Object-specific hardware error." }, +#if defined(LINUX) + { SIGBUS, BUS_MCEERR_AR,"BUS_MCEERR_AR","Hardware memory error consumed on a machine check: action required." }, + { SIGBUS, BUS_MCEERR_AO,"BUS_MCEERR_AO","Hardware memory error detected in process but not consumed: action optional." }, + + { SIGSYS, SYS_SECCOMP, "SYS_SECCOMP", "Secure computing (seccomp) filter failure." }, +#endif { SIGTRAP, TRAP_BRKPT, "TRAP_BRKPT", "Process breakpoint." }, { SIGTRAP, TRAP_TRACE, "TRAP_TRACE", "Process trace trap." }, +#if defined(LINUX) + { SIGTRAP, TRAP_BRANCH, "TRAP_BRANCH", "Process taken branch trap." }, + { SIGTRAP, TRAP_HWBKPT, "TRAP_HWBKPT", "Hardware breakpoint/watchpoint." }, +#endif { SIGCHLD, CLD_EXITED, "CLD_EXITED", "Child has exited." }, { SIGCHLD, CLD_KILLED, "CLD_KILLED", "Child has terminated abnormally and did not create a core file." }, { SIGCHLD, CLD_DUMPED, "CLD_DUMPED", "Child has terminated abnormally and created a core file." }, @@ -993,6 +1030,7 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t { SIGCHLD, CLD_STOPPED, "CLD_STOPPED", "Child has stopped." }, { SIGCHLD, CLD_CONTINUED,"CLD_CONTINUED","Stopped child has continued." }, #ifdef SIGPOLL + { SIGPOLL, POLL_IN, "POLL_IN", "Data input available." }, { SIGPOLL, POLL_OUT, "POLL_OUT", "Output buffers available." }, { SIGPOLL, POLL_MSG, "POLL_MSG", "Input message available." }, { SIGPOLL, POLL_ERR, "POLL_ERR", "I/O error." }, From 586846b84a38d285c5905437e903cfc57f609410 Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Tue, 13 Jan 2026 06:49:04 +0000 Subject: [PATCH 071/204] 8374450: GTest opto.canonicalize_constraints cannot run without VM Reviewed-by: qamai, thartmann, shade --- test/hotspot/gtest/opto/test_rangeinference.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index 61a9ff7fb70..42767e9fcab 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.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 @@ -202,7 +202,7 @@ static void test_canonicalize_constraints_random() { } } -TEST(opto, canonicalize_constraints) { +TEST_VM(opto, canonicalize_constraints) { test_canonicalize_constraints_trivial(); test_canonicalize_constraints_exhaustive, uintn_t<1>>(); test_canonicalize_constraints_exhaustive, uintn_t<2>>(); From c000343bbb1d822d2cee37e1a27672cfb3128bee Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 13 Jan 2026 07:30:13 +0000 Subject: [PATCH 072/204] 8374876: Epsilon: Convert to use Atomic Reviewed-by: tschatzl, stefank --- src/hotspot/share/gc/epsilon/epsilonHeap.cpp | 10 ++++------ src/hotspot/share/gc/epsilon/epsilonHeap.hpp | 5 +++-- .../share/gc/epsilon/epsilonMonitoringSupport.cpp | 5 ++--- .../share/gc/epsilon/epsilonMonitoringSupport.hpp | 3 ++- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp index 24182c22a23..59ab69b2427 100644 --- a/src/hotspot/share/gc/epsilon/epsilonHeap.cpp +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.cpp @@ -62,8 +62,6 @@ jint EpsilonHeap::initialize() { // Enable monitoring _monitoring_support = new EpsilonMonitoringSupport(this); - _last_counter_update = 0; - _last_heap_print = 0; // Install barrier set BarrierSet::set_barrier_set(new EpsilonBarrierSet()); @@ -156,17 +154,17 @@ HeapWord* EpsilonHeap::allocate_work(size_t size) { // At this point, some diagnostic subsystems might not yet be initialized. // We pretend the printout happened either way. This keeps allocation path // from obsessively checking the subsystems' status on every allocation. - size_t last_counter = AtomicAccess::load(&_last_counter_update); + size_t last_counter = _last_counter_update.load_relaxed(); if ((used - last_counter >= _step_counter_update) && - AtomicAccess::cmpxchg(&_last_counter_update, last_counter, used) == last_counter) { + _last_counter_update.compare_set(last_counter, used)) { if (_monitoring_support->is_ready()) { _monitoring_support->update_counters(); } } - size_t last_heap = AtomicAccess::load(&_last_heap_print); + size_t last_heap = _last_heap_print.load_relaxed(); if ((used - last_heap >= _step_heap_print) && - AtomicAccess::cmpxchg(&_last_heap_print, last_heap, used) == last_heap) { + _last_heap_print.compare_set(last_heap, used)) { print_heap_info(used); if (Metaspace::initialized()) { print_metaspace_info(); diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp index 9693c63b15c..8d7aa7960fd 100644 --- a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp @@ -31,6 +31,7 @@ #include "gc/shared/collectedHeap.hpp" #include "gc/shared/space.hpp" #include "memory/virtualspace.hpp" +#include "runtime/atomic.hpp" #include "services/memoryManager.hpp" class EpsilonHeap : public CollectedHeap { @@ -45,8 +46,8 @@ private: size_t _step_counter_update; size_t _step_heap_print; int64_t _decay_time_ns; - volatile size_t _last_counter_update; - volatile size_t _last_heap_print; + Atomic _last_counter_update; + Atomic _last_heap_print; void print_tracing_info() const override; void stop() override {}; diff --git a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp index 38be736df74..213fc18b8ff 100644 --- a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp +++ b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp @@ -96,7 +96,6 @@ public: EpsilonMonitoringSupport::EpsilonMonitoringSupport(EpsilonHeap* heap) { _heap_counters = new EpsilonGenerationCounters(heap); _space_counters = new EpsilonSpaceCounters("Heap", 0, heap->max_capacity(), 0, _heap_counters); - _ready = false; } void EpsilonMonitoringSupport::update_counters() { @@ -114,9 +113,9 @@ void EpsilonMonitoringSupport::update_counters() { } bool EpsilonMonitoringSupport::is_ready() { - return AtomicAccess::load_acquire(&_ready); + return _ready.load_acquire(); } void EpsilonMonitoringSupport::mark_ready() { - return AtomicAccess::release_store(&_ready, true); + _ready.release_store(true); } diff --git a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.hpp b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.hpp index 76cdac6df1b..9dc52c2a659 100644 --- a/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.hpp +++ b/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_EPSILON_EPSILONMONITORINGSUPPORT_HPP #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" class EpsilonGenerationCounters; class EpsilonSpaceCounters; @@ -35,7 +36,7 @@ class EpsilonMonitoringSupport : public CHeapObj { private: EpsilonGenerationCounters* _heap_counters; EpsilonSpaceCounters* _space_counters; - volatile bool _ready; + Atomic _ready; public: EpsilonMonitoringSupport(EpsilonHeap* heap); From d6f43d7329bf0ba08464f6d0a22de7e27ca8b399 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Tue, 13 Jan 2026 08:05:57 +0000 Subject: [PATCH 073/204] 8375066: Test tools/sincechecker/modules/java.base/JavaBaseCheckSince.java broken by JDK-8369564 Reviewed-by: jpai, shade --- .../share/classes/java/lang/foreign/MemorySegment.java | 2 ++ .../share/classes/java/lang/foreign/SegmentAllocator.java | 1 + 2 files changed, 3 insertions(+) diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java index 2b931a7d2e0..78098e39a17 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -1354,6 +1354,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws WrongThreadException if this method is called from a thread {@code T}, * such that {@code isAccessibleBy(T) == false} * @throws IllegalArgumentException if {@code byteLength < 0} + * @since 27 */ String getString(long offset, Charset charset, long byteLength); @@ -2669,6 +2670,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * @throws IndexOutOfBoundsException if {@code dstOffset > dstSegment.byteSize() - B} where {@code B} is the size, * in bytes, of the substring of {@code src} encoded using the given charset * @return the number of copied bytes. + * @since 27 */ @ForceInline static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars) { diff --git a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java index 5b213af544f..03d92ec24ef 100644 --- a/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java +++ b/src/java.base/share/classes/java/lang/foreign/SegmentAllocator.java @@ -183,6 +183,7 @@ public interface SegmentAllocator { * {@code this.allocate(B)}, where {@code B} is the size, in bytes, of * the string encoded using the provided charset * (e.g. {@code str.getBytes(charset).length}); + * @since 27 */ @ForceInline default MemorySegment allocateFrom(String str, Charset charset, int srcIndex, int numChars) { From 578204f8c49f06be8b9c4855359ca61c9e107678 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Tue, 13 Jan 2026 08:12:35 +0000 Subject: [PATCH 074/204] 8374379: Type annotation in new array dimension expression causes java.lang.AssertionError Reviewed-by: vromero --- .../sun/tools/javac/code/TypeAnnotations.java | 3 ++- .../com/sun/tools/javac/comp/Annotate.java | 3 ++- .../classfile/TestNewCastArray.java | 22 +++++++++++++++++-- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java index 6aae8eb855d..86319f20c73 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, 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 @@ -1403,6 +1403,7 @@ public class TypeAnnotations { break; } } + scan(tree.dims); scan(tree.elems); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index f5fdc1578b8..f865afe11fb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.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 @@ -1107,6 +1107,7 @@ public class Annotate { for (List dimAnnos : tree.dimAnnotations) enterTypeAnnotations(dimAnnos, env, sym, false); scan(tree.elemtype); + scan(tree.dims); scan(tree.elems); } diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/TestNewCastArray.java b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/TestNewCastArray.java index 803e0c8865b..a65c4503238 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/classfile/TestNewCastArray.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/classfile/TestNewCastArray.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 @@ -44,7 +44,8 @@ public class TestNewCastArray { // 'b' tests fail with only even numbers of annotations (8005681). String[] testclasses = {"Test1", "Test2a", "Test3a", "Test4a", "Test5a", - "Test2b", "Test3b", "Test4b", "Test5b" + "Test2b", "Test3b", "Test4b", "Test5b", + "Test6a" }; public static void main(String[] args) throws Exception { @@ -182,6 +183,11 @@ public class TestNewCastArray { case "ci2": expected = 0; break; case "ci22": expected = 0; break; + case "Test6a": cexpected=4; break; + case "test6aPrimitiveArray": expected = 0; break; + case "test6aRefArray": expected = 0; break; + case "test6aMethod": cexpected = 4; break; + default: expected = 0; break; } if(codeattr) @@ -353,6 +359,18 @@ public class TestNewCastArray { Integer ci22 = (@A @A @B @B Integer)o; // FAIL expect 3, got 1 } + static class Test6a { + Test6a(){} + long l = 0; + // Cast expressions inside new array dimensions: + int[] test6aPrimitiveArray = new int[(@A @A @B @B int) l]; + Integer[] test6aRefArray = new Integer[(@A @A @B @B int) l]; + private void test6aMethod() { + int[] primitiveArray = new int[(@A @A @B @B int) l]; + Integer[] refArray = new Integer[(@A @A @B @B int) l]; + } + } + @Retention(RUNTIME) @Target({TYPE_USE}) @Repeatable( AC.class ) @interface A { } @Retention(RUNTIME) @Target({TYPE_USE}) @Repeatable( BC.class ) @interface B { } @Retention(RUNTIME) @Target({FIELD}) @Repeatable( FC.class ) @interface F { } From 543a972222118155e4c72c6f2d32d154c5dfd442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Gr=C3=B6nlund?= Date: Tue, 13 Jan 2026 11:44:32 +0000 Subject: [PATCH 075/204] 8373485: JFR Crash during sampling: assert(jt->has_last_Java_frame()) failed: invariant Reviewed-by: shade, egahlin --- .../jfr/periodic/sampling/jfrThreadSampler.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp index 7e7747ba396..805426078c4 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.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 @@ -351,15 +351,22 @@ bool JfrSamplerThread::sample_native_thread(JavaThread* jt) { // outside the safepoint protocol. // OrderAccess::fence() as part of acquiring the lock prevents loads from floating up. - JfrMutexTryLock threads_lock(Threads_lock); + JfrMutexTryLock lock(Threads_lock); - if (!threads_lock.acquired() || !jt->has_last_Java_frame()) { + if (!lock.acquired()) { // Remove the native sample request and release the potentially waiting thread. JfrSampleMonitor jsm(tl); return false; } - if (jt->thread_state() != _thread_in_native) { + // Separate the arming of the poll (above) from the reading of JavaThread state (below). + if (UseSystemMemoryBarrier) { + SystemMemoryBarrier::emit(); + } else { + OrderAccess::fence(); + } + + if (jt->thread_state() != _thread_in_native || !jt->has_last_Java_frame()) { assert_lock_strong(Threads_lock); JfrSampleMonitor jsm(tl); if (jsm.is_waiting()) { From a90c7eee6f7e950edea4d94cf2b109fdb5e49909 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Tue, 13 Jan 2026 12:42:25 +0000 Subject: [PATCH 076/204] 8374969: Incorrect results of LoadStoreNode::adr_type and SCMemProj::adr_type Reviewed-by: roland, mhaessig --- src/hotspot/share/opto/memnode.cpp | 7 ++++++- src/hotspot/share/opto/memnode.hpp | 11 ++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 5b76f5b42cf..0d4fb6791a4 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -3913,7 +3913,6 @@ const Type* SCMemProjNode::Value(PhaseGVN* phase) const LoadStoreNode::LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* rt, uint required ) : Node(required), _type(rt), - _adr_type(at), _barrier_data(0) { init_req(MemNode::Control, c ); @@ -3921,6 +3920,7 @@ LoadStoreNode::LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const Ty init_req(MemNode::Address, adr); init_req(MemNode::ValueIn, val); init_class_id(Class_LoadStore); + DEBUG_ONLY(_adr_type = at; adr_type();) } //------------------------------Value----------------------------------------- @@ -3944,6 +3944,11 @@ const Type* LoadStoreNode::Value(PhaseGVN* phase) const { return bottom_type(); } +const TypePtr* LoadStoreNode::adr_type() const { + const TypePtr* cross_check = DEBUG_ONLY(_adr_type) NOT_DEBUG(nullptr); + return MemNode::calculate_adr_type(in(MemNode::Address)->bottom_type(), cross_check); +} + uint LoadStoreNode::ideal_reg() const { return _type->ideal_reg(); } diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index d554c037012..e84556528e6 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -797,11 +797,6 @@ public: virtual int Opcode() const; virtual bool is_CFG() const { return false; } virtual const Type *bottom_type() const {return Type::MEMORY;} - virtual const TypePtr *adr_type() const { - Node* ctrl = in(0); - if (ctrl == nullptr) return nullptr; // node is dead - return ctrl->in(MemNode::Memory)->adr_type(); - } virtual uint ideal_reg() const { return 0;} // memory projections don't have a register virtual const Type* Value(PhaseGVN* phase) const; #ifndef PRODUCT @@ -814,9 +809,11 @@ public: class LoadStoreNode : public Node { private: const Type* const _type; // What kind of value is loaded? - const TypePtr* _adr_type; // What kind of memory is being addressed? uint8_t _barrier_data; // Bit field with barrier information virtual uint size_of() const; // Size is bigger +#ifdef ASSERT + const TypePtr* _adr_type; // What kind of memory is being addressed? +#endif // ASSERT public: LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* rt, uint required ); virtual bool depends_only_on_test() const { return false; } @@ -824,7 +821,7 @@ public: virtual const Type *bottom_type() const { return _type; } virtual uint ideal_reg() const; - virtual const class TypePtr *adr_type() const { return _adr_type; } // returns bottom_type of address + virtual const TypePtr* adr_type() const; virtual const Type* Value(PhaseGVN* phase) const; bool result_not_used() const; From f7be1dcf296d28f8e004d180038ab715153a6c15 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Tue, 13 Jan 2026 13:33:41 +0000 Subject: [PATCH 077/204] 8375054: Removed "signed" property from jpackage app image file Reviewed-by: almatvee --- .../jdk/jpackage/internal/AppImageSigner.java | 1 + .../jdk/jpackage/internal/MacFromOptions.java | 23 ++-- .../internal/MacPackagingPipeline.java | 61 ++++++++-- .../internal/model/MacApplication.java | 6 +- .../internal/cli/OptionSpecBuilder.java | 39 +++++-- .../cli/StandardAppImageFileOption.java | 11 +- .../jpackage/internal/cli/StandardOption.java | 6 + .../internal/cli/StandardValidator.java | 7 +- .../jdk/jpackage/internal/cli/Validator.java | 54 ++++++--- .../resources/MainResources.properties | 1 + .../jpackage/internal/util}/MacBundle.java | 43 ++----- .../jdk/jpackage/test/AppImageFile.java | 13 +-- .../jdk/jpackage/test/JPackageCommand.java | 13 +-- .../helpers/jdk/jpackage/test/MacHelper.java | 67 +++++++---- .../jdk/jpackage/test/MacSignVerify.java | 51 ++++++--- .../jpackage/internal/AppImageFileTest.java | 8 +- .../jdk/jpackage/internal/cli/TestUtils.java | 29 ++++- .../jpackage/internal/cli/ValidatorTest.java | 107 ++++++++++++++---- .../jpackage/share/AppImagePackageTest.java | 24 +++- test/jdk/tools/jpackage/share/ErrorTest.java | 7 +- 20 files changed, 380 insertions(+), 191 deletions(-) rename src/jdk.jpackage/{macosx/classes/jdk/jpackage/internal => share/classes/jdk/jpackage/internal/util}/MacBundle.java (63%) diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java index 81e04ad7ed1..4c5edf43627 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java @@ -47,6 +47,7 @@ import jdk.jpackage.internal.model.ApplicationLayout; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.MacApplication; import jdk.jpackage.internal.model.RuntimeLayout; +import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.Result; import jdk.jpackage.internal.util.function.ExceptionBox; 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 b1094331740..cdf33d6dcba 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.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,8 +30,8 @@ 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.cli.StandardBundlingOperation.SIGN_MAC_APP_IMAGE; -import static jdk.jpackage.internal.cli.StandardOption.ICON; import static jdk.jpackage.internal.cli.StandardOption.APPCLASS; +import static jdk.jpackage.internal.cli.StandardOption.ICON; import static jdk.jpackage.internal.cli.StandardOption.MAC_APP_CATEGORY; import static jdk.jpackage.internal.cli.StandardOption.MAC_APP_IMAGE_SIGN_IDENTITY; import static jdk.jpackage.internal.cli.StandardOption.MAC_APP_STORE; @@ -52,11 +52,13 @@ import static jdk.jpackage.internal.model.StandardPackageType.MAC_PKG; import static jdk.jpackage.internal.util.function.ExceptionBox.toUnchecked; import java.nio.file.Path; +import java.util.List; import java.util.Objects; import java.util.Optional; import jdk.jpackage.internal.ApplicationBuilder.MainLauncherStartupInfo; import jdk.jpackage.internal.SigningIdentityBuilder.ExpiredCertificateException; import jdk.jpackage.internal.SigningIdentityBuilder.StandardCertificateSelector; +import jdk.jpackage.internal.cli.OptionValue; import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.cli.StandardFaOption; import jdk.jpackage.internal.model.ApplicationLaunchers; @@ -71,6 +73,7 @@ import jdk.jpackage.internal.model.MacPackage; import jdk.jpackage.internal.model.MacPkgPackage; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.RuntimeLayout; +import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.Result; import jdk.jpackage.internal.util.function.ExceptionBox; @@ -276,16 +279,12 @@ final class MacFromOptions { final var builder = new MacPackageBuilder(createPackageBuilder(options, app.app(), type)); - app.externalApp() - .map(ExternalApplication::extra) - .flatMap(MAC_SIGN::findIn) - .ifPresent(builder::predefinedAppImageSigned); - - PREDEFINED_RUNTIME_IMAGE.findIn(options) - .map(MacBundle::new) - .filter(MacBundle::isValid) - .map(MacBundle::isSigned) - .ifPresent(builder::predefinedAppImageSigned); + for (OptionValue ov : List.of(PREDEFINED_APP_IMAGE, PREDEFINED_RUNTIME_IMAGE)) { + ov.findIn(options) + .flatMap(MacBundle::fromPath) + .map(MacPackagingPipeline::isSigned) + .ifPresent(builder::predefinedAppImageSigned); + } return builder; } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java index 53f297282ba..a53df7f83c2 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.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,6 +76,8 @@ import jdk.jpackage.internal.model.MacPackage; import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.util.FileUtils; +import jdk.jpackage.internal.util.MacBundle; +import jdk.jpackage.internal.util.PListReader; import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.function.ThrowingConsumer; @@ -178,13 +180,10 @@ final class MacPackagingPipeline { builder.task(MacCopyAppImageTaskID.COPY_RUNTIME_JLILIB) .appImageAction(MacPackagingPipeline::copyJliLib).add(); - final var predefinedRuntimeBundle = Optional.of( - new MacBundle(p.predefinedAppImage().orElseThrow())).filter(MacBundle::isValid); - // Don't create ".package" file. disabledTasks.add(MacCopyAppImageTaskID.COPY_PACKAGE_FILE); - if (predefinedRuntimeBundle.isPresent()) { + if (MacBundle.fromPath(p.predefinedAppImage().orElseThrow()).isPresent()) { // The input runtime image is a macOS bundle. // Disable all alterations of the input bundle, but keep the signing enabled. disabledTasks.addAll(List.of(MacCopyAppImageTaskID.values())); @@ -195,7 +194,7 @@ final class MacPackagingPipeline { .appImageAction(MacPackagingPipeline::writeRuntimeInfoPlist).add(); } - if (predefinedRuntimeBundle.map(MacBundle::isSigned).orElse(false) && !((MacPackage)p).app().sign()) { + if (((MacPackage)p).predefinedAppImageSigned().orElse(false) && !((MacPackage)p).app().sign()) { // The input runtime is a signed bundle; explicit signing is not requested for the package. // Disable the signing, i.e. don't re-sign the input bundle. disabledTasks.add(MacCopyAppImageTaskID.COPY_SIGN); @@ -279,6 +278,30 @@ final class MacPackagingPipeline { } } + static boolean isSigned(MacBundle bundle) { + + var result = toSupplier(Executor.of( + "/usr/sbin/spctl", + "-vv", + "--raw", + "--assess", + "--type", "exec", + bundle.root().toString()).setQuiet(true).saveOutput(true).binaryOutput()::execute).get(); + + switch (result.getExitCode()) { + case 0, 3 -> { + // These exit codes are accompanied with valid plist xml. + return toSupplier(() -> { + return new PListReader(result.byteStdout()).findValue("assessment:originator").isPresent(); + }).get(); + } + default -> { + // Likely to be an "a sealed resource is missing or invalid" error. + return false; + } + } + } + private static void copyAppImage(MacPackage pkg, AppImageLayout srcAppImage, AppImageLayout dstAppImage) throws IOException { @@ -286,7 +309,7 @@ final class MacPackagingPipeline { final Optional srcMacBundle; if (pkg.isRuntimeInstaller()) { - srcMacBundle = MacBundle.fromAppImageLayout(srcAppImage); + srcMacBundle = macBundleFromAppImageLayout(srcAppImage); } else { srcMacBundle = Optional.empty(); } @@ -297,7 +320,7 @@ final class MacPackagingPipeline { try { FileUtils.copyRecursive( inputBundle.root(), - MacBundle.fromAppImageLayout(dstAppImage).orElseThrow().root(), + macBundleFromAppImageLayout(dstAppImage).orElseThrow().root(), LinkOption.NOFOLLOW_LINKS); } catch (IOException ex) { throw new UncheckedIOException(ex); @@ -415,7 +438,7 @@ final class MacPackagingPipeline { final var app = env.app(); - final var infoPlistFile = MacBundle.fromAppImageLayout(env.resolvedLayout()).orElseThrow().infoPlistFile(); + final var infoPlistFile = macBundleFromAppImageLayout(env.resolvedLayout()).orElseThrow().infoPlistFile(); Log.verbose(I18N.format("message.preparing-info-plist", PathUtils.normalizedAbsolutePathString(infoPlistFile))); @@ -468,7 +491,7 @@ final class MacPackagingPipeline { } final Runnable signAction = () -> { - AppImageSigner.createSigner(app, codesignConfigBuilder.create()).accept(MacBundle.fromAppImageLayout(env.resolvedLayout()).orElseThrow()); + AppImageSigner.createSigner(app, codesignConfigBuilder.create()).accept(macBundleFromAppImageLayout(env.resolvedLayout()).orElseThrow()); }; app.signingConfig().flatMap(AppImageSigningConfig::keychain).map(Keychain::new).ifPresentOrElse(keychain -> { @@ -550,7 +573,7 @@ final class MacPackagingPipeline { private static MacBundle runtimeBundle(AppImageBuildEnv env) { if (env.app().isRuntime()) { - return MacBundle.fromAppImageLayout(env.resolvedLayout()).orElseThrow(); + return macBundleFromAppImageLayout(env.resolvedLayout()).orElseThrow(); } else { return new MacBundle(((MacApplicationLayout)env.resolvedLayout()).runtimeRootDirectory()); } @@ -595,6 +618,22 @@ final class MacPackagingPipeline { }; } + private static Optional macBundleFromAppImageLayout(AppImageLayout layout) { + final var root = layout.rootDirectory(); + final var bundleSubdir = root.relativize(layout.runtimeDirectory()); + final var contentsDirname = Path.of("Contents"); + var bundleRoot = root; + for (int i = 0; i != bundleSubdir.getNameCount(); i++) { + var nameComponent = bundleSubdir.getName(i); + if (contentsDirname.equals(nameComponent)) { + return Optional.of(new MacBundle(bundleRoot)); + } else { + bundleRoot = bundleRoot.resolve(nameComponent); + } + } + return Optional.empty(); + } + private record TaskContextProxy(TaskContext delegate, boolean forApp, boolean copyAppImage) implements TaskContext { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacApplication.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacApplication.java index cfe10e8a012..e2b3d30b7ae 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacApplication.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacApplication.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,6 @@ import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toUnmodifiableMap; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.MAC_APP_STORE; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.MAC_MAIN_CLASS; -import static jdk.jpackage.internal.cli.StandardAppImageFileOption.MAC_SIGNED; import java.nio.file.Path; import java.util.Map; @@ -96,9 +95,6 @@ public interface MacApplication extends Application, MacApplicationMixin { } public enum ExtraAppImageFileField { - SIGNED(MAC_SIGNED, app -> { - return Optional.of(Boolean.toString(app.sign())); - }), APP_STORE(MAC_APP_STORE, app -> { return Optional.of(Boolean.toString(app.appStore())); }), diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java index e27d6472369..6cd1c05b57e 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.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 @@ -73,6 +73,7 @@ final class OptionSpecBuilder { valuePattern = other.valuePattern; converterBuilder = other.converterBuilder.copy(); validatorBuilder = other.validatorBuilder.copy(); + validator = other.validator; if (other.arrayDefaultValue != null) { arrayDefaultValue = Arrays.copyOf(other.arrayDefaultValue, other.arrayDefaultValue.length); @@ -135,10 +136,20 @@ final class OptionSpecBuilder { scope, OptionSpecBuilder.this.mergePolicy().orElse(MergePolicy.CONCATENATE), defaultArrayOptionalValue(), - Optional.of(arryValuePattern()), + Optional.of(arrayValuePattern()), OptionSpecBuilder.this.description().orElse("")); } + Optional> createValidator() { + return Optional.ofNullable(validator).or(() -> { + if (validatorBuilder.hasValidatingMethod()) { + return Optional.of(validatorBuilder.create()); + } else { + return Optional.empty(); + } + }); + } + OptionSpecBuilder tokenizer(String splitRegexp) { Objects.requireNonNull(splitRegexp); return tokenizer(str -> { @@ -162,11 +173,13 @@ final class OptionSpecBuilder { OptionSpecBuilder validatorExceptionFormatString(String v) { validatorBuilder.formatString(v); + validator = null; return this; } OptionSpecBuilder validatorExceptionFormatString(UnaryOperator mutator) { validatorBuilder.formatString(mutator.apply(validatorBuilder.formatString().orElse(null))); + validator = null; return this; } @@ -182,6 +195,7 @@ final class OptionSpecBuilder { OptionSpecBuilder validatorExceptionFactory(OptionValueExceptionFactory v) { validatorBuilder.exceptionFactory(v); + validator = null; return this; } @@ -225,18 +239,27 @@ final class OptionSpecBuilder { OptionSpecBuilder validator(Predicate v) { validatorBuilder.predicate(v::test); + validator = null; return this; } @SuppressWarnings("overloads") OptionSpecBuilder validator(Consumer v) { validatorBuilder.consumer(v::accept); + validator = null; return this; } @SuppressWarnings("overloads") OptionSpecBuilder validator(UnaryOperator> mutator) { validatorBuilder = mutator.apply(validatorBuilder); + validator = null; + return this; + } + + OptionSpecBuilder validator(Validator v) { + validatorBuilder.predicate(null).consumer(null); + validator = Objects.requireNonNull(v); return this; } @@ -247,6 +270,7 @@ final class OptionSpecBuilder { OptionSpecBuilder withoutValidator() { validatorBuilder.predicate(null).consumer(null); + validator = null; return this; } @@ -423,14 +447,6 @@ final class OptionSpecBuilder { } } - private Optional> createValidator() { - if (validatorBuilder.hasValidatingMethod()) { - return Optional.of(validatorBuilder.create()); - } else { - return Optional.empty(); - } - } - private OptionValueConverter createArrayConverter() { final var newBuilder = converterBuilder.copy(); newBuilder.tokenizer(Optional.ofNullable(arrayTokenizer).orElse(str -> { @@ -440,7 +456,7 @@ final class OptionSpecBuilder { return newBuilder.createArray(); } - private String arryValuePattern() { + private String arrayValuePattern() { final var elementValuePattern = OptionSpecBuilder.this.valuePattern().orElseThrow(); if (arrayValuePatternSeparator == null) { return elementValuePattern; @@ -468,6 +484,7 @@ final class OptionSpecBuilder { private String valuePattern; private OptionValueConverter.Builder converterBuilder = OptionValueConverter.build(); private Validator.Builder validatorBuilder = Validator.build(); + private Validator validator; private T[] arrayDefaultValue; private String arrayValuePatternSeparator; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java index 330a650e513..42f90536753 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.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 @@ -169,15 +169,6 @@ public final class StandardAppImageFileOption { .mutate(setPlatformScope(OperatingSystem.MACOS)) .toOptionValueBuilder().id(StandardOption.MAC_APP_STORE.id()).create(); - /** - * Is an application image is signed. macOS-only. - */ - public static final OptionValue MAC_SIGNED = booleanOption("signed") - .inScope(AppImageFileOptionScope.APP) - .mutate(setPlatformScope(OperatingSystem.MACOS)) - .toOptionValueBuilder().id(StandardOption.MAC_SIGN.id()).create(); - - public static final class InvalidOptionValueException extends RuntimeException { InvalidOptionValueException(String str, Throwable t) { 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 0fa0af296dc..dddaef8399b 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 @@ -233,6 +233,12 @@ public final class StandardOption { .mutate(createOptionSpecBuilderMutator((b, context) -> { if (context.os() == OperatingSystem.MACOS) { b.description("help.option.app-image" + resourceKeySuffix(context.os())); + var directoryValidator = b.createValidator().orElseThrow(); + var macBundleValidator = b + .validatorExceptionFormatString("error.parameter-not-mac-bundle") + .validator(StandardValidator.IS_VALID_MAC_BUNDLE) + .createValidator().orElseThrow(); + b.validator(Validator.and(directoryValidator, macBundleValidator)); } })) .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 0038740f9df..cfa97439592 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 @@ -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 @@ -38,6 +38,7 @@ import java.util.function.Consumer; import java.util.function.Predicate; import jdk.jpackage.internal.cli.Validator.ValidatingConsumerException; import jdk.jpackage.internal.util.FileUtils; +import jdk.jpackage.internal.util.MacBundle; final public class StandardValidator { @@ -138,6 +139,10 @@ final public class StandardValidator { return true; }; + public static Predicate IS_VALID_MAC_BUNDLE = path -> { + return MacBundle.fromPath(path).isPresent(); + }; + public static final class DirectoryListingIOException extends RuntimeException { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java index 0ddf0e1984f..91d9d03bd9f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.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,20 +24,55 @@ */ package jdk.jpackage.internal.cli; -import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.stream.Stream; @FunctionalInterface interface Validator { List validate(OptionName optionName, ParsedValue optionValue); - default Validator andThen(Validator after) { - return reduce(this, after); + default Validator and(Validator after) { + Objects.requireNonNull(after); + var before = this; + return (optionName, optionValue) -> { + return Stream.concat( + before.validate(optionName, optionValue).stream(), + after.validate(optionName, optionValue).stream() + ).toList(); + }; + } + + default Validator or(Validator after) { + Objects.requireNonNull(after); + var before = this; + return (optionName, optionValue) -> { + var bErrors = before.validate(optionName, optionValue); + if (bErrors.isEmpty()) { + return List.of(); + } + + var aErrors = after.validate(optionName, optionValue); + if (aErrors.isEmpty()) { + return List.of(); + } + + return Stream.concat(bErrors.stream(), aErrors.stream()).toList(); + }; + } + + @SuppressWarnings("unchecked") + static Validator and(Validator first, Validator second) { + return (Validator)first.and(second); + } + + @SuppressWarnings("unchecked") + static Validator or(Validator first, Validator second) { + return (Validator)first.or(second); } /** @@ -251,15 +286,4 @@ interface Validator { } } } - - @SafeVarargs - private static Validator reduce(Validator... validators) { - @SuppressWarnings("varargs") - var theValidators = List.of(validators); - return (optionName, optionValue) -> { - return theValidators.stream().map(validator -> { - return validator.validate(optionName, optionValue); - }).flatMap(Collection::stream).map(Exception.class::cast).toList(); - }; - } } 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 e97bee79e6e..245d3b892da 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 @@ -75,6 +75,7 @@ error.parameter-not-directory=The value "{0}" provided for parameter {1} is not error.parameter-not-empty-directory=The value "{0}" provided for parameter {1} is not an empty directory or non existent path 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.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/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/MacBundle.java similarity index 63% rename from src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java rename to src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/MacBundle.java index 723614f9bd6..95629e7d4b5 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundle.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/MacBundle.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,54 +23,49 @@ * questions. */ -package jdk.jpackage.internal; +package jdk.jpackage.internal.util; import java.nio.file.Files; import java.nio.file.Path; import java.util.Objects; import java.util.Optional; -import jdk.jpackage.internal.model.AppImageLayout; /** * An abstraction of macOS Application bundle. * * @see https://en.wikipedia.org/wiki/Bundle_(macOS)#Application_bundles */ -record MacBundle(Path root) { +public record MacBundle(Path root) { - MacBundle { + public MacBundle { Objects.requireNonNull(root); } - boolean isValid() { + public boolean isValid() { return Files.isDirectory(contentsDir()) && Files.isDirectory(macOsDir()) && Files.isRegularFile(infoPlistFile()); } - boolean isSigned() { - return Files.isDirectory(contentsDir().resolve("_CodeSignature")); - } - - Path contentsDir() { + public Path contentsDir() { return root.resolve("Contents"); } - Path homeDir() { + public Path homeDir() { return contentsDir().resolve("Home"); } - Path macOsDir() { + public Path macOsDir() { return contentsDir().resolve("MacOS"); } - Path resourcesDir() { + public Path resourcesDir() { return contentsDir().resolve("Resources"); } - Path infoPlistFile() { + public Path infoPlistFile() { return contentsDir().resolve("Info.plist"); } - static Optional fromPath(Path path) { + public static Optional fromPath(Path path) { var bundle = new MacBundle(path); if (bundle.isValid()) { return Optional.of(bundle); @@ -78,20 +73,4 @@ record MacBundle(Path root) { return Optional.empty(); } } - - static Optional fromAppImageLayout(AppImageLayout layout) { - final var root = layout.rootDirectory(); - final var bundleSubdir = root.relativize(layout.runtimeDirectory()); - final var contentsDirname = Path.of("Contents"); - var bundleRoot = root; - for (int i = 0; i != bundleSubdir.getNameCount(); i++) { - var nameComponent = bundleSubdir.getName(i); - if (contentsDirname.equals(nameComponent)) { - return Optional.of(new MacBundle(bundleRoot)); - } else { - bundleRoot = bundleRoot.resolve(nameComponent); - } - } - return Optional.empty(); - } } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java index 1c6c0ce4447..55b13a06620 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/AppImageFile.java @@ -47,7 +47,7 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; public record AppImageFile(String mainLauncherName, Optional mainLauncherClassName, - String version, boolean macSigned, boolean macAppStore, Map> launchers) { + String version, boolean macAppStore, Map> launchers) { public static Path getPathInAppImage(Path appImageDir) { return ApplicationLayout.platformAppImage() @@ -66,7 +66,7 @@ public record AppImageFile(String mainLauncherName, Optional mainLaunche } public AppImageFile(String mainLauncherName, Optional mainLauncherClassName) { - this(mainLauncherName, mainLauncherClassName, "1.0", false, false, Map.of(mainLauncherName, Map.of())); + this(mainLauncherName, mainLauncherClassName, "1.0", false, Map.of(mainLauncherName, Map.of())); } public AppImageFile(String mainLauncherName, String mainLauncherClassName) { @@ -103,10 +103,6 @@ public record AppImageFile(String mainLauncherName, Optional mainLaunche xml.writeEndElement(); })); - xml.writeStartElement("signed"); - xml.writeCharacters(Boolean.toString(macSigned)); - xml.writeEndElement(); - xml.writeStartElement("app-store"); xml.writeCharacters(Boolean.toString(macAppStore)); xml.writeEndElement(); @@ -140,10 +136,6 @@ public record AppImageFile(String mainLauncherName, Optional mainLaunche var mainLauncherClassName = Optional.ofNullable(xPath.evaluate( "/jpackage-state/main-class/text()", doc)); - var macSigned = Optional.ofNullable(xPath.evaluate( - "/jpackage-state/signed/text()", doc)).map( - Boolean::parseBoolean).orElse(false); - var macAppStore = Optional.ofNullable(xPath.evaluate( "/jpackage-state/app-store/text()", doc)).map( Boolean::parseBoolean).orElse(false); @@ -171,7 +163,6 @@ public record AppImageFile(String mainLauncherName, Optional mainLaunche mainLauncherName, mainLauncherClassName, version, - macSigned, macAppStore, Collections.unmodifiableMap(launchers)); 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 c5c4f87b097..9cfb75bcdb3 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -1385,7 +1385,7 @@ public class JPackageCommand extends CommandArguments { if (!isImagePackageType() && hasArgument("--app-image")) { // Build native macOS package from an external app image. // If the external app image is signed, ".jpackage.xml" file should be kept, otherwise removed. - return AppImageFile.load(Path.of(getArgumentValue("--app-image"))).macSigned(); + return MacHelper.isBundleSigned(Path.of(getArgumentValue("--app-image"))); } } @@ -1406,13 +1406,8 @@ public class JPackageCommand extends CommandArguments { final AppImageFile aif = AppImageFile.load(rootDir); if (TKit.isOSX()) { - boolean expectedValue = MacHelper.appImageSigned(this); - boolean actualValue = aif.macSigned(); - TKit.assertEquals(expectedValue, actualValue, - "Check for unexpected value of property in app image file"); - - expectedValue = hasArgument("--mac-app-store"); - actualValue = aif.macAppStore(); + var expectedValue = hasArgument("--mac-app-store"); + var actualValue = aif.macAppStore(); TKit.assertEquals(expectedValue, actualValue, "Check for unexpected value of property in app image file"); } @@ -1437,7 +1432,7 @@ public class JPackageCommand extends CommandArguments { } else { if (TKit.isOSX() && hasArgument("--app-image")) { String appImage = getArgumentValue("--app-image"); - if (AppImageFile.load(Path.of(appImage)).macSigned()) { + if (MacHelper.isBundleSigned(Path.of(appImage))) { assertFileNotInAppImage(lookupPath); } else { assertFileInAppImage(lookupPath); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java index 6a5be77457a..1cb5532d46a 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java @@ -33,6 +33,7 @@ import static jdk.jpackage.internal.util.PListWriter.writeStringArray; import static jdk.jpackage.internal.util.PListWriter.writeStringOptional; import static jdk.jpackage.internal.util.XmlUtils.initDocumentBuilder; import static jdk.jpackage.internal.util.XmlUtils.toXmlConsumer; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import static jdk.jpackage.internal.util.function.ThrowingRunnable.toRunnable; import java.io.ByteArrayInputStream; @@ -45,6 +46,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -59,14 +61,13 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.UnaryOperator; -import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; import jdk.jpackage.internal.util.FileUtils; +import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.PListReader; import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.RetryExecutor; @@ -89,8 +90,8 @@ public final class MacHelper { // See JDK-8373105. "hdiutil" does not handle such cases very good. final var mountRoot = TKit.createTempDirectory("mountRoot"); - // Explode DMG assuming this can require interaction, thus use `yes`. - final var attachStdout = Executor.of("sh", "-c", String.join(" ", + // Explode the DMG assuming this can require interaction if the DMG has a license, thus use `yes`. + final var attachExec = Executor.of("sh", "-c", String.join(" ", "yes", "|", "/usr/bin/hdiutil", @@ -99,14 +100,34 @@ public final class MacHelper { "-mountroot", PathUtils.normalizedAbsolutePathString(mountRoot), "-nobrowse", "-plist" - )).saveOutput().storeOutputInFiles().executeAndRepeatUntilExitCode(0, 10, 6).stdout(); + )).saveOutput().storeOutputInFiles().binaryOutput(); + + final var attachResult = attachExec.executeAndRepeatUntilExitCode(0, 10, 6); final Path mountPoint; boolean mountPointInitialized = false; try { + byte[] stdout = attachResult.byteStdout(); + + // If the DMG has a license, it will be printed to the stdout before the plist content. + // All bytes before the XML declaration of the plist must be skipped. + // We need to find the location of the {'<', '?', 'x', 'm', 'l'} byte array + // (the XML declaration) in the captured binary stdout. + // Instead of crafting an ad-hoc function that operates on byte arrays, + // we will convert the byte array into a String instance using + // an 8-bit character set (ISO-8859-1) and use the standard String#indexOf(). + var startPlistIndex = new String(stdout, StandardCharsets.ISO_8859_1).indexOf(" 0) { + plistXml = Arrays.copyOfRange(stdout, startPlistIndex, stdout.length); + } else { + plistXml = stdout; + } + // One of "dict" items of "system-entities" array property should contain "mount-point" string property. - mountPoint = readPList(attachStdout).queryArrayValue("system-entities", false) + mountPoint = readPList(plistXml).queryArrayValue("system-entities", false) .map(PListReader.class::cast) .map(dict -> { return dict.findValue("mount-point"); @@ -117,7 +138,7 @@ public final class MacHelper { } finally { if (!mountPointInitialized) { TKit.trace("Unexpected plist file missing `system-entities` array:"); - attachStdout.forEach(TKit::trace); + attachResult.toCharacterResult(attachExec.charset(), false).stdout().forEach(TKit::trace); TKit.trace("Done"); } } @@ -168,19 +189,13 @@ public final class MacHelper { public static PListReader readPList(Path path) { TKit.assertReadableFileExists(path); - return ThrowingSupplier.toSupplier(() -> readPList(Files.readAllLines( - path))).get(); + return readPList(toFunction(Files::readAllBytes).apply(path)); } - public static PListReader readPList(List lines) { - return readPList(lines.stream()); - } - - public static PListReader readPList(Stream lines) { - return ThrowingSupplier.toSupplier(() -> new PListReader(lines - // Skip leading lines before xml declaration - .dropWhile(Pattern.compile("\\s?<\\?xml\\b.+\\?>").asPredicate().negate()) - .collect(Collectors.joining()).getBytes(StandardCharsets.UTF_8))).get(); + public static PListReader readPList(byte[] xml) { + return ThrowingSupplier.toSupplier(() -> { + return new PListReader(xml); + }).get(); } public static Map flatMapPList(PListReader plistReader) { @@ -265,13 +280,13 @@ public final class MacHelper { throw new UnsupportedOperationException(); } - var runtimeImage = Optional.ofNullable(cmd.getArgumentValue("--runtime-image")).map(Path::of); + var runtimeImageBundle = Optional.ofNullable(cmd.getArgumentValue("--runtime-image")).map(Path::of).flatMap(MacBundle::fromPath); var appImage = Optional.ofNullable(cmd.getArgumentValue("--app-image")).map(Path::of); - if (cmd.isRuntime() && Files.isDirectory(runtimeImage.orElseThrow().resolve("Contents/_CodeSignature"))) { + if (cmd.isRuntime() && runtimeImageBundle.map(MacHelper::isBundleSigned).orElse(false)) { // If the predefined runtime is a signed bundle, bundled image should be signed too. return true; - } else if (appImage.map(AppImageFile::load).map(AppImageFile::macSigned).orElse(false)) { + } else if (appImage.map(MacHelper::isBundleSigned).orElse(false)) { // The external app image is signed, so the app image is signed too. return true; } @@ -301,6 +316,14 @@ public final class MacHelper { }).run(); } + static boolean isBundleSigned(Path bundleRoot) { + return isBundleSigned(MacBundle.fromPath(bundleRoot).orElseThrow(IllegalArgumentException::new)); + } + + static boolean isBundleSigned(MacBundle bundle) { + return MacSignVerify.findSpctlSignOrigin(MacSignVerify.SpctlType.EXEC, bundle.root(), true).isPresent(); + } + private static void createFaPListFragmentFromFaProperties(JPackageCommand cmd, XMLStreamWriter xml) throws XMLStreamException, IOException { @@ -383,7 +406,7 @@ public final class MacHelper { var predefinedAppImage = Path.of(Optional.ofNullable(cmd.getArgumentValue("--app-image")).orElseThrow(IllegalArgumentException::new)); - var plistPath = ApplicationLayout.macAppImage().resolveAt(predefinedAppImage).contentDirectory().resolve("Info.plist"); + var plistPath = MacBundle.fromPath(predefinedAppImage).orElseThrow().infoPlistFile(); try (var plistStream = Files.newInputStream(plistPath)) { var plist = new PListReader(initDocumentBuilder().parse(plistStream)); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java index 0ecfd4c3432..9c469c9362e 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java @@ -22,7 +22,6 @@ */ package jdk.jpackage.test; -import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; import static jdk.jpackage.test.MacSign.DigestAlgorithm.SHA256; import java.nio.file.Path; @@ -30,10 +29,8 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HexFormat; import java.util.List; -import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.regex.Pattern; import jdk.jpackage.internal.util.PListReader; import jdk.jpackage.test.MacSign.CertificateHash; @@ -66,7 +63,7 @@ public final class MacSignVerify { }); // Set to "null" if the sign origin is not found, instead of bailing out with an exception. - // Let is fail in the following TKit.assertEquals() call with a proper log message. + // Let it fail in the following TKit.assertEquals() call with a proper log message. var signOrigin = findSpctlSignOrigin(SpctlType.EXEC, bundleRoot).orElse(null); TKit.assertEquals(certRequest.name(), signOrigin, @@ -92,10 +89,14 @@ public final class MacSignVerify { } public static Optional findEntitlements(Path path) { - final var exec = Executor.of("/usr/bin/codesign", "-d", "--entitlements", "-", "--xml", path.toString()).saveOutput().dumpOutput(); + final var exec = Executor.of( + "/usr/bin/codesign", + "-d", + "--entitlements", "-", + "--xml", path.toString()).saveOutput().dumpOutput().binaryOutput(); final var result = exec.execute(); - var xml = result.stdout(); - if (xml.isEmpty()) { + var xml = result.byteStdout(); + if (xml.length == 0) { return Optional.empty(); } else { return Optional.of(MacHelper.readPList(xml)); @@ -135,17 +136,33 @@ public final class MacSignVerify { public static final String ADHOC_SIGN_ORIGIN = "-"; public static Optional findSpctlSignOrigin(SpctlType type, Path path) { - final var exec = Executor.of("/usr/sbin/spctl", "-vv", "--raw", "--assess", "--type", type.value(), path.toString()).saveOutput().discardStderr(); - final var result = exec.executeWithoutExitCodeCheck(); - TKit.assertTrue(Set.of(0, 3).contains(result.getExitCode()), - String.format("Check exit code of command %s is either 0 or 3", exec.getPrintableCommandLine())); - return toSupplier(() -> { - try { - return Optional.of(new PListReader(String.join("", result.getOutput()).getBytes()).queryValue("assessment:originator")); - } catch (NoSuchElementException ex) { - return Optional.empty(); + return findSpctlSignOrigin(type, path, false); + } + + public static Optional findSpctlSignOrigin(SpctlType type, Path path, boolean acceptBrokenSignature) { + final var exec = Executor.of( + "/usr/sbin/spctl", + "-vv", + "--raw", + "--assess", + "--type", type.value(), + path.toString()).saveOutput().discardStderr().binaryOutput(); + Executor.Result result; + if (acceptBrokenSignature) { + result = exec.executeWithoutExitCodeCheck(); + switch (result.getExitCode()) { + case 0, 3 -> { + // NOP + } + default -> { + // No plist XML to process. + return Optional.empty(); + } } - }).get(); + } else { + result = exec.execute(0, 3); + } + return MacHelper.readPList(result.byteStdout()).findValue("assessment:originator"); } public static Optional findCodesignSignOrigin(Path path) { diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java index 375c6aa637a..53f4b9b95aa 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.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 @@ -29,7 +29,6 @@ import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_AS_S import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LAUNCHER_NAME; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.LINUX_LAUNCHER_SHORTCUT; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.MAC_APP_STORE; -import static jdk.jpackage.internal.cli.StandardAppImageFileOption.MAC_SIGNED; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.WIN_LAUNCHER_DESKTOP_SHORTCUT; import static jdk.jpackage.internal.cli.StandardAppImageFileOption.WIN_LAUNCHER_MENU_SHORTCUT; import static jdk.jpackage.internal.cli.StandardOption.APPCLASS; @@ -514,7 +513,6 @@ public class AppImageFileTest { "Foo", "", "property-x", - "true", "False", "", " Quick brown fox", @@ -546,8 +544,7 @@ public class AppImageFileTest { .addExtra(WIN_LAUNCHER_MENU_SHORTCUT, new LauncherShortcut(LauncherShortcutStartupDirectory.APP_DIR)).commit()).create()); testCases.add(builder.os(OperatingSystem.MACOS).expect(appBuilder.get().commit() - .addExtra(MAC_APP_STORE, false) - .addExtra(MAC_SIGNED, true)).create()); + .addExtra(MAC_APP_STORE, false)).create()); return testCases; } @@ -580,7 +577,6 @@ public class AppImageFileTest { "OverwrittenMain", "Main", "property-x", - "true", "", " foo", " service-launcher description", diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/TestUtils.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/TestUtils.java index d3e9ecb09e9..2acf45b663d 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/TestUtils.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/TestUtils.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 @@ -34,6 +34,8 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.UnaryOperator; import java.util.stream.StreamSupport; +import jdk.jpackage.internal.cli.Validator.ParsedValue; +import jdk.jpackage.internal.cli.Validator.ValidatorException; import jdk.jpackage.test.JUnitUtils; final class TestUtils { @@ -152,6 +154,31 @@ final class TestUtils { } + static final class RecordingValidator implements Validator { + + RecordingValidator(Validator validator) { + this.validator = Objects.requireNonNull(validator); + } + + @Override + public List validate(OptionName optionName, ParsedValue optionValue) { + counter++; + return validator.validate(optionName, optionValue); + } + + int counter() { + return counter; + } + + void resetCounter() { + counter = 0; + } + + private final Validator validator; + private int counter; + } + + private record RecordingExceptionFactory(OptionValueExceptionFactory factory, Consumer sink) implements OptionValueExceptionFactory { diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/ValidatorTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/ValidatorTest.java index d4c48df6a0c..d8d69027f77 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/ValidatorTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/ValidatorTest.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 @@ -32,10 +32,11 @@ import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Stream; import jdk.jpackage.internal.cli.TestUtils.TestException; +import jdk.jpackage.internal.cli.TestUtils.RecordingValidator; import jdk.jpackage.internal.cli.Validator.ParsedValue; import jdk.jpackage.internal.cli.Validator.ValidatingConsumerException; import jdk.jpackage.internal.cli.Validator.ValidatorException; @@ -187,46 +188,97 @@ public class ValidatorTest { } @Test - public void test_andThen() { - - Function> createFailingValidator = exceptionMessage -> { - Objects.requireNonNull(exceptionMessage); - var exceptionFactory = OptionValueExceptionFactory.build().ctor(TestException::new).messageFormatter((_, _) -> { - return exceptionMessage; - }).create(); - - return Validator.build() - .predicate(_ -> false) - .formatString("") - .exceptionFactory(exceptionFactory).create(); - }; + public void test_and() { Function, List> validate = validator -> { return validator.validate(OptionName.of("a"), ParsedValue.create("str", StringToken.of("str"))); }; - var pass = Validator.build().predicate(_ -> true).create(); + var pass = new RecordingValidator<>(Validator.build().predicate(_ -> true).create()); - var foo = createFailingValidator.apply("foo"); - var bar = createFailingValidator.apply("bar"); - var buz = createFailingValidator.apply("buz"); + var foo = failingValidator("foo"); + var bar = failingValidator("bar"); + var buz = failingValidator("buz"); assertExceptionListEquals(List.of( new TestException("foo"), new TestException("bar"), new TestException("buz") - ), validate.apply(foo.andThen(bar).andThen(pass).andThen(buz))); + ), validate.apply(foo.and(bar).and(pass).and(buz))); + assertEquals(1, pass.counter()); + pass.resetCounter(); assertExceptionListEquals(List.of( new TestException("bar"), new TestException("buz"), new TestException("foo") - ), validate.apply(pass.andThen(bar).andThen(buz).andThen(foo))); + ), validate.apply(pass.and(bar).and(buz).and(foo))); + assertEquals(1, pass.counter()); assertExceptionListEquals(List.of( new TestException("foo"), new TestException("foo") - ), validate.apply(foo.andThen(foo))); + ), validate.apply(foo.and(foo))); + + pass.resetCounter(); + assertExceptionListEquals(List.of( + ), validate.apply(pass.and(pass))); + assertEquals(2, pass.counter()); + } + + @Test + public void test_or() { + + Function, List> validate = validator -> { + return validator.validate(OptionName.of("a"), ParsedValue.create("str", StringToken.of("str"))); + }; + + var pass = new RecordingValidator<>(Validator.build().predicate(_ -> true).create()); + + var foo = new RecordingValidator<>(failingValidator("foo")); + var bar = new RecordingValidator<>(failingValidator("bar")); + var buz = new RecordingValidator<>(failingValidator("buz")); + + Runnable resetCounters = () -> { + Stream.of(pass, foo, bar, buz).forEach(RecordingValidator::resetCounter); + }; + + assertExceptionListEquals(List.of( + new TestException("foo"), + new TestException("bar"), + new TestException("buz") + ), validate.apply(foo.or(bar).or(buz))); + assertEquals(1, foo.counter()); + assertEquals(1, bar.counter()); + assertEquals(1, buz.counter()); + + resetCounters.run(); + assertExceptionListEquals(List.of( + ), validate.apply(foo.or(bar).or(pass).or(buz))); + assertEquals(1, foo.counter()); + assertEquals(1, bar.counter()); + assertEquals(1, pass.counter()); + assertEquals(0, buz.counter()); + + resetCounters.run(); + assertExceptionListEquals(List.of( + ), validate.apply(pass.or(bar).or(buz).or(foo))); + assertEquals(1, pass.counter()); + assertEquals(0, bar.counter()); + assertEquals(0, buz.counter()); + assertEquals(0, foo.counter()); + + resetCounters.run(); + assertExceptionListEquals(List.of( + new TestException("foo"), + new TestException("foo") + ), validate.apply(foo.or(foo))); + assertEquals(2, foo.counter()); + + resetCounters.run(); + assertExceptionListEquals(List.of( + ), validate.apply(pass.or(pass))); + assertEquals(1, pass.counter()); } @ParameterizedTest @@ -269,6 +321,17 @@ public class ValidatorTest { return data; } + private static Validator failingValidator(String exceptionMessage) { + var exceptionFactory = OptionValueExceptionFactory.build().ctor(TestException::new).messageFormatter((_, _) -> { + return exceptionMessage; + }).create(); + + return Validator.build() + .predicate(_ -> false) + .formatString("") + .exceptionFactory(exceptionFactory).create(); + } + static final class FooException extends Exception { diff --git a/test/jdk/tools/jpackage/share/AppImagePackageTest.java b/test/jdk/tools/jpackage/share/AppImagePackageTest.java index bfd731b67d5..ce2300c92d1 100644 --- a/test/jdk/tools/jpackage/share/AppImagePackageTest.java +++ b/test/jdk/tools/jpackage/share/AppImagePackageTest.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 @@ -26,6 +26,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.function.Predicate; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.XmlUtils; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.Test; @@ -133,8 +134,7 @@ public class AppImagePackageTest { */ @Test public static void testBadAppImage() throws IOException { - Path appImageDir = TKit.createTempDirectory("appimage"); - Files.createFile(appImageDir.resolve("foo")); + Path appImageDir = createInvalidAppImage(); configureBadAppImage(appImageDir).addInitializer(cmd -> { cmd.removeArgumentWithValue("--name"); }).run(Action.CREATE); @@ -145,8 +145,7 @@ public class AppImagePackageTest { */ @Test public static void testBadAppImage2() throws IOException { - Path appImageDir = TKit.createTempDirectory("appimage"); - Files.createFile(appImageDir.resolve("foo")); + Path appImageDir = createInvalidAppImage(); configureBadAppImage(appImageDir).run(Action.CREATE); } @@ -227,4 +226,19 @@ public class AppImagePackageTest { + TKit.ICON_SUFFIX)); } + private static Path createInvalidAppImage() throws IOException { + Path appImageDir = TKit.createTempDirectory("appimage"); + if (TKit.isOSX()) { + // Create minimal macOS bundle to prevent jpackage bail out early + // with "error.parameter-not-mac-bundle" error. + var bundle = new MacBundle(appImageDir); + Files.createDirectories(bundle.macOsDir()); + Files.createFile(bundle.infoPlistFile()); + } else { + Files.createFile(appImageDir.resolve("foo")); + } + + return appImageDir; + } + } diff --git a/test/jdk/tools/jpackage/share/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java index ca1189a191e..f31ac42dea0 100644 --- a/test/jdk/tools/jpackage/share/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -643,7 +643,12 @@ public final class ErrorTest { .error("message.invalid-identifier", "#1"), // 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") + .error("ERR_MissingJLinkOptMacAppStore", "--strip-native-commands"), + // Predefined app image must be a valid macOS bundle. + testSpec().noAppDesc().nativeType().addArgs("--app-image", Token.EMPTY_DIR.token()) + .error("error.parameter-not-mac-bundle", JPackageCommand.cannedArgument(cmd -> { + return Path.of(cmd.getArgumentValue("--app-image")); + }, Token.EMPTY_DIR.token()), "--app-image") ).map(TestSpec.Builder::create).toList()); macInvalidRuntime(testCases::add); From 47029ccfec988e0a9298e35dcc729d9eeffc45e1 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Tue, 13 Jan 2026 13:36:44 +0000 Subject: [PATCH 078/204] 8375050: Simplify process management in jpackage tests Reviewed-by: almatvee --- .../helpers/jdk/jpackage/test/Executor.java | 33 ++----- .../helpers/jdk/jpackage/test/HelloApp.java | 30 ++++--- .../jdk/jpackage/test/WindowsHelper.java | 87 ------------------- .../macosx/ArgumentsFilteringTest.java | 12 ++- .../tools/jpackage/share/MainClassTest.java | 7 +- .../jpackage/windows/Win8301247Test.java | 47 +++++++--- .../jpackage/windows/WinChildProcessTest.java | 20 ++--- .../jpackage/windows/WinNoRestartTest.java | 45 ++++++++-- 8 files changed, 111 insertions(+), 170 deletions(-) diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java index 5d3033e2e8c..6e94133a543 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java @@ -35,6 +35,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import java.util.function.Supplier; import java.util.spi.ToolProvider; import java.util.stream.IntStream; @@ -109,15 +110,6 @@ public final class Executor extends CommandArguments { return this; } - public Executor setWinRunWithEnglishOutput(boolean value) { - if (!TKit.isWindows()) { - throw new UnsupportedOperationException( - "setWinRunWithEnglishOutput is only valid on Windows platform"); - } - winEnglishOutput = value; - return this; - } - public Executor setWindowsTmpDir(String tmp) { if (!TKit.isWindows()) { throw new UnsupportedOperationException( @@ -195,6 +187,11 @@ public final class Executor extends CommandArguments { return storeOutputInFiles(true); } + public Executor processListener(Consumer v) { + commandOutputControl.processListener(v); + return this; + } + public record Result(CommandOutputControl.Result base) { public Result { Objects.requireNonNull(base); @@ -310,11 +307,6 @@ public final class Executor extends CommandArguments { "Can't change directory when using tool provider"); } - if (toolProvider != null && winEnglishOutput) { - throw new IllegalArgumentException( - "Can't change locale when using tool provider"); - } - return ThrowingSupplier.toSupplier(() -> { if (toolProvider != null) { return runToolProvider(); @@ -434,17 +426,8 @@ public final class Executor extends CommandArguments { return executable.toAbsolutePath(); } - private List prefixCommandLineArgs() { - if (winEnglishOutput) { - return List.of("cmd.exe", "/c", "chcp", "437", ">nul", "2>&1", "&&"); - } else { - return List.of(); - } - } - private Result runExecutable() throws IOException, InterruptedException { List command = new ArrayList<>(); - command.addAll(prefixCommandLineArgs()); command.add(executablePath().toString()); command.addAll(args); ProcessBuilder builder = new ProcessBuilder(command); @@ -522,8 +505,7 @@ public final class Executor extends CommandArguments { exec = executablePath().toString(); } - var cmdline = Stream.of(prefixCommandLineArgs(), List.of(exec), args).flatMap( - List::stream).toList(); + var cmdline = Stream.of(List.of(exec), args).flatMap(List::stream).toList(); return String.format(format, CommandLineFormat.DEFAULT.apply(cmdline), cmdline.size()); } @@ -559,6 +541,5 @@ public final class Executor extends CommandArguments { private Path directory; private Set removeEnvVars = new HashSet<>(); private Map setEnvVars = new HashMap<>(); - private boolean winEnglishOutput; private String winTmpDir = null; } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java index 6c7b6a25255..bc731e18136 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.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 @@ -35,6 +35,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.regex.Matcher; @@ -315,37 +316,33 @@ public final class HelloApp { public static void executeLauncherAndVerifyOutput(JPackageCommand cmd, String... args) { - AppOutputVerifier av = assertMainLauncher(cmd, args); - if (av != null) { + assertMainLauncher(cmd, args).ifPresent(av -> { av.executeAndVerifyOutput(args); - } + }); } public static Executor.Result executeLauncher(JPackageCommand cmd, String... args) { - AppOutputVerifier av = assertMainLauncher(cmd, args); - if (av != null) { + return assertMainLauncher(cmd, args).map(av -> { return av.saveOutput(true).execute(args); - } else { - return null; - } + }).orElseThrow(); } - public static AppOutputVerifier assertMainLauncher(JPackageCommand cmd, + public static Optional assertMainLauncher(JPackageCommand cmd, String... args) { final Path launcherPath = cmd.appLauncherPath(); if (!cmd.canRunLauncher(String.format("Not running [%s] launcher", launcherPath))) { - return null; + return Optional.empty(); } - return assertApp(launcherPath) + return Optional.of(assertApp(launcherPath) .addDefaultArguments(Optional .ofNullable(cmd.getAllArgumentValues("--arguments")) .orElseGet(() -> new String[0])) .addJavaOptions(Optional .ofNullable(cmd.getAllArgumentValues("--java-options")) - .orElseGet(() -> new String[0])); + .orElseGet(() -> new String[0]))); } @@ -426,6 +423,11 @@ public final class HelloApp { .collect(Collectors.toList())); } + public AppOutputVerifier processListener(Consumer v) { + processListener = v; + return this; + } + public void verifyOutput(String... args) { final List launcherArgs = List.of(args); final List appArgs; @@ -479,6 +481,7 @@ public final class HelloApp { .saveOutput(saveOutput) .dumpOutput() .setExecutable(executablePath) + .processListener(processListener) .addArguments(List.of(args)); env.forEach((envVarName, envVarValue) -> { @@ -493,6 +496,7 @@ public final class HelloApp { private final Path launcherPath; private Path outputFilePath; private int expectedExitCode; + private Consumer processListener; private final List defaultLauncherArgs; private final Map params; private final Map env; 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 f9fbf285b49..c7b55ed1de7 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java @@ -42,8 +42,6 @@ import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Stream; import jdk.jpackage.internal.util.function.ThrowingRunnable; import jdk.jpackage.test.PackageTest.PackageHandlers; @@ -306,91 +304,6 @@ public class WindowsHelper { "Failed to get file description of [%s]", pathToExeFile)); } - public static void killProcess(long pid) { - Executor.of("taskkill", "/F", "/PID", Long.toString(pid)).dumpOutput(true).execute(); - } - - public static void killAppLauncherProcess(JPackageCommand cmd, - String launcherName, int expectedCount) { - var pids = findAppLauncherPIDs(cmd, launcherName); - try { - TKit.assertEquals(expectedCount, pids.length, String.format( - "Check [%d] %s app launcher processes found running", - expectedCount, Optional.ofNullable(launcherName).map( - str -> "[" + str + "]").orElse("

      "))); - } finally { - if (pids.length != 0) { - killProcess(pids[0]); - } - } - } - - private static long[] findAppLauncherPIDs(JPackageCommand cmd, String launcherName) { - // Get the list of PIDs and PPIDs of app launcher processes. Run setWinRunWithEnglishOutput(true) for JDK-8344275. - // powershell -NoLogo -NoProfile -NonInteractive -Command - // "Get-CimInstance Win32_Process -Filter \"Name = 'foo.exe'\" | select ProcessID,ParentProcessID" - String command = "Get-CimInstance Win32_Process -Filter \\\"Name = '" - + cmd.appLauncherPath(launcherName).getFileName().toString() - + "'\\\" | select ProcessID,ParentProcessID"; - List output = Executor.of("powershell", "-NoLogo", "-NoProfile", "-NonInteractive", "-Command", command) - .dumpOutput(true).saveOutput().setWinRunWithEnglishOutput(true).executeAndGetOutput(); - - if (output.size() < 1) { - return new long[0]; - } - - String[] headers = Stream.of(output.get(1).split("\\s+", 2)).map( - String::trim).map(String::toLowerCase).toArray(String[]::new); - Pattern pattern; - if (headers[0].equals("parentprocessid") && headers[1].equals( - "processid")) { - pattern = Pattern.compile("^\\s+(?\\d+)\\s+(?\\d+)$"); - } else if (headers[1].equals("parentprocessid") && headers[0].equals( - "processid")) { - pattern = Pattern.compile("^\\s+(?\\d+)\\s+(?\\d+)$"); - } else { - throw new RuntimeException( - "Unrecognizable output of \'Get-CimInstance Win32_Process\' command"); - } - - List processes = output.stream().skip(3).map(line -> { - Matcher m = pattern.matcher(line); - long[] pids = null; - if (m.matches()) { - pids = new long[]{Long.parseLong(m.group("pid")), Long. - parseLong(m.group("ppid"))}; - } - return pids; - }).filter(Objects::nonNull).toList(); - - switch (processes.size()) { - case 2 -> { - final long parentPID; - final long childPID; - if (processes.get(0)[0] == processes.get(1)[1]) { - parentPID = processes.get(0)[0]; - childPID = processes.get(1)[0]; - } else if (processes.get(1)[0] == processes.get(0)[1]) { - parentPID = processes.get(1)[0]; - childPID = processes.get(0)[0]; - } else { - TKit.assertUnexpected("App launcher processes unrelated"); - return null; // Unreachable - } - return new long[]{parentPID, childPID}; - } - case 1 -> { - return new long[]{processes.get(0)[0]}; - } - default -> { - TKit.assertUnexpected(String.format( - "Unexpected number of running processes [%d]", - processes.size())); - return null; // Unreachable - } - } - } - static boolean isUserLocalInstall(JPackageCommand cmd) { return cmd.hasArgument("--win-per-user-install"); } diff --git a/test/jdk/tools/jpackage/macosx/ArgumentsFilteringTest.java b/test/jdk/tools/jpackage/macosx/ArgumentsFilteringTest.java index 1a42a30c00e..e4adf3b9616 100644 --- a/test/jdk/tools/jpackage/macosx/ArgumentsFilteringTest.java +++ b/test/jdk/tools/jpackage/macosx/ArgumentsFilteringTest.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 @@ -49,11 +49,10 @@ public class ArgumentsFilteringTest { public void test1() { JPackageCommand cmd = JPackageCommand.helloAppImage(); cmd.executeAndAssertHelloAppImageCreated(); - var appVerifier = HelloApp.assertMainLauncher(cmd); - if (appVerifier != null) { + HelloApp.assertMainLauncher(cmd).ifPresent(appVerifier -> { appVerifier.execute("-psn_1_1"); appVerifier.verifyOutput(); - } + }); } @Test @@ -61,10 +60,9 @@ public class ArgumentsFilteringTest { JPackageCommand cmd = JPackageCommand.helloAppImage() .addArguments("--arguments", "-psn_2_2"); cmd.executeAndAssertHelloAppImageCreated(); - var appVerifier = HelloApp.assertMainLauncher(cmd); - if (appVerifier != null) { + HelloApp.assertMainLauncher(cmd).ifPresent(appVerifier -> { appVerifier.execute("-psn_1_1"); appVerifier.verifyOutput("-psn_2_2"); - } + }); } } diff --git a/test/jdk/tools/jpackage/share/MainClassTest.java b/test/jdk/tools/jpackage/share/MainClassTest.java index bc813c4ec15..72e77bbbff5 100644 --- a/test/jdk/tools/jpackage/share/MainClassTest.java +++ b/test/jdk/tools/jpackage/share/MainClassTest.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 @@ -240,8 +240,7 @@ public final class MainClassTest { cmd.executeAndAssertHelloAppImageCreated(); } else { cmd.executeAndAssertImageCreated(); - var appVerifier = HelloApp.assertMainLauncher(cmd); - if (appVerifier != null) { + HelloApp.assertMainLauncher(cmd).ifPresent(appVerifier -> { List output = appVerifier .saveOutput(true) .expectedExitCode(1) @@ -249,7 +248,7 @@ public final class MainClassTest { TKit.assertTextStream(String.format( "Error: Could not find or load main class %s", nonExistingMainClass)).apply(output); - } + }); } CfgFile cfg = cmd.readLauncherCfgFile(); diff --git a/test/jdk/tools/jpackage/windows/Win8301247Test.java b/test/jdk/tools/jpackage/windows/Win8301247Test.java index 2f98141dcb4..3cdd9810d0f 100644 --- a/test/jdk/tools/jpackage/windows/Win8301247Test.java +++ b/test/jdk/tools/jpackage/windows/Win8301247Test.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 @@ -21,12 +21,14 @@ * questions. */ -import static jdk.jpackage.test.WindowsHelper.killAppLauncherProcess; - import java.time.Duration; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.TKit; /** * Test that terminating of the parent app launcher process automatically @@ -46,7 +48,7 @@ import jdk.jpackage.test.JPackageCommand; public class Win8301247Test { @Test - public void test() throws InterruptedException { + public void test() throws InterruptedException, ExecutionException { var cmd = JPackageCommand.helloAppImage().ignoreFakeRuntime(); // Launch the app in a way it doesn't exit to let us trap app laucnher @@ -54,20 +56,41 @@ public class Win8301247Test { cmd.addArguments("--java-options", "-Djpackage.test.noexit=true"); cmd.executeAndAssertImageCreated(); + var f = new CompletableFuture(); + // Launch the app in a separate thread new Thread(() -> { - HelloApp.executeLauncher(cmd); + HelloApp.assertMainLauncher(cmd).get().processListener(f::complete).execute(); }).start(); - // Wait a bit to let the app start - Thread.sleep(Duration.ofSeconds(10)); + var mainLauncherProcess = f.get(); - // Find the main app launcher process and kill it - killAppLauncherProcess(cmd, null, 2); + Optional childProcess = Optional.empty(); - // Wait a bit and check if child app launcher process is still running (it must NOT) - Thread.sleep(Duration.ofSeconds(5)); + try { + // Wait a bit to let the app start + Thread.sleep(Duration.ofSeconds(10)); - killAppLauncherProcess(cmd, null, 0); + try (var children = mainLauncherProcess.children()) { + childProcess = children.filter(p -> { + return mainLauncherProcess.info().command().equals(p.info().command()); + }).findFirst(); + } + + TKit.assertTrue(childProcess.isPresent(), + String.format("Check the main launcher process with PID=%d restarted", mainLauncherProcess.pid())); + } finally { + // Kill the main app launcher process + TKit.trace("About to kill the main launcher process..."); + mainLauncherProcess.destroyForcibly(); + + // Wait a bit and check if child app launcher process is still running (it must NOT) + Thread.sleep(Duration.ofSeconds(5)); + + childProcess.ifPresent(p -> { + TKit.assertTrue(!p.isAlive(), String.format( + "Check restarted main launcher process with PID=%d is not alive", p.pid())); + }); + } } } diff --git a/test/jdk/tools/jpackage/windows/WinChildProcessTest.java b/test/jdk/tools/jpackage/windows/WinChildProcessTest.java index a83ef837331..e5de19d182a 100644 --- a/test/jdk/tools/jpackage/windows/WinChildProcessTest.java +++ b/test/jdk/tools/jpackage/windows/WinChildProcessTest.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 @@ -44,7 +44,6 @@ import static jdk.jpackage.test.HelloApp.configureAndExecute; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Executor; import jdk.jpackage.test.TKit; -import static jdk.jpackage.test.WindowsHelper.killProcess; public class WinChildProcessTest { private static final Path TEST_APP_JAVA = TKit.TEST_SRC_ROOT @@ -52,7 +51,7 @@ public class WinChildProcessTest { @Test public static void test() { - long childPid = 0; + Optional child = Optional.empty(); try { JPackageCommand cmd = JPackageCommand .helloAppImage(TEST_APP_JAVA + "*Hello") @@ -69,21 +68,18 @@ public class WinChildProcessTest { String pidStr = output.get(0); // parse child PID - childPid = Long.parseLong(pidStr.split("=", 2)[1]); + var childPid = Long.parseLong(pidStr.split("=", 2)[1]); // Check whether the termination of third party application launcher // also terminating the launched third party application // If third party application is not terminated the test is // successful else failure - Optional processHandle = ProcessHandle.of(childPid); - boolean isAlive = processHandle.isPresent() - && processHandle.get().isAlive(); - TKit.assertTrue(isAlive, "Check child process is alive"); + child = ProcessHandle.of(childPid); + boolean isAlive = child.map(ProcessHandle::isAlive).orElse(false); + TKit.assertTrue(isAlive, String.format("Check child process with PID=%d is alive", childPid)); } finally { - if (childPid != 0) { - // Kill only a specific child instance - killProcess(childPid); - } + TKit.trace("About to kill the child process..."); + child.ifPresent(ProcessHandle::destroyForcibly); } } } diff --git a/test/jdk/tools/jpackage/windows/WinNoRestartTest.java b/test/jdk/tools/jpackage/windows/WinNoRestartTest.java index 909ee06b01a..984ddfcdf06 100644 --- a/test/jdk/tools/jpackage/windows/WinNoRestartTest.java +++ b/test/jdk/tools/jpackage/windows/WinNoRestartTest.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,15 +21,18 @@ * questions. */ -import static jdk.jpackage.test.WindowsHelper.killAppLauncherProcess; import java.io.IOException; import java.time.Duration; import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.CfgFile; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.TKit; /* @test * @bug 8340311 @@ -47,7 +50,7 @@ import jdk.jpackage.test.JPackageCommand; public class WinNoRestartTest { @Test - public static void test() throws InterruptedException, IOException { + public static void test() throws InterruptedException, IOException, ExecutionException { var cmd = JPackageCommand.helloAppImage().ignoreFakeRuntime(); // Configure test app to launch in a way it will not exit @@ -77,7 +80,7 @@ public class WinNoRestartTest { private static record NoRerunConfig(NoRerunSectionConfig firstSection, NoRerunSectionConfig secondSection, boolean expectedNoRestarted) { - void apply(JPackageCommand cmd, CfgFile origCfgFile) throws InterruptedException { + void apply(JPackageCommand cmd, CfgFile origCfgFile) throws InterruptedException, ExecutionException { // Alter the main launcher .cfg file var cfgFile = new CfgFile(); if (firstSection != null) { @@ -92,16 +95,40 @@ public class WinNoRestartTest { // Save updated main launcher .cfg file cfgFile.save(cmd.appLauncherCfgPath(null)); + var f = new CompletableFuture(); + // Launch the app in a separate thread new Thread(() -> { - HelloApp.executeLauncher(cmd); + HelloApp.assertMainLauncher(cmd).get().processListener(f::complete).execute(); }).start(); - // Wait a bit to let the app start - Thread.sleep(Duration.ofSeconds(10)); + var mainLauncherProcess = f.get(); - // Find the main app launcher process and kill it - killAppLauncherProcess(cmd, null, expectedNoRestarted ? 1 : 2); + try { + // Wait a bit to let the app start + Thread.sleep(Duration.ofSeconds(10)); + + try (var children = mainLauncherProcess.children()) { + Optional childPid = children.filter(p -> { + return mainLauncherProcess.info().command().equals(p.info().command()); + }).map(ProcessHandle::pid).map(Object::toString).findFirst(); + + Optional expectedChildPid; + if (expectedNoRestarted) { + expectedChildPid = Optional.empty(); + } else { + expectedChildPid = childPid.or(() -> { + return Optional.of(""); + }); + } + TKit.assertEquals(expectedChildPid, childPid, String.format( + "Check the main launcher process with PID=%d restarted", + mainLauncherProcess.pid())); + } + } finally { + TKit.trace("About to kill the main launcher process..."); + mainLauncherProcess.destroyForcibly(); + } } } From 7330e1a996fd43d92430a73b818f33552bc6ae9c Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 13 Jan 2026 13:51:00 +0000 Subject: [PATCH 079/204] 8374990: Check include and jmods folder of JDK image for unwanted files Reviewed-by: erikj --- test/jdk/build/CheckFiles.java | 37 ++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/test/jdk/build/CheckFiles.java b/test/jdk/build/CheckFiles.java index 412e66ebf01..eb903c0a224 100644 --- a/test/jdk/build/CheckFiles.java +++ b/test/jdk/build/CheckFiles.java @@ -60,6 +60,8 @@ public class CheckFiles { System.out.println("Main directory to scan:" + mainDirToScan); Path binDir = mainDirToScan.resolve("bin"); Path libDir = mainDirToScan.resolve("lib"); + Path includeDir = mainDirToScan.resolve("include"); + Path jmodsDir = mainDirToScan.resolve("jmods"); System.out.println("Bin directory to scan:" + binDir); ArrayList allowedEndingsBinDir = new ArrayList<>(); @@ -108,13 +110,44 @@ public class CheckFiles { } boolean libDirRes = scanFiles(libDir, allowedEndingsLibDir); - if (!binDirRes) { + if (binDirRes) { + System.out.println("Bin directory scan successful."); + } else { throw new Error("bin dir scan failed"); } - if (!libDirRes) { + if (libDirRes) { + System.out.println("Lib directory scan successful."); + } else { throw new Error("lib dir scan failed"); } + + if (Files.isDirectory(includeDir)) { + System.out.println("Include directory to scan:" + includeDir); + ArrayList allowedEndingsIncludeDir = new ArrayList<>(); + allowedEndingsIncludeDir.add(".h"); + allowedEndingsIncludeDir.add(".hpp"); + boolean includeDirRes = scanFiles(includeDir, allowedEndingsIncludeDir); + if (includeDirRes) { + System.out.println("Include directory scan successful."); + } else { + throw new Error("include dir scan failed"); + } + } + + // when enabling "JEP 493: Linking Run-Time Images without JMODs" we do not + // have the jmods folder at all, so first test the presence of the folder + if (Files.isDirectory(jmodsDir)) { + System.out.println("Jmods directory to scan:" + jmodsDir); + ArrayList allowedEndingsJmodsDir = new ArrayList<>(); + allowedEndingsJmodsDir.add(".jmod"); + boolean jmodsDirRes = scanFiles(jmodsDir, allowedEndingsJmodsDir); + if (jmodsDirRes) { + System.out.println("Jmods directory scan successful."); + } else { + throw new Error("jmods dir scan failed"); + } + } } private static boolean scanFiles(Path root, ArrayList allowedEndings) throws IOException { From 49f7265894652ea243f3a531cf3f9d0b06e53565 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 13 Jan 2026 13:54:04 +0000 Subject: [PATCH 080/204] 8374872: Cleanup outdated SAP AG copyright header info Reviewed-by: clanger, mdoerr --- .../jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_A.java | 2 +- .../runtime/exceptionMsgs/IllegalAccessError/IAE_Loader2.java | 2 +- test/hotspot/jtreg/runtime/execstack/Test.java | 2 +- test/hotspot/jtreg/runtime/execstack/TestMT.java | 2 +- test/hotspot/jtreg/runtime/execstack/libtest-rw.c | 2 +- test/hotspot/jtreg/runtime/execstack/libtest-rwx.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_A.java b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_A.java index e0491b4b89f..0684ad1aacc 100644 --- a/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_A.java +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE78_A.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 by SAP AG, Walldorf, Germany. + * Copyright (c) 2018, 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 diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader2.java b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader2.java index 373ee584679..d13e5e5d3c1 100644 --- a/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader2.java +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/IllegalAccessError/IAE_Loader2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 by SAP AG, Walldorf, Germany. + * Copyright (c) 2018, 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 diff --git a/test/hotspot/jtreg/runtime/execstack/Test.java b/test/hotspot/jtreg/runtime/execstack/Test.java index 67891a523aa..22063a56dd5 100644 --- a/test/hotspot/jtreg/runtime/execstack/Test.java +++ b/test/hotspot/jtreg/runtime/execstack/Test.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011 SAP AG. All Rights Reserved. + * Copyright (c) 2011, 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 diff --git a/test/hotspot/jtreg/runtime/execstack/TestMT.java b/test/hotspot/jtreg/runtime/execstack/TestMT.java index 0be1a461c0a..282b09f9a35 100644 --- a/test/hotspot/jtreg/runtime/execstack/TestMT.java +++ b/test/hotspot/jtreg/runtime/execstack/TestMT.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011 SAP AG. All Rights Reserved. + * Copyright (c) 2011, 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 diff --git a/test/hotspot/jtreg/runtime/execstack/libtest-rw.c b/test/hotspot/jtreg/runtime/execstack/libtest-rw.c index 7ad4b95d25e..52e1e8328a1 100644 --- a/test/hotspot/jtreg/runtime/execstack/libtest-rw.c +++ b/test/hotspot/jtreg/runtime/execstack/libtest-rw.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011 SAP AG. All Rights Reserved. + * Copyright (c) 2011, 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 diff --git a/test/hotspot/jtreg/runtime/execstack/libtest-rwx.c b/test/hotspot/jtreg/runtime/execstack/libtest-rwx.c index bce4f853106..e0d8e424809 100644 --- a/test/hotspot/jtreg/runtime/execstack/libtest-rwx.c +++ b/test/hotspot/jtreg/runtime/execstack/libtest-rwx.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011 SAP AG. All Rights Reserved. + * Copyright (c) 2011, 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 From 45990d796ffafc228c6e843049c80aefedb0f12b Mon Sep 17 00:00:00 2001 From: Volodymyr Paprotski Date: Tue, 13 Jan 2026 15:15:36 +0000 Subject: [PATCH 081/204] 8374570: Assertion failure in ClearArray.java with -XX:+EnableX86EcoreOpts Reviewed-by: thartmann, epeter, qamai --- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 4 ++-- test/hotspot/jtreg/compiler/c2/ClearArray.java | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index be7deb884ce..7f7bb2c4c7f 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_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 @@ -6086,7 +6086,7 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned, vpbroadcastd(xtmp, xtmp, Assembler::AVX_512bit); subptr(count, 16 << shift); - jccb(Assembler::less, L_check_fill_32_bytes); + jcc(Assembler::less, L_check_fill_32_bytes); align(16); BIND(L_fill_64_bytes_loop_avx3); diff --git a/test/hotspot/jtreg/compiler/c2/ClearArray.java b/test/hotspot/jtreg/compiler/c2/ClearArray.java index ee376641650..d218eef5780 100644 --- a/test/hotspot/jtreg/compiler/c2/ClearArray.java +++ b/test/hotspot/jtreg/compiler/c2/ClearArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, 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 @@ -24,7 +24,7 @@ /* * @test ClearArray.java - * @bug 8284883 + * @bug 8284883 8374570 * @compile ClearArray.java * @summary ClearArray instruction overflows scratch buffer * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -Xbatch @@ -33,6 +33,8 @@ * -XX:InitArrayShortSize=32768 -XX:-IdealizeClearArrayNode -XX:UseAVX=3 compiler.c2.ClearArray * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -Xbatch * -XX:InitArrayShortSize=32768 -XX:MaxVectorSize=8 -XX:-IdealizeClearArrayNode -XX:UseAVX=3 compiler.c2.ClearArray + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -Xbatch + * -XX:+EnableX86ECoreOpts -XX:MaxVectorSize=8 -XX:UseAVX=3 compiler.c2.ClearArray */ package compiler.c2; From 7f707ba8e746d859ac171d71ef8f731953a92e6a Mon Sep 17 00:00:00 2001 From: Damon Nguyen Date: Tue, 13 Jan 2026 16:55:03 +0000 Subject: [PATCH 082/204] 8373727: New XBM images parser regression: only the first line of the bitmap array is parsed Reviewed-by: prr, jdv --- .../sun/awt/image/XbmImageDecoder.java | 138 +++++++++++------- .../awt/image/XBMDecoder/XBMDecoderTest.java | 28 +++- .../awt/image/XBMDecoder/invalid_empty.xbm | 6 + .../java/awt/image/XBMDecoder/invalid_hex.xbm | 4 +- .../awt/image/XBMDecoder/invalid_plus.xbm | 3 + .../awt/image/XBMDecoder/valid_multiline.xbm | 8 + 6 files changed, 128 insertions(+), 59 deletions(-) create mode 100644 test/jdk/java/awt/image/XBMDecoder/invalid_empty.xbm create mode 100644 test/jdk/java/awt/image/XBMDecoder/invalid_plus.xbm create mode 100644 test/jdk/java/awt/image/XBMDecoder/valid_multiline.xbm diff --git a/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java b/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java index cac9f8baab2..bc025b87f3c 100644 --- a/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.java +++ b/src/java.desktop/share/classes/sun/awt/image/XbmImageDecoder.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 @@ -92,8 +92,8 @@ public class XbmImageDecoder extends ImageDecoder { byte[] raster = null; IndexColorModel model = null; - String matchRegex = "(0[xX])?[0-9a-fA-F]+[\\s+]?[,|};]"; - String replaceRegex = "(0[xX])|,|[\\s+]|[};]"; + String matchRegex = "\\s*(0[xX])?((?:(?!,|\\};).)+)(,|\\};)"; + String replaceRegex = "0[xX]|,|\\s+|\\};"; String line; int lineNum = 0; @@ -111,11 +111,19 @@ public class XbmImageDecoder extends ImageDecoder { } try { if (!token[2].isBlank() && state == 0) { - W = Integer.parseInt(token[2]); - state = 1; // after width is set + if (token[1].endsWith("th")) { + W = Integer.parseInt(token[2]); + } else if (token[1].endsWith("t")) { + H = Integer.parseInt(token[2]); + } + state = 1; // after first dimension is set } else if (!token[2].isBlank() && state == 1) { - H = Integer.parseInt(token[2]); - state = 2; // after height is set + if (token[1].endsWith("th")) { + W = Integer.parseInt(token[2]); + } else if (token[1].endsWith("t")) { + H = Integer.parseInt(token[2]); + } + state = 2; // after second dimension is set } } catch (NumberFormatException nfe) { // parseInt() can throw NFE @@ -147,59 +155,81 @@ public class XbmImageDecoder extends ImageDecoder { error("Width or Height of XBM file not defined"); } + boolean contFlag = false; + StringBuilder sb = new StringBuilder(); + // loop to process image data while (!aborted && (line = br.readLine()) != null) { lineNum++; - if (line.contains("[]")) { - Matcher matcher = Pattern.compile(matchRegex).matcher(line); - while (matcher.find()) { - if (y >= H) { - error("Scan size of XBM file exceeds" - + " the defined width x height"); - } - - int startIndex = matcher.start(); - int endIndex = matcher.end(); - String hexByte = line.substring(startIndex, endIndex); - - if (!(hexByte.startsWith("0x") - || hexByte.startsWith("0X"))) { - error("Invalid hexadecimal number at Ln#:" + lineNum - + " Col#:" + (startIndex + 1)); - } - hexByte = hexByte.replaceAll(replaceRegex, ""); - if (hexByte.length() != 2) { - error("Invalid hexadecimal number at Ln#:" + lineNum - + " Col#:" + (startIndex + 1)); - } - - try { - n = Integer.parseInt(hexByte, 16); - } catch (NumberFormatException nfe) { - error("Error parsing hexadecimal at Ln#:" + lineNum - + " Col#:" + (startIndex + 1)); - } - for (int mask = 1; mask <= 0x80; mask <<= 1) { - if (x < W) { - if ((n & mask) != 0) - raster[x] = 1; - else - raster[x] = 0; - } - x++; - } - - if (x >= W) { - int result = setPixels(0, y, W, 1, model, raster, 0, W); - if (result <= 0) { - error("Unexpected error occurred during setPixel()"); - } - x = 0; - y++; - } + if (!contFlag) { + if (line.contains("[]")) { + contFlag = true; + } else { + continue; } } + + int end = line.indexOf(';'); + if (end >= 0) { + sb.append(line, 0, end + 1); + break; + } else { + sb.append(line).append(System.lineSeparator()); + } + } + + String resultLine = sb.toString(); + int cutOffIndex = resultLine.indexOf('{'); + resultLine = resultLine.substring(cutOffIndex + 1); + + Matcher matcher = Pattern.compile(matchRegex).matcher(resultLine); + while (matcher.find()) { + if (y >= H) { + error("Scan size of XBM file exceeds" + + " the defined width x height"); + } + + int startIndex = matcher.start(); + int endIndex = matcher.end(); + String hexByte = resultLine.substring(startIndex, endIndex); + hexByte = hexByte.replaceAll("^\\s+", ""); + + if (!(hexByte.startsWith("0x") + || hexByte.startsWith("0X"))) { + error("Invalid hexadecimal number at Ln#:" + lineNum + + " Col#:" + (startIndex + 1)); + } + hexByte = hexByte.replaceAll(replaceRegex, ""); + if (hexByte.length() != 2) { + error("Invalid hexadecimal number at Ln#:" + lineNum + + " Col#:" + (startIndex + 1)); + } + + try { + n = Integer.parseInt(hexByte, 16); + } catch (NumberFormatException nfe) { + error("Error parsing hexadecimal at Ln#:" + lineNum + + " Col#:" + (startIndex + 1)); + } + for (int mask = 1; mask <= 0x80; mask <<= 1) { + if (x < W) { + if ((n & mask) != 0) + raster[x] = 1; + else + raster[x] = 0; + } + x++; + } + + if (x >= W) { + int result = setPixels(0, y, W, 1, model, raster, 0, W); + if (result <= 0) { + error("Unexpected error occurred during setPixel()"); + } + x = 0; + y++; + } } imageComplete(ImageConsumer.STATICIMAGEDONE, true); } diff --git a/test/jdk/java/awt/image/XBMDecoder/XBMDecoderTest.java b/test/jdk/java/awt/image/XBMDecoder/XBMDecoderTest.java index 19bc6d95c39..9694043d1bb 100644 --- a/test/jdk/java/awt/image/XBMDecoder/XBMDecoderTest.java +++ b/test/jdk/java/awt/image/XBMDecoder/XBMDecoderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/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 @@ -29,10 +29,14 @@ * @run main XBMDecoderTest */ +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.PrintStream; +import java.util.Arrays; import javax.swing.ImageIcon; public class XBMDecoderTest { @@ -57,21 +61,39 @@ public class XBMDecoderTest { ImageIcon icon = new ImageIcon(fis.readAllBytes()); boolean isErrEmpty = errContent.toString().isEmpty(); + if (!isErrEmpty) { System.out.println("Expected ImageFormatException occurred."); System.out.print(errContent); } - if (validCase && !isErrEmpty) { throw new RuntimeException("Test failed: Error stream not empty"); - } else if (!validCase && isErrEmpty) { + } else if (!validCase && isErrEmpty && hasPixelData(icon.getImage())) { throw new RuntimeException("Test failed: ImageFormatException" + " expected but not thrown"); } + if (validCase && !hasPixelData(icon.getImage())) { + throw new RuntimeException("Test failed: the parsed image " + + "does not contain any pixel data"); + } System.out.println("PASSED\n"); } finally { System.setErr(originalErr); } } } + + private static boolean hasPixelData(Image img) { + int w = img.getWidth(null); + int h = img.getHeight(null); + BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = bi.createGraphics(); + g.drawImage(img, 0, 0, null); + g.dispose(); + int[] pixels = bi.getRGB(0, 0, w, h, null, 0, w); + if (Arrays.stream(pixels).allMatch(i -> i == 0)) { + return false; + } + return true; + } } diff --git a/test/jdk/java/awt/image/XBMDecoder/invalid_empty.xbm b/test/jdk/java/awt/image/XBMDecoder/invalid_empty.xbm new file mode 100644 index 00000000000..5cfb8e21cf8 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/invalid_empty.xbm @@ -0,0 +1,6 @@ +#define test_width 16 +#define test_height 3 +#define ht_x 1 +#define ht_y 2 +static unsigned char test_bits[] = { +}; diff --git a/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm b/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm index c6f819582d0..1286eee1d9b 100644 --- a/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm +++ b/test/jdk/java/awt/image/XBMDecoder/invalid_hex.xbm @@ -1,3 +1,3 @@ -#define k_wt 16 -#define k_ht 1 +#define k_width 16 +#define k_height 1 k[] = { 0x10, 1234567890}; diff --git a/test/jdk/java/awt/image/XBMDecoder/invalid_plus.xbm b/test/jdk/java/awt/image/XBMDecoder/invalid_plus.xbm new file mode 100644 index 00000000000..714907084f2 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/invalid_plus.xbm @@ -0,0 +1,3 @@ +#define test_width 16 +#define test_height 2 +static unsigned char test_bits[] = { 0x13, 0x11, 0xAB+, 0xff }; \ No newline at end of file diff --git a/test/jdk/java/awt/image/XBMDecoder/valid_multiline.xbm b/test/jdk/java/awt/image/XBMDecoder/valid_multiline.xbm new file mode 100644 index 00000000000..e24bc10e9b0 --- /dev/null +++ b/test/jdk/java/awt/image/XBMDecoder/valid_multiline.xbm @@ -0,0 +1,8 @@ +#define test_width 16 +#define test_height 3 +#define ht_x 1 +#define ht_y 2 +static unsigned char test_bits[] = { +0x20, 0x10, +0x25, 0x01, +0xAC, 0xab }; From 074038438f5b8b91e9390430b4fa58ff53e5df26 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 13 Jan 2026 16:57:30 +0000 Subject: [PATCH 083/204] 8374727: Audio configuration Platform class - use nio for getting endianness of the underlying platform Reviewed-by: prr, kizune --- .../libjsound/PLATFORM_API_MacOSX_PCM.cpp | 7 ++- .../classes/com/sun/media/sound/Platform.java | 17 ++------ .../share/native/libjsound/Platform.c | 43 ------------------- .../share/native/libjsound/Utilities.c | 11 +---- .../share/native/libjsound/Utilities.h | 6 +-- 5 files changed, 8 insertions(+), 76 deletions(-) delete mode 100644 src/java.desktop/share/native/libjsound/Platform.c diff --git a/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_PCM.cpp b/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_PCM.cpp index 441a71f5c50..bae16cb0a9c 100644 --- a/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_PCM.cpp +++ b/src/java.desktop/macosx/native/libjsound/PLATFORM_API_MacOSX_PCM.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2020, 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 @@ -162,8 +162,7 @@ void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* cre sampleRate, // sample rate DAUDIO_PCM, // only accept PCM bits == 8 ? FALSE : TRUE, // signed - bits == 8 ? FALSE // little-endian for 8bit - : UTIL_IsBigEndianPlatform()); + FALSE); // all supported macOS versions run on LE } } // add default format @@ -175,7 +174,7 @@ void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* cre defSampleRate, // sample rate DAUDIO_PCM, // PCM TRUE, // signed - UTIL_IsBigEndianPlatform()); // native endianness + FALSE); // native endianness; all supported macOS versions run on LE } TRACE0("< Date: Tue, 13 Jan 2026 18:06:04 +0000 Subject: [PATCH 084/204] 8371014: Dump JFR recording on CrashOnOutOfMemoryError is incorrectly implemented Reviewed-by: ysuenaga --- src/hotspot/share/jfr/jfr.cpp | 14 +- src/hotspot/share/jfr/jfr.hpp | 5 +- src/hotspot/share/jfr/jni/jfrJniMethod.cpp | 5 +- .../recorder/repository/jfrEmergencyDump.cpp | 56 +++--- .../recorder/repository/jfrEmergencyDump.hpp | 4 +- .../share/jfr/recorder/service/jfrPostBox.cpp | 7 +- .../share/jfr/recorder/service/jfrPostBox.hpp | 25 +-- .../recorder/service/jfrRecorderService.cpp | 186 ++++++++++++++++-- .../recorder/service/jfrRecorderService.hpp | 20 +- .../service/jfrRecorderThreadLoop.cpp | 8 +- src/hotspot/share/runtime/java.cpp | 7 +- src/hotspot/share/utilities/debug.cpp | 7 +- src/hotspot/share/utilities/vmError.cpp | 4 +- test/jdk/ProblemList.txt | 1 - .../oldobject/TestEmergencyDumpAtOOM.java | 6 +- 15 files changed, 278 insertions(+), 77 deletions(-) diff --git a/src/hotspot/share/jfr/jfr.cpp b/src/hotspot/share/jfr/jfr.cpp index b09a89594ad..d9892f80b6f 100644 --- a/src/hotspot/share/jfr/jfr.cpp +++ b/src/hotspot/share/jfr/jfr.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 @@ -32,6 +32,7 @@ #include "jfr/recorder/repository/jfrEmergencyDump.hpp" #include "jfr/recorder/repository/jfrRepository.hpp" #include "jfr/recorder/service/jfrOptionSet.hpp" +#include "jfr/recorder/service/jfrRecorderService.hpp" #include "jfr/support/jfrClassDefineEvent.hpp" #include "jfr/support/jfrKlassExtension.hpp" #include "jfr/support/jfrResolution.hpp" @@ -43,6 +44,7 @@ #include "runtime/java.hpp" #include "runtime/javaThread.hpp" + bool Jfr::is_enabled() { return JfrRecorder::is_enabled(); } @@ -153,9 +155,9 @@ void Jfr::on_resolution(const Method* caller, const Method* target, TRAPS) { } #endif -void Jfr::on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown, bool halt) { +void Jfr::on_vm_shutdown(bool exception_handler /* false */, bool halt /* false */, bool oom /* false */) { if (!halt && JfrRecorder::is_recording()) { - JfrEmergencyDump::on_vm_shutdown(emit_old_object_samples, emit_event_shutdown); + JfrEmergencyDump::on_vm_shutdown(exception_handler, oom); } } @@ -173,6 +175,12 @@ bool Jfr::on_start_flight_recording_option(const JavaVMOption** option, char* de return JfrOptionSet::parse_start_flight_recording_option(option, delimiter); } +void Jfr::on_report_java_out_of_memory() { + if (CrashOnOutOfMemoryError && JfrRecorder::is_recording()) { + JfrRecorderService::emit_leakprofiler_events_on_oom(); + } +} + #if INCLUDE_CDS void Jfr::on_restoration(const Klass* k, JavaThread* jt) { assert(k != nullptr, "invariant"); diff --git a/src/hotspot/share/jfr/jfr.hpp b/src/hotspot/share/jfr/jfr.hpp index db567cc9a29..ac6a232dda1 100644 --- a/src/hotspot/share/jfr/jfr.hpp +++ b/src/hotspot/share/jfr/jfr.hpp @@ -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 @@ -71,7 +71,7 @@ class Jfr : AllStatic { static void on_resolution(const Method* caller, const Method* target, TRAPS); static void on_java_thread_start(JavaThread* starter, JavaThread* startee); static void on_set_current_thread(JavaThread* jt, oop thread); - static void on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown, bool halt = false); + static void on_vm_shutdown(bool exception_handler = false, bool halt = false, bool oom = false); static void on_vm_error_report(outputStream* st); static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter); static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter); @@ -79,6 +79,7 @@ class Jfr : AllStatic { static void initialize_main_thread(JavaThread* jt); static bool has_sample_request(JavaThread* jt); static void check_and_process_sample_request(JavaThread* jt); + static void on_report_java_out_of_memory(); CDS_ONLY(static void on_restoration(const Klass* k, JavaThread* jt);) }; diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp index 6a1146587bc..d8a30f9b5ee 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.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 @@ -364,8 +364,7 @@ JVM_ENTRY_NO_ENV(void, jfr_set_force_instrumentation(JNIEnv* env, jclass jvm, jb JVM_END NO_TRANSITION(void, jfr_emit_old_object_samples(JNIEnv* env, jclass jvm, jlong cutoff_ticks, jboolean emit_all, jboolean skip_bfs)) - JfrRecorderService service; - service.emit_leakprofiler_events(cutoff_ticks, emit_all == JNI_TRUE, skip_bfs == JNI_TRUE); + JfrRecorderService::emit_leakprofiler_events(cutoff_ticks, emit_all == JNI_TRUE, skip_bfs == JNI_TRUE); NO_TRANSITION_END JVM_ENTRY_NO_ENV(void, jfr_exclude_thread(JNIEnv* env, jclass jvm, jobject t)) diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp index 309ae961808..cdebcbcfcb5 100644 --- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp +++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.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 @@ -38,6 +38,8 @@ #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" #include "runtime/thread.inline.hpp" +#include "runtime/vmOperations.hpp" +#include "runtime/vmThread.hpp" #include "utilities/growableArray.hpp" #include "utilities/ostream.hpp" @@ -460,15 +462,6 @@ static void release_locks(Thread* thread) { assert(thread != nullptr, "invariant"); assert(!thread->is_Java_thread() || JavaThread::cast(thread)->thread_state() == _thread_in_vm, "invariant"); -#ifdef ASSERT - Mutex* owned_lock = thread->owned_locks(); - while (owned_lock != nullptr) { - Mutex* next = owned_lock->next(); - owned_lock->unlock(); - owned_lock = next; - } -#endif // ASSERT - if (Threads_lock->owned_by_self()) { Threads_lock->unlock(); } @@ -550,17 +543,14 @@ class JavaThreadInVMAndNative : public StackObj { } }; -static void post_events(bool emit_old_object_samples, bool emit_event_shutdown, Thread* thread) { - if (emit_old_object_samples) { - LeakProfiler::emit_events(max_jlong, false, false); - } - if (emit_event_shutdown) { +static void post_events(bool exception_handler, bool oom, Thread * thread) { + if (exception_handler) { EventShutdown e; - e.set_reason("VM Error"); + e.set_reason(oom ? "CrashOnOutOfMemoryError" : "VM Error"); e.commit(); } EventDumpReason event; - event.set_reason(emit_old_object_samples ? "Out of Memory" : "Crash"); + event.set_reason(exception_handler && oom ? "CrashOnOutOfMemoryError" : exception_handler ? "Crash" : "Out of Memory"); event.set_recordingId(-1); event.commit(); } @@ -594,20 +584,40 @@ static bool guard_reentrancy() { return false; } -void JfrEmergencyDump::on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown) { +void JfrEmergencyDump::on_vm_shutdown(bool exception_handler, bool oom) { if (!guard_reentrancy()) { return; } + Thread* const thread = Thread::current_or_null_safe(); assert(thread != nullptr, "invariant"); - if (thread->is_Watcher_thread()) { - log_info(jfr, system)("The Watcher thread crashed so no jfr emergency dump will be generated."); - return; - } + // Ensure a JavaThread is _thread_in_vm when we make this call JavaThreadInVMAndNative jtivm(thread); + post_events(exception_handler, oom, thread); + + if (thread->is_Watcher_thread()) { + // We cannot attempt an emergency dump using the Watcher thread + // because we rely on the WatcherThread task "is_error_reported()", + // to exit the VM after a hardcoded timeout, should the relatively + // risky operation of an emergency dump fail (deadlock, livelock). + log_warning(jfr, system) + ("The Watcher thread crashed so no jfr emergency dump will be generated."); + return; + } + + if (thread->is_VM_thread()) { + const VM_Operation* const operation = VMThread::vm_operation(); + if (operation != nullptr && operation->type() == VM_Operation::VMOp_JFROldObject) { + // We will not be able to issue a rotation because the rotation lock + // is held by the JFR Recorder Thread that issued the VM_Operation. + log_warning(jfr, system) + ("The VM Thread crashed as part of emitting leak profiler events so no jfr emergency dump will be generated."); + return; + } + } + release_locks(thread); - post_events(emit_old_object_samples, emit_event_shutdown, thread); // if JavaThread, transition to _thread_in_native to issue a final flushpoint NoHandleMark nhm; diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp index 04c2851a516..b337d73364a 100644 --- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp +++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp @@ -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 @@ -39,7 +39,7 @@ class JfrEmergencyDump : AllStatic { static const char* chunk_path(const char* repository_path); static void on_vm_error(const char* repository_path); static void on_vm_error_report(outputStream* st, const char* repository_path); - static void on_vm_shutdown(bool emit_old_object_samples, bool emit_event_shutdown); + static void on_vm_shutdown(bool exception_handler, bool oom); }; #endif // SHARE_JFR_RECORDER_REPOSITORY_JFREMERGENCYDUMP_HPP diff --git a/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp b/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp index a9ba456ad76..6db7a42ab27 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp @@ -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 @@ -34,7 +34,8 @@ (MSGBIT(MSG_START)) | \ (MSGBIT(MSG_CLONE_IN_MEMORY)) | \ (MSGBIT(MSG_VM_ERROR)) | \ - (MSGBIT(MSG_FLUSHPOINT)) \ + (MSGBIT(MSG_FLUSHPOINT)) | \ + (MSGBIT(MSG_EMIT_LEAKP_REFCHAINS)) \ ) static JfrPostBox* _instance = nullptr; @@ -165,7 +166,7 @@ void JfrPostBox::notify_waiters() { assert(JfrMsg_lock->owned_by_self(), "incrementing _msg_handled_serial is protected by JfrMsg_lock."); // Update made visible on release of JfrMsg_lock via fence instruction in Monitor::IUnlock. ++_msg_handled_serial; - JfrMsg_lock->notify(); + JfrMsg_lock->notify_all(); } // safeguard to ensure no threads are left waiting diff --git a/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp b/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp index 10457261643..92f70b1dc9b 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp +++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, 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 @@ -43,6 +43,7 @@ enum JFR_Msg { MSG_SHUTDOWN, MSG_VM_ERROR, MSG_FLUSHPOINT, + MSG_EMIT_LEAKP_REFCHAINS, MSG_NO_OF_MSGS }; @@ -51,23 +52,25 @@ enum JFR_Msg { * * Synchronous messages (posting thread waits for message completion): * - * MSG_CLONE_IN_MEMORY (0) ; MSGBIT(MSG_CLONE_IN_MEMORY) == (1 << 0) == 0x1 - * MSG_START(1) ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2 - * MSG_STOP (2) ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4 - * MSG_ROTATE (3) ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8 - * MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 0x8) == 0x100 - * MSG_FLUSHPOINT (9) ; MSGBIT(MSG_FLUSHPOINT) == (1 << 0x9) == 0x200 + * MSG_CLONE_IN_MEMORY (0) ; MSGBIT(MSG_CLONE_IN_MEMORY) == (1 << 0) == 0x1 + * MSG_START(1) ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2 + * MSG_STOP (2) ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4 + * MSG_ROTATE (3) ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8 + * MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 0x8) == 0x100 + * MSG_FLUSHPOINT (9) ; MSGBIT(MSG_FLUSHPOINT) == (1 << 0x9) == 0x200 + * MSG_EMIT_LEAKP_REFCHAINS (10); MSGBIT(MSG_EMIT_LEAKP_REFCHAINS) == (1 << 0xa) == 0x400 * * Asynchronous messages (posting thread returns immediately upon deposit): * - * MSG_FULLBUFFER (4) ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10 - * MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 0x5) == 0x20 - * MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 0x6) == 0x40 - * MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 0x7) == 0x80 + * MSG_FULLBUFFER (4) ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10 + * MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 0x5) == 0x20 + * MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 0x6) == 0x40 + * MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 0x7) == 0x80 */ class JfrPostBox : public JfrCHeapObj { friend class JfrRecorder; + friend class JfrRecorderService; public: void post(JFR_Msg msg); diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp index 08250a1ae59..6f8d44fb1a4 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.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 @@ -55,6 +55,7 @@ #include "runtime/safepoint.hpp" #include "runtime/vmOperations.hpp" #include "runtime/vmThread.hpp" +#include "utilities/growableArray.hpp" // incremented on each flushpoint static u8 flushpoint_id = 0; @@ -391,6 +392,7 @@ class JfrSafepointWriteVMOperation : public VM_Operation { JfrRecorderService::JfrRecorderService() : _checkpoint_manager(JfrCheckpointManager::instance()), _chunkwriter(JfrRepository::chunkwriter()), + _post_box(JfrPostBox::instance()), _repository(JfrRepository::instance()), _stack_trace_repository(JfrStackTraceRepository::instance()), _storage(JfrStorage::instance()), @@ -670,17 +672,173 @@ void JfrRecorderService::evaluate_chunk_size_for_rotation() { JfrChunkRotation::evaluate(_chunkwriter); } -void JfrRecorderService::emit_leakprofiler_events(int64_t cutoff_ticks, bool emit_all, bool skip_bfs) { - DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(JavaThread::current())); - // Take the rotation lock to exclude flush() during event emits. This is because event emit - // also creates a number checkpoint events. Those checkpoint events require a future typeset checkpoint - // event for completeness, i.e. to be generated before being flushed to a segment. - // The upcoming flush() or rotation() after event emit completes this typeset checkpoint - // and serializes all event emit checkpoint events to the same segment. - JfrRotationLock lock; - // Take the rotation lock before the transition. - JavaThread* current_thread = JavaThread::current(); - MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current_thread)); - ThreadInVMfromNative transition(current_thread); - LeakProfiler::emit_events(cutoff_ticks, emit_all, skip_bfs); +// LeakProfiler event serialization support. + +struct JfrLeakProfilerEmitRequest { + int64_t cutoff_ticks; + bool emit_all; + bool skip_bfs; + bool oom; +}; + +typedef GrowableArrayCHeap JfrLeakProfilerEmitRequestQueue; +static JfrLeakProfilerEmitRequestQueue* _queue = nullptr; +constexpr const static int64_t _no_path_to_gc_roots = 0; +static bool _oom_emit_request_posted = false; +static bool _oom_emit_request_delivered = false; + +static inline bool exclude_paths_to_gc_roots(int64_t cutoff_ticks) { + return cutoff_ticks <= _no_path_to_gc_roots; +} + +static void enqueue(const JfrLeakProfilerEmitRequest& request) { + assert(JfrRotationLock::is_owner(), "invariant"); + if (_queue == nullptr) { + _queue = new JfrLeakProfilerEmitRequestQueue(4); + } + assert(_queue != nullptr, "invariant"); + assert(!_oom_emit_request_posted, "invariant"); + if (request.oom) { + _oom_emit_request_posted = true; + } + _queue->append(request); +} + +static JfrLeakProfilerEmitRequest dequeue() { + assert(JfrRotationLock::is_owner(), "invariant"); + assert(_queue != nullptr, "invariant"); + assert(_queue->is_nonempty(), "invariant"); + const JfrLeakProfilerEmitRequest& request = _queue->first(); + _queue->remove_at(0); + return request; +} + +// This version of emit excludes path-to-gc-roots, i.e. it skips reference chains. +static void emit_leakprofiler_events(bool emit_all, bool skip_bfs, JavaThread* jt) { + assert(jt != nullptr, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); + // Take the rotation lock to exclude flush() during event emits. This is because the event emit operation + // also creates a number of checkpoint events. Those checkpoint events require a future typeset checkpoint + // event for completeness, i.e., to be generated before being flushed to a segment. + // The upcoming flush() or rotation() after event emit completes this typeset checkpoint + // and serializes all checkpoint events to the same segment. + JfrRotationLock lock; + // Take the rotation lock before the thread transition, to avoid blocking safepoints. + if (_oom_emit_request_posted) { + // A request to emit leakprofiler events in response to CrashOnOutOfMemoryError + // is pending or has already been completed. We are about to crash at any time now. + assert(CrashOnOutOfMemoryError, "invariant"); + return; + } + MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt)); + ThreadInVMfromNative transition(jt); + // Since we are not requesting path-to-gc-roots, i.e., reference chains, we need not issue a VM_Operation. + // Therefore, we can let the requesting thread process the request directly, since it already holds the requisite lock. + LeakProfiler::emit_events(_no_path_to_gc_roots, emit_all, skip_bfs); +} + +void JfrRecorderService::transition_and_post_leakprofiler_emit_msg(JavaThread* jt) { + assert(jt != nullptr, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);) + assert(!JfrRotationLock::is_owner(), "invariant"); + // Transition to _thread_in_VM and post a synchronous message to the JFR Recorder Thread + // for it to process our enqueued request, which includes paths-to-gc-roots, i.e., reference chains. + MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt)); + ThreadInVMfromNative transition(jt); + _post_box.post(MSG_EMIT_LEAKP_REFCHAINS); +} + +// This version of emit includes path-to-gc-roots, i.e., it includes in the request traversing of reference chains. +// Traversing reference chains is performed as part of a VM_Operation, and we initiate it from the JFR Recorder Thread. +// Because multiple threads can concurrently report_on_java_out_of_memory(), having them all post a synchronous JFR msg, +// they rendezvous at a safepoint in a convenient state, ThreadBlockInVM. This mechanism prevents any thread from racing past +// this point and begin executing VMError::report_and_die(), until at least one oom request has been delivered. +void JfrRecorderService::emit_leakprofiler_events_paths_to_gc_roots(int64_t cutoff_ticks, + bool emit_all, + bool skip_bfs, + bool oom, + JavaThread* jt) { + assert(jt != nullptr, "invariant"); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);) + assert(!exclude_paths_to_gc_roots(cutoff_ticks), "invariant"); + + { + JfrRotationLock lock; + // Take the rotation lock to read and post a request for the JFR Recorder Thread. + if (_oom_emit_request_posted) { + if (!oom) { + // A request to emit leakprofiler events in response to CrashOnOutOfMemoryError + // is pending or has already been completed. We are about to crash at any time now. + assert(CrashOnOutOfMemoryError, "invariant"); + return; + } + } else { + assert(!_oom_emit_request_posted, "invariant"); + JfrLeakProfilerEmitRequest request = { cutoff_ticks, emit_all, skip_bfs, oom }; + enqueue(request); + } + } + JfrRecorderService service; + service.transition_and_post_leakprofiler_emit_msg(jt); +} + +// Leakprofiler serialization request, the jdk.jfr.internal.JVM.emitOldObjectSamples() Java entry point. +void JfrRecorderService::emit_leakprofiler_events(int64_t cutoff_ticks, + bool emit_all, + bool skip_bfs) { + JavaThread* const jt = JavaThread::current(); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);) + if (exclude_paths_to_gc_roots(cutoff_ticks)) { + ::emit_leakprofiler_events(emit_all, skip_bfs, jt); + return; + } + emit_leakprofiler_events_paths_to_gc_roots(cutoff_ticks, emit_all, skip_bfs, /* oom */ false, jt); +} + +// Leakprofiler serialization request, the report_on_java_out_of_memory VM entry point. +void JfrRecorderService::emit_leakprofiler_events_on_oom() { + assert(CrashOnOutOfMemoryError, "invariant"); + if (EventOldObjectSample::is_enabled()) { + JavaThread* const jt = JavaThread::current(); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt);) + ThreadToNativeFromVM transition(jt); + emit_leakprofiler_events_paths_to_gc_roots(max_jlong, false, false, /* oom */ true, jt); + } +} + +// The worker routine for the JFR Recorder Thread when processing MSG_EMIT_LEAKP_REFCHAINS messages. +void JfrRecorderService::emit_leakprofiler_events() { + JavaThread* const jt = JavaThread::current(); + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); + // Take the rotation lock before the transition. + JfrRotationLock lock; + if (_oom_emit_request_delivered) { + // A request to emit leakprofiler events in response to CrashOnOutOfMemoryError + // has already been completed. We are about to crash at any time now. + assert(_oom_emit_request_posted, "invariant"); + assert(CrashOnOutOfMemoryError, "invariant"); + return; + } + + assert(_queue->is_nonempty(), "invariant"); + + { + MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt)); + ThreadInVMfromNative transition(jt); + while (_queue->is_nonempty()) { + const JfrLeakProfilerEmitRequest& request = dequeue(); + LeakProfiler::emit_events(request.cutoff_ticks, request.emit_all, request.skip_bfs); + if (_oom_emit_request_posted && request.oom) { + assert(CrashOnOutOfMemoryError, "invariant"); + _oom_emit_request_delivered = true; + break; + } + } + } + + // If processing involved an out-of-memory request, issue an immediate flush operation. + DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); + if (_chunkwriter.is_valid() && _oom_emit_request_delivered) { + invoke_flush(); + } } diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp index e5b4500afc0..3759ff98828 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp @@ -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 @@ -27,19 +27,23 @@ #include "jfr/utilities/jfrAllocation.hpp" +class JavaThread; class JfrCheckpointManager; class JfrChunkWriter; +class JfrPostBox; class JfrRepository; class JfrStackTraceRepository; class JfrStorage; class JfrStringPool; class JfrRecorderService : public StackObj { + friend class Jfr; friend class JfrSafepointClearVMOperation; friend class JfrSafepointWriteVMOperation; private: JfrCheckpointManager& _checkpoint_manager; JfrChunkWriter& _chunkwriter; + JfrPostBox& _post_box; JfrRepository& _repository; JfrStackTraceRepository& _stack_trace_repository; JfrStorage& _storage; @@ -64,6 +68,14 @@ class JfrRecorderService : public StackObj { void invoke_safepoint_write(); void post_safepoint_write(); + void transition_and_post_leakprofiler_emit_msg(JavaThread* jt); + + static void emit_leakprofiler_events_on_oom(); + static void emit_leakprofiler_events_paths_to_gc_roots(int64_t cutoff_ticks, + bool emit_all, + bool skip_bfs, + bool oom, + JavaThread* jt); public: JfrRecorderService(); void start(); @@ -72,8 +84,12 @@ class JfrRecorderService : public StackObj { void flushpoint(); void process_full_buffers(); void evaluate_chunk_size_for_rotation(); - void emit_leakprofiler_events(int64_t cutoff_ticks, bool emit_all, bool skip_bfs); + void emit_leakprofiler_events(); + static bool is_recording(); + static void emit_leakprofiler_events(int64_t cutoff_ticks, + bool emit_all, + bool skip_bfs); }; #endif // SHARE_JFR_RECORDER_SERVICE_JFRRECORDERSERVICE_HPP diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp index de015e9a502..bd01adf5b3a 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.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 @@ -44,6 +44,7 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) { #define ROTATE (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP))) #define FLUSHPOINT (msgs & (MSGBIT(MSG_FLUSHPOINT))) #define PROCESS_FULL_BUFFERS (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)|MSGBIT(MSG_FULLBUFFER))) + #define LEAKPROFILER_REFCHAINS (msgs & MSGBIT(MSG_EMIT_LEAKP_REFCHAINS)) JfrPostBox& post_box = JfrRecorderThreadEntry::post_box(); log_debug(jfr, system)("Recorder thread STARTED"); @@ -70,6 +71,9 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) { if (PROCESS_FULL_BUFFERS) { service.process_full_buffers(); } + if (LEAKPROFILER_REFCHAINS) { + service.emit_leakprofiler_events(); + } // Check amount of data written to chunk already // if it warrants asking for a new chunk. service.evaluate_chunk_size_for_rotation(); @@ -98,5 +102,5 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) { #undef ROTATE #undef FLUSHPOINT #undef PROCESS_FULL_BUFFERS - #undef SCAVENGE + #undef LEAKPROFILER_REFCHAINS } diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index c49a9f5d4b8..ee4f776df06 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.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 @@ -466,10 +466,7 @@ void before_exit(JavaThread* thread, bool halt) { event.commit(); } - // 2nd argument (emit_event_shutdown) should be set to false - // because EventShutdown would be emitted at Threads::destroy_vm(). - // (one of the callers of before_exit()) - JFR_ONLY(Jfr::on_vm_shutdown(true, false, halt);) + JFR_ONLY(Jfr::on_vm_shutdown(false, halt);) // Stop the WatcherThread. We do this before disenrolling various // PeriodicTasks to reduce the likelihood of races. diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index de39fe32dc1..9e167141259 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.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 @@ -63,6 +63,9 @@ #include "utilities/nativeStackPrinter.hpp" #include "utilities/unsigned5.hpp" #include "utilities/vmError.hpp" +#if INCLUDE_JFR +#include "jfr/jfr.hpp" +#endif #include #include @@ -262,6 +265,8 @@ void report_untested(const char* file, int line, const char* message) { void report_java_out_of_memory(const char* message) { static int out_of_memory_reported = 0; + JFR_ONLY(Jfr::on_report_java_out_of_memory();) + // A number of threads may attempt to report OutOfMemoryError at around the // same time. To avoid dumping the heap or executing the data collection // commands multiple times we just do it once when the first threads reports diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index a290602e0be..88f81b31293 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.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) 2017, 2024 SAP SE. All rights reserved. * Copyright (c) 2023, 2025, Red Hat, Inc. and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -1898,7 +1898,7 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt log.set_fd(-1); } - JFR_ONLY(Jfr::on_vm_shutdown(static_cast(_id) == OOM_JAVA_HEAP_FATAL, true);) + JFR_ONLY(Jfr::on_vm_shutdown(true, false, static_cast(_id) == OOM_JAVA_HEAP_FATAL);) if (PrintNMTStatistics) { fdStream fds(fd_out); diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 9cfc23ea8da..291b2163b0d 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -710,7 +710,6 @@ jdk/incubator/vector/LoadJsvmlTest.java 8305390 windows- # jdk_jfr jdk/jfr/event/compiler/TestCodeSweeper.java 8338127 generic-all -jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.java 8371014 aix-ppc64,linux-ppc64le jdk/jfr/event/oldobject/TestShenandoah.java 8342951 generic-all jdk/jfr/event/runtime/TestResidentSetSizeEvent.java 8309846 aix-ppc64 jdk/jfr/jvm/TestWaste.java 8371630 generic-all diff --git a/test/jdk/jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.java b/test/jdk/jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.java index d540acd853b..b3630fa7f77 100644 --- a/test/jdk/jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.java +++ b/test/jdk/jdk/jfr/event/oldobject/TestEmergencyDumpAtOOM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, NTT DATA. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -98,8 +98,8 @@ public class TestEmergencyDumpAtOOM { // Check OldObjectSample events if (oldObjects.get() > 0L) { if (shouldCrash) { - Asserts.assertEquals("VM Error", shutdownReason.get()); - Asserts.assertEquals("Out of Memory", dumpReason.get()); + Asserts.assertEquals("CrashOnOutOfMemoryError", shutdownReason.get()); + Asserts.assertEquals("CrashOnOutOfMemoryError", dumpReason.get()); } else { Asserts.assertEquals("No remaining non-daemon Java threads", shutdownReason.get()); } From b070367bdf980ef1c257cab485927db39b544241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20Gr=C3=B6nlund?= Date: Tue, 13 Jan 2026 19:40:20 +0000 Subject: [PATCH 085/204] 8373106: JFR suspend/resume deadlock on macOS in pthreads library Reviewed-by: egahlin --- .../periodic/sampling/jfrThreadSampler.cpp | 92 +++++++++---------- 1 file changed, 43 insertions(+), 49 deletions(-) diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp index 805426078c4..0a8b3975139 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp @@ -232,41 +232,50 @@ void JfrSamplerThread::task_stacktrace(JfrSampleRequestType type, JavaThread** l JavaThread* start = nullptr; elapsedTimer sample_time; sample_time.start(); - ThreadsListHandle tlh; - // Resolve a sample session relative start position index into the thread list array. - // In cases where the last sampled thread is null or not-null but stale, find_index() returns -1. - _cur_index = tlh.list()->find_index_of_JavaThread(*last_thread); - JavaThread* current = _cur_index != -1 ? *last_thread : nullptr; + { + /* + * Take the Threads_lock for three purposes: + * + * 1) Avoid sampling right through a safepoint, + * which could result in touching oops in case of virtual threads. + * 2) Prevent JFR from issuing an epoch rotation while the sampler thread + * is actively processing a thread in state native, as both threads are outside the safepoint protocol. + * 3) Some operating systems (BSD / Mac) require a process lock when sending a signal with pthread_kill. + * Holding the Threads_lock prevents a JavaThread from calling os::create_thread(), which also takes the process lock. + * In a sense, we provide a coarse signal mask, so we can always send the resume signal. + */ + MutexLocker tlock(Threads_lock); + ThreadsListHandle tlh; + // Resolve a sample session relative start position index into the thread list array. + // In cases where the last sampled thread is null or not-null but stale, find_index() returns -1. + _cur_index = tlh.list()->find_index_of_JavaThread(*last_thread); + JavaThread* current = _cur_index != -1 ? *last_thread : nullptr; - while (num_samples < sample_limit) { - current = next_thread(tlh.list(), start, current); - if (current == nullptr) { - break; - } - if (is_excluded(current)) { - continue; - } - if (start == nullptr) { - start = current; // remember the thread where we started to attempt sampling - } - bool success; - if (JAVA_SAMPLE == type) { - success = sample_java_thread(current); - } else { - assert(type == NATIVE_SAMPLE, "invariant"); - success = sample_native_thread(current); - } - if (success) { - num_samples++; - } - if (SafepointSynchronize::is_at_safepoint()) { - // For _thread_in_native, we cannot get the Threads_lock. - // For _thread_in_Java, well, there are none. - break; + while (num_samples < sample_limit) { + current = next_thread(tlh.list(), start, current); + if (current == nullptr) { + break; + } + if (is_excluded(current)) { + continue; + } + if (start == nullptr) { + start = current; // remember the thread where we started to attempt sampling + } + bool success; + if (JAVA_SAMPLE == type) { + success = sample_java_thread(current); + } else { + assert(type == NATIVE_SAMPLE, "invariant"); + success = sample_native_thread(current); + } + if (success) { + num_samples++; + } } + + *last_thread = current; // remember the thread we last attempted to sample } - - *last_thread = current; // remember the thread we last attempted to sample sample_time.stop(); log_trace(jfr)("JFR thread sampling done in %3.7f secs with %d java %d native samples", sample_time.seconds(), type == JAVA_SAMPLE ? num_samples : 0, type == NATIVE_SAMPLE ? num_samples : 0); @@ -297,6 +306,7 @@ class OSThreadSampler : public SuspendedThreadTask { // Sampling a thread in state _thread_in_Java // involves a platform-specific thread suspend and CPU context retrieval. bool JfrSamplerThread::sample_java_thread(JavaThread* jt) { + assert_lock_strong(Threads_lock); if (jt->thread_state() != _thread_in_Java) { return false; } @@ -328,6 +338,7 @@ static JfrSamplerThread* _sampler_thread = nullptr; // without thread suspension and CPU context retrieval, // if we carefully order the loads of the thread state. bool JfrSamplerThread::sample_native_thread(JavaThread* jt) { + assert_lock_strong(Threads_lock); if (jt->thread_state() != _thread_in_native) { return false; } @@ -343,22 +354,6 @@ bool JfrSamplerThread::sample_native_thread(JavaThread* jt) { SafepointMechanism::arm_local_poll_release(jt); - // Take the Threads_lock for two purposes: - // 1) Avoid sampling through a safepoint which could result - // in touching oops in case of virtual threads. - // 2) Prevent JFR from issuing an epoch rotation while the sampler thread - // is actively processing a thread in native, as both threads are now - // outside the safepoint protocol. - - // OrderAccess::fence() as part of acquiring the lock prevents loads from floating up. - JfrMutexTryLock lock(Threads_lock); - - if (!lock.acquired()) { - // Remove the native sample request and release the potentially waiting thread. - JfrSampleMonitor jsm(tl); - return false; - } - // Separate the arming of the poll (above) from the reading of JavaThread state (below). if (UseSystemMemoryBarrier) { SystemMemoryBarrier::emit(); @@ -367,7 +362,6 @@ bool JfrSamplerThread::sample_native_thread(JavaThread* jt) { } if (jt->thread_state() != _thread_in_native || !jt->has_last_Java_frame()) { - assert_lock_strong(Threads_lock); JfrSampleMonitor jsm(tl); if (jsm.is_waiting()) { // The thread has already returned from native, From 4d0ad0a4a391286c683ebb8c8d711ea0be68c31a Mon Sep 17 00:00:00 2001 From: Brent Christian Date: Tue, 13 Jan 2026 19:47:11 +0000 Subject: [PATCH 086/204] 8373718: jdk/internal/misc/VM/RuntimeArguments.java test fails in Virtual threads mode Reviewed-by: alanb --- test/jdk/jdk/internal/misc/VM/RuntimeArguments.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/jdk/jdk/internal/misc/VM/RuntimeArguments.java b/test/jdk/jdk/internal/misc/VM/RuntimeArguments.java index dbcb30255a8..b86593d84ba 100644 --- a/test/jdk/jdk/internal/misc/VM/RuntimeArguments.java +++ b/test/jdk/jdk/internal/misc/VM/RuntimeArguments.java @@ -24,6 +24,7 @@ /** * @test * @requires vm.flagless + * @requires test.thread.factory == null * @library /test/lib * @modules java.base/jdk.internal.misc * jdk.zipfs From 9ed0ecbcc1b4796bc56b7cb341ff8f9d3898713d Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Tue, 13 Jan 2026 22:38:12 +0000 Subject: [PATCH 087/204] 8375061: Multiple jpackage tool providers may share the same logging config Reviewed-by: almatvee --- .../jdk/jpackage/internal/Globals.java | 14 + .../classes/jdk/jpackage/internal/Log.java | 41 +-- .../jdk/jpackage/internal/cli/Main.java | 16 +- .../jpackage/test/JPackageCommandTest.java | 183 +++++++++++++ .../helpers/jdk/jpackage/test/Executor.java | 5 + .../jdk/jpackage/test/JPackageCommand.java | 150 +++++++---- .../helpers/jdk/jpackage/test/Main.java | 55 +++- .../helpers/jdk/jpackage/test/TKit.java | 247 +++++++++--------- .../cli/OptionsValidationFailTest.java | 7 +- .../tools/jdk/jpackage/test/JUnitAdapter.java | 16 +- test/jdk/tools/jpackage/share/AsyncTest.java | 96 +++---- .../jpackage/windows/Win8301247Test.java | 5 +- .../jpackage/windows/WinNoRestartTest.java | 5 +- 13 files changed, 532 insertions(+), 308 deletions(-) create mode 100644 test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JPackageCommandTest.java diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java index c1b56b24e0a..91ae37870a5 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Globals.java @@ -24,6 +24,7 @@ */ package jdk.jpackage.internal; +import java.io.PrintWriter; import java.util.Optional; import java.util.function.Supplier; @@ -46,6 +47,18 @@ public final class Globals { return objectFactory(ObjectFactory.build(objectFactory).executorFactory(v).create()); } + Log.Logger logger() { + return logger; + } + + public void loggerOutputStreams(PrintWriter out, PrintWriter err) { + logger.setPrintWriter(out, err); + } + + public void loggerVerbose() { + logger.setVerbose(); + } + public static int main(Supplier mainBody) { if (INSTANCE.isBound()) { return mainBody.get(); @@ -65,6 +78,7 @@ public final class Globals { } private ObjectFactory objectFactory = ObjectFactory.DEFAULT; + private final Log.Logger logger = new Log.Logger(); private static final ScopedValue INSTANCE = ScopedValue.newInstance(); private static final Globals DEFAULT = new Globals(); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java index 0f51fa166f9..5c27ef67500 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Log.java @@ -61,16 +61,6 @@ public class Log { this.err = err; } - public void flush() { - if (out != null) { - out.flush(); - } - - if (err != null) { - err.flush(); - } - } - public void info(String msg) { if (out != null) { out.println(msg); @@ -111,46 +101,27 @@ public class Log { } } - private static final InheritableThreadLocal instance = - new InheritableThreadLocal() { - @Override protected Logger initialValue() { - return new Logger(); - } - }; - - public static void setPrintWriter (PrintWriter out, PrintWriter err) { - instance.get().setPrintWriter(out, err); - } - - public static void flush() { - instance.get().flush(); - } - public static void info(String msg) { - instance.get().info(msg); + Globals.instance().logger().info(msg); } public static void fatalError(String msg) { - instance.get().fatalError(msg); + Globals.instance().logger().fatalError(msg); } public static void error(String msg) { - instance.get().error(msg); - } - - public static void setVerbose() { - instance.get().setVerbose(); + Globals.instance().logger().error(msg); } public static boolean isVerbose() { - return instance.get().isVerbose(); + return Globals.instance().logger().isVerbose(); } public static void verbose(String msg) { - instance.get().verbose(msg); + Globals.instance().logger().verbose(msg); } public static void verbose(Throwable t) { - instance.get().verbose(t); + Globals.instance().logger().verbose(t); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java index 31be2bb33c5..270ba0c927f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java @@ -79,8 +79,8 @@ public final class Main { @Override public int run(PrintStream out, PrintStream err, String... args) { - PrintWriter outWriter = new PrintWriter(out, true); - PrintWriter errWriter = new PrintWriter(err, true); + PrintWriter outWriter = toPrintWriter(out); + PrintWriter errWriter = toPrintWriter(err); try { try { return run(outWriter, errWriter, args); @@ -98,8 +98,8 @@ public final class Main { } public static void main(String... args) { - var out = new PrintWriter(System.out, true); - var err = new PrintWriter(System.err, true); + var out = toPrintWriter(System.out); + var err = toPrintWriter(System.err); System.exit(run(out, err, args)); } @@ -127,7 +127,7 @@ public final class Main { Objects.requireNonNull(out); Objects.requireNonNull(err); - Log.setPrintWriter(out, err); + Globals.instance().loggerOutputStreams(out, err); final var runner = new Runner(t -> { new ErrorReporter(_ -> { @@ -179,7 +179,7 @@ public final class Main { } if (VERBOSE.containsIn(options)) { - Log.setVerbose(); + Globals.instance().loggerVerbose(); } final var optionsProcessor = new OptionsProcessor(parsedOptionsBuilder, bundlingEnv); @@ -310,6 +310,10 @@ public final class Main { return System.getProperty("java.version"); } + private static PrintWriter toPrintWriter(PrintStream ps) { + return new PrintWriter(ps, true, ps.charset()); + } + private enum DefaultBundlingEnvironmentLoader implements Supplier { INSTANCE; diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JPackageCommandTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JPackageCommandTest.java new file mode 100644 index 00000000000..b0d44702d47 --- /dev/null +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/JPackageCommandTest.java @@ -0,0 +1,183 @@ +/* + * 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.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.spi.ToolProvider; +import jdk.jpackage.internal.util.function.ExceptionBox; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class JPackageCommandTest extends JUnitAdapter.TestSrcInitializer { + + @ParameterizedTest + @MethodSource + void testUseToolProvider(UseToolProviderTestSpec spec) { + // Run the test with the new state to avoid UnsupportedOperationException + // that will be thrown if it attempts to alter global variables in the default R/O state. + TKit.withNewState(spec::test); + } + + private static List testUseToolProvider() { + + var testCases = new ArrayList(); + + for (var globalToolProvider : ExecutableSetterType.values()) { + for (var instanceToolProvider : ExecutableSetterType.values()) { + testCases.add(new UseToolProviderTestSpec(globalToolProvider, instanceToolProvider)); + } + } + + return testCases; + } + + record UseToolProviderTestSpec(ExecutableSetterType globalType, ExecutableSetterType instanceType) { + + UseToolProviderTestSpec { + Objects.requireNonNull(globalType); + Objects.requireNonNull(instanceType); + } + + @Override + public String toString() { + return String.format("%s, global=%s", instanceType, globalType); + } + + void test() { + + final Optional global; + switch (globalType) { + case SET_CUSTOM_TOOL_PROVIDER -> { + global = Optional.of(createNewToolProvider("jpackage-mock-global")); + JPackageCommand.useToolProviderByDefault(global.get()); + } + case SET_DEFAULT_TOOL_PROVIDER -> { + global = Optional.of(JavaTool.JPACKAGE.asToolProvider()); + JPackageCommand.useToolProviderByDefault(); + } + case SET_PROCESS -> { + global = Optional.empty(); + JPackageCommand.useExecutableByDefault(); + } + case SET_NONE -> { + global = Optional.empty(); + } + default -> { + throw ExceptionBox.reachedUnreachable(); + } + } + + var cmd = new JPackageCommand(); + + final Optional instance; + switch (instanceType) { + case SET_CUSTOM_TOOL_PROVIDER -> { + instance = Optional.of(createNewToolProvider("jpackage-mock")); + cmd.useToolProvider(instance.get()); + } + case SET_DEFAULT_TOOL_PROVIDER -> { + instance = Optional.of(JavaTool.JPACKAGE.asToolProvider()); + cmd.useToolProvider(true); + } + case SET_PROCESS -> { + instance = Optional.empty(); + cmd.useToolProvider(false); + } + case SET_NONE -> { + instance = Optional.empty(); + } + default -> { + throw ExceptionBox.reachedUnreachable(); + } + } + + var actual = cmd.createExecutor().getToolProvider(); + + switch (instanceType) { + case SET_CUSTOM_TOOL_PROVIDER -> { + assertSame(actual.get(), instance.get()); + assertTrue(cmd.isWithToolProvider()); + } + case SET_DEFAULT_TOOL_PROVIDER -> { + global.ifPresentOrElse(expected -> { + assertEquals(expected.name(), actual.orElseThrow().name()); + }, () -> { + assertEquals(instance.get().name(), actual.get().name()); + }); + assertTrue(cmd.isWithToolProvider()); + } + case SET_PROCESS -> { + assertFalse(actual.isPresent()); + assertFalse(cmd.isWithToolProvider()); + } + case SET_NONE -> { + switch (globalType) { + case SET_CUSTOM_TOOL_PROVIDER -> { + assertSame(global.get(), actual.get()); + assertTrue(cmd.isWithToolProvider()); + } + case SET_DEFAULT_TOOL_PROVIDER -> { + assertEquals(global.get().name(), actual.orElseThrow().name()); + assertTrue(cmd.isWithToolProvider()); + } + case SET_PROCESS, SET_NONE -> { + assertFalse(actual.isPresent()); + assertFalse(cmd.isWithToolProvider()); + } + } + } + } + } + + private static ToolProvider createNewToolProvider(String name) { + return new ToolProvider() { + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + throw new UnsupportedOperationException(); + } + + @Override + public String name() { + return name; + } + }; + } + } + + enum ExecutableSetterType { + SET_DEFAULT_TOOL_PROVIDER, + SET_CUSTOM_TOOL_PROVIDER, + SET_PROCESS, + SET_NONE, + ; + } +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java index 6e94133a543..d4833eb9736 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java @@ -63,6 +63,7 @@ public final class Executor extends CommandArguments { } public Executor() { + commandOutputControl.dumpStdout(TKit.state().out()).dumpStderr(TKit.state().err()); } public Executor setExecutable(String v) { @@ -85,6 +86,10 @@ public final class Executor extends CommandArguments { return setToolProvider(v.asToolProvider()); } + public Optional getToolProvider() { + return Optional.ofNullable(toolProvider); + } + public Optional getExecutable() { return Optional.ofNullable(executable); } 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 9cfb75bcdb3..d2b423b2ed2 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -52,8 +52,6 @@ import java.util.Optional; import java.util.OptionalInt; import java.util.Set; import java.util.TreeSet; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -63,6 +61,7 @@ import java.util.spi.ToolProvider; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import jdk.jpackage.internal.util.function.ExceptionBox; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.internal.util.function.ThrowingFunction; import jdk.jpackage.internal.util.function.ThrowingRunnable; @@ -77,6 +76,7 @@ public class JPackageCommand extends CommandArguments { @SuppressWarnings("this-escape") public JPackageCommand() { + toolProviderSource = new ToolProviderSource(); prerequisiteActions = new Actions(); verifyActions = new Actions(); excludeStandardAsserts(StandardAssert.MAIN_LAUNCHER_DESCRIPTION); @@ -84,7 +84,7 @@ public class JPackageCommand extends CommandArguments { private JPackageCommand(JPackageCommand cmd, boolean immutable) { args.addAll(cmd.args); - withToolProvider = cmd.withToolProvider; + toolProviderSource = cmd.toolProviderSource.copy(); saveConsoleOutput = cmd.saveConsoleOutput; discardStdout = cmd.discardStdout; discardStderr = cmd.discardStderr; @@ -770,7 +770,7 @@ public class JPackageCommand extends CommandArguments { } public static void useToolProviderByDefault(ToolProvider jpackageToolProvider) { - defaultToolProvider.set(Optional.of(jpackageToolProvider)); + TKit.state().setProperty(DefaultToolProviderKey.VALUE, Objects.requireNonNull(jpackageToolProvider)); } public static void useToolProviderByDefault() { @@ -778,45 +778,22 @@ public class JPackageCommand extends CommandArguments { } public static void useExecutableByDefault() { - defaultToolProvider.set(Optional.empty()); - } - - /** - * In a separate thread calls {@link #useToolProviderByDefault(ToolProvider)} - * with the specified {@code jpackageToolProvider} and then calls - * {@code workload.run()}. Joins the thread. - *

      - * The idea is to run the {@code workload} in the context of the specified - * jpackage {@code ToolProvider} without altering the global variable holding - * the default jpackage {@code ToolProvider}. The global variable is - * thread-local; setting its value in a new thread doesn't alter its copy in the - * calling thread. - * - * @param jpackageToolProvider jpackage {@code ToolProvider} - * @param workload the workload to run - */ - public static void withToolProvider(Runnable workload, ToolProvider jpackageToolProvider) { - Objects.requireNonNull(workload); - Objects.requireNonNull(jpackageToolProvider); - - CompletableFuture.runAsync(() -> { - var oldValue = defaultToolProvider.get(); - useToolProviderByDefault(jpackageToolProvider); - try { - workload.run(); - } finally { - defaultToolProvider.set(oldValue); - } - // Run the future in a new native thread. Don't run it in a virtual/pooled thread. - // Pooled and/or virtual threads are problematic when used with inheritable thread-local variables. - // TKit class depends on such a variable, which results in intermittent test failures - // if the default executor runs this future. - }, Executors.newThreadPerTaskExecutor(Thread.ofPlatform().factory())).join(); + TKit.state().setProperty(DefaultToolProviderKey.VALUE, null); } public JPackageCommand useToolProvider(boolean v) { verifyMutable(); - withToolProvider = v; + if (v) { + toolProviderSource.useDefaultToolProvider(); + } else { + toolProviderSource.useProcess(); + } + return this; + } + + public JPackageCommand useToolProvider(ToolProvider v) { + verifyMutable(); + toolProviderSource.useToolProvider(v); return this; } @@ -928,9 +905,7 @@ public class JPackageCommand extends CommandArguments { } public boolean isWithToolProvider() { - return Optional.ofNullable(withToolProvider).orElseGet(() -> { - return defaultToolProvider.get().isPresent(); - }); + return toolProviderSource.toolProvider().isPresent(); } public JPackageCommand executePrerequisiteActions() { @@ -938,21 +913,19 @@ public class JPackageCommand extends CommandArguments { return this; } - private Executor createExecutor() { + Executor createExecutor() { Executor exec = new Executor() .saveOutput(saveConsoleOutput).dumpOutput(!suppressOutput) .discardStdout(discardStdout).discardStderr(discardStderr) .setDirectory(executeInDirectory) .addArguments(args); - if (isWithToolProvider()) { - exec.setToolProvider(defaultToolProvider.get().orElseGet(JavaTool.JPACKAGE::asToolProvider)); - } else { - exec.setExecutable(JavaTool.JPACKAGE); - if (TKit.isWindows()) { - exec.setWindowsTmpDir(System.getProperty("java.io.tmpdir")); - } - } + toolProviderSource.toolProvider().ifPresentOrElse(exec::setToolProvider, () -> { + exec.setExecutable(JavaTool.JPACKAGE); + if (TKit.isWindows()) { + exec.setWindowsTmpDir(System.getProperty("java.io.tmpdir")); + } + }); return exec; } @@ -1731,7 +1704,70 @@ public class JPackageCommand extends CommandArguments { private final List actions; } - private Boolean withToolProvider; + private static final class ToolProviderSource { + + ToolProviderSource copy() { + return new ToolProviderSource(this); + } + + void useDefaultToolProvider() { + customToolProvider = null; + mode = Mode.USE_TOOL_PROVIDER; + } + + void useToolProvider(ToolProvider tp) { + customToolProvider = Objects.requireNonNull(tp); + mode = Mode.USE_TOOL_PROVIDER; + } + + void useProcess() { + customToolProvider = null; + mode = Mode.USE_PROCESS; + } + + Optional toolProvider() { + switch (mode) { + case USE_PROCESS -> { + return Optional.empty(); + } + case USE_TOOL_PROVIDER -> { + if (customToolProvider != null) { + return Optional.of(customToolProvider); + } else { + return TKit.state().findProperty(DefaultToolProviderKey.VALUE).map(ToolProvider.class::cast).or(() -> { + return Optional.of(JavaTool.JPACKAGE.asToolProvider()); + }); + } + } + case INHERIT_DEFAULTS -> { + return TKit.state().findProperty(DefaultToolProviderKey.VALUE).map(ToolProvider.class::cast); + } + default -> { + throw ExceptionBox.reachedUnreachable(); + } + } + } + + ToolProviderSource() { + mode = Mode.INHERIT_DEFAULTS; + } + + private ToolProviderSource(ToolProviderSource other) { + this.customToolProvider = other.customToolProvider; + this.mode = other.mode; + } + + private enum Mode { + INHERIT_DEFAULTS, + USE_PROCESS, + USE_TOOL_PROVIDER + } + + private ToolProvider customToolProvider; + private Mode mode; + } + + private final ToolProviderSource toolProviderSource; private boolean saveConsoleOutput; private boolean discardStdout; private boolean discardStderr; @@ -1747,12 +1783,10 @@ public class JPackageCommand extends CommandArguments { private Set readOnlyPathAsserts = Set.of(ReadOnlyPathAssert.values()); private Set standardAsserts = Set.of(StandardAssert.values()); private List>> outputValidators = new ArrayList<>(); - private static InheritableThreadLocal> defaultToolProvider = new InheritableThreadLocal<>() { - @Override - protected Optional initialValue() { - return Optional.empty(); - } - }; + + private enum DefaultToolProviderKey { + VALUE + } private static final Map PACKAGE_TYPES = Stream.of(PackageType.values()).collect(toMap(PackageType::getType, x -> x)); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java index fee5b65c897..c0bc858eb1e 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.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 @@ -27,17 +27,24 @@ import static java.util.stream.Collectors.toCollection; import static java.util.stream.Collectors.toMap; import static jdk.jpackage.test.TestBuilder.CMDLINE_ARG_PREFIX; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Comparator; import java.util.Deque; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.internal.util.function.ThrowingRunnable; public final class Main { @@ -47,10 +54,52 @@ public final class Main { } public static void main(TestBuilder.Builder builder, String... args) throws Exception { + Objects.requireNonNull(builder); + + var argList = List.of(args); + + var ignoreLogfile = argList.contains(CMDLINE_ARG_PREFIX + "ignore-logfile"); + + List filteredArgs; + if (ignoreLogfile) { + filteredArgs = argList.stream().filter(Predicate.isEqual(CMDLINE_ARG_PREFIX + "ignore-logfile").negate()).toList(); + } else { + filteredArgs = argList; + } + + ThrowingRunnable workload = () -> { + run(builder, filteredArgs); + }; + + try { + Optional.ofNullable(TKit.getConfigProperty("logfile")).filter(_ -> { + return !ignoreLogfile; + }).map(Path::of).ifPresentOrElse(logfile -> { + + try (var out = new PrintStream( + Files.newOutputStream(logfile, StandardOpenOption.CREATE, StandardOpenOption.APPEND), + true, + System.out.charset())) { + + TKit.withOutput(workload, out, out); + + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + + }, () -> { + ThrowingRunnable.toRunnable(workload).run(); + }); + } catch (Exception ex) { + throw ExceptionBox.unbox(ex); + } + } + + private static void run(TestBuilder.Builder builder, List args) throws Exception { boolean listTests = false; List tests = new ArrayList<>(); try (TestBuilder testBuilder = builder.testConsumer(tests::add).create()) { - Deque argsAsList = new ArrayDeque<>(List.of(args)); + Deque argsAsList = new ArrayDeque<>(args); while (!argsAsList.isEmpty()) { var arg = argsAsList.pop(); TestBuilder.trace(String.format("Parsing [%s]...", arg)); @@ -115,7 +164,7 @@ public final class Main { return; } - TKit.withExtraLogStream(() -> runTests(orderedTests)); + runTests(orderedTests); } private static void runTests(List tests) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java index 47ab838f8d6..1639beadb28 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.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 @@ -38,7 +38,6 @@ import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.nio.file.StandardOpenOption; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; @@ -52,6 +51,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -109,56 +109,45 @@ public final class TKit { throw throwUnknownPlatformError(); }).get(); - static void withExtraLogStream(ThrowingRunnable action) { - if (state().extraLogStream != null) { - ThrowingRunnable.toRunnable(action).run(); - } else { - try (PrintStream logStream = openLogStream()) { - withExtraLogStream(action, logStream); + public static void withOutput(ThrowingRunnable action, PrintStream out, PrintStream err) { + Objects.requireNonNull(action); + Objects.requireNonNull(out); + Objects.requireNonNull(err); + + try { + withState(action, stateBuilder -> { + stateBuilder.out(out).err(err); + }); + } finally { + try { + out.flush(); + } finally { + err.flush(); } } } - static void withExtraLogStream(ThrowingRunnable action, PrintStream logStream) { - withNewState(action, stateBuilder -> { - stateBuilder.extraLogStream(logStream); - }); - } - - public static void withMainLogStream(ThrowingRunnable action, PrintStream logStream) { - withNewState(action, stateBuilder -> { - stateBuilder.mainLogStream(logStream); - }); - } - - public static void withStackTraceStream(ThrowingRunnable action, PrintStream logStream) { - withNewState(action, stateBuilder -> { - stateBuilder.stackTraceStream(logStream); - }); - } - - public static State state() { - return STATE.get(); - } - - public static void state(State v) { - STATE.set(Objects.requireNonNull(v)); - } - - private static void withNewState(ThrowingRunnable action, Consumer stateBuilderMutator) { + public static void withState(ThrowingRunnable action, Consumer stateBuilderMutator) { Objects.requireNonNull(action); Objects.requireNonNull(stateBuilderMutator); - var oldState = state(); - var builder = oldState.buildCopy(); - stateBuilderMutator.accept(builder); - var newState = builder.create(); - try { - state(newState); - ThrowingRunnable.toRunnable(action).run(); - } finally { - state(oldState); - } + var stateBuilder = state().buildCopy(); + stateBuilderMutator.accept(stateBuilder); + withState(action, stateBuilder.create()); + } + + public static void withNewState(ThrowingRunnable action) { + withState(action, _ -> {}); + } + + public static void withState(ThrowingRunnable action, State state) { + Objects.requireNonNull(action); + Objects.requireNonNull(state); + ScopedValue.where(STATE, state).run(ThrowingRunnable.toRunnable(action)); + } + + public static State state() { + return STATE.orElse(DEFAULT_STATE); } enum RunTestMode { @@ -178,33 +167,19 @@ public final class TKit { throw new IllegalStateException("Unexpected nested Test.run() call"); } - withExtraLogStream(() -> { - tests.stream().forEach(test -> { - withNewState(() -> { - try { - if (modes.contains(RunTestMode.FAIL_FAST)) { - test.run(); - } else { - ignoreExceptions(test).run(); - } - } finally { - Optional.ofNullable(state().extraLogStream).ifPresent(PrintStream::flush); - } - }, stateBuilder -> { - stateBuilder.currentTest(test); - }); + tests.stream().forEach(test -> { + withState(() -> { + if (modes.contains(RunTestMode.FAIL_FAST)) { + test.run(); + } else { + ignoreExceptions(test).run(); + } + }, stateBuilder -> { + stateBuilder.currentTest(test); }); }); } - static T runAdhocTest(ThrowingSupplier action) { - final List box = new ArrayList<>(); - runAdhocTest(() -> { - box.add(action.get()); - }); - return box.getFirst(); - } - static void runAdhocTest(ThrowingRunnable action) { Objects.requireNonNull(action); @@ -281,10 +256,7 @@ public final class TKit { static void log(String v) { v = addTimestamp(v); var state = state(); - state.mainLogStream.println(v); - if (state.extraLogStream != null) { - state.extraLogStream.println(v); - } + state.out.println(v); } static Path removeRootFromAbsolutePath(Path v) { @@ -692,8 +664,7 @@ public final class TKit { static void printStackTrace(Throwable throwable) { var state = state(); - Optional.ofNullable(state.extraLogStream).ifPresent(throwable::printStackTrace); - throwable.printStackTrace(state.stackTraceStream); + throwable.printStackTrace(state.err); } private static String concatMessages(String msg, String msg2) { @@ -1255,16 +1226,6 @@ public final class TKit { return new TextStreamVerifier(what); } - private static PrintStream openLogStream() { - return state().logFile.map(logfile -> { - try { - return Files.newOutputStream(logfile, StandardOpenOption.CREATE, StandardOpenOption.APPEND); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - }).map(PrintStream::new).orElse(null); - } - public record PathSnapshot(List contentHashes) { public PathSnapshot { contentHashes.forEach(Objects::requireNonNull); @@ -1376,25 +1337,23 @@ public final class TKit { public static final class State { private State( - Optional logFile, TestInstance currentTest, - PrintStream mainLogStream, - PrintStream stackTraceStream, - PrintStream extraLogStream, + PrintStream out, + PrintStream err, + Map properties, boolean trace, boolean traceAsserts, boolean verboseJPackage, boolean verboseTestSetup) { - Objects.requireNonNull(logFile); - Objects.requireNonNull(mainLogStream); - Objects.requireNonNull(stackTraceStream); + Objects.requireNonNull(out); + Objects.requireNonNull(err); + Objects.requireNonNull(properties); - this.logFile = logFile; this.currentTest = currentTest; - this.mainLogStream = mainLogStream; - this.stackTraceStream = stackTraceStream; - this.extraLogStream = extraLogStream; + this.out = out; + this.err = err; + this.properties = Collections.synchronizedMap(properties); this.trace = trace; this.traceAsserts = traceAsserts; @@ -1403,11 +1362,30 @@ public final class TKit { this.verboseTestSetup = verboseTestSetup; } - Builder buildCopy() { return build().initFrom(this); } + PrintStream out() { + return out; + } + + PrintStream err() { + return err; + } + + Optional findProperty(Object key) { + return Optional.ofNullable(properties.get(Objects.requireNonNull(key))); + } + + void setProperty(Object key, Object value) { + if (value == null) { + properties.remove(Objects.requireNonNull(key)); + } else { + properties.put(Objects.requireNonNull(key), value); + } + } + static Builder build() { return new Builder(); } @@ -1416,11 +1394,9 @@ public final class TKit { static final class Builder { Builder initDefaults() { - logFile = Optional.ofNullable(getConfigProperty("logfile")).map(Path::of); currentTest = null; - mainLogStream = System.out; - stackTraceStream = System.err; - extraLogStream = null; + out = System.out; + err = System.err; var logOptions = tokenizeConfigProperty("suppress-logging"); if (logOptions == null) { @@ -1444,15 +1420,17 @@ public final class TKit { verboseTestSetup = isNonOf.test(Set.of("init", "i")); } + mutable = true; + return this; } Builder initFrom(State state) { - logFile = state.logFile; currentTest = state.currentTest; - mainLogStream = state.mainLogStream; - stackTraceStream = state.stackTraceStream; - extraLogStream = state.extraLogStream; + out = state.out; + err = state.err; + properties.clear(); + properties.putAll(state.properties); trace = state.trace; traceAsserts = state.traceAsserts; @@ -1463,54 +1441,67 @@ public final class TKit { return this; } - Builder logFile(Optional v) { - logFile = v; - return this; - } - Builder currentTest(TestInstance v) { currentTest = v; return this; } - Builder mainLogStream(PrintStream v) { - mainLogStream = v; + Builder out(PrintStream v) { + out = v; return this; } - Builder stackTraceStream(PrintStream v) { - stackTraceStream = v; + Builder err(PrintStream v) { + err = v; return this; } - Builder extraLogStream(PrintStream v) { - extraLogStream = v; + Builder property(Object key, Object value) { + if (value == null) { + properties.remove(Objects.requireNonNull(key)); + } else { + properties.put(Objects.requireNonNull(key), value); + } + return this; + } + + Builder mutable(boolean v) { + mutable = v; return this; } State create() { - return new State(logFile, currentTest, mainLogStream, stackTraceStream, extraLogStream, trace, traceAsserts, verboseJPackage, verboseTestSetup); + return new State( + currentTest, + out, + err, + mutable ? new HashMap<>(properties) : Map.copyOf(properties), + trace, + traceAsserts, + verboseJPackage, + verboseTestSetup); } - private Optional logFile; private TestInstance currentTest; - private PrintStream mainLogStream; - private PrintStream stackTraceStream; - private PrintStream extraLogStream; + private PrintStream out; + private PrintStream err; + private Map properties = new HashMap<>(); private boolean trace; private boolean traceAsserts; private boolean verboseJPackage; private boolean verboseTestSetup; + + private boolean mutable = true; } - private final Optional logFile; private final TestInstance currentTest; - private final PrintStream mainLogStream; - private final PrintStream stackTraceStream; - private final PrintStream extraLogStream; + private final PrintStream out; + private final PrintStream err; + + private final Map properties; private final boolean trace; private final boolean traceAsserts; @@ -1520,10 +1511,6 @@ public final class TKit { } - private static final InheritableThreadLocal STATE = new InheritableThreadLocal<>() { - @Override - protected State initialValue() { - return State.build().initDefaults().create(); - } - }; + private static final ScopedValue STATE = ScopedValue.newInstance(); + private static final State DEFAULT_STATE = State.build().initDefaults().mutable(false).create(); } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java index e15d5130d43..9826f0e9069 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionsValidationFailTest.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 @@ -200,13 +200,14 @@ public class OptionsValidationFailTest { Stream.of("--jpt-run=ErrorTest") ).flatMap(x -> x).toArray(String[]::new)).map(dynamicTest -> { return DynamicTest.dynamicTest(dynamicTest.getDisplayName(), () -> { - JPackageCommand.withToolProvider(() -> { + TKit.withNewState(() -> { + JPackageCommand.useToolProviderByDefault(jpackageToolProviderMock); try { dynamicTest.getExecutable().execute(); } catch (Throwable t) { throw ExceptionBox.toUnchecked(ExceptionBox.unbox(t)); } - }, jpackageToolProviderMock); + }); }); }); } diff --git a/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.java b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.java index 952b4b520d5..4ab2c89873c 100644 --- a/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.java +++ b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitAdapter.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 @@ -29,11 +29,11 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; +import jdk.jpackage.internal.util.TeeOutputStream; import jdk.jpackage.internal.util.function.ThrowingRunnable; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; @@ -70,12 +70,16 @@ public class JUnitAdapter { static List captureJPackageTestLog(ThrowingRunnable runnable) { final var buf = new ByteArrayOutputStream(); - try (PrintStream ps = new PrintStream(buf, true, StandardCharsets.UTF_8)) { - TKit.withExtraLogStream(runnable, ps); - } + var ps = new PrintStream(buf, false, TKit.state().out().charset()); + + final var out = new PrintStream(new TeeOutputStream(List.of(TKit.state().out(), ps)), true, ps.charset()); + + TKit.withOutput(runnable, out, TKit.state().err()); + + ps.flush(); try (final var in = new ByteArrayInputStream(buf.toByteArray()); - final var reader = new InputStreamReader(in, StandardCharsets.UTF_8); + final var reader = new InputStreamReader(in, ps.charset()); final var bufReader = new BufferedReader(reader)) { return bufReader.lines().map(line -> { // Skip timestamp diff --git a/test/jdk/tools/jpackage/share/AsyncTest.java b/test/jdk/tools/jpackage/share/AsyncTest.java index e3a3f7e3cb8..b7dd13f91fa 100644 --- a/test/jdk/tools/jpackage/share/AsyncTest.java +++ b/test/jdk/tools/jpackage/share/AsyncTest.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,6 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; -import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.Collection; @@ -34,15 +33,12 @@ import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; -import java.util.spi.ToolProvider; import java.util.stream.IntStream; -import jdk.jpackage.internal.util.function.ThrowingRunnable; import jdk.jpackage.internal.util.Slot; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.JavaTool; import jdk.jpackage.test.JavaAppDesc; import jdk.jpackage.test.Main; import jdk.jpackage.test.PackageTest; @@ -66,10 +62,10 @@ public class AsyncTest { // Create test jar only once. // Besides of saving time, this avoids asynchronous invocations of java tool provider that randomly fail. - APP_JAR.set(HelloApp.createBundle(JavaAppDesc.parse("Hello!"), TKit.workDir())); + var appJar = HelloApp.createBundle(JavaAppDesc.parse("Hello!"), TKit.workDir()); // - // Run test cases from InternalAsyncTest class asynchronously. + // Run test cases from AsyncInnerTest class asynchronously. // Spawn a thread for every test case. // Input data for test cases will be cooked asynchronously but in a safe way because every test case has an isolated work directory. // Multiple jpackage tool provider instances will be invoked asynchronously. @@ -79,14 +75,10 @@ public class AsyncTest { var testFuncNames = List.of("testAppImage", "testNativeBundle"); - var runArg = String.format("--jpt-run=%s", AsyncInnerTest.class.getName()); - var futures = executor.invokeAll(IntStream.range(0, JOB_COUNT).mapToObj(Integer::toString).mapMulti((idx, consumer) -> { for (var testFuncName : testFuncNames) { var id = String.format("%s(%s)", testFuncName, idx); - consumer.accept(new Workload(() -> { - Main.main(runArg, String.format("--jpt-include=%s", id)); - }, id)); + consumer.accept(new Workload(id, appJar)); } }).toList()); @@ -99,10 +91,8 @@ public class AsyncTest { for (var future : futures) { var result = future.get(); - TKit.trace(String.format("[%s] STDOUT BEGIN\n%s", result.id(), result.stdoutBuffer())); - TKit.trace(String.format("[%s] STDOUT END", result.id())); - TKit.trace(String.format("[%s] STDERR BEGIN\n%s", result.id(), result.stderrBuffer())); - TKit.trace(String.format("[%s] STDERR END", result.id())); + TKit.trace(String.format("[%s] OUTPUT BEGIN\n%s", result.testCaseId(), result.testOutput())); + TKit.trace(String.format("[%s] OUTPUT END", result.testCaseId())); result.exception().filter(Predicate.not(TKit::isSkippedException)).ifPresent(fatalError::set); } @@ -142,80 +132,56 @@ public class AsyncTest { } - private record Result(String stdoutBuffer, String stderrBuffer, String id, Optional exception) { + private record Result(String testOutput, String testCaseId, Optional exception) { Result { - Objects.requireNonNull(stdoutBuffer); - Objects.requireNonNull(stderrBuffer); - Objects.requireNonNull(id); + Objects.requireNonNull(testOutput); + Objects.requireNonNull(testCaseId); Objects.requireNonNull(exception); } } private record Workload( - ByteArrayOutputStream stdoutBuffer, - ByteArrayOutputStream stderrBuffer, - ThrowingRunnable runnable, - String id) implements Callable { + String testCaseId, + ByteArrayOutputStream outputSink, + Path appJar) implements Callable { Workload { - Objects.requireNonNull(stdoutBuffer); - Objects.requireNonNull(stderrBuffer); - Objects.requireNonNull(runnable); - Objects.requireNonNull(id); + Objects.requireNonNull(testCaseId); + Objects.requireNonNull(outputSink); + Objects.requireNonNull(appJar); } - Workload(ThrowingRunnable runnable, String id) { - this(new ByteArrayOutputStream(), new ByteArrayOutputStream(), runnable, id); + Workload(String testCaseId, Path appJar) { + this(testCaseId, new ByteArrayOutputStream(), appJar); } - private String stdoutBufferAsString() { - return new String(stdoutBuffer.toByteArray(), StandardCharsets.UTF_8); - } - - private String stderrBufferAsString() { - return new String(stderrBuffer.toByteArray(), StandardCharsets.UTF_8); + private String testOutput() { + return new String(outputSink.toByteArray(), StandardCharsets.UTF_8); } @Override public Result call() { - // Reset the current test inherited in the state from the parent thread. - TKit.state(DEFAULT_STATE); - - var defaultToolProvider = JavaTool.JPACKAGE.asToolProvider(); - - JPackageCommand.useToolProviderByDefault(new ToolProvider() { - - @Override - public int run(PrintWriter out, PrintWriter err, String... args) { - try (var bufOut = new PrintWriter(stdoutBuffer, true, StandardCharsets.UTF_8); - var bufErr = new PrintWriter(stderrBuffer, true, StandardCharsets.UTF_8)) { - return defaultToolProvider.run(bufOut, bufErr, args); - } - } - - @Override - public String name() { - return defaultToolProvider.name(); - } - }); + var runArg = String.format("--jpt-run=%s", AsyncInnerTest.class.getName()); Optional err = Optional.empty(); - try (var bufOut = new PrintStream(stdoutBuffer, true, StandardCharsets.UTF_8); - var bufErr = new PrintStream(stderrBuffer, true, StandardCharsets.UTF_8)) { - TKit.withStackTraceStream(() -> { - TKit.withMainLogStream(runnable, bufOut); - }, bufErr); + try { + try (var out = new PrintStream(outputSink, false, System.out.charset())) { + ScopedValue.where(APP_JAR, appJar).run(() -> { + TKit.withOutput(() -> { + JPackageCommand.useToolProviderByDefault(); + Main.main("--jpt-ignore-logfile", runArg, String.format("--jpt-include=%s", testCaseId)); + }, out, out); + }); + } } catch (Exception ex) { err = Optional.of(ex); } - return new Result(stdoutBufferAsString(), stderrBufferAsString(), id, err); + return new Result(testOutput(), testCaseId, err); } } - private static final int JOB_COUNT = 30; - private static final TKit.State DEFAULT_STATE = TKit.state(); - private static final InheritableThreadLocal APP_JAR = new InheritableThreadLocal<>(); + private static final ScopedValue APP_JAR = ScopedValue.newInstance(); } diff --git a/test/jdk/tools/jpackage/windows/Win8301247Test.java b/test/jdk/tools/jpackage/windows/Win8301247Test.java index 3cdd9810d0f..bed795281f4 100644 --- a/test/jdk/tools/jpackage/windows/Win8301247Test.java +++ b/test/jdk/tools/jpackage/windows/Win8301247Test.java @@ -56,11 +56,14 @@ public class Win8301247Test { cmd.addArguments("--java-options", "-Djpackage.test.noexit=true"); cmd.executeAndAssertImageCreated(); + var state = TKit.state(); var f = new CompletableFuture(); // Launch the app in a separate thread new Thread(() -> { - HelloApp.assertMainLauncher(cmd).get().processListener(f::complete).execute(); + TKit.withState(() -> { + HelloApp.assertMainLauncher(cmd).get().processListener(f::complete).execute(); + }, state); }).start(); var mainLauncherProcess = f.get(); diff --git a/test/jdk/tools/jpackage/windows/WinNoRestartTest.java b/test/jdk/tools/jpackage/windows/WinNoRestartTest.java index 984ddfcdf06..7390b2f79a3 100644 --- a/test/jdk/tools/jpackage/windows/WinNoRestartTest.java +++ b/test/jdk/tools/jpackage/windows/WinNoRestartTest.java @@ -95,11 +95,14 @@ public class WinNoRestartTest { // Save updated main launcher .cfg file cfgFile.save(cmd.appLauncherCfgPath(null)); + var state = TKit.state(); var f = new CompletableFuture(); // Launch the app in a separate thread new Thread(() -> { - HelloApp.assertMainLauncher(cmd).get().processListener(f::complete).execute(); + TKit.withState(() -> { + HelloApp.assertMainLauncher(cmd).get().processListener(f::complete).execute(); + }, state); }).start(); var mainLauncherProcess = f.get(); From 0d19d91b44e5232dbd99d34dcdf6500f892e3048 Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Tue, 13 Jan 2026 23:48:14 +0000 Subject: [PATCH 088/204] 8369048: GenShen: Defer ShenFreeSet::available() during rebuild Reviewed-by: wkemper, ysr --- .../share/gc/shenandoah/shenandoahFreeSet.cpp | 11 ++- .../share/gc/shenandoah/shenandoahFreeSet.hpp | 74 +++++++++++++------ .../share/gc/shenandoah/shenandoahFullGC.cpp | 21 +++--- .../gc/shenandoah/shenandoahGeneration.cpp | 3 +- .../share/gc/shenandoah/shenandoahHeap.cpp | 16 ++-- .../share/gc/shenandoah/shenandoahMetrics.cpp | 5 +- .../gc/shenandoah/shenandoahOldGeneration.cpp | 7 +- 7 files changed, 84 insertions(+), 53 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index c03e66e28da..a8c97801824 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -338,7 +338,7 @@ void ShenandoahRegionPartitions::make_all_regions_unavailable() { _empty_region_counts[partition_id] = 0; _used[partition_id] = 0; _humongous_waste[partition_id] = 0; - _available[partition_id] = FreeSetUnderConstruction; + _available[partition_id] = 0; } } @@ -2495,6 +2495,10 @@ void ShenandoahFreeSet::move_regions_from_collector_to_mutator(size_t max_xfer_r void ShenandoahFreeSet::prepare_to_rebuild(size_t &young_trashed_regions, size_t &old_trashed_regions, size_t &first_old_region, size_t &last_old_region, size_t &old_region_count) { shenandoah_assert_heaplocked(); + assert(rebuild_lock() != nullptr, "sanity"); + rebuild_lock()->lock(false); + // This resets all state information, removing all regions from all sets. + clear(); log_debug(gc, free)("Rebuilding FreeSet"); // This places regions that have alloc_capacity into the old_collector set if they identify as is_old() or the @@ -2524,6 +2528,9 @@ void ShenandoahFreeSet::finish_rebuild(size_t young_trashed_regions, size_t old_ _total_young_regions = _heap->num_regions() - old_region_count; _total_global_regions = _heap->num_regions(); establish_old_collector_alloc_bias(); + + // Release the rebuild lock now. What remains in this function is read-only + rebuild_lock()->unlock(); _partitions.assert_bounds(true); log_status(); } @@ -3058,7 +3065,7 @@ void ShenandoahFreeSet::log_status() { size_t max_humongous = max_contig * ShenandoahHeapRegion::region_size_bytes(); // capacity() is capacity of mutator // used() is used of mutator - size_t free = capacity() - used(); + size_t free = capacity_holding_lock() - used_holding_lock(); // Since certain regions that belonged to the Mutator free partition at the time of most recent rebuild may have been // retired, the sum of used and capacities within regions that are still in the Mutator free partition may not match // my internally tracked values of used() and free(). diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp index 95f9fbe6856..364637740f2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp @@ -28,9 +28,13 @@ #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.hpp" +#include "gc/shenandoah/shenandoahLock.hpp" #include "gc/shenandoah/shenandoahSimpleBitMap.hpp" #include "logging/logStream.hpp" +typedef ShenandoahLock ShenandoahRebuildLock; +typedef ShenandoahLocker ShenandoahRebuildLocker; + // Each ShenandoahHeapRegion is associated with a ShenandoahFreeSetPartitionId. enum class ShenandoahFreeSetPartitionId : uint8_t { Mutator, // Region is in the Mutator free set: available memory is available to mutators. @@ -139,8 +143,6 @@ public: ShenandoahRegionPartitions(size_t max_regions, ShenandoahFreeSet* free_set); ~ShenandoahRegionPartitions() {} - static const size_t FreeSetUnderConstruction = SIZE_MAX; - inline idx_t max() const { return _max; } // At initialization, reset OldCollector tallies @@ -352,6 +354,16 @@ public: return _available[int(which_partition)]; } + // Return available_in assuming caller does not hold the heap lock but does hold the rebuild_lock. + // The returned value may be "slightly stale" because we do not assure that every fetch of this value + // sees the most recent update of this value. Requiring the caller to hold the rebuild_lock assures + // that we don't see "bogus" values that are "worse than stale". During rebuild of the freeset, the + // value of _available is not reliable. + inline size_t available_in_locked_for_rebuild(ShenandoahFreeSetPartitionId which_partition) const { + assert (which_partition < NumPartitions, "selected free set must be valid"); + return _available[int(which_partition)]; + } + // Returns bytes of humongous waste inline size_t humongous_waste(ShenandoahFreeSetPartitionId which_partition) const { assert (which_partition < NumPartitions, "selected free set must be valid"); @@ -359,23 +371,6 @@ public: return _humongous_waste[int(which_partition)]; } - // Return available_in assuming caller does not hold the heap lock. In production builds, available is - // returned without acquiring the lock. In debug builds, the global heap lock is acquired in order to - // enforce a consistency assert. - inline size_t available_in_not_locked(ShenandoahFreeSetPartitionId which_partition) const { - assert (which_partition < NumPartitions, "selected free set must be valid"); - shenandoah_assert_not_heaplocked(); -#ifdef ASSERT - ShenandoahHeapLocker locker(ShenandoahHeap::heap()->lock()); - assert((_available[int(which_partition)] == FreeSetUnderConstruction) || - (_available[int(which_partition)] == _capacity[int(which_partition)] - _used[int(which_partition)]), - "Expect available (%zu) equals capacity (%zu) - used (%zu) for partition %s", - _available[int(which_partition)], _capacity[int(which_partition)], _used[int(which_partition)], - partition_membership_name(idx_t(which_partition))); -#endif - return _available[int(which_partition)]; - } - inline void set_capacity_of(ShenandoahFreeSetPartitionId which_partition, size_t value); inline void set_used_by(ShenandoahFreeSetPartitionId which_partition, size_t value) { @@ -440,6 +435,15 @@ private: ShenandoahHeap* const _heap; ShenandoahRegionPartitions _partitions; + // This locks the rebuild process (in combination with the global heap lock). Whenever we rebuild the free set, + // we first acquire the global heap lock and then we acquire this _rebuild_lock in a nested context. Threads that + // need to check available, acquire only the _rebuild_lock to make sure that they are not obtaining the value of + // available for a partially reconstructed free-set. + // + // Note that there is rank ordering of nested locks to prevent deadlock. All threads that need to acquire both + // locks will acquire them in the same order: first the global heap lock and then the rebuild lock. + ShenandoahRebuildLock _rebuild_lock; + size_t _total_humongous_waste; HeapWord* allocate_aligned_plab(size_t size, ShenandoahAllocRequest& req, ShenandoahHeapRegion* r); @@ -635,10 +639,12 @@ private: void log_status(); public: - static const size_t FreeSetUnderConstruction = ShenandoahRegionPartitions::FreeSetUnderConstruction; - ShenandoahFreeSet(ShenandoahHeap* heap, size_t max_regions); + ShenandoahRebuildLock* rebuild_lock() { + return &_rebuild_lock; + } + inline size_t max_regions() const { return _partitions.max(); } ShenandoahFreeSetPartitionId membership(size_t index) const { return _partitions.membership(index); } inline void shrink_interval_if_range_modifies_either_boundary(ShenandoahFreeSetPartitionId partition, @@ -776,9 +782,29 @@ public: // adjusts available with respect to lock holders. However, sequential calls to these three functions may produce // inconsistent data: available may not equal capacity - used because the intermediate states of any "atomic" // locked action can be seen by these unlocked functions. - inline size_t capacity() const { return _partitions.capacity_of(ShenandoahFreeSetPartitionId::Mutator); } - inline size_t used() const { return _partitions.used_by(ShenandoahFreeSetPartitionId::Mutator); } - inline size_t available() const { return _partitions.available_in_not_locked(ShenandoahFreeSetPartitionId::Mutator); } + inline size_t capacity_holding_lock() const { + shenandoah_assert_heaplocked(); + return _partitions.capacity_of(ShenandoahFreeSetPartitionId::Mutator); + } + inline size_t capacity_not_holding_lock() { + shenandoah_assert_not_heaplocked(); + ShenandoahRebuildLocker locker(rebuild_lock()); + return _partitions.capacity_of(ShenandoahFreeSetPartitionId::Mutator); + } + inline size_t used_holding_lock() const { + shenandoah_assert_heaplocked(); + return _partitions.used_by(ShenandoahFreeSetPartitionId::Mutator); + } + inline size_t used_not_holding_lock() { + shenandoah_assert_not_heaplocked(); + ShenandoahRebuildLocker locker(rebuild_lock()); + return _partitions.used_by(ShenandoahFreeSetPartitionId::Mutator); + } + inline size_t available() { + shenandoah_assert_not_heaplocked(); + ShenandoahRebuildLocker locker(rebuild_lock()); + return _partitions.available_in_locked_for_rebuild(ShenandoahFreeSetPartitionId::Mutator); + } 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/shenandoahFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp index 027d7e02268..fa3a7a42209 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFullGC.cpp @@ -1113,18 +1113,17 @@ void ShenandoahFullGC::phase5_epilog() { ShenandoahPostCompactClosure post_compact; heap->heap_region_iterate(&post_compact); heap->collection_set()->clear(); - size_t young_cset_regions, old_cset_regions; - size_t first_old, last_old, num_old; - heap->free_set()->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old, last_old, num_old); - - // We also do not expand old generation size following Full GC because we have scrambled age populations and - // no longer have objects separated by age into distinct regions. - if (heap->mode()->is_generational()) { - ShenandoahGenerationalFullGC::compute_balances(); + size_t young_cset_regions, old_cset_regions, first_old, last_old, num_old; + ShenandoahFreeSet* free_set = heap->free_set(); + { + free_set->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old, last_old, num_old); + // We also do not expand old generation size following Full GC because we have scrambled age populations and + // no longer have objects separated by age into distinct regions. + if (heap->mode()->is_generational()) { + ShenandoahGenerationalFullGC::compute_balances(); + } + free_set->finish_rebuild(young_cset_regions, old_cset_regions, num_old); } - - heap->free_set()->finish_rebuild(young_cset_regions, old_cset_regions, num_old); - // Set mark incomplete because the marking bitmaps have been reset except pinned regions. _generation->set_mark_incomplete(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index d74ee872cd1..a5d8cca458d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -815,10 +815,9 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { ShenandoahGCPhase phase(concurrent ? ShenandoahPhaseTimings::final_rebuild_freeset : ShenandoahPhaseTimings::degen_gc_final_rebuild_freeset); ShenandoahHeapLocker locker(heap->lock()); - size_t young_cset_regions, old_cset_regions; // We are preparing for evacuation. At this time, we ignore cset region tallies. - size_t first_old, last_old, num_old; + size_t young_cset_regions, old_cset_regions, first_old, last_old, num_old; _free_set->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old, last_old, num_old); if (heap->mode()->is_generational()) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 3bf53f800a2..683e2959a92 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -426,8 +426,6 @@ jint ShenandoahHeap::initialize() { _affiliations[i] = ShenandoahAffiliation::FREE; } _free_set = new ShenandoahFreeSet(this, _num_regions); - - post_initialize_heuristics(); // We are initializing free set. We ignore cset region tallies. size_t young_cset_regions, old_cset_regions, first_old, last_old, num_old; @@ -1658,7 +1656,7 @@ void ShenandoahHeap::verify(VerifyOption vo) { } } size_t ShenandoahHeap::tlab_capacity() const { - return _free_set->capacity(); + return _free_set->capacity_not_holding_lock(); } class ObjectIterateScanRootClosure : public BasicOopIterateClosure { @@ -2138,7 +2136,7 @@ GCTracer* ShenandoahHeap::tracer() { } size_t ShenandoahHeap::tlab_used() const { - return _free_set->used(); + return _free_set->used_not_holding_lock(); } bool ShenandoahHeap::try_cancel_gc(GCCause::Cause cause) { @@ -2528,8 +2526,7 @@ void ShenandoahHeap::rebuild_free_set(bool concurrent) { ShenandoahPhaseTimings::final_update_refs_rebuild_freeset : ShenandoahPhaseTimings::degen_gc_final_update_refs_rebuild_freeset); ShenandoahHeapLocker locker(lock()); - size_t young_cset_regions, old_cset_regions; - size_t first_old_region, last_old_region, old_region_count; + size_t young_cset_regions, old_cset_regions, first_old_region, last_old_region, old_region_count; _free_set->prepare_to_rebuild(young_cset_regions, old_cset_regions, first_old_region, last_old_region, old_region_count); // If there are no old regions, first_old_region will be greater than last_old_region assert((first_old_region > last_old_region) || @@ -2548,13 +2545,14 @@ void ShenandoahHeap::rebuild_free_set(bool concurrent) { // The computation of bytes_of_allocation_runway_before_gc_trigger is quite conservative so consider all of this // available for transfer to old. Note that transfer of humongous regions does not impact available. ShenandoahGenerationalHeap* gen_heap = ShenandoahGenerationalHeap::heap(); - size_t allocation_runway = gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_cset_regions); + size_t allocation_runway = + gen_heap->young_generation()->heuristics()->bytes_of_allocation_runway_before_gc_trigger(young_cset_regions); gen_heap->compute_old_generation_balance(allocation_runway, old_cset_regions); // Total old_available may have been expanded to hold anticipated promotions. We trigger if the fragmented available // memory represents more than 16 regions worth of data. Note that fragmentation may increase when we promote regular - // regions in place when many of these regular regions have an abundant amount of available memory within them. Fragmentation - // will decrease as promote-by-copy consumes the available memory within these partially consumed regions. + // regions in place when many of these regular regions have an abundant amount of available memory within them. + // Fragmentation will decrease as promote-by-copy consumes the available memory within these partially consumed regions. // // We consider old-gen to have excessive fragmentation if more than 12.5% of old-gen is free memory that resides // within partially consumed regions of memory. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp index d774a8dba42..81c62ebbda9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMetrics.cpp @@ -30,7 +30,7 @@ ShenandoahMetricsSnapshot::ShenandoahMetricsSnapshot(ShenandoahFreeSet* free_set) : _free_set(free_set) - , _used_before(free_set->used()) + , _used_before(free_set->used_not_holding_lock()) , _if_before(free_set->internal_fragmentation()) , _ef_before(free_set->external_fragmentation()) { } @@ -38,7 +38,6 @@ ShenandoahMetricsSnapshot::ShenandoahMetricsSnapshot(ShenandoahFreeSet* free_set bool ShenandoahMetricsSnapshot::is_good_progress() const { // Under the critical threshold? const size_t free_actual = _free_set->available(); - assert(free_actual != ShenandoahFreeSet::FreeSetUnderConstruction, "Avoid this race"); // ShenandoahCriticalFreeThreshold is expressed as a percentage. We multiply this percentage by 1/100th // of the soft max capacity to determine whether the available memory within the mutator partition of the @@ -52,7 +51,7 @@ bool ShenandoahMetricsSnapshot::is_good_progress() const { } // Freed up enough? - const size_t used_after = _free_set->used(); + const size_t used_after = _free_set->used_not_holding_lock(); const size_t progress_actual = (_used_before > used_after) ? _used_before - used_after : 0; const size_t progress_expected = ShenandoahHeapRegion::region_size_bytes(); const bool prog_used = progress_actual >= progress_expected; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index c7cf013d034..c795eda3d96 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -412,9 +412,12 @@ void ShenandoahOldGeneration::prepare_regions_and_collection_set(bool concurrent ShenandoahGCPhase phase(concurrent ? ShenandoahPhaseTimings::final_rebuild_freeset : ShenandoahPhaseTimings::degen_gc_final_rebuild_freeset); + ShenandoahFreeSet* free_set = heap->free_set(); ShenandoahHeapLocker locker(heap->lock()); - size_t young_trash_regions, old_trash_regions; - size_t first_old, last_old, num_old; + + // This is completion of old-gen marking. We rebuild in order to reclaim immediate garbage and to + // prepare for subsequent mixed evacuations. + size_t young_trash_regions, old_trash_regions, first_old, last_old, num_old; heap->free_set()->prepare_to_rebuild(young_trash_regions, old_trash_regions, first_old, last_old, num_old); // At the end of old-gen, we may find that we have reclaimed immediate garbage, allowing a longer allocation runway. // We may also find that we have accumulated canddiate regions for mixed evacuation. If so, we will want to expand From de6f35eff988e737496d5e99e991868e97d72db4 Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Wed, 14 Jan 2026 01:01:52 +0000 Subject: [PATCH 089/204] 8375094: RISC-V: Fix client builds after JDK-8368732 Reviewed-by: fyang, wenanjian, fjiang --- src/hotspot/cpu/riscv/vm_version_riscv.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index 22f19c4f5ea..36f0864da0b 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -167,11 +167,6 @@ void VM_Version::common_initialize() { (unaligned_scalar.value() == MISALIGNED_SCALAR_FAST)); } - if (FLAG_IS_DEFAULT(AlignVector)) { - FLAG_SET_DEFAULT(AlignVector, - unaligned_vector.value() != MISALIGNED_VECTOR_FAST); - } - #ifdef __riscv_ztso // Hotspot is compiled with TSO support, it will only run on hardware which // supports Ztso @@ -242,6 +237,11 @@ void VM_Version::c2_initialize() { } } + if (FLAG_IS_DEFAULT(AlignVector)) { + FLAG_SET_DEFAULT(AlignVector, + unaligned_vector.value() != MISALIGNED_VECTOR_FAST); + } + // NOTE: Make sure codes dependent on UseRVV are put after MaxVectorSize initialize, // as there are extra checks inside it which could disable UseRVV // in some situations. From 5da70b180461d46b1aa44f24ba3c05efdeb03f49 Mon Sep 17 00:00:00 2001 From: Jonas Norlinder Date: Wed, 14 Jan 2026 02:13:13 +0000 Subject: [PATCH 090/204] 8375006: [Linux] Remove obsolete O_CLOEXEC check in os::open Reviewed-by: dholmes, jsjolen --- src/hotspot/os/linux/os_linux.cpp | 40 +------------------------------ 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 88e5e9b582a..6a2a3974a16 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -4878,31 +4878,8 @@ int os::open(const char *path, int oflag, int mode) { // All file descriptors that are opened in the Java process and not // specifically destined for a subprocess should have the close-on-exec // flag set. If we don't set it, then careless 3rd party native code - // might fork and exec without closing all appropriate file descriptors, - // and this in turn might: - // - // - cause end-of-file to fail to be detected on some file - // descriptors, resulting in mysterious hangs, or - // - // - might cause an fopen in the subprocess to fail on a system - // suffering from bug 1085341. - // - // (Yes, the default setting of the close-on-exec flag is a Unix - // design flaw) - // - // See: - // 1085341: 32-bit stdio routines should support file descriptors >255 - // 4843136: (process) pipe file descriptor from Runtime.exec not being closed - // 6339493: (process) Runtime.exec does not close all file descriptors on Solaris 9 - // - // Modern Linux kernels (after 2.6.23 2007) support O_CLOEXEC with open(). - // O_CLOEXEC is preferable to using FD_CLOEXEC on an open file descriptor - // because it saves a system call and removes a small window where the flag - // is unset. On ancient Linux kernels the O_CLOEXEC flag will be ignored - // and we fall back to using FD_CLOEXEC (see below). -#ifdef O_CLOEXEC + // might fork and exec without closing all appropriate file descriptors. oflag |= O_CLOEXEC; -#endif int fd = ::open(path, oflag, mode); if (fd == -1) return -1; @@ -4925,21 +4902,6 @@ int os::open(const char *path, int oflag, int mode) { } } -#ifdef FD_CLOEXEC - // Validate that the use of the O_CLOEXEC flag on open above worked. - // With recent kernels, we will perform this check exactly once. - static sig_atomic_t O_CLOEXEC_is_known_to_work = 0; - if (!O_CLOEXEC_is_known_to_work) { - int flags = ::fcntl(fd, F_GETFD); - if (flags != -1) { - if ((flags & FD_CLOEXEC) != 0) - O_CLOEXEC_is_known_to_work = 1; - else - ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC); - } - } -#endif - return fd; } From b082a390b77fca7134000bfe631f73bfd082bfa1 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Wed, 14 Jan 2026 04:04:08 +0000 Subject: [PATCH 091/204] 8375240: Make bundling progress messages issued by jpackage consistent across platforms Reviewed-by: almatvee --- .../jpackage/internal/LinuxDebPackager.java | 4 - .../jpackage/internal/LinuxRpmPackager.java | 6 +- .../resources/LinuxResources.properties | 6 +- .../internal/MacBundlingEnvironment.java | 6 +- .../jdk/jpackage/internal/MacDmgPackager.java | 16 -- .../internal/MacPackagingPipeline.java | 5 + .../resources/MacResources.properties | 5 - .../internal/DefaultBundlingEnvironment.java | 86 ++------ .../jpackage/internal/PackagingPipeline.java | 200 +++++++++++++++--- .../internal/cli/OptionsAnalyzer.java | 33 +-- .../cli/StandardBundlingOperation.java | 42 ++-- .../jpackage/internal/cli/StandardOption.java | 30 ++- .../internal/model/AppImageBundleType.java | 51 +++++ ...pImagePackageType.java => BundleType.java} | 20 +- .../jpackage/internal/model/PackageType.java | 6 +- .../internal/model/StandardPackageType.java | 27 ++- .../resources/MainResources.properties | 19 +- .../jdk/jpackage/internal/WinExePackager.java | 6 +- .../jdk/jpackage/internal/WinMsiPackager.java | 3 +- .../resources/WinResources.properties | 6 +- .../jdk/jpackage/test/JPackageCommand.java | 54 +++-- .../jdk/jpackage/test/PackageTest.java | 26 ++- .../DefaultBundlingEnvironmentTest.java | 8 +- .../internal/PackagingPipelineTest.java | 9 +- .../internal/cli/StandardOptionTest.java | 29 ++- test/jdk/tools/jpackage/share/BasicTest.java | 106 ++++++---- .../tools/jpackage/share/OutputErrorTest.java | 119 +++++++++++ 27 files changed, 639 insertions(+), 289 deletions(-) create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageBundleType.java rename src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/{AppImagePackageType.java => BundleType.java} (77%) create mode 100644 test/jdk/tools/jpackage/share/OutputErrorTest.java diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackager.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackager.java index 0ec6a77e683..7c1f06f54a3 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackager.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebPackager.java @@ -147,8 +147,6 @@ final class LinuxDebPackager extends LinuxPackager { Path debFile = outputPackageFile(); - Log.verbose(I18N.format("message.outputting-to-location", debFile.toAbsolutePath())); - List cmdline = new ArrayList<>(); Stream.of(sysEnv.fakeroot(), sysEnv.dpkgdeb()).map(Path::toString).forEach(cmdline::add); if (Log.isVerbose()) { @@ -159,8 +157,6 @@ final class LinuxDebPackager extends LinuxPackager { // run dpkg Executor.of(cmdline).retryOnKnownErrorMessage( "semop(1): encountered an error: Invalid argument").execute(); - - Log.verbose(I18N.format("message.output-to-location", debFile.toAbsolutePath())); } @Override diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmPackager.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmPackager.java index 60355d0d1a2..e22b9c24fdd 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmPackager.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmPackager.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 @@ -133,8 +133,6 @@ final class LinuxRpmPackager extends LinuxPackager { Path rpmFile = outputPackageFile(); - Log.verbose(I18N.format("message.outputting-bundle-location", rpmFile.getParent())); - //run rpmbuild Executor.of(sysEnv.rpmbuild().toString(), "-bb", specFile().toAbsolutePath().toString(), @@ -147,8 +145,6 @@ final class LinuxRpmPackager extends LinuxPackager { env.buildRoot().toAbsolutePath()), "--define", String.format("%%_rpmfilename %s", rpmFile.getFileName()) ).executeExpectSuccess(); - - Log.verbose(I18N.format("message.output-bundle-location", rpmFile.getParent())); } private Path installPrefix() { diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties index 3aabe1f4ba5..3aa0e0e92a0 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.properties +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/resources/LinuxResources.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 @@ -49,11 +49,7 @@ error.rpm-arch-not-detected="Failed to detect RPM arch" message.icon-not-png=The specified icon "{0}" is not a PNG file and will not be used. The default icon will be used in it's place. message.test-for-tool=Test for [{0}]. Result: {1} -message.outputting-to-location=Generating DEB for installer to: {0}. -message.output-to-location=Package (.deb) saved to: {0}. message.debs-like-licenses=Debian packages should specify a license. The absence of a license will cause some linux distributions to complain about the quality of the application. -message.outputting-bundle-location=Generating RPM for installer to: {0}. -message.output-bundle-location=Package (.rpm) saved to: {0}. message.ldd-not-available=ldd command not found. Package dependencies will not be generated. message.deb-ldd-not-available.advice=Install "libc-bin" DEB package to get ldd. diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java index 0531559e052..224ea20f249 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacBundlingEnvironment.java @@ -54,7 +54,6 @@ public class MacBundlingEnvironment extends DefaultBundlingEnvironment { buildEnv()::create, MacBundlingEnvironment::buildPipeline, (env, pkg, outputDir) -> { - Log.verbose(I18N.format("message.building-dmg", pkg.app().name())); return new MacDmgPackager(env, pkg, outputDir, sysEnv); }); } @@ -64,10 +63,7 @@ public class MacBundlingEnvironment extends DefaultBundlingEnvironment { MacFromOptions.createMacPkgPackage(options), buildEnv()::create, MacBundlingEnvironment::buildPipeline, - (env, pkg, outputDir) -> { - Log.verbose(I18N.format("message.building-pkg", pkg.app().name())); - return new MacPkgPackager(env, pkg, outputDir); - }); + MacPkgPackager::new); } private static void signAppImage(Options options) { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java index 20a687487ef..82bb9fc4dad 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java @@ -235,17 +235,6 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, final Path srcFolder = env.appImageDir(); - Log.verbose(MessageFormat.format(I18N.getString( - "message.creating-dmg-file"), finalDMG.toAbsolutePath())); - - try { - Files.deleteIfExists(finalDMG); - } catch (IOException ex) { - throw new IOException(MessageFormat.format(I18N.getString( - "message.dmg-cannot-be-overwritten"), - finalDMG.toAbsolutePath())); - } - Files.createDirectories(protoDMG.getParent()); Files.createDirectories(finalDMG.getParent()); @@ -383,11 +372,6 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, } catch (IOException ex) { // Don't care if fails } - - Log.verbose(MessageFormat.format(I18N.getString( - "message.output-to-location"), - pkg.app().name(), normalizedAbsolutePathString(finalDMG))); - } private void detachVolume() throws IOException { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java index a53df7f83c2..4e63f6db178 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPackagingPipeline.java @@ -217,6 +217,11 @@ final class MacPackagingPipeline { enum SignAppImagePackageType implements PackageType { VALUE; + + @Override + public String label() { + throw new UnsupportedOperationException(); + } } static Package createSignAppImagePackage(MacApplication app, BuildEnv env) { 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 240e82dcc9b..ceeab587f66 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 @@ -57,12 +57,7 @@ message.version-string-first-number-not-zero=The first number in an app-version 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.building-dmg=Building DMG package for {0}. message.preparing-dmg-setup=Preparing dmg setup: {0}. -message.creating-dmg-file=Creating DMG file: {0}. -message.dmg-cannot-be-overwritten=Dmg file exists [{0}] and can not be removed. -message.output-to-location=Result DMG installer for {0}: {1}. -message.building-pkg=Building PKG package for {0}. message.preparing-scripts=Preparing package scripts. message.preparing-distribution-dist=Preparing distribution.dist: {0}. message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trust" for your certificate using "Keychain Access" tool. diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java index e4473b1e5ce..3a99dfb04da 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java @@ -42,17 +42,15 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.PackagingPipeline.PackageTaskID; import jdk.jpackage.internal.cli.CliBundlingEnvironment; import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.cli.StandardBundlingOperation; -import jdk.jpackage.internal.model.AppImagePackageType; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.BundlingOperationDescriptor; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.Package; -import jdk.jpackage.internal.model.PackageType; -import jdk.jpackage.internal.model.StandardPackageType; +import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.Result; class DefaultBundlingEnvironment implements CliBundlingEnvironment { @@ -134,7 +132,9 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { Objects.requireNonNull(app); Objects.requireNonNull(pipelineBuilder); - final var outputDir = OptionUtils.outputDir(options).resolve(app.appImageDirName()); + final var outputDir = PathUtils.normalizedAbsolutePath(OptionUtils.outputDir(options).resolve(app.appImageDirName())); + + Log.verbose(I18N.getString("message.create-app-image")); IOUtils.writableOutputDir(outputDir.getParent()); @@ -142,14 +142,14 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { .predefinedAppImageLayout(app.asApplicationLayout().orElseThrow()) .create(options, app); - Log.verbose(I18N.format("message.creating-app-bundle", outputDir.getFileName(), outputDir.toAbsolutePath().getParent())); - if (Files.exists(outputDir)) { - throw new JPackageException(I18N.format("error.root-exists", outputDir.toAbsolutePath())); + throw new JPackageException(I18N.format("error.root-exists", outputDir)); } pipelineBuilder.excludeDirFromCopying(outputDir.getParent()) .create().execute(BuildEnv.withAppImageDir(env, outputDir), app); + + Log.verbose(I18N.getString("message.app-image-created")); } static void createNativePackage(Options options, @@ -174,11 +174,20 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { Objects.requireNonNull(createPipelineBuilder); Objects.requireNonNull(pipelineBuilderMutatorFactory); + var pipelineBuilder = Objects.requireNonNull(createPipelineBuilder.apply(pkg)); + + // Delete an old output package file (if any) before creating a new one. + pipelineBuilder.task(PackageTaskID.DELETE_OLD_PACKAGE_FILE) + .addDependencies(pipelineBuilder.taskGraphSnapshot().getTailsOf(PackageTaskID.CREATE_PACKAGE_FILE)) + .addDependent(PackageTaskID.CREATE_PACKAGE_FILE) + .packageAction(PackagingPipeline::deleteOutputBundle) + .add(); + Packager.build().pkg(pkg) - .outputDir(OptionUtils.outputDir(options)) - .env(Objects.requireNonNull(createBuildEnv.apply(options, pkg))) - .pipelineBuilderMutatorFactory(pipelineBuilderMutatorFactory) - .execute(Objects.requireNonNull(createPipelineBuilder.apply(pkg))); + .outputDir(OptionUtils.outputDir(options)) + .env(Objects.requireNonNull(createBuildEnv.apply(options, pkg))) + .pipelineBuilderMutatorFactory(pipelineBuilderMutatorFactory) + .execute(pipelineBuilder); } @Override @@ -195,10 +204,6 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { permanentWorkDirectory = Optional.of(tempDir.path()); } bundler.accept(tempDir.options()); - - var packageType = OptionUtils.bundlingOperation(cmdline).packageType(); - - Log.verbose(I18N.format("message.bundle-created", I18N.getString(bundleTypeDescription(packageType, op.os())))); } catch (IOException ex) { throw new UncheckedIOException(ex); } finally { @@ -219,55 +224,6 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { }); } - private String bundleTypeDescription(PackageType type, OperatingSystem os) { - switch (type) { - case StandardPackageType stdType -> { - switch (stdType) { - case WIN_MSI -> { - return "bundle-type.win-msi"; - } - case WIN_EXE -> { - return "bundle-type.win-exe"; - } - case LINUX_DEB -> { - return "bundle-type.linux-deb"; - } - case LINUX_RPM -> { - return "bundle-type.linux-rpm"; - } - case MAC_DMG -> { - return "bundle-type.mac-dmg"; - } - case MAC_PKG -> { - return "bundle-type.mac-pkg"; - } - default -> { - throw new AssertionError(); - } - } - } - case AppImagePackageType appImageType -> { - switch (os) { - case WINDOWS -> { - return "bundle-type.win-app"; - } - case LINUX -> { - return "bundle-type.linux-app"; - } - case MACOS -> { - return "bundle-type.mac-app"; - } - default -> { - throw new AssertionError(); - } - } - } - default -> { - throw new AssertionError(); - } - } - } - private static final class CachingSupplier implements Supplier { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java index c94dada9262..f15768b2cbf 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.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 @@ -29,6 +29,7 @@ import static java.util.stream.Collectors.toMap; import static jdk.jpackage.internal.model.AppImageLayout.toPathGroup; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.util.ArrayList; @@ -39,12 +40,16 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.Callable; +import java.util.function.BiConsumer; +import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.stream.Stream; import jdk.jpackage.internal.model.AppImageLayout; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; +import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.Package; import jdk.jpackage.internal.pipeline.DirectedEdge; import jdk.jpackage.internal.pipeline.FixedDAG; @@ -97,7 +102,7 @@ final class PackagingPipeline { /** * The way to access packaging build environment before building a package in a pipeline. */ - interface StartupParameters { + sealed interface StartupParameters { BuildEnv packagingEnv(); } @@ -127,6 +132,7 @@ final class PackagingPipeline { enum PackageTaskID implements TaskID { RUN_POST_IMAGE_USER_SCRIPT, CREATE_CONFIG_FILES, + DELETE_OLD_PACKAGE_FILE, CREATE_PACKAGE_FILE } @@ -183,9 +189,11 @@ final class PackagingPipeline { void execute() throws IOException; } - record TaskConfig(Optional action) { + record TaskConfig(Optional action, Optional beforeAction, Optional afterAction) { TaskConfig { Objects.requireNonNull(action); + Objects.requireNonNull(beforeAction); + Objects.requireNonNull(afterAction); } } @@ -196,47 +204,64 @@ final class PackagingPipeline { final class TaskBuilder extends TaskSpecBuilder { - private TaskBuilder(TaskID id) { - super(id); - } - - private TaskBuilder(TaskID id, TaskConfig config) { - this(id); - config.action().ifPresent(this::setAction); - } - - private TaskBuilder setAction(TaskAction v) { - action = v; - return this; - } - TaskBuilder noaction() { - action = null; - return this; + return setAction(ActionRole.WORKLOAD, null); } TaskBuilder applicationAction(ApplicationImageTaskAction action) { - return setAction(action); + return applicationAction(ActionRole.WORKLOAD, action); } TaskBuilder appImageAction(AppImageTaskAction action) { - return setAction(action); + return appImageAction(ActionRole.WORKLOAD, action); } TaskBuilder copyAction(CopyAppImageTaskAction action) { - return setAction(action); + return copyAction(ActionRole.WORKLOAD, action); } TaskBuilder packageAction(PackageTaskAction action) { - return setAction(action); + return packageAction(ActionRole.WORKLOAD, action); } TaskBuilder action(NoArgTaskAction action) { - return setAction(action); + return action(ActionRole.WORKLOAD, action); + } + + TaskBuilder logAppImageActionBegin(String keyId, Function, Object[]> formatArgsSupplier) { + return logAppImageAction(ActionRole.BEFORE, keyId, formatArgsSupplier); + } + + TaskBuilder logAppImageActionEnd(String keyId, Function, Object[]> formatArgsSupplier) { + return logAppImageAction(ActionRole.AFTER, keyId, formatArgsSupplier); + } + + TaskBuilder logPackageActionBegin(String keyId, Function, Object[]> argsSupplier) { + return logPackageAction(ActionRole.BEFORE, keyId, argsSupplier); + } + + TaskBuilder logPackageActionEnd(String keyId, Function, Object[]> argsSupplier) { + return logPackageAction(ActionRole.AFTER, keyId, argsSupplier); + } + + TaskBuilder logActionBegin(String keyId, Supplier formatArgsSupplier) { + return logAction(ActionRole.BEFORE, keyId, formatArgsSupplier); + } + + TaskBuilder logActionBegin(String keyId, Object... formatArgsSupplier) { + return logAction(ActionRole.BEFORE, keyId, () -> formatArgsSupplier); + } + + TaskBuilder logActionEnd(String keyId, Supplier formatArgsSupplier) { + return logAction(ActionRole.AFTER, keyId, formatArgsSupplier); + } + + TaskBuilder logActionEnd(String keyId, Object... formatArgsSupplier) { + return logAction(ActionRole.AFTER, keyId, () -> formatArgsSupplier); } boolean hasAction() { - return action != null; + return workloadAction != null; } @Override @@ -272,13 +297,109 @@ final class PackagingPipeline { } Builder add() { - final var config = new TaskConfig(Optional.ofNullable(action)); + final var config = new TaskConfig( + Optional.ofNullable(workloadAction), + Optional.ofNullable(beforeAction), + Optional.ofNullable(afterAction)); taskConfig.put(task(), config); createLinks().forEach(Builder.this::linkTasks); return Builder.this; } - private TaskAction action; + + private enum ActionRole { + WORKLOAD(TaskBuilder::setWorkloadAction), + BEFORE(TaskBuilder::setBeforeAction), + AFTER(TaskBuilder::setAfterAction), + ; + + ActionRole(BiConsumer actionSetter) { + this.actionSetter = Objects.requireNonNull(actionSetter); + } + + TaskBuilder setAction(TaskBuilder taskBuilder, TaskAction action) { + actionSetter.accept(taskBuilder, action); + return taskBuilder; + } + + private final BiConsumer actionSetter; + } + + + private TaskBuilder(TaskID id) { + super(id); + } + + private TaskBuilder(TaskID id, TaskConfig config) { + this(id); + config.action().ifPresent(this::setWorkloadAction); + config.beforeAction().ifPresent(this::setBeforeAction); + config.afterAction().ifPresent(this::setAfterAction); + } + + private TaskBuilder setAction(ActionRole role, TaskAction v) { + return role.setAction(this, v); + } + + private TaskBuilder setWorkloadAction(TaskAction v) { + workloadAction = v; + return this; + } + + private TaskBuilder setBeforeAction(TaskAction v) { + beforeAction = v; + return this; + } + + private TaskBuilder setAfterAction(TaskAction v) { + afterAction = v; + return this; + } + + private TaskBuilder applicationAction(ActionRole role, ApplicationImageTaskAction action) { + return setAction(role, action); + } + + private TaskBuilder appImageAction(ActionRole role, AppImageTaskAction action) { + return setAction(role, action); + } + + private TaskBuilder copyAction(ActionRole role, CopyAppImageTaskAction action) { + return setAction(role, action); + } + + private TaskBuilder packageAction(ActionRole role, PackageTaskAction action) { + return setAction(role, action); + } + + private TaskBuilder action(ActionRole role, NoArgTaskAction action) { + return setAction(role, action); + } + + private TaskBuilder logAppImageAction(ActionRole role, String keyId, Function, Object[]> formatArgsSupplier) { + Objects.requireNonNull(keyId); + return appImageAction(role, (AppImageBuildEnv env) -> { + Log.verbose(I18N.format(keyId, formatArgsSupplier.apply(env))); + }); + } + + private TaskBuilder logPackageAction(ActionRole role, String keyId, Function, Object[]> formatArgsSupplier) { + Objects.requireNonNull(keyId); + return packageAction(role, (PackageBuildEnv env) -> { + Log.verbose(I18N.format(keyId, formatArgsSupplier.apply(env))); + }); + } + + private TaskBuilder logAction(ActionRole role, String keyId, Supplier formatArgsSupplier) { + Objects.requireNonNull(keyId); + return action(role, () -> { + Log.verbose(I18N.format(keyId, formatArgsSupplier.get())); + }); + } + + private TaskAction workloadAction; + private TaskAction beforeAction; + private TaskAction afterAction; } Builder linkTasks(DirectedEdge edge) { @@ -294,7 +415,11 @@ final class PackagingPipeline { } TaskBuilder task(TaskID id) { - return new TaskBuilder(id); + return Optional.ofNullable(taskConfig.get(id)).map(taskConfig -> { + return new TaskBuilder(id, taskConfig); + }).orElseGet(() -> { + return new TaskBuilder(id); + }); } Stream configuredTasks() { @@ -392,6 +517,8 @@ final class PackagingPipeline { builder.task(PackageTaskID.CREATE_PACKAGE_FILE) .addDependent(PrimaryTaskID.PACKAGE) + .logActionBegin("message.create-package") + .logActionEnd("message.package-created") .add(); builder.task(PrimaryTaskID.PACKAGE).add(); @@ -425,6 +552,17 @@ final class PackagingPipeline { .run(env.env(), env.pkg().app().name()); } + static void deleteOutputBundle(PackageBuildEnv env) throws IOException { + + var outputBundle = env.outputDir().resolve(env.pkg().packageFileNameWithSuffix()); + + try { + Files.deleteIfExists(outputBundle); + } catch (IOException ex) { + throw new JPackageException(I18N.format("error.output-bundle-cannot-be-overwritten", outputBundle.toAbsolutePath()), ex); + } + } + private PackagingPipeline(FixedDAG taskGraph, Map taskConfig, UnaryOperator contextMapper) { this.taskGraph = Objects.requireNonNull(taskGraph); @@ -645,7 +783,13 @@ final class PackagingPipeline { } if (accepted) { + if (config.beforeAction.isPresent()) { + context.execute(config.beforeAction.orElseThrow()); + } context.execute(config.action.orElseThrow()); + if (config.afterAction.isPresent()) { + context.execute(config.afterAction.orElseThrow()); + } } return null; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java index 67e87d55d8b..363aa1b863e 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.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 @@ -56,7 +56,7 @@ import jdk.jpackage.internal.model.BundlingEnvironment; import jdk.jpackage.internal.model.BundlingOperationDescriptor; import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.JPackageException; -import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.model.BundleType; /** * Analyzes jpackage command line structure. @@ -250,7 +250,7 @@ final class OptionsAnalyzer { return error("ERR_NoInstallerEntryPoint", mapFormatArguments(optionSpec)); } else { return error("ERR_InvalidTypeOption", mapFormatArguments( - optionSpec, bundlingOperation.packageTypeValue())); + optionSpec, bundlingOperation.bundleTypeValue())); } } @@ -267,30 +267,31 @@ final class OptionsAnalyzer { final var typeOption = TYPE.getOption(); return cmdline.find(typeOption).map(obj -> { - if (obj instanceof PackageType packageType) { - return packageType; + if (obj instanceof BundleType bundleType) { + return bundleType; } else { - return typeOption.spec() + var spec = new StandardOptionContext(os).mapOptionSpec(typeOption.spec()); + return spec .converter().orElseThrow() - .convert(typeOption.spec().name(), StringToken.of(((String[])obj)[0])) + .convert(spec.name(), StringToken.of(((String[])obj)[0])) .orElseThrow(); } - }).map(packageType -> { - // Find standard bundling operations producing the given package type. + }).map(bundleType -> { + // Find standard bundling operations producing the given bundle type. var bundlingOperations = Stream.of(StandardBundlingOperation.values()).filter(op -> { - return op.packageType().equals(packageType); + return op.bundleType().equals(bundleType); }).toList(); if (bundlingOperations.isEmpty()) { // jpackage internal error: none of the standard bundling operations produce - // bundles of the `packageType`. + // bundles of the `bundleType`. throw new AssertionError(String.format( "None of the standard bundling operations produce bundles of type [%s]", - packageType)); + bundleType)); } else if (bundlingOperations.size() == 1) { return bundlingOperations.getFirst(); } else { - // Multiple standard bundling operations produce the `packageType` package type. + // Multiple standard bundling operations produce the `bundleType` bundle type. // Filter those that belong to the current OS bundlingOperations = bundlingOperations.stream().filter(op -> { return op.os().equals(OperatingSystem.current()); @@ -298,10 +299,10 @@ final class OptionsAnalyzer { if (bundlingOperations.isEmpty()) { // jpackage internal error: none of the standard bundling operations produce - // bundles of the `packageType` on the current OS. + // bundles of the `bundleType` on the current OS. throw new AssertionError(String.format( "None of the standard bundling operations produce bundles of type [%s] on %s", - packageType, OperatingSystem.current())); + bundleType, OperatingSystem.current())); } else if (bundlingOperations.size() == 1) { return bundlingOperations.getFirst(); } else if (StandardBundlingOperation.MACOS_APP_IMAGE.containsAll(bundlingOperations)) { @@ -316,7 +317,7 @@ final class OptionsAnalyzer { } } }).orElseGet(() -> { - // No package type specified, use the default bundling operation in the given environment. + // No bundle type specified, use the default bundling operation in the given environment. return env.defaultOperation().map(descriptor -> { return Stream.of(StandardBundlingOperation.values()).filter(op -> { return descriptor.equals(op.descriptor()); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardBundlingOperation.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardBundlingOperation.java index 45f9194db0b..f6fcd68a6d8 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardBundlingOperation.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardBundlingOperation.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,8 +24,6 @@ */ package jdk.jpackage.internal.cli; -import static jdk.jpackage.internal.model.AppImagePackageType.APP_IMAGE; - import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -34,6 +32,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.model.AppImageBundleType; +import jdk.jpackage.internal.model.BundleType; import jdk.jpackage.internal.model.BundlingOperationDescriptor; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.StandardPackageType; @@ -44,16 +44,16 @@ import jdk.jpackage.internal.util.SetBuilder; * Standard jpackage operations. */ public enum StandardBundlingOperation implements BundlingOperationOptionScope { - CREATE_WIN_APP_IMAGE(APP_IMAGE, "^(?!(linux-|mac-|win-exe-|win-msi-))", OperatingSystem.WINDOWS), - CREATE_LINUX_APP_IMAGE(APP_IMAGE, "^(?!(win-|mac-|linux-rpm-|linux-deb-))", OperatingSystem.LINUX), - CREATE_MAC_APP_IMAGE(APP_IMAGE, "^(?!(linux-|win-|mac-dmg-|mac-pkg-))", OperatingSystem.MACOS), + CREATE_WIN_APP_IMAGE(AppImageBundleType.WIN_APP_IMAGE, "^(?!(linux-|mac-|win-exe-|win-msi-))", OperatingSystem.WINDOWS), + CREATE_LINUX_APP_IMAGE(AppImageBundleType.LINUX_APP_IMAGE, "^(?!(win-|mac-|linux-rpm-|linux-deb-))", OperatingSystem.LINUX), + CREATE_MAC_APP_IMAGE(AppImageBundleType.MAC_APP_IMAGE, "^(?!(linux-|win-|mac-dmg-|mac-pkg-))", OperatingSystem.MACOS), CREATE_WIN_EXE(StandardPackageType.WIN_EXE, "^(?!(linux-|mac-|win-msi-))", OperatingSystem.WINDOWS), CREATE_WIN_MSI(StandardPackageType.WIN_MSI, "^(?!(linux-|mac-|win-exe-))", OperatingSystem.WINDOWS), CREATE_LINUX_RPM(StandardPackageType.LINUX_RPM, "^(?!(win-|mac-|linux-deb-))", OperatingSystem.LINUX), CREATE_LINUX_DEB(StandardPackageType.LINUX_DEB, "^(?!(win-|mac-|linux-rpm-))", OperatingSystem.LINUX), CREATE_MAC_PKG(StandardPackageType.MAC_PKG, "^(?!(linux-|win-|mac-dmg-))", OperatingSystem.MACOS), CREATE_MAC_DMG(StandardPackageType.MAC_DMG, "^(?!(linux-|win-|mac-pkg-))", OperatingSystem.MACOS), - SIGN_MAC_APP_IMAGE(APP_IMAGE, OperatingSystem.MACOS, Verb.SIGN); + SIGN_MAC_APP_IMAGE(AppImageBundleType.MAC_APP_IMAGE, OperatingSystem.MACOS, Verb.SIGN); /** * Supported values of the {@link BundlingOperationDescriptor#verb()} property. @@ -78,19 +78,19 @@ public enum StandardBundlingOperation implements BundlingOperationOptionScope { private final String value; } - StandardBundlingOperation(PackageType packageType, String optionNameRegexp, OperatingSystem os, Verb descriptorVerb) { - this.packageType = Objects.requireNonNull(packageType); + StandardBundlingOperation(BundleType bundleType, String optionNameRegexp, OperatingSystem os, Verb descriptorVerb) { + this.bundleType = Objects.requireNonNull(bundleType); optionNamePredicate = Pattern.compile(optionNameRegexp).asPredicate(); this.os = Objects.requireNonNull(os); this.descriptorVerb = Objects.requireNonNull(descriptorVerb); } - StandardBundlingOperation(PackageType packageType, String optionNameRegexp, OperatingSystem os) { - this(packageType, optionNameRegexp, os, Verb.CREATE); + StandardBundlingOperation(BundleType bundleType, String optionNameRegexp, OperatingSystem os) { + this(bundleType, optionNameRegexp, os, Verb.CREATE); } - StandardBundlingOperation(PackageType packageType, OperatingSystem os, Verb descriptorVerb) { - this.packageType = Objects.requireNonNull(packageType); + StandardBundlingOperation(BundleType bundleType, OperatingSystem os, Verb descriptorVerb) { + this.bundleType = Objects.requireNonNull(bundleType); optionNamePredicate = v -> false; this.os = Objects.requireNonNull(os); this.descriptorVerb = Objects.requireNonNull(descriptorVerb); @@ -100,16 +100,20 @@ public enum StandardBundlingOperation implements BundlingOperationOptionScope { return os; } - public String packageTypeValue() { - if (packageType.equals(APP_IMAGE)) { + public String bundleTypeValue() { + if (bundleType instanceof AppImageBundleType) { return "app-image"; } else { - return ((StandardPackageType)packageType).suffix().substring(1); + return ((StandardPackageType)bundleType).suffix().substring(1); } } + public BundleType bundleType() { + return bundleType; + } + public PackageType packageType() { - return packageType; + return (PackageType)bundleType(); } /** @@ -122,7 +126,7 @@ public enum StandardBundlingOperation implements BundlingOperationOptionScope { @Override public BundlingOperationDescriptor descriptor() { - return new BundlingOperationDescriptor(os(), packageTypeValue(), descriptorVerb.value()); + return new BundlingOperationDescriptor(os(), bundleTypeValue(), descriptorVerb.value()); } public static Optional valueOf(BundlingOperationDescriptor descriptor) { @@ -199,6 +203,6 @@ public enum StandardBundlingOperation implements BundlingOperationOptionScope { private final Predicate optionNamePredicate; private final OperatingSystem os; - private final PackageType packageType; + private final BundleType bundleType; private final Verb descriptorVerb; } 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 dddaef8399b..9c828705a4d 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 @@ -55,11 +55,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.model.AppImageBundleType; +import jdk.jpackage.internal.model.BundleType; import jdk.jpackage.internal.model.BundlingOperationDescriptor; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; -import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.util.SetBuilder; /** @@ -103,18 +104,18 @@ public final class StandardOption { public static final OptionValue VERBOSE = auxilaryOption("verbose").create(); - public static final OptionValue TYPE = option("type", PackageType.class).addAliases("t") + public static final OptionValue TYPE = option("type", BundleType.class).addAliases("t") .scope(StandardBundlingOperation.values()).inScope(NOT_BUILDING_APP_IMAGE) .converterExceptionFactory(ERROR_WITH_VALUE).converterExceptionFormatString("ERR_InvalidInstallerType") .converter(str -> { - Objects.requireNonNull(str); - return Stream.of(StandardBundlingOperation.values()).filter(bundlingOperation -> { - return bundlingOperation.packageTypeValue().equals(str); - }).map(StandardBundlingOperation::packageType).findFirst().orElseThrow(IllegalArgumentException::new); + return parseBundleType(str, OperatingSystem.current()); }) .description("help.option.type" + resourceKeySuffix(OperatingSystem.current())) .mutate(createOptionSpecBuilderMutator((b, context) -> { b.description("help.option.type" + resourceKeySuffix(context.os())); + b.converter(str -> { + return parseBundleType(str, context.os()); + }); })).create(); public static final OptionValue INPUT = directoryOption("input").addAliases("i") @@ -665,6 +666,23 @@ public final class StandardOption { }).defaultArrayValue(new AdditionalLauncher[0]).createArray(); } + private static BundleType parseBundleType(String str, OperatingSystem appImageOS) { + Objects.requireNonNull(str); + Objects.requireNonNull(appImageOS); + + return Stream.of(StandardBundlingOperation.values()).filter(bundlingOperation -> { + return bundlingOperation.bundleTypeValue().equals(str); + }) + .filter(bundlingOperation -> { + // Skip app image bundle type if it is from another platform. + return !(bundlingOperation.bundleType() instanceof AppImageBundleType) + || (bundlingOperation.os() == appImageOS); + }) + .map(StandardBundlingOperation::bundleType) + .findFirst() + .orElseThrow(IllegalArgumentException::new); + } + private static String resourceKeySuffix(OperatingSystem os) { switch (os) { case LINUX -> { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageBundleType.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageBundleType.java new file mode 100644 index 00000000000..c5d2f0d1569 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImageBundleType.java @@ -0,0 +1,51 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.internal.model; + +import java.util.Objects; + +/** + * App image bundle type. + * + * @see StandardPackageType + */ +public enum AppImageBundleType implements BundleType { + + WIN_APP_IMAGE("bundle-type.win-app"), + LINUX_APP_IMAGE("bundle-type.linux-app"), + MAC_APP_IMAGE("bundle-type.mac-app"), + ; + + private AppImageBundleType(String key) { + this.key = Objects.requireNonNull(key); + } + + @Override + public String label() { + return I18N.getString(key); + } + + private final String key; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImagePackageType.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/BundleType.java similarity index 77% rename from src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImagePackageType.java rename to src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/BundleType.java index 4e28bb05aef..009725f3e92 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/AppImagePackageType.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/BundleType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/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 @@ -22,20 +22,18 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package jdk.jpackage.internal.model; -/** - * App image packaging type. - * - * @see StandardPackageType - */ -public final class AppImagePackageType implements PackageType { - private AppImagePackageType() { - } +/** + * Generic bundle type. E.g.: application image, rpm, msi are all bundle types. + */ +public sealed interface BundleType permits PackageType, AppImageBundleType { /** - * Singleton + * Returns a user-facing label of this bundle type. + * @return a user-facing label of this bundle type. */ - public static final AppImagePackageType APP_IMAGE = new AppImagePackageType(); + String label(); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/PackageType.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/PackageType.java index d0a4fd010e6..e7273d27ba5 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/PackageType.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/PackageType.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,8 +27,8 @@ package jdk.jpackage.internal.model; /** - * Generic package type. E.g.: application image, rpm, msi are all package types. + * Native package type. E.g.: dmg, rpm, msi are all package types. * * @see jdk.jpackage.internal.model.Package */ -public interface PackageType {} +public non-sealed interface PackageType extends BundleType {} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/StandardPackageType.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/StandardPackageType.java index ccdeceb4a04..6fadc748ecc 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/StandardPackageType.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/StandardPackageType.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,19 +24,22 @@ */ package jdk.jpackage.internal.model; +import java.util.Objects; + /** * Standard native package types. */ public enum StandardPackageType implements PackageType { - WIN_MSI(".msi"), - WIN_EXE(".exe"), - LINUX_DEB(".deb"), - LINUX_RPM(".rpm"), - MAC_PKG(".pkg"), - MAC_DMG(".dmg"); + WIN_MSI("bundle-type.win-msi", ".msi"), + WIN_EXE("bundle-type.win-exe", ".exe"), + LINUX_DEB("bundle-type.linux-deb", ".deb"), + LINUX_RPM("bundle-type.linux-rpm", ".rpm"), + MAC_PKG("bundle-type.mac-pkg", ".pkg"), + MAC_DMG("bundle-type.mac-dmg", ".dmg"); - StandardPackageType(String suffix) { - this.suffix = suffix; + StandardPackageType(String key, String suffix) { + this.key = Objects.requireNonNull(key); + this.suffix = Objects.requireNonNull(suffix); } /** @@ -48,5 +51,11 @@ public enum StandardPackageType implements PackageType { return suffix; } + @Override + public String label() { + return I18N.getString(key); + } + + private final String key; private final String suffix; } 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 245d3b892da..588a3702839 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 @@ -28,14 +28,14 @@ param.copyright.default=Copyright (C) {0,date,YYYY} param.vendor.default=Unknown bundle-type.win-app=Windows Application Image -bundle-type.win-exe=EXE Installer Package -bundle-type.win-msi=MSI Installer Package +bundle-type.win-exe=Windows EXE Installer +bundle-type.win-msi=Windows MSI Installer bundle-type.mac-app=Mac Application Image bundle-type.mac-dmg=Mac DMG Package bundle-type.mac-pkg=Mac PKG Package bundle-type.linux-app=Linux Application Image -bundle-type.linux-deb=DEB Bundle -bundle-type.linux-rpm=RPM Bundle +bundle-type.linux-deb=Linux DEB Package +bundle-type.linux-rpm=Linux RPM Package resource.post-app-image-script=script to run after application image is populated @@ -43,9 +43,14 @@ message.using-default-resource=Using default package resource {0} {1} (add {2} t message.no-default-resource=No default package resource {0} (add {1} to the resource-dir to customize). message.using-custom-resource-from-file=Using custom package resource {0} (loaded from file {1}). message.using-custom-resource=Using custom package resource {0} (loaded from {1}). -message.creating-app-bundle=Creating app package: {0} in {1} + +message.create-package=Building output package file... +message.create-app-image=Building output application image directory... +message.package-created=Succeeded in building output package file +message.app-image-created=Succeeded in building output application image directory + message.debug-working-directory=Kept working directory for debug: {0} -message.bundle-created=Succeeded in building {0} package + message.module-version=Using version "{0}" from module "{1}" as application version message.error-header=Error: {0} @@ -97,6 +102,8 @@ error.tool-not-found.advice=Please install "{0}" error.tool-old-version=Can not find "{0}" {1} or newer error.tool-old-version.advice=Please install "{0}" {1} or newer +error.output-bundle-cannot-be-overwritten=Output package file "{0}" exists and can not be removed. + error.blocked.option=jlink option [{0}] is not permitted in --jlink-options error.no.name=Name not specified with --name and cannot infer one from app-image error.no.name.advice=Specify name with --name diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java index 9a13a0f954d..fd86331e2f1 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.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 @@ -79,8 +79,6 @@ final record WinExePackager(BuildEnv env, WinExePackage pkg, Path outputDir, Pat private void wrapMsiInExe() throws IOException { - Log.verbose(I18N.format("message.outputting-to-location", outputDir.toAbsolutePath())); - final var msi = msi(); // Copy template msi wrapper next to msi file @@ -102,7 +100,5 @@ final record WinExePackager(BuildEnv env, WinExePackage pkg, Path outputDir, Pat Files.copy(exePath, dstExePath, StandardCopyOption.REPLACE_EXISTING); dstExePath.toFile().setExecutable(true); - - Log.verbose(I18N.format("message.output-location", outputDir.toAbsolutePath())); } } 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 915d034bd82..c52be726fd2 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.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 @@ -315,7 +315,6 @@ final class WinMsiPackager implements Consumer { private void buildPackage() throws IOException { final var msiOut = outputDir.resolve(pkg.packageFileNameWithSuffix()); - Log.verbose(I18N.format("message.generating-msi", msiOut.toAbsolutePath())); wixPipeline.buildMsi(msiOut.toAbsolutePath()); } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties index 1f485e6c6c8..38d0bd02bbb 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.properties +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/resources/WinResources.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 @@ -56,13 +56,9 @@ error.missing-service-installer.advice=Add 'service-installer.exe' service insta message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place. message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}". -message.outputting-to-location=Generating EXE for installer to: {0}. -message.output-location=Installer (.exe) saved to: {0} message.tool-version=Detected [{0}] version [{1}]. message.wrong-tool-version=Detected [{0}] version {1} but version {2} is required. message.use-wix36-features=WiX {0} detected. Enabling advanced cleanup action. message.product-code=MSI ProductCode: {0}. message.upgrade-code=MSI UpgradeCode: {0}. message.preparing-msi-config=Preparing MSI config: {0}. -message.generating-msi=Generating MSI: {0}. - 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 d2b423b2ed2..9b8b05af93b 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -80,6 +80,7 @@ public class JPackageCommand extends CommandArguments { prerequisiteActions = new Actions(); verifyActions = new Actions(); excludeStandardAsserts(StandardAssert.MAIN_LAUNCHER_DESCRIPTION); + removeOldOutputBundle = true; } private JPackageCommand(JPackageCommand cmd, boolean immutable) { @@ -91,6 +92,7 @@ public class JPackageCommand extends CommandArguments { suppressOutput = cmd.suppressOutput; ignoreDefaultRuntime = cmd.ignoreDefaultRuntime; ignoreDefaultVerbose = cmd.ignoreDefaultVerbose; + removeOldOutputBundle = cmd.removeOldOutputBundle; this.immutable = immutable; prerequisiteActions = new Actions(cmd.prerequisiteActions); verifyActions = new Actions(cmd.verifyActions); @@ -844,6 +846,28 @@ public class JPackageCommand extends CommandArguments { return this; } + /** + * Configures this instance to optionally remove the existing output bundle + * before running the jpackage command. + * + * @param v {@code true} to remove existing output bundle before running the + * jpackage command, and {@code false} otherwise + * @return this + */ + public JPackageCommand removeOldOutputBundle(boolean v) { + verifyMutable(); + removeOldOutputBundle = v; + return this; + } + + /** + * Returns {@code true} if this instance will remove existing output bundle + * before running the jpackage command, and {@code false} otherwise. + */ + public boolean isRemoveOldOutputBundle() { + return removeOldOutputBundle; + } + public JPackageCommand validateOutput(TKit.TextStreamVerifier validator) { return validateOutput(validator::apply); } @@ -946,21 +970,18 @@ public class JPackageCommand extends CommandArguments { verifyMutable(); executePrerequisiteActions(); - if (hasArgument("--dest")) { - nullableOutputBundle().ifPresent(path -> { - ThrowingRunnable.toRunnable(() -> { - if (Files.isDirectory(path)) { - TKit.deleteDirectoryRecursive(path, String.format( - "Delete [%s] folder before running jpackage", - path)); - } else if (TKit.deleteIfExists(path)) { - TKit.trace(String.format( - "Deleted [%s] file before running jpackage", - path)); - } - }).run(); - }); - } + nullableOutputBundle().filter(_ -> { + return removeOldOutputBundle; + }).ifPresent(path -> { + ThrowingRunnable.toRunnable(() -> { + if (Files.isDirectory(path)) { + TKit.deleteDirectoryRecursive(path, + String.format("Delete [%s] folder before running jpackage", path)); + } else if (TKit.deleteIfExists(path)) { + TKit.trace(String.format("Deleted [%s] file before running jpackage", path)); + } + }).run(); + }); Path resourceDir = getArgumentValue("--resource-dir", () -> null, Path::of); if (resourceDir != null && Files.isDirectory(resourceDir)) { @@ -1090,7 +1111,7 @@ public class JPackageCommand extends CommandArguments { private final Map> snapshots; } - public static enum ReadOnlyPathAssert{ + public static enum ReadOnlyPathAssert { APP_IMAGE(new Builder("--app-image").enable(cmd -> { // External app image should be R/O unless it is an app image signing on macOS. return !(TKit.isOSX() && MacHelper.signPredefinedAppImage(cmd)); @@ -1774,6 +1795,7 @@ public class JPackageCommand extends CommandArguments { private boolean suppressOutput; private boolean ignoreDefaultRuntime; private boolean ignoreDefaultVerbose; + private boolean removeOldOutputBundle; private boolean immutable; private final Actions prerequisiteActions; private final Actions verifyActions; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java index 2e4f11d056f..2baf6683fdf 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.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 @@ -726,12 +726,30 @@ public final class PackageTest extends RunnablePackageTest { } case CREATE -> { - Executor.Result result = cmd.execute(expectedJPackageExitCode); + var nullableOutputBundle = cmd.nullableOutputBundle(); + + var oldOutputBundleSnapshot = nullableOutputBundle + .filter(Files::exists) + .filter(_ -> { + return !cmd.isRemoveOldOutputBundle(); + }) + .map(TKit.PathSnapshot::new); + + var result = cmd.execute(expectedJPackageExitCode); + if (expectedJPackageExitCode == 0) { TKit.assertFileExists(cmd.outputBundle()); } else { - cmd.nullableOutputBundle().ifPresent(outputBundle -> { - TKit.assertPathExists(outputBundle, false); + nullableOutputBundle.ifPresent(outputBundle -> { + oldOutputBundleSnapshot.ifPresentOrElse(snapshot -> { + // jpackage failed, but the output bundle exists. + // This output bundle existed before the jpackage was invoked. + // Verify jpackage didn't modify it. + new TKit.PathSnapshot(outputBundle).assertEquals(snapshot, String.format( + "Check jpackage didn't modify the old output bundle [%s]", outputBundle)); + }, () -> { + TKit.assertPathExists(outputBundle, false); + }); }); } verifyPackageBundle(cmd, result, expectedJPackageExitCode); diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java index 1a14330fe6e..709f0f8413b 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DefaultBundlingEnvironmentTest.java @@ -41,7 +41,7 @@ import java.util.spi.ToolProvider; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.cli.StandardBundlingOperation; -import jdk.jpackage.internal.model.AppImagePackageType; +import jdk.jpackage.internal.model.AppImageBundleType; import jdk.jpackage.internal.model.BundlingOperationDescriptor; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.StandardPackageType; @@ -119,9 +119,9 @@ public class DefaultBundlingEnvironmentTest extends JUnitAdapter { // #2 - jpackage should bail out earlier). // - final var type = op.packageTypeValue(); + final var type = op.bundleTypeValue(); final int iterationCount; - if (op.packageType() instanceof AppImagePackageType) { + if (op.bundleType() instanceof AppImageBundleType) { iterationCount = 1; } else { iterationCount = 2; @@ -165,7 +165,7 @@ public class DefaultBundlingEnvironmentTest extends JUnitAdapter { private static Script createMockScript(StandardBundlingOperation op) { - if (op.packageType() instanceof AppImagePackageType) { + if (op.bundleType() instanceof AppImageBundleType) { return Script.build().createSequence(); } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java index 721e0802d16..86a6cb075d0 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java @@ -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 @@ -633,7 +633,12 @@ public class PackagingPipelineTest { Package create() { return new Package.Stub( app, - new PackageType() {}, + new PackageType() { + @Override + public String label() { + throw new UnsupportedOperationException(); + } + }, "the-package", "My package", "1.0", 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 4aa3d5f72c1..0b70f4151cc 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 @@ -53,6 +53,8 @@ import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.cli.JOptSimpleOptionsBuilder.ConvertedOptionsBuilder; import jdk.jpackage.internal.cli.JOptSimpleOptionsBuilder.OptionsBuilder; 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.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; @@ -175,16 +177,21 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { assertEquals(DirectoryNotEmptyException.class, ex.getCause().getClass()); } + @ParameterizedTest + @EnumSource(names = {"WINDOWS", "LINUX", "MACOS"}) + public void test_TYPE_valid(OperatingSystem appImageOS) { + + var spec = new StandardOptionContext(appImageOS).mapOptionSpec(StandardOption.TYPE.getSpec()); + + test_TYPE_valid(spec, appImageOS); + } + @Test public void test_TYPE_valid() { var spec = StandardOption.TYPE.getSpec(); - Stream.of(StandardBundlingOperation.values()).forEach(bundlingOperation -> { - var pkgTypeStr = bundlingOperation.packageTypeValue(); - var pkgType = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(pkgTypeStr)).orElseThrow(); - assertSame(bundlingOperation.packageType(), pkgType); - }); + test_TYPE_valid(spec, OperatingSystem.current()); } @ParameterizedTest @@ -336,6 +343,18 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { assertEquals(expectedOptionTable, optionTable); } + private void test_TYPE_valid(OptionSpec spec, OperatingSystem appImageOS) { + Stream.of(StandardBundlingOperation.values()).filter(bundlingOperation -> { + // Skip app image bundle type if it is from another platform. + return !(bundlingOperation.bundleType() instanceof AppImageBundleType) + || (bundlingOperation.os() == appImageOS); + }).forEach(bundlingOperation -> { + var bundleTypeStr = bundlingOperation.bundleTypeValue(); + var bundleType = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(bundleTypeStr)).orElseThrow(); + assertSame(bundlingOperation.bundleType(), bundleType); + }); + } + private static Collection test_ARGUMENTS() { return List.of( Arguments.of("abc", List.of("abc")), diff --git a/test/jdk/tools/jpackage/share/BasicTest.java b/test/jdk/tools/jpackage/share/BasicTest.java index afa847f6e1b..977b7c7c057 100644 --- a/test/jdk/tools/jpackage/share/BasicTest.java +++ b/test/jdk/tools/jpackage/share/BasicTest.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 @@ -22,6 +22,7 @@ */ +import static jdk.jpackage.test.RunnablePackageTest.Action.CREATE; import static jdk.jpackage.test.RunnablePackageTest.Action.CREATE_AND_UNPACK; import java.io.IOException; @@ -32,6 +33,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -41,6 +43,7 @@ import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.CannedFormattedString; +import jdk.jpackage.test.ConfigurationTarget; import jdk.jpackage.test.Executor; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; @@ -176,57 +179,74 @@ public final class BasicTest { } @Test - @SuppressWarnings("unchecked") - public void testVerbose() { - JPackageCommand cmd = JPackageCommand.helloAppImage() - // Disable default logic adding `--verbose` option - // to jpackage command line. - .ignoreDefaultVerbose(true) - .saveConsoleOutput(true) - .setFakeRuntime().executePrerequisiteActions(); + @Parameter("false") + @Parameter("true") + public void testQuiet(boolean appImage) { - List expectedVerboseOutputStrings = new ArrayList<>(); - expectedVerboseOutputStrings.add("Creating app package:"); - if (TKit.isWindows()) { - expectedVerboseOutputStrings.add( - "Succeeded in building Windows Application Image package"); - } else if (TKit.isLinux()) { - expectedVerboseOutputStrings.add( - "Succeeded in building Linux Application Image package"); - } else if (TKit.isOSX()) { - expectedVerboseOutputStrings.add("Preparing Info.plist:"); - expectedVerboseOutputStrings.add( - "Succeeded in building Mac Application Image package"); + ConfigurationTarget target; + if (appImage) { + target = new ConfigurationTarget(JPackageCommand.helloAppImage()); } else { - TKit.throwUnknownPlatformError(); + target = new ConfigurationTarget(new PackageTest().configureHelloApp()); } - TKit.deleteDirectoryContentsRecursive(cmd.outputDir()); - List nonVerboseOutput = cmd.execute().getOutput(); - List[] verboseOutput = (List[])new List[1]; - - // Directory clean up is not 100% reliable on Windows because of - // antivirus software that can lock .exe files. Setup - // different output directory instead of cleaning the default one for - // verbose jpackage run. - TKit.withTempDirectory("verbose-output", tempDir -> { - cmd.setArgumentValue("--dest", tempDir); - cmd.addArgument("--verbose"); - verboseOutput[0] = cmd.execute().getOutput(); + target.addInitializer(cmd -> { + // Disable the default logic adding `--verbose` option to jpackage command line. + cmd.ignoreDefaultVerbose(true) + .useToolProvider(true) + .saveConsoleOutput(true) + .setFakeRuntime(); }); - TKit.assertTrue(nonVerboseOutput.size() < verboseOutput[0].size(), - "Check verbose output is longer than regular"); + Consumer asserter = result -> { + TKit.assertStringListEquals(List.of(), result.getOutput(), "Check output is empty"); + }; - expectedVerboseOutputStrings.forEach(str -> { - TKit.assertTextStream(str).label("regular output") - .predicate(String::contains).negate() - .apply(nonVerboseOutput); + target.cmd().map(JPackageCommand::execute).ifPresent(asserter); + target.test().ifPresent(test -> { + test.addBundleVerifier((_, result) -> { + asserter.accept(result); + }).run(CREATE); + }); + } + + @Test + @Parameter("false") + @Parameter("true") + public void testVerbose(boolean appImage) { + + ConfigurationTarget target; + if (appImage) { + target = new ConfigurationTarget(JPackageCommand.helloAppImage()); + } else { + target = new ConfigurationTarget(new PackageTest().configureHelloApp()); + } + + target.addInitializer(cmd -> { + // Disable the default logic adding `--verbose` option to jpackage command line. + cmd.ignoreDefaultVerbose(true) + .useToolProvider(true) + .addArgument("--verbose") + .saveConsoleOutput(true) + .setFakeRuntime(); + + List verboseContent; + if (appImage) { + verboseContent = List.of( + JPackageStringBundle.MAIN.cannedFormattedString("message.create-app-image"), + JPackageStringBundle.MAIN.cannedFormattedString("message.app-image-created")); + } else { + verboseContent = List.of( + JPackageStringBundle.MAIN.cannedFormattedString("message.create-package"), + JPackageStringBundle.MAIN.cannedFormattedString("message.package-created")); + } + + cmd.validateOutput(verboseContent.toArray(CannedFormattedString[]::new)); }); - expectedVerboseOutputStrings.forEach(str -> { - TKit.assertTextStream(str).label("verbose output") - .apply(verboseOutput[0]); + target.cmd().ifPresent(JPackageCommand::execute); + target.test().ifPresent(test -> { + test.run(CREATE); }); } diff --git a/test/jdk/tools/jpackage/share/OutputErrorTest.java b/test/jdk/tools/jpackage/share/OutputErrorTest.java new file mode 100644 index 00000000000..110e86e67d9 --- /dev/null +++ b/test/jdk/tools/jpackage/share/OutputErrorTest.java @@ -0,0 +1,119 @@ +/* + * 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.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.spi.ToolProvider; +import jdk.internal.util.OperatingSystem; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageStringBundle; +import jdk.jpackage.test.JavaTool; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.TKit; + +/* + * @test + * @summary Test how jpackage handles errors writing output bundle + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.* + * @compile -Xlint:all -Werror OutputErrorTest.java + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=OutputErrorTest + */ + +public final class OutputErrorTest { + + @Test + @Parameter("DIR") + // "Locked file error" reliably works only on Windows + @Parameter(value = "LOCKED_FILE", ifOS = OperatingSystem.WINDOWS) + public void testPackage(ExistingOutputBundleType existingOutputBundleType) { + + new PackageTest().configureHelloApp().addInitializer(cmd -> { + + cmd.setFakeRuntime(); + cmd.setArgumentValue("--dest", TKit.createTempDirectory("output")); + cmd.removeOldOutputBundle(false); + cmd.validateOutput(JPackageCommand.makeError(JPackageStringBundle.MAIN.cannedFormattedString( + "error.output-bundle-cannot-be-overwritten", cmd.outputBundle().toAbsolutePath()))); + + var outputBundle = cmd.outputBundle(); + + switch (existingOutputBundleType) { + case DIR -> { + Files.createDirectories(outputBundle); + Files.writeString(outputBundle.resolve("foo.txt"), "Hello"); + } + case LOCKED_FILE -> { + Files.writeString(outputBundle, "Hello"); + cmd.useToolProvider(createToolProviderWithLockedFile( + JavaTool.JPACKAGE.asToolProvider(), outputBundle)); + } + } + + }).setExpectedExitCode(1).run(); + } + + enum ExistingOutputBundleType { + DIR, + LOCKED_FILE, + ; + } + + private static ToolProvider createToolProviderWithLockedFile(ToolProvider tp, Path lockedFile) { + Objects.requireNonNull(tp); + if (!Files.isRegularFile(lockedFile)) { + throw new IllegalArgumentException(); + } + + return new ToolProvider() { + + @Override + public String name() { + return "jpackage-mock"; + } + + @SuppressWarnings("try") + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + try { + var lastModifiedTime = Files.getLastModifiedTime(lockedFile); + try (var fos = new FileOutputStream(lockedFile.toFile()); var lock = fos.getChannel().lock()) { + Files.setLastModifiedTime(lockedFile, lastModifiedTime); + return tp.run(out, err, args); + } + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + }; + } +} From 56d7b524b3ddb49b985b4e6f061a7128b10cffb5 Mon Sep 17 00:00:00 2001 From: Eric Fang Date: Wed, 14 Jan 2026 06:17:04 +0000 Subject: [PATCH 092/204] 8372978: [VectorAPI] Fix incorrect identity values in UMIN/UMAX reductions Reviewed-by: psandoz, qamai, xgong --- .../jdk/incubator/vector/ByteVector.java | 8 +- .../jdk/incubator/vector/DoubleVector.java | 2 +- .../jdk/incubator/vector/FloatVector.java | 2 +- .../jdk/incubator/vector/IntVector.java | 8 +- .../jdk/incubator/vector/LongVector.java | 8 +- .../jdk/incubator/vector/ShortVector.java | 8 +- .../incubator/vector/X-Vector.java.template | 8 +- .../incubator/vector/Byte128VectorTests.java | 671 +++++++++++------- .../incubator/vector/Byte256VectorTests.java | 671 +++++++++++------- .../incubator/vector/Byte512VectorTests.java | 671 +++++++++++------- .../incubator/vector/Byte64VectorTests.java | 671 +++++++++++------- .../incubator/vector/ByteMaxVectorTests.java | 670 ++++++++++------- .../vector/Double128VectorTests.java | 309 ++++---- .../vector/Double256VectorTests.java | 309 ++++---- .../vector/Double512VectorTests.java | 309 ++++---- .../incubator/vector/Double64VectorTests.java | 309 ++++---- .../vector/DoubleMaxVectorTests.java | 310 ++++---- .../incubator/vector/Float128VectorTests.java | 309 ++++---- .../incubator/vector/Float256VectorTests.java | 309 ++++---- .../incubator/vector/Float512VectorTests.java | 309 ++++---- .../incubator/vector/Float64VectorTests.java | 309 ++++---- .../incubator/vector/FloatMaxVectorTests.java | 310 ++++---- .../incubator/vector/Int128VectorTests.java | 671 +++++++++++------- .../incubator/vector/Int256VectorTests.java | 671 +++++++++++------- .../incubator/vector/Int512VectorTests.java | 671 +++++++++++------- .../incubator/vector/Int64VectorTests.java | 671 +++++++++++------- .../incubator/vector/IntMaxVectorTests.java | 670 ++++++++++------- .../incubator/vector/Long128VectorTests.java | 671 +++++++++++------- .../incubator/vector/Long256VectorTests.java | 671 +++++++++++------- .../incubator/vector/Long512VectorTests.java | 671 +++++++++++------- .../incubator/vector/Long64VectorTests.java | 671 +++++++++++------- .../incubator/vector/LongMaxVectorTests.java | 670 ++++++++++------- .../incubator/vector/Short128VectorTests.java | 671 +++++++++++------- .../incubator/vector/Short256VectorTests.java | 671 +++++++++++------- .../incubator/vector/Short512VectorTests.java | 671 +++++++++++------- .../incubator/vector/Short64VectorTests.java | 671 +++++++++++------- .../incubator/vector/ShortMaxVectorTests.java | 670 ++++++++++------- test/jdk/jdk/incubator/vector/gen-template.sh | 24 +- .../Kernel-Reduction-Masked-op-func.template | 13 +- .../Kernel-Reduction-Masked-op.template | 13 +- .../Kernel-Reduction-op-func.template | 13 +- .../templates/Kernel-Reduction-op.template | 13 +- ...nel-SaturatingReduction-Masked-op.template | 13 +- .../Kernel-SaturatingReduction-op.template | 13 +- .../templates/Unit-Reduction-op-func.template | 23 + .../templates/Unit-Reduction-op.template | 23 + .../Unit-SaturatingReduction-op.template | 23 + .../vector/templates/Unit-header.template | 24 +- 48 files changed, 10315 insertions(+), 6432 deletions(-) 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 08406fef518..02e15d5f8dd 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 @@ -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 @@ -2873,9 +2873,9 @@ public abstract class ByteVector extends AbstractVector { case VECTOR_OP_MAX: return (v, m) -> toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (byte) Math.max(a, b))); case VECTOR_OP_UMIN: return (v, m) -> - toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> (byte) VectorMath.minUnsigned(a, b))); + toBits(v.rOp(UMAX_VALUE, m, (i, a, b) -> (byte) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> - toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (byte) VectorMath.maxUnsigned(a, b))); + toBits(v.rOp(UMIN_VALUE, m, (i, a, b) -> (byte) VectorMath.maxUnsigned(a, b))); case VECTOR_OP_SUADD: return (v, m) -> toBits(v.rOp((byte)0, m, (i, a, b) -> (byte) VectorMath.addSaturatingUnsigned(a, b))); case VECTOR_OP_AND: return (v, m) -> @@ -2890,6 +2890,8 @@ public abstract class ByteVector extends AbstractVector { private static final byte MIN_OR_INF = Byte.MIN_VALUE; private static final byte MAX_OR_INF = Byte.MAX_VALUE; + private static final byte UMIN_VALUE = (byte)0; // Minimum unsigned value + private static final byte UMAX_VALUE = (byte)-1; // Maximum unsigned value public @Override abstract long reduceLanesToLong(VectorOperators.Associative op); public @Override abstract long reduceLanesToLong(VectorOperators.Associative op, 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 786cd089ebe..08fda9c96e6 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 @@ -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 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 b481d5a51d7..0f70a2b81c8 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 @@ -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 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 43356b9ea6c..23e703dcada 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 @@ -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 @@ -2858,9 +2858,9 @@ public abstract class IntVector extends AbstractVector { case VECTOR_OP_MAX: return (v, m) -> toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (int) Math.max(a, b))); case VECTOR_OP_UMIN: return (v, m) -> - toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> (int) VectorMath.minUnsigned(a, b))); + toBits(v.rOp(UMAX_VALUE, m, (i, a, b) -> (int) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> - toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (int) VectorMath.maxUnsigned(a, b))); + toBits(v.rOp(UMIN_VALUE, m, (i, a, b) -> (int) VectorMath.maxUnsigned(a, b))); case VECTOR_OP_SUADD: return (v, m) -> toBits(v.rOp((int)0, m, (i, a, b) -> (int) VectorMath.addSaturatingUnsigned(a, b))); case VECTOR_OP_AND: return (v, m) -> @@ -2875,6 +2875,8 @@ public abstract class IntVector extends AbstractVector { private static final int MIN_OR_INF = Integer.MIN_VALUE; private static final int MAX_OR_INF = Integer.MAX_VALUE; + private static final int UMIN_VALUE = (int)0; // Minimum unsigned value + private static final int UMAX_VALUE = (int)-1; // Maximum unsigned value public @Override abstract long reduceLanesToLong(VectorOperators.Associative op); public @Override abstract long reduceLanesToLong(VectorOperators.Associative op, 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 8947343ff30..58bfd4d7772 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 @@ -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 @@ -2724,9 +2724,9 @@ public abstract class LongVector extends AbstractVector { case VECTOR_OP_MAX: return (v, m) -> toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (long) Math.max(a, b))); case VECTOR_OP_UMIN: return (v, m) -> - toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> (long) VectorMath.minUnsigned(a, b))); + toBits(v.rOp(UMAX_VALUE, m, (i, a, b) -> (long) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> - toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (long) VectorMath.maxUnsigned(a, b))); + toBits(v.rOp(UMIN_VALUE, m, (i, a, b) -> (long) VectorMath.maxUnsigned(a, b))); case VECTOR_OP_SUADD: return (v, m) -> toBits(v.rOp((long)0, m, (i, a, b) -> (long) VectorMath.addSaturatingUnsigned(a, b))); case VECTOR_OP_AND: return (v, m) -> @@ -2741,6 +2741,8 @@ public abstract class LongVector extends AbstractVector { private static final long MIN_OR_INF = Long.MIN_VALUE; private static final long MAX_OR_INF = Long.MAX_VALUE; + private static final long UMIN_VALUE = (long)0; // Minimum unsigned value + private static final long UMAX_VALUE = (long)-1; // Maximum unsigned value public @Override abstract long reduceLanesToLong(VectorOperators.Associative op); public @Override abstract long reduceLanesToLong(VectorOperators.Associative op, 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 e222c6d25f3..7ab7e7c4417 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 @@ -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 @@ -2874,9 +2874,9 @@ public abstract class ShortVector extends AbstractVector { case VECTOR_OP_MAX: return (v, m) -> toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (short) Math.max(a, b))); case VECTOR_OP_UMIN: return (v, m) -> - toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> (short) VectorMath.minUnsigned(a, b))); + toBits(v.rOp(UMAX_VALUE, m, (i, a, b) -> (short) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> - toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> (short) VectorMath.maxUnsigned(a, b))); + toBits(v.rOp(UMIN_VALUE, m, (i, a, b) -> (short) VectorMath.maxUnsigned(a, b))); case VECTOR_OP_SUADD: return (v, m) -> toBits(v.rOp((short)0, m, (i, a, b) -> (short) VectorMath.addSaturatingUnsigned(a, b))); case VECTOR_OP_AND: return (v, m) -> @@ -2891,6 +2891,8 @@ public abstract class ShortVector extends AbstractVector { private static final short MIN_OR_INF = Short.MIN_VALUE; private static final short MAX_OR_INF = Short.MAX_VALUE; + private static final short UMIN_VALUE = (short)0; // Minimum unsigned value + private static final short UMAX_VALUE = (short)-1; // Maximum unsigned value public @Override abstract long reduceLanesToLong(VectorOperators.Associative op); public @Override abstract long reduceLanesToLong(VectorOperators.Associative op, 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 f7d987fd280..ce3b7512c93 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 @@ -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 @@ -3448,9 +3448,9 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> ($type$) Math.max(a, b))); #if[!FP] case VECTOR_OP_UMIN: return (v, m) -> - toBits(v.rOp(MAX_OR_INF, m, (i, a, b) -> ($type$) VectorMath.minUnsigned(a, b))); + toBits(v.rOp(UMAX_VALUE, m, (i, a, b) -> ($type$) VectorMath.minUnsigned(a, b))); case VECTOR_OP_UMAX: return (v, m) -> - toBits(v.rOp(MIN_OR_INF, m, (i, a, b) -> ($type$) VectorMath.maxUnsigned(a, b))); + toBits(v.rOp(UMIN_VALUE, m, (i, a, b) -> ($type$) VectorMath.maxUnsigned(a, b))); case VECTOR_OP_SUADD: return (v, m) -> toBits(v.rOp(($type$)0, m, (i, a, b) -> ($type$) VectorMath.addSaturatingUnsigned(a, b))); #end[!FP] @@ -3472,6 +3472,8 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { #else[FP] private static final $type$ MIN_OR_INF = $Boxtype$.MIN_VALUE; private static final $type$ MAX_OR_INF = $Boxtype$.MAX_VALUE; + private static final $type$ UMIN_VALUE = ($type$)0; // Minimum unsigned value + private static final $type$ UMAX_VALUE = ($type$)-1; // Maximum unsigned value #end[FP] public @Override abstract long reduceLanesToLong(VectorOperators.Associative op); diff --git a/test/jdk/jdk/incubator/vector/Byte128VectorTests.java b/test/jdk/jdk/incubator/vector/Byte128VectorTests.java index 3ad6d6f320f..fae7b678a09 100644 --- a/test/jdk/jdk/incubator/vector/Byte128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte128VectorTests.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 @@ -63,9 +63,21 @@ public class Byte128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final byte CONST_SHIFT = Byte.SIZE / 2; + // Identity values for reduction operations + private static final byte ADD_IDENTITY = (byte)0; + private static final byte AND_IDENTITY = (byte)-1; + private static final byte FIRST_NONZERO_IDENTITY = (byte)0; + private static final byte MAX_IDENTITY = Byte.MIN_VALUE; + private static final byte MIN_IDENTITY = Byte.MAX_VALUE; + private static final byte MUL_IDENTITY = (byte)1; + private static final byte OR_IDENTITY = (byte)0; + private static final byte SUADD_IDENTITY = (byte)0; + private static final byte UMAX_IDENTITY = (byte)0; // Minimum unsigned value + private static final byte UMIN_IDENTITY = (byte)-1; // Maximum unsigned value + private static final byte XOR_IDENTITY = (byte)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); static void assertArraysStrictlyEquals(byte[] r, byte[] a) { @@ -3611,7 +3623,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ANDReduce(byte[] a, int idx) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3620,7 +3632,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ANDReduceAll(byte[] a) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3632,20 +3644,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void ANDReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + byte v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3653,8 +3660,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::ANDReduce, Byte128VectorTests::ANDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = AND_IDENTITY; + + Assert.assertEquals((byte) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id & x), x); + Assert.assertEquals((byte) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3664,7 +3694,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ANDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3678,20 +3708,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + byte v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3700,7 +3725,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ORReduce(byte[] a, int idx) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3709,7 +3734,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ORReduceAll(byte[] a) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3724,17 +3749,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + byte v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3742,8 +3762,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::ORReduce, Byte128VectorTests::ORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = OR_IDENTITY; + + Assert.assertEquals((byte) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id | x), x); + Assert.assertEquals((byte) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3753,7 +3796,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3770,17 +3813,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + byte v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3789,7 +3827,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte XORReduce(byte[] a, int idx) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3798,7 +3836,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte XORReduceAll(byte[] a) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3813,17 +3851,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + byte v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3831,8 +3864,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::XORReduce, Byte128VectorTests::XORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = XOR_IDENTITY; + + Assert.assertEquals((byte) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id ^ x), x); + Assert.assertEquals((byte) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3842,7 +3898,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte XORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3859,17 +3915,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + byte v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3878,7 +3929,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3887,7 +3938,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ADDReduceAll(byte[] a) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3902,17 +3953,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + byte v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3920,8 +3966,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::ADDReduce, Byte128VectorTests::ADDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = ADD_IDENTITY; + + Assert.assertEquals((byte) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id + x), x); + Assert.assertEquals((byte) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3931,7 +4000,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte ADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3948,17 +4017,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + byte v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3967,7 +4031,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MULReduce(byte[] a, int idx) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3976,7 +4040,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MULReduceAll(byte[] a) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3988,20 +4052,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void MULReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + byte v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4009,8 +4068,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::MULReduce, Byte128VectorTests::MULReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MUL_IDENTITY; + + Assert.assertEquals((byte) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id * x), x); + Assert.assertEquals((byte) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4020,7 +4102,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MULReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4034,20 +4116,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + byte v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4056,7 +4133,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.min(res, a[i]); } @@ -4065,7 +4142,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduce(a, i)); } @@ -4077,20 +4154,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void MINReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + byte v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4098,8 +4170,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::MINReduce, Byte128VectorTests::MINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MIN_IDENTITY; + + Assert.assertEquals((byte) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.min(id, x), x); + Assert.assertEquals((byte) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.min(res, a[i]); @@ -4109,7 +4204,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4123,20 +4218,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + byte v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4145,7 +4235,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.max(res, a[i]); } @@ -4154,7 +4244,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduce(a, i)); } @@ -4166,20 +4256,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void MAXReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + byte v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4187,8 +4272,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::MAXReduce, Byte128VectorTests::MAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MAX_IDENTITY; + + Assert.assertEquals((byte) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.max(id, x), x); + Assert.assertEquals((byte) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.max(res, a[i]); @@ -4198,7 +4306,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte MAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4212,20 +4320,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + byte v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4234,7 +4337,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte UMINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.minUnsigned(res, a[i]); } @@ -4243,7 +4346,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte UMINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4255,20 +4358,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void UMINReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + byte v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4276,8 +4374,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::UMINReduce, Byte128VectorTests::UMINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMIN_IDENTITY; + + Assert.assertEquals((byte) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.minUnsigned(res, a[i]); @@ -4287,7 +4408,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte UMINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4301,20 +4422,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + byte v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4323,7 +4439,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte UMAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.maxUnsigned(res, a[i]); } @@ -4332,7 +4448,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte UMAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4344,20 +4460,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void UMAXReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + byte v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4365,8 +4476,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::UMAXReduce, Byte128VectorTests::UMAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMAX_IDENTITY; + + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.maxUnsigned(res, a[i]); @@ -4376,7 +4510,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte UMAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4390,20 +4524,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + byte v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4412,7 +4541,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduce(byte[] a, int idx) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4421,7 +4550,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAll(byte[] a) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4433,20 +4562,15 @@ public class Byte128VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4454,8 +4578,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::FIRST_NONZEROReduce, Byte128VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4465,7 +4612,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAllMasked(byte[] a, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4479,20 +4626,15 @@ public class Byte128VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4549,7 +4691,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte SUADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4558,7 +4700,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte SUADDReduceAll(byte[] a) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4573,17 +4715,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + byte v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4591,8 +4728,31 @@ public class Byte128VectorTests extends AbstractVectorTest { Byte128VectorTests::SUADDReduce, Byte128VectorTests::SUADDReduceAll); } + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = SUADD_IDENTITY; + + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4602,7 +4762,7 @@ public class Byte128VectorTests extends AbstractVectorTest { } static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4618,17 +4778,12 @@ public class Byte128VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + byte v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Byte256VectorTests.java b/test/jdk/jdk/incubator/vector/Byte256VectorTests.java index d64caa3424f..0e59db7d05d 100644 --- a/test/jdk/jdk/incubator/vector/Byte256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte256VectorTests.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 @@ -63,9 +63,21 @@ public class Byte256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final byte CONST_SHIFT = Byte.SIZE / 2; + // Identity values for reduction operations + private static final byte ADD_IDENTITY = (byte)0; + private static final byte AND_IDENTITY = (byte)-1; + private static final byte FIRST_NONZERO_IDENTITY = (byte)0; + private static final byte MAX_IDENTITY = Byte.MIN_VALUE; + private static final byte MIN_IDENTITY = Byte.MAX_VALUE; + private static final byte MUL_IDENTITY = (byte)1; + private static final byte OR_IDENTITY = (byte)0; + private static final byte SUADD_IDENTITY = (byte)0; + private static final byte UMAX_IDENTITY = (byte)0; // Minimum unsigned value + private static final byte UMIN_IDENTITY = (byte)-1; // Maximum unsigned value + private static final byte XOR_IDENTITY = (byte)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); static void assertArraysStrictlyEquals(byte[] r, byte[] a) { @@ -3611,7 +3623,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ANDReduce(byte[] a, int idx) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3620,7 +3632,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ANDReduceAll(byte[] a) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3632,20 +3644,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void ANDReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + byte v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3653,8 +3660,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::ANDReduce, Byte256VectorTests::ANDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = AND_IDENTITY; + + Assert.assertEquals((byte) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id & x), x); + Assert.assertEquals((byte) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3664,7 +3694,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ANDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3678,20 +3708,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + byte v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3700,7 +3725,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ORReduce(byte[] a, int idx) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3709,7 +3734,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ORReduceAll(byte[] a) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3724,17 +3749,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + byte v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3742,8 +3762,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::ORReduce, Byte256VectorTests::ORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = OR_IDENTITY; + + Assert.assertEquals((byte) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id | x), x); + Assert.assertEquals((byte) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3753,7 +3796,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3770,17 +3813,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + byte v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3789,7 +3827,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte XORReduce(byte[] a, int idx) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3798,7 +3836,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte XORReduceAll(byte[] a) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3813,17 +3851,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + byte v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3831,8 +3864,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::XORReduce, Byte256VectorTests::XORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = XOR_IDENTITY; + + Assert.assertEquals((byte) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id ^ x), x); + Assert.assertEquals((byte) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3842,7 +3898,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte XORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3859,17 +3915,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + byte v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3878,7 +3929,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3887,7 +3938,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ADDReduceAll(byte[] a) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3902,17 +3953,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + byte v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3920,8 +3966,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::ADDReduce, Byte256VectorTests::ADDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = ADD_IDENTITY; + + Assert.assertEquals((byte) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id + x), x); + Assert.assertEquals((byte) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3931,7 +4000,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte ADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3948,17 +4017,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + byte v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3967,7 +4031,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MULReduce(byte[] a, int idx) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3976,7 +4040,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MULReduceAll(byte[] a) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3988,20 +4052,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void MULReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + byte v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4009,8 +4068,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::MULReduce, Byte256VectorTests::MULReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MUL_IDENTITY; + + Assert.assertEquals((byte) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id * x), x); + Assert.assertEquals((byte) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4020,7 +4102,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MULReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4034,20 +4116,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + byte v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4056,7 +4133,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.min(res, a[i]); } @@ -4065,7 +4142,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduce(a, i)); } @@ -4077,20 +4154,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void MINReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + byte v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4098,8 +4170,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::MINReduce, Byte256VectorTests::MINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MIN_IDENTITY; + + Assert.assertEquals((byte) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.min(id, x), x); + Assert.assertEquals((byte) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.min(res, a[i]); @@ -4109,7 +4204,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4123,20 +4218,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + byte v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4145,7 +4235,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.max(res, a[i]); } @@ -4154,7 +4244,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduce(a, i)); } @@ -4166,20 +4256,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void MAXReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + byte v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4187,8 +4272,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::MAXReduce, Byte256VectorTests::MAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MAX_IDENTITY; + + Assert.assertEquals((byte) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.max(id, x), x); + Assert.assertEquals((byte) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.max(res, a[i]); @@ -4198,7 +4306,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte MAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4212,20 +4320,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + byte v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4234,7 +4337,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte UMINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.minUnsigned(res, a[i]); } @@ -4243,7 +4346,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte UMINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4255,20 +4358,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void UMINReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + byte v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4276,8 +4374,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::UMINReduce, Byte256VectorTests::UMINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMIN_IDENTITY; + + Assert.assertEquals((byte) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.minUnsigned(res, a[i]); @@ -4287,7 +4408,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte UMINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4301,20 +4422,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + byte v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4323,7 +4439,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte UMAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.maxUnsigned(res, a[i]); } @@ -4332,7 +4448,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte UMAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4344,20 +4460,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void UMAXReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + byte v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4365,8 +4476,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::UMAXReduce, Byte256VectorTests::UMAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMAX_IDENTITY; + + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.maxUnsigned(res, a[i]); @@ -4376,7 +4510,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte UMAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4390,20 +4524,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + byte v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4412,7 +4541,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduce(byte[] a, int idx) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4421,7 +4550,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAll(byte[] a) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4433,20 +4562,15 @@ public class Byte256VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4454,8 +4578,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::FIRST_NONZEROReduce, Byte256VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4465,7 +4612,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAllMasked(byte[] a, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4479,20 +4626,15 @@ public class Byte256VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4549,7 +4691,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte SUADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4558,7 +4700,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte SUADDReduceAll(byte[] a) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4573,17 +4715,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + byte v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4591,8 +4728,31 @@ public class Byte256VectorTests extends AbstractVectorTest { Byte256VectorTests::SUADDReduce, Byte256VectorTests::SUADDReduceAll); } + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = SUADD_IDENTITY; + + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4602,7 +4762,7 @@ public class Byte256VectorTests extends AbstractVectorTest { } static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4618,17 +4778,12 @@ public class Byte256VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + byte v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Byte512VectorTests.java b/test/jdk/jdk/incubator/vector/Byte512VectorTests.java index f494e3d3ae8..5ad3bbdbc05 100644 --- a/test/jdk/jdk/incubator/vector/Byte512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte512VectorTests.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 @@ -63,9 +63,21 @@ public class Byte512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final byte CONST_SHIFT = Byte.SIZE / 2; + // Identity values for reduction operations + private static final byte ADD_IDENTITY = (byte)0; + private static final byte AND_IDENTITY = (byte)-1; + private static final byte FIRST_NONZERO_IDENTITY = (byte)0; + private static final byte MAX_IDENTITY = Byte.MIN_VALUE; + private static final byte MIN_IDENTITY = Byte.MAX_VALUE; + private static final byte MUL_IDENTITY = (byte)1; + private static final byte OR_IDENTITY = (byte)0; + private static final byte SUADD_IDENTITY = (byte)0; + private static final byte UMAX_IDENTITY = (byte)0; // Minimum unsigned value + private static final byte UMIN_IDENTITY = (byte)-1; // Maximum unsigned value + private static final byte XOR_IDENTITY = (byte)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); static void assertArraysStrictlyEquals(byte[] r, byte[] a) { @@ -3611,7 +3623,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ANDReduce(byte[] a, int idx) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3620,7 +3632,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ANDReduceAll(byte[] a) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3632,20 +3644,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void ANDReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + byte v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3653,8 +3660,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::ANDReduce, Byte512VectorTests::ANDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = AND_IDENTITY; + + Assert.assertEquals((byte) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id & x), x); + Assert.assertEquals((byte) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3664,7 +3694,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ANDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3678,20 +3708,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + byte v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3700,7 +3725,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ORReduce(byte[] a, int idx) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3709,7 +3734,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ORReduceAll(byte[] a) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3724,17 +3749,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + byte v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3742,8 +3762,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::ORReduce, Byte512VectorTests::ORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = OR_IDENTITY; + + Assert.assertEquals((byte) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id | x), x); + Assert.assertEquals((byte) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3753,7 +3796,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3770,17 +3813,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + byte v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3789,7 +3827,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte XORReduce(byte[] a, int idx) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3798,7 +3836,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte XORReduceAll(byte[] a) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3813,17 +3851,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + byte v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3831,8 +3864,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::XORReduce, Byte512VectorTests::XORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = XOR_IDENTITY; + + Assert.assertEquals((byte) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id ^ x), x); + Assert.assertEquals((byte) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3842,7 +3898,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte XORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3859,17 +3915,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + byte v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3878,7 +3929,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3887,7 +3938,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ADDReduceAll(byte[] a) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3902,17 +3953,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + byte v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3920,8 +3966,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::ADDReduce, Byte512VectorTests::ADDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = ADD_IDENTITY; + + Assert.assertEquals((byte) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id + x), x); + Assert.assertEquals((byte) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3931,7 +4000,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte ADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3948,17 +4017,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + byte v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3967,7 +4031,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MULReduce(byte[] a, int idx) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3976,7 +4040,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MULReduceAll(byte[] a) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3988,20 +4052,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void MULReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + byte v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4009,8 +4068,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::MULReduce, Byte512VectorTests::MULReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MUL_IDENTITY; + + Assert.assertEquals((byte) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id * x), x); + Assert.assertEquals((byte) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4020,7 +4102,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MULReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4034,20 +4116,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + byte v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4056,7 +4133,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.min(res, a[i]); } @@ -4065,7 +4142,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduce(a, i)); } @@ -4077,20 +4154,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void MINReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + byte v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4098,8 +4170,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::MINReduce, Byte512VectorTests::MINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MIN_IDENTITY; + + Assert.assertEquals((byte) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.min(id, x), x); + Assert.assertEquals((byte) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.min(res, a[i]); @@ -4109,7 +4204,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4123,20 +4218,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + byte v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4145,7 +4235,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.max(res, a[i]); } @@ -4154,7 +4244,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduce(a, i)); } @@ -4166,20 +4256,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void MAXReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + byte v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4187,8 +4272,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::MAXReduce, Byte512VectorTests::MAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MAX_IDENTITY; + + Assert.assertEquals((byte) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.max(id, x), x); + Assert.assertEquals((byte) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.max(res, a[i]); @@ -4198,7 +4306,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte MAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4212,20 +4320,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + byte v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4234,7 +4337,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte UMINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.minUnsigned(res, a[i]); } @@ -4243,7 +4346,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte UMINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4255,20 +4358,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void UMINReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + byte v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4276,8 +4374,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::UMINReduce, Byte512VectorTests::UMINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMIN_IDENTITY; + + Assert.assertEquals((byte) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.minUnsigned(res, a[i]); @@ -4287,7 +4408,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte UMINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4301,20 +4422,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + byte v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4323,7 +4439,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte UMAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.maxUnsigned(res, a[i]); } @@ -4332,7 +4448,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte UMAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4344,20 +4460,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void UMAXReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + byte v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4365,8 +4476,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::UMAXReduce, Byte512VectorTests::UMAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMAX_IDENTITY; + + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.maxUnsigned(res, a[i]); @@ -4376,7 +4510,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte UMAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4390,20 +4524,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + byte v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4412,7 +4541,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduce(byte[] a, int idx) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4421,7 +4550,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAll(byte[] a) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4433,20 +4562,15 @@ public class Byte512VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4454,8 +4578,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::FIRST_NONZEROReduce, Byte512VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4465,7 +4612,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAllMasked(byte[] a, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4479,20 +4626,15 @@ public class Byte512VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4549,7 +4691,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte SUADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4558,7 +4700,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte SUADDReduceAll(byte[] a) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4573,17 +4715,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + byte v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4591,8 +4728,31 @@ public class Byte512VectorTests extends AbstractVectorTest { Byte512VectorTests::SUADDReduce, Byte512VectorTests::SUADDReduceAll); } + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = SUADD_IDENTITY; + + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4602,7 +4762,7 @@ public class Byte512VectorTests extends AbstractVectorTest { } static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4618,17 +4778,12 @@ public class Byte512VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + byte v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Byte64VectorTests.java b/test/jdk/jdk/incubator/vector/Byte64VectorTests.java index da2961cd97b..e28fb2b2001 100644 --- a/test/jdk/jdk/incubator/vector/Byte64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Byte64VectorTests.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 @@ -63,9 +63,21 @@ public class Byte64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final byte CONST_SHIFT = Byte.SIZE / 2; + // Identity values for reduction operations + private static final byte ADD_IDENTITY = (byte)0; + private static final byte AND_IDENTITY = (byte)-1; + private static final byte FIRST_NONZERO_IDENTITY = (byte)0; + private static final byte MAX_IDENTITY = Byte.MIN_VALUE; + private static final byte MIN_IDENTITY = Byte.MAX_VALUE; + private static final byte MUL_IDENTITY = (byte)1; + private static final byte OR_IDENTITY = (byte)0; + private static final byte SUADD_IDENTITY = (byte)0; + private static final byte UMAX_IDENTITY = (byte)0; // Minimum unsigned value + private static final byte UMIN_IDENTITY = (byte)-1; // Maximum unsigned value + private static final byte XOR_IDENTITY = (byte)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); static void assertArraysStrictlyEquals(byte[] r, byte[] a) { @@ -3611,7 +3623,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ANDReduce(byte[] a, int idx) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3620,7 +3632,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ANDReduceAll(byte[] a) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3632,20 +3644,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void ANDReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + byte v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3653,8 +3660,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::ANDReduce, Byte64VectorTests::ANDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = AND_IDENTITY; + + Assert.assertEquals((byte) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id & x), x); + Assert.assertEquals((byte) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3664,7 +3694,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ANDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3678,20 +3708,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + byte v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3700,7 +3725,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ORReduce(byte[] a, int idx) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3709,7 +3734,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ORReduceAll(byte[] a) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3724,17 +3749,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + byte v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3742,8 +3762,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::ORReduce, Byte64VectorTests::ORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = OR_IDENTITY; + + Assert.assertEquals((byte) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id | x), x); + Assert.assertEquals((byte) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3753,7 +3796,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3770,17 +3813,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + byte v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3789,7 +3827,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte XORReduce(byte[] a, int idx) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3798,7 +3836,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte XORReduceAll(byte[] a) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3813,17 +3851,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + byte v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3831,8 +3864,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::XORReduce, Byte64VectorTests::XORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = XOR_IDENTITY; + + Assert.assertEquals((byte) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id ^ x), x); + Assert.assertEquals((byte) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3842,7 +3898,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte XORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3859,17 +3915,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + byte v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3878,7 +3929,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3887,7 +3938,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ADDReduceAll(byte[] a) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3902,17 +3953,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + byte v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3920,8 +3966,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::ADDReduce, Byte64VectorTests::ADDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = ADD_IDENTITY; + + Assert.assertEquals((byte) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id + x), x); + Assert.assertEquals((byte) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3931,7 +4000,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte ADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3948,17 +4017,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + byte v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3967,7 +4031,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MULReduce(byte[] a, int idx) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3976,7 +4040,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MULReduceAll(byte[] a) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3988,20 +4052,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void MULReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + byte v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4009,8 +4068,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::MULReduce, Byte64VectorTests::MULReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MUL_IDENTITY; + + Assert.assertEquals((byte) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id * x), x); + Assert.assertEquals((byte) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4020,7 +4102,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MULReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4034,20 +4116,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + byte v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4056,7 +4133,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.min(res, a[i]); } @@ -4065,7 +4142,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduce(a, i)); } @@ -4077,20 +4154,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void MINReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + byte v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4098,8 +4170,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::MINReduce, Byte64VectorTests::MINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MIN_IDENTITY; + + Assert.assertEquals((byte) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.min(id, x), x); + Assert.assertEquals((byte) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.min(res, a[i]); @@ -4109,7 +4204,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4123,20 +4218,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + byte v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4145,7 +4235,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.max(res, a[i]); } @@ -4154,7 +4244,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduce(a, i)); } @@ -4166,20 +4256,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void MAXReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + byte v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4187,8 +4272,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::MAXReduce, Byte64VectorTests::MAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MAX_IDENTITY; + + Assert.assertEquals((byte) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.max(id, x), x); + Assert.assertEquals((byte) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.max(res, a[i]); @@ -4198,7 +4306,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte MAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4212,20 +4320,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + byte v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4234,7 +4337,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte UMINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.minUnsigned(res, a[i]); } @@ -4243,7 +4346,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte UMINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4255,20 +4358,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void UMINReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + byte v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4276,8 +4374,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::UMINReduce, Byte64VectorTests::UMINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMIN_IDENTITY; + + Assert.assertEquals((byte) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.minUnsigned(res, a[i]); @@ -4287,7 +4408,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte UMINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4301,20 +4422,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + byte v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4323,7 +4439,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte UMAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.maxUnsigned(res, a[i]); } @@ -4332,7 +4448,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte UMAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4344,20 +4460,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void UMAXReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + byte v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4365,8 +4476,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::UMAXReduce, Byte64VectorTests::UMAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMAX_IDENTITY; + + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.maxUnsigned(res, a[i]); @@ -4376,7 +4510,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte UMAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4390,20 +4524,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + byte v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4412,7 +4541,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduce(byte[] a, int idx) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4421,7 +4550,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAll(byte[] a) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4433,20 +4562,15 @@ public class Byte64VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4454,8 +4578,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::FIRST_NONZEROReduce, Byte64VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4465,7 +4612,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAllMasked(byte[] a, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4479,20 +4626,15 @@ public class Byte64VectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4549,7 +4691,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte SUADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4558,7 +4700,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte SUADDReduceAll(byte[] a) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4573,17 +4715,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + byte v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4591,8 +4728,31 @@ public class Byte64VectorTests extends AbstractVectorTest { Byte64VectorTests::SUADDReduce, Byte64VectorTests::SUADDReduceAll); } + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = SUADD_IDENTITY; + + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4602,7 +4762,7 @@ public class Byte64VectorTests extends AbstractVectorTest { } static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4618,17 +4778,12 @@ public class Byte64VectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + byte v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java b/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java index a5872219f10..b6932785b55 100644 --- a/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/ByteMaxVectorTests.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 @@ -71,6 +71,19 @@ public class ByteMaxVectorTests extends AbstractVectorTest { private static final byte CONST_SHIFT = Byte.SIZE / 2; + // Identity values for reduction operations + private static final byte ADD_IDENTITY = (byte)0; + private static final byte AND_IDENTITY = (byte)-1; + private static final byte FIRST_NONZERO_IDENTITY = (byte)0; + private static final byte MAX_IDENTITY = Byte.MIN_VALUE; + private static final byte MIN_IDENTITY = Byte.MAX_VALUE; + private static final byte MUL_IDENTITY = (byte)1; + private static final byte OR_IDENTITY = (byte)0; + private static final byte SUADD_IDENTITY = (byte)0; + private static final byte UMAX_IDENTITY = (byte)0; // Minimum unsigned value + private static final byte UMIN_IDENTITY = (byte)-1; // Maximum unsigned value + private static final byte XOR_IDENTITY = (byte)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); static void assertArraysStrictlyEquals(byte[] r, byte[] a) { @@ -3616,7 +3629,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ANDReduce(byte[] a, int idx) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3625,7 +3638,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ANDReduceAll(byte[] a) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3637,20 +3650,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void ANDReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + byte v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3658,8 +3666,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::ANDReduce, ByteMaxVectorTests::ANDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = AND_IDENTITY; + + Assert.assertEquals((byte) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id & x), x); + Assert.assertEquals((byte) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3669,7 +3700,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ANDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = -1; + byte res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3683,20 +3714,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + byte v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3705,7 +3731,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ORReduce(byte[] a, int idx) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3714,7 +3740,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ORReduceAll(byte[] a) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3729,17 +3755,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + byte v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3747,8 +3768,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::ORReduce, ByteMaxVectorTests::ORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = OR_IDENTITY; + + Assert.assertEquals((byte) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id | x), x); + Assert.assertEquals((byte) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3758,7 +3802,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3775,17 +3819,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + byte v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3794,7 +3833,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte XORReduce(byte[] a, int idx) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3803,7 +3842,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte XORReduceAll(byte[] a) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3818,17 +3857,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + byte v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3836,8 +3870,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::XORReduce, ByteMaxVectorTests::XORReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = XOR_IDENTITY; + + Assert.assertEquals((byte) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id ^ x), x); + Assert.assertEquals((byte) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3847,7 +3904,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte XORReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3864,17 +3921,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + byte v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3883,7 +3935,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3892,7 +3944,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ADDReduceAll(byte[] a) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3907,17 +3959,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + byte v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3925,8 +3972,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::ADDReduce, ByteMaxVectorTests::ADDReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = ADD_IDENTITY; + + Assert.assertEquals((byte) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id + x), x); + Assert.assertEquals((byte) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3936,7 +4006,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte ADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3953,17 +4023,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + byte v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3972,7 +4037,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MULReduce(byte[] a, int idx) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3981,7 +4046,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MULReduceAll(byte[] a) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3993,20 +4058,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void MULReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + byte v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4014,8 +4074,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::MULReduce, ByteMaxVectorTests::MULReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MUL_IDENTITY; + + Assert.assertEquals((byte) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) (id * x), x); + Assert.assertEquals((byte) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4025,7 +4108,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MULReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 1; + byte res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4039,20 +4122,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + byte v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4061,7 +4139,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.min(res, a[i]); } @@ -4070,7 +4148,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduce(a, i)); } @@ -4082,20 +4160,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void MINReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + byte v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4103,8 +4176,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::MINReduce, ByteMaxVectorTests::MINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MIN_IDENTITY; + + Assert.assertEquals((byte) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.min(id, x), x); + Assert.assertEquals((byte) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.min(res, a[i]); @@ -4114,7 +4210,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4128,20 +4224,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + byte v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (byte) Math.min(ra, v); } } @@ -4150,7 +4241,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) Math.max(res, a[i]); } @@ -4159,7 +4250,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduce(a, i)); } @@ -4171,20 +4262,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void MAXReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + byte v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4192,8 +4278,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::MAXReduce, ByteMaxVectorTests::MAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = MAX_IDENTITY; + + Assert.assertEquals((byte) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) Math.max(id, x), x); + Assert.assertEquals((byte) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) Math.max(res, a[i]); @@ -4203,7 +4312,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte MAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4217,20 +4326,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + byte v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (byte) Math.max(ra, v); } } @@ -4239,7 +4343,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte UMINReduce(byte[] a, int idx) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.minUnsigned(res, a[i]); } @@ -4248,7 +4352,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte UMINReduceAll(byte[] a) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4260,20 +4364,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void UMINReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + byte v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4281,8 +4380,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::UMINReduce, ByteMaxVectorTests::UMINReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMIN_IDENTITY; + + Assert.assertEquals((byte) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.minUnsigned(res, a[i]); @@ -4292,7 +4414,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte UMINReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MAX_VALUE; + byte res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4306,20 +4428,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + byte v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (byte) VectorMath.minUnsigned(ra, v); } } @@ -4328,7 +4445,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte UMAXReduce(byte[] a, int idx) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.maxUnsigned(res, a[i]); } @@ -4337,7 +4454,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte UMAXReduceAll(byte[] a) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4349,20 +4466,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void UMAXReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + byte v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4370,8 +4482,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::UMAXReduce, ByteMaxVectorTests::UMAXReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = UMAX_IDENTITY; + + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.maxUnsigned(res, a[i]); @@ -4381,7 +4516,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte UMAXReduceAllMasked(byte[] a, boolean[] mask) { - byte res = Byte.MIN_VALUE; + byte res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4395,20 +4530,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + byte v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (byte) VectorMath.maxUnsigned(ra, v); } } @@ -4417,7 +4547,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduce(byte[] a, int idx) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4426,7 +4556,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAll(byte[] a) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4438,20 +4568,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4459,8 +4584,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::FIRST_NONZEROReduce, ByteMaxVectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "byteUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4470,7 +4618,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte FIRST_NONZEROReduceAllMasked(byte[] a, boolean[] mask) { - byte res = (byte) 0; + byte res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4484,20 +4632,15 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + byte v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4554,7 +4697,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte SUADDReduce(byte[] a, int idx) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4563,7 +4706,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte SUADDReduceAll(byte[] a) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4578,17 +4721,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + byte v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4596,8 +4734,31 @@ public class ByteMaxVectorTests extends AbstractVectorTest { ByteMaxVectorTests::SUADDReduce, ByteMaxVectorTests::SUADDReduceAll); } + @Test(dataProvider = "byteSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + byte[] a = fa.apply(SPECIES.length()); + byte id = SUADD_IDENTITY; + + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + byte x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((byte) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (byte) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4607,7 +4768,7 @@ public class ByteMaxVectorTests extends AbstractVectorTest { } static byte SUADDReduceAllMasked(byte[] a, boolean[] mask) { - byte res = 0; + byte res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (byte) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4623,17 +4784,12 @@ public class ByteMaxVectorTests extends AbstractVectorTest { byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - ra = (byte) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + byte v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (byte) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Double128VectorTests.java b/test/jdk/jdk/incubator/vector/Double128VectorTests.java index 215edbb3a66..1d4cadd2158 100644 --- a/test/jdk/jdk/incubator/vector/Double128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double128VectorTests.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 @@ -62,6 +62,12 @@ public class Double128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final double ADD_IDENTITY = (double)0; + private static final double FIRST_NONZERO_IDENTITY = (double)0; + private static final double MAX_IDENTITY = Double.NEGATIVE_INFINITY; + private static final double MIN_IDENTITY = Double.POSITIVE_INFINITY; + private static final double MUL_IDENTITY = (double)1; // for floating point addition reduction ops that may introduce rounding errors private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; @@ -2384,7 +2390,7 @@ relativeError)); } static double ADDReduce(double[] a, int idx) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2393,7 +2399,7 @@ relativeError)); } static double ADDReduceAll(double[] a) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2408,17 +2414,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + double v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2426,8 +2427,31 @@ relativeError)); Double128VectorTests::ADDReduce, Double128VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = ADD_IDENTITY; + + Assert.assertEquals((double) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id + x), x); + Assert.assertEquals((double) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2437,7 +2461,7 @@ relativeError)); } static double ADDReduceAllMasked(double[] a, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2454,17 +2478,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + double v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2473,7 +2492,7 @@ relativeError)); } static double MULReduce(double[] a, int idx) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2482,7 +2501,7 @@ relativeError)); } static double MULReduceAll(double[] a) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2494,20 +2513,15 @@ relativeError)); static void MULReduceDouble128VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + double v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2515,8 +2529,31 @@ relativeError)); Double128VectorTests::MULReduce, Double128VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MUL_IDENTITY; + + Assert.assertEquals((double) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id * x), x); + Assert.assertEquals((double) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static double MULReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2526,7 +2563,7 @@ relativeError)); } static double MULReduceAllMasked(double[] a, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2540,20 +2577,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + double v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2562,7 +2594,7 @@ relativeError)); } static double MINReduce(double[] a, int idx) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.min(res, a[i]); } @@ -2571,7 +2603,7 @@ relativeError)); } static double MINReduceAll(double[] a) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduce(a, i)); } @@ -2583,20 +2615,15 @@ relativeError)); static void MINReduceDouble128VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + double v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2604,8 +2631,31 @@ relativeError)); Double128VectorTests::MINReduce, Double128VectorTests::MINReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MIN_IDENTITY; + + Assert.assertEquals((double) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.min(id, x), x); + Assert.assertEquals((double) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static double MINReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.min(res, a[i]); @@ -2615,7 +2665,7 @@ relativeError)); } static double MINReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2629,20 +2679,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + double v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2651,7 +2696,7 @@ relativeError)); } static double MAXReduce(double[] a, int idx) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.max(res, a[i]); } @@ -2660,7 +2705,7 @@ relativeError)); } static double MAXReduceAll(double[] a) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduce(a, i)); } @@ -2672,20 +2717,15 @@ relativeError)); static void MAXReduceDouble128VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + double v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2693,8 +2733,31 @@ relativeError)); Double128VectorTests::MAXReduce, Double128VectorTests::MAXReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MAX_IDENTITY; + + Assert.assertEquals((double) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.max(id, x), x); + Assert.assertEquals((double) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.max(res, a[i]); @@ -2704,7 +2767,7 @@ relativeError)); } static double MAXReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2718,20 +2781,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + double v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2740,7 +2798,7 @@ relativeError)); } static double FIRST_NONZEROReduce(double[] a, int idx) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2749,7 +2807,7 @@ relativeError)); } static double FIRST_NONZEROReduceAll(double[] a) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2761,20 +2819,15 @@ relativeError)); static void FIRST_NONZEROReduceDouble128VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2782,8 +2835,31 @@ relativeError)); Double128VectorTests::FIRST_NONZEROReduce, Double128VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2793,7 +2869,7 @@ relativeError)); } static double FIRST_NONZEROReduceAllMasked(double[] a, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2807,20 +2883,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Double256VectorTests.java b/test/jdk/jdk/incubator/vector/Double256VectorTests.java index 14d52320ce8..b5acfe0ef34 100644 --- a/test/jdk/jdk/incubator/vector/Double256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double256VectorTests.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 @@ -62,6 +62,12 @@ public class Double256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final double ADD_IDENTITY = (double)0; + private static final double FIRST_NONZERO_IDENTITY = (double)0; + private static final double MAX_IDENTITY = Double.NEGATIVE_INFINITY; + private static final double MIN_IDENTITY = Double.POSITIVE_INFINITY; + private static final double MUL_IDENTITY = (double)1; // for floating point addition reduction ops that may introduce rounding errors private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; @@ -2384,7 +2390,7 @@ relativeError)); } static double ADDReduce(double[] a, int idx) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2393,7 +2399,7 @@ relativeError)); } static double ADDReduceAll(double[] a) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2408,17 +2414,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + double v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2426,8 +2427,31 @@ relativeError)); Double256VectorTests::ADDReduce, Double256VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = ADD_IDENTITY; + + Assert.assertEquals((double) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id + x), x); + Assert.assertEquals((double) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2437,7 +2461,7 @@ relativeError)); } static double ADDReduceAllMasked(double[] a, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2454,17 +2478,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + double v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2473,7 +2492,7 @@ relativeError)); } static double MULReduce(double[] a, int idx) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2482,7 +2501,7 @@ relativeError)); } static double MULReduceAll(double[] a) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2494,20 +2513,15 @@ relativeError)); static void MULReduceDouble256VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + double v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2515,8 +2529,31 @@ relativeError)); Double256VectorTests::MULReduce, Double256VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MUL_IDENTITY; + + Assert.assertEquals((double) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id * x), x); + Assert.assertEquals((double) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static double MULReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2526,7 +2563,7 @@ relativeError)); } static double MULReduceAllMasked(double[] a, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2540,20 +2577,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + double v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2562,7 +2594,7 @@ relativeError)); } static double MINReduce(double[] a, int idx) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.min(res, a[i]); } @@ -2571,7 +2603,7 @@ relativeError)); } static double MINReduceAll(double[] a) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduce(a, i)); } @@ -2583,20 +2615,15 @@ relativeError)); static void MINReduceDouble256VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + double v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2604,8 +2631,31 @@ relativeError)); Double256VectorTests::MINReduce, Double256VectorTests::MINReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MIN_IDENTITY; + + Assert.assertEquals((double) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.min(id, x), x); + Assert.assertEquals((double) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static double MINReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.min(res, a[i]); @@ -2615,7 +2665,7 @@ relativeError)); } static double MINReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2629,20 +2679,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + double v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2651,7 +2696,7 @@ relativeError)); } static double MAXReduce(double[] a, int idx) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.max(res, a[i]); } @@ -2660,7 +2705,7 @@ relativeError)); } static double MAXReduceAll(double[] a) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduce(a, i)); } @@ -2672,20 +2717,15 @@ relativeError)); static void MAXReduceDouble256VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + double v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2693,8 +2733,31 @@ relativeError)); Double256VectorTests::MAXReduce, Double256VectorTests::MAXReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MAX_IDENTITY; + + Assert.assertEquals((double) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.max(id, x), x); + Assert.assertEquals((double) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.max(res, a[i]); @@ -2704,7 +2767,7 @@ relativeError)); } static double MAXReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2718,20 +2781,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + double v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2740,7 +2798,7 @@ relativeError)); } static double FIRST_NONZEROReduce(double[] a, int idx) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2749,7 +2807,7 @@ relativeError)); } static double FIRST_NONZEROReduceAll(double[] a) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2761,20 +2819,15 @@ relativeError)); static void FIRST_NONZEROReduceDouble256VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2782,8 +2835,31 @@ relativeError)); Double256VectorTests::FIRST_NONZEROReduce, Double256VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2793,7 +2869,7 @@ relativeError)); } static double FIRST_NONZEROReduceAllMasked(double[] a, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2807,20 +2883,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Double512VectorTests.java b/test/jdk/jdk/incubator/vector/Double512VectorTests.java index 91a4669a1b1..3f85d0e52a6 100644 --- a/test/jdk/jdk/incubator/vector/Double512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double512VectorTests.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 @@ -62,6 +62,12 @@ public class Double512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final double ADD_IDENTITY = (double)0; + private static final double FIRST_NONZERO_IDENTITY = (double)0; + private static final double MAX_IDENTITY = Double.NEGATIVE_INFINITY; + private static final double MIN_IDENTITY = Double.POSITIVE_INFINITY; + private static final double MUL_IDENTITY = (double)1; // for floating point addition reduction ops that may introduce rounding errors private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; @@ -2384,7 +2390,7 @@ relativeError)); } static double ADDReduce(double[] a, int idx) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2393,7 +2399,7 @@ relativeError)); } static double ADDReduceAll(double[] a) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2408,17 +2414,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + double v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2426,8 +2427,31 @@ relativeError)); Double512VectorTests::ADDReduce, Double512VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = ADD_IDENTITY; + + Assert.assertEquals((double) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id + x), x); + Assert.assertEquals((double) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2437,7 +2461,7 @@ relativeError)); } static double ADDReduceAllMasked(double[] a, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2454,17 +2478,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + double v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2473,7 +2492,7 @@ relativeError)); } static double MULReduce(double[] a, int idx) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2482,7 +2501,7 @@ relativeError)); } static double MULReduceAll(double[] a) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2494,20 +2513,15 @@ relativeError)); static void MULReduceDouble512VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + double v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2515,8 +2529,31 @@ relativeError)); Double512VectorTests::MULReduce, Double512VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MUL_IDENTITY; + + Assert.assertEquals((double) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id * x), x); + Assert.assertEquals((double) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static double MULReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2526,7 +2563,7 @@ relativeError)); } static double MULReduceAllMasked(double[] a, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2540,20 +2577,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + double v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2562,7 +2594,7 @@ relativeError)); } static double MINReduce(double[] a, int idx) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.min(res, a[i]); } @@ -2571,7 +2603,7 @@ relativeError)); } static double MINReduceAll(double[] a) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduce(a, i)); } @@ -2583,20 +2615,15 @@ relativeError)); static void MINReduceDouble512VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + double v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2604,8 +2631,31 @@ relativeError)); Double512VectorTests::MINReduce, Double512VectorTests::MINReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MIN_IDENTITY; + + Assert.assertEquals((double) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.min(id, x), x); + Assert.assertEquals((double) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static double MINReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.min(res, a[i]); @@ -2615,7 +2665,7 @@ relativeError)); } static double MINReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2629,20 +2679,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + double v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2651,7 +2696,7 @@ relativeError)); } static double MAXReduce(double[] a, int idx) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.max(res, a[i]); } @@ -2660,7 +2705,7 @@ relativeError)); } static double MAXReduceAll(double[] a) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduce(a, i)); } @@ -2672,20 +2717,15 @@ relativeError)); static void MAXReduceDouble512VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + double v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2693,8 +2733,31 @@ relativeError)); Double512VectorTests::MAXReduce, Double512VectorTests::MAXReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MAX_IDENTITY; + + Assert.assertEquals((double) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.max(id, x), x); + Assert.assertEquals((double) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.max(res, a[i]); @@ -2704,7 +2767,7 @@ relativeError)); } static double MAXReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2718,20 +2781,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + double v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2740,7 +2798,7 @@ relativeError)); } static double FIRST_NONZEROReduce(double[] a, int idx) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2749,7 +2807,7 @@ relativeError)); } static double FIRST_NONZEROReduceAll(double[] a) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2761,20 +2819,15 @@ relativeError)); static void FIRST_NONZEROReduceDouble512VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2782,8 +2835,31 @@ relativeError)); Double512VectorTests::FIRST_NONZEROReduce, Double512VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2793,7 +2869,7 @@ relativeError)); } static double FIRST_NONZEROReduceAllMasked(double[] a, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2807,20 +2883,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Double64VectorTests.java b/test/jdk/jdk/incubator/vector/Double64VectorTests.java index 659e18cd8af..ab3586fb424 100644 --- a/test/jdk/jdk/incubator/vector/Double64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Double64VectorTests.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 @@ -62,6 +62,12 @@ public class Double64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final double ADD_IDENTITY = (double)0; + private static final double FIRST_NONZERO_IDENTITY = (double)0; + private static final double MAX_IDENTITY = Double.NEGATIVE_INFINITY; + private static final double MIN_IDENTITY = Double.POSITIVE_INFINITY; + private static final double MUL_IDENTITY = (double)1; // for floating point addition reduction ops that may introduce rounding errors private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; @@ -2384,7 +2390,7 @@ relativeError)); } static double ADDReduce(double[] a, int idx) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2393,7 +2399,7 @@ relativeError)); } static double ADDReduceAll(double[] a) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2408,17 +2414,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + double v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2426,8 +2427,31 @@ relativeError)); Double64VectorTests::ADDReduce, Double64VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = ADD_IDENTITY; + + Assert.assertEquals((double) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id + x), x); + Assert.assertEquals((double) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2437,7 +2461,7 @@ relativeError)); } static double ADDReduceAllMasked(double[] a, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2454,17 +2478,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + double v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2473,7 +2492,7 @@ relativeError)); } static double MULReduce(double[] a, int idx) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2482,7 +2501,7 @@ relativeError)); } static double MULReduceAll(double[] a) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2494,20 +2513,15 @@ relativeError)); static void MULReduceDouble64VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + double v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2515,8 +2529,31 @@ relativeError)); Double64VectorTests::MULReduce, Double64VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MUL_IDENTITY; + + Assert.assertEquals((double) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id * x), x); + Assert.assertEquals((double) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static double MULReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2526,7 +2563,7 @@ relativeError)); } static double MULReduceAllMasked(double[] a, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2540,20 +2577,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + double v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2562,7 +2594,7 @@ relativeError)); } static double MINReduce(double[] a, int idx) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.min(res, a[i]); } @@ -2571,7 +2603,7 @@ relativeError)); } static double MINReduceAll(double[] a) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduce(a, i)); } @@ -2583,20 +2615,15 @@ relativeError)); static void MINReduceDouble64VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + double v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2604,8 +2631,31 @@ relativeError)); Double64VectorTests::MINReduce, Double64VectorTests::MINReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MIN_IDENTITY; + + Assert.assertEquals((double) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.min(id, x), x); + Assert.assertEquals((double) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static double MINReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.min(res, a[i]); @@ -2615,7 +2665,7 @@ relativeError)); } static double MINReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2629,20 +2679,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + double v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2651,7 +2696,7 @@ relativeError)); } static double MAXReduce(double[] a, int idx) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.max(res, a[i]); } @@ -2660,7 +2705,7 @@ relativeError)); } static double MAXReduceAll(double[] a) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduce(a, i)); } @@ -2672,20 +2717,15 @@ relativeError)); static void MAXReduceDouble64VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + double v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2693,8 +2733,31 @@ relativeError)); Double64VectorTests::MAXReduce, Double64VectorTests::MAXReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MAX_IDENTITY; + + Assert.assertEquals((double) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.max(id, x), x); + Assert.assertEquals((double) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.max(res, a[i]); @@ -2704,7 +2767,7 @@ relativeError)); } static double MAXReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2718,20 +2781,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + double v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2740,7 +2798,7 @@ relativeError)); } static double FIRST_NONZEROReduce(double[] a, int idx) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2749,7 +2807,7 @@ relativeError)); } static double FIRST_NONZEROReduceAll(double[] a) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2761,20 +2819,15 @@ relativeError)); static void FIRST_NONZEROReduceDouble64VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2782,8 +2835,31 @@ relativeError)); Double64VectorTests::FIRST_NONZEROReduce, Double64VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2793,7 +2869,7 @@ relativeError)); } static double FIRST_NONZEROReduceAllMasked(double[] a, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2807,20 +2883,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java index ac85f0bd07b..8f135cd221a 100644 --- a/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/DoubleMaxVectorTests.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 @@ -68,6 +68,13 @@ public class DoubleMaxVectorTests extends AbstractVectorTest { private static final int Max = 256; // juts so we can do N/Max + // Identity values for reduction operations + private static final double ADD_IDENTITY = (double)0; + private static final double FIRST_NONZERO_IDENTITY = (double)0; + private static final double MAX_IDENTITY = Double.NEGATIVE_INFINITY; + private static final double MIN_IDENTITY = Double.POSITIVE_INFINITY; + private static final double MUL_IDENTITY = (double)1; + // for floating point addition reduction ops that may introduce rounding errors private static final double RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (double)10.0; @@ -2389,7 +2396,7 @@ relativeError)); } static double ADDReduce(double[] a, int idx) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2398,7 +2405,7 @@ relativeError)); } static double ADDReduceAll(double[] a) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2413,17 +2420,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + double v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2431,8 +2433,31 @@ relativeError)); DoubleMaxVectorTests::ADDReduce, DoubleMaxVectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = ADD_IDENTITY; + + Assert.assertEquals((double) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id + x), x); + Assert.assertEquals((double) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2442,7 +2467,7 @@ relativeError)); } static double ADDReduceAllMasked(double[] a, boolean[] mask) { - double res = 0; + double res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2459,17 +2484,12 @@ relativeError)); double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + double v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2478,7 +2498,7 @@ relativeError)); } static double MULReduce(double[] a, int idx) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2487,7 +2507,7 @@ relativeError)); } static double MULReduceAll(double[] a) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2499,20 +2519,15 @@ relativeError)); static void MULReduceDoubleMaxVectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + double v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2520,8 +2535,31 @@ relativeError)); DoubleMaxVectorTests::MULReduce, DoubleMaxVectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MUL_IDENTITY; + + Assert.assertEquals((double) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) (id * x), x); + Assert.assertEquals((double) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static double MULReduceMasked(double[] a, int idx, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2531,7 +2569,7 @@ relativeError)); } static double MULReduceAllMasked(double[] a, boolean[] mask) { - double res = 1; + double res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2545,20 +2583,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + double v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2567,7 +2600,7 @@ relativeError)); } static double MINReduce(double[] a, int idx) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.min(res, a[i]); } @@ -2576,7 +2609,7 @@ relativeError)); } static double MINReduceAll(double[] a) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduce(a, i)); } @@ -2588,20 +2621,15 @@ relativeError)); static void MINReduceDoubleMaxVectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + double v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2609,8 +2637,31 @@ relativeError)); DoubleMaxVectorTests::MINReduce, DoubleMaxVectorTests::MINReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MIN_IDENTITY; + + Assert.assertEquals((double) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.min(id, x), x); + Assert.assertEquals((double) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static double MINReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.min(res, a[i]); @@ -2620,7 +2671,7 @@ relativeError)); } static double MINReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.POSITIVE_INFINITY; + double res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2634,20 +2685,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + double v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (double) Math.min(ra, v); } } @@ -2656,7 +2702,7 @@ relativeError)); } static double MAXReduce(double[] a, int idx) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (double) Math.max(res, a[i]); } @@ -2665,7 +2711,7 @@ relativeError)); } static double MAXReduceAll(double[] a) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduce(a, i)); } @@ -2677,20 +2723,15 @@ relativeError)); static void MAXReduceDoubleMaxVectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + double v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2698,8 +2739,31 @@ relativeError)); DoubleMaxVectorTests::MAXReduce, DoubleMaxVectorTests::MAXReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = MAX_IDENTITY; + + Assert.assertEquals((double) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((double) Math.max(id, x), x); + Assert.assertEquals((double) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((double) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((double) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (double) Math.max(res, a[i]); @@ -2709,7 +2773,7 @@ relativeError)); } static double MAXReduceAllMasked(double[] a, boolean[] mask) { - double res = Double.NEGATIVE_INFINITY; + double res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (double) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2723,20 +2787,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = (double) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + double v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (double) Math.max(ra, v); } } @@ -2745,7 +2804,7 @@ relativeError)); } static double FIRST_NONZEROReduce(double[] a, int idx) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2754,7 +2813,7 @@ relativeError)); } static double FIRST_NONZEROReduceAll(double[] a) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2766,20 +2825,15 @@ relativeError)); static void FIRST_NONZEROReduceDoubleMaxVectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2787,8 +2841,31 @@ relativeError)); DoubleMaxVectorTests::FIRST_NONZEROReduce, DoubleMaxVectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "doubleUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + double[] a = fa.apply(SPECIES.length()); + double id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + double x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2798,7 +2875,7 @@ relativeError)); } static double FIRST_NONZEROReduceAllMasked(double[] a, boolean[] mask) { - double res = (double) 0; + double res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2812,20 +2889,15 @@ relativeError)); double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + double v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Float128VectorTests.java b/test/jdk/jdk/incubator/vector/Float128VectorTests.java index 32b93293fb9..f97c5b0064c 100644 --- a/test/jdk/jdk/incubator/vector/Float128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float128VectorTests.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 @@ -62,6 +62,12 @@ public class Float128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final float ADD_IDENTITY = (float)0; + private static final float FIRST_NONZERO_IDENTITY = (float)0; + private static final float MAX_IDENTITY = Float.NEGATIVE_INFINITY; + private static final float MIN_IDENTITY = Float.POSITIVE_INFINITY; + private static final float MUL_IDENTITY = (float)1; // for floating point addition reduction ops that may introduce rounding errors private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; @@ -2395,7 +2401,7 @@ relativeError)); } static float ADDReduce(float[] a, int idx) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2404,7 +2410,7 @@ relativeError)); } static float ADDReduceAll(float[] a) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2419,17 +2425,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + float v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2437,8 +2438,31 @@ relativeError)); Float128VectorTests::ADDReduce, Float128VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "floatUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = ADD_IDENTITY; + + Assert.assertEquals((float) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id + x), x); + Assert.assertEquals((float) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2448,7 +2472,7 @@ relativeError)); } static float ADDReduceAllMasked(float[] a, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2465,17 +2489,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + float v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2484,7 +2503,7 @@ relativeError)); } static float MULReduce(float[] a, int idx) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2493,7 +2512,7 @@ relativeError)); } static float MULReduceAll(float[] a) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2505,20 +2524,15 @@ relativeError)); static void MULReduceFloat128VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + float v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2526,8 +2540,31 @@ relativeError)); Float128VectorTests::MULReduce, Float128VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MUL_IDENTITY; + + Assert.assertEquals((float) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id * x), x); + Assert.assertEquals((float) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static float MULReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2537,7 +2574,7 @@ relativeError)); } static float MULReduceAllMasked(float[] a, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2551,20 +2588,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + float v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2573,7 +2605,7 @@ relativeError)); } static float MINReduce(float[] a, int idx) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.min(res, a[i]); } @@ -2582,7 +2614,7 @@ relativeError)); } static float MINReduceAll(float[] a) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduce(a, i)); } @@ -2594,20 +2626,15 @@ relativeError)); static void MINReduceFloat128VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + float v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2615,8 +2642,31 @@ relativeError)); Float128VectorTests::MINReduce, Float128VectorTests::MINReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MIN_IDENTITY; + + Assert.assertEquals((float) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.min(id, x), x); + Assert.assertEquals((float) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static float MINReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.min(res, a[i]); @@ -2626,7 +2676,7 @@ relativeError)); } static float MINReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2640,20 +2690,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + float v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2662,7 +2707,7 @@ relativeError)); } static float MAXReduce(float[] a, int idx) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.max(res, a[i]); } @@ -2671,7 +2716,7 @@ relativeError)); } static float MAXReduceAll(float[] a) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduce(a, i)); } @@ -2683,20 +2728,15 @@ relativeError)); static void MAXReduceFloat128VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + float v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2704,8 +2744,31 @@ relativeError)); Float128VectorTests::MAXReduce, Float128VectorTests::MAXReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MAX_IDENTITY; + + Assert.assertEquals((float) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.max(id, x), x); + Assert.assertEquals((float) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.max(res, a[i]); @@ -2715,7 +2778,7 @@ relativeError)); } static float MAXReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2729,20 +2792,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + float v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2751,7 +2809,7 @@ relativeError)); } static float FIRST_NONZEROReduce(float[] a, int idx) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2760,7 +2818,7 @@ relativeError)); } static float FIRST_NONZEROReduceAll(float[] a) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2772,20 +2830,15 @@ relativeError)); static void FIRST_NONZEROReduceFloat128VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2793,8 +2846,31 @@ relativeError)); Float128VectorTests::FIRST_NONZEROReduce, Float128VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2804,7 +2880,7 @@ relativeError)); } static float FIRST_NONZEROReduceAllMasked(float[] a, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2818,20 +2894,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Float256VectorTests.java b/test/jdk/jdk/incubator/vector/Float256VectorTests.java index 9847865eacb..d9746d3291c 100644 --- a/test/jdk/jdk/incubator/vector/Float256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float256VectorTests.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 @@ -62,6 +62,12 @@ public class Float256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final float ADD_IDENTITY = (float)0; + private static final float FIRST_NONZERO_IDENTITY = (float)0; + private static final float MAX_IDENTITY = Float.NEGATIVE_INFINITY; + private static final float MIN_IDENTITY = Float.POSITIVE_INFINITY; + private static final float MUL_IDENTITY = (float)1; // for floating point addition reduction ops that may introduce rounding errors private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; @@ -2395,7 +2401,7 @@ relativeError)); } static float ADDReduce(float[] a, int idx) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2404,7 +2410,7 @@ relativeError)); } static float ADDReduceAll(float[] a) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2419,17 +2425,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + float v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2437,8 +2438,31 @@ relativeError)); Float256VectorTests::ADDReduce, Float256VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "floatUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = ADD_IDENTITY; + + Assert.assertEquals((float) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id + x), x); + Assert.assertEquals((float) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2448,7 +2472,7 @@ relativeError)); } static float ADDReduceAllMasked(float[] a, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2465,17 +2489,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + float v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2484,7 +2503,7 @@ relativeError)); } static float MULReduce(float[] a, int idx) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2493,7 +2512,7 @@ relativeError)); } static float MULReduceAll(float[] a) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2505,20 +2524,15 @@ relativeError)); static void MULReduceFloat256VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + float v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2526,8 +2540,31 @@ relativeError)); Float256VectorTests::MULReduce, Float256VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MUL_IDENTITY; + + Assert.assertEquals((float) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id * x), x); + Assert.assertEquals((float) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static float MULReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2537,7 +2574,7 @@ relativeError)); } static float MULReduceAllMasked(float[] a, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2551,20 +2588,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + float v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2573,7 +2605,7 @@ relativeError)); } static float MINReduce(float[] a, int idx) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.min(res, a[i]); } @@ -2582,7 +2614,7 @@ relativeError)); } static float MINReduceAll(float[] a) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduce(a, i)); } @@ -2594,20 +2626,15 @@ relativeError)); static void MINReduceFloat256VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + float v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2615,8 +2642,31 @@ relativeError)); Float256VectorTests::MINReduce, Float256VectorTests::MINReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MIN_IDENTITY; + + Assert.assertEquals((float) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.min(id, x), x); + Assert.assertEquals((float) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static float MINReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.min(res, a[i]); @@ -2626,7 +2676,7 @@ relativeError)); } static float MINReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2640,20 +2690,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + float v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2662,7 +2707,7 @@ relativeError)); } static float MAXReduce(float[] a, int idx) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.max(res, a[i]); } @@ -2671,7 +2716,7 @@ relativeError)); } static float MAXReduceAll(float[] a) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduce(a, i)); } @@ -2683,20 +2728,15 @@ relativeError)); static void MAXReduceFloat256VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + float v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2704,8 +2744,31 @@ relativeError)); Float256VectorTests::MAXReduce, Float256VectorTests::MAXReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MAX_IDENTITY; + + Assert.assertEquals((float) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.max(id, x), x); + Assert.assertEquals((float) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.max(res, a[i]); @@ -2715,7 +2778,7 @@ relativeError)); } static float MAXReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2729,20 +2792,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + float v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2751,7 +2809,7 @@ relativeError)); } static float FIRST_NONZEROReduce(float[] a, int idx) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2760,7 +2818,7 @@ relativeError)); } static float FIRST_NONZEROReduceAll(float[] a) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2772,20 +2830,15 @@ relativeError)); static void FIRST_NONZEROReduceFloat256VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2793,8 +2846,31 @@ relativeError)); Float256VectorTests::FIRST_NONZEROReduce, Float256VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2804,7 +2880,7 @@ relativeError)); } static float FIRST_NONZEROReduceAllMasked(float[] a, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2818,20 +2894,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Float512VectorTests.java b/test/jdk/jdk/incubator/vector/Float512VectorTests.java index 0daa5310ff0..ca395221c6b 100644 --- a/test/jdk/jdk/incubator/vector/Float512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float512VectorTests.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 @@ -62,6 +62,12 @@ public class Float512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final float ADD_IDENTITY = (float)0; + private static final float FIRST_NONZERO_IDENTITY = (float)0; + private static final float MAX_IDENTITY = Float.NEGATIVE_INFINITY; + private static final float MIN_IDENTITY = Float.POSITIVE_INFINITY; + private static final float MUL_IDENTITY = (float)1; // for floating point addition reduction ops that may introduce rounding errors private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; @@ -2395,7 +2401,7 @@ relativeError)); } static float ADDReduce(float[] a, int idx) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2404,7 +2410,7 @@ relativeError)); } static float ADDReduceAll(float[] a) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2419,17 +2425,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + float v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2437,8 +2438,31 @@ relativeError)); Float512VectorTests::ADDReduce, Float512VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "floatUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = ADD_IDENTITY; + + Assert.assertEquals((float) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id + x), x); + Assert.assertEquals((float) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2448,7 +2472,7 @@ relativeError)); } static float ADDReduceAllMasked(float[] a, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2465,17 +2489,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + float v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2484,7 +2503,7 @@ relativeError)); } static float MULReduce(float[] a, int idx) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2493,7 +2512,7 @@ relativeError)); } static float MULReduceAll(float[] a) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2505,20 +2524,15 @@ relativeError)); static void MULReduceFloat512VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + float v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2526,8 +2540,31 @@ relativeError)); Float512VectorTests::MULReduce, Float512VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MUL_IDENTITY; + + Assert.assertEquals((float) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id * x), x); + Assert.assertEquals((float) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static float MULReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2537,7 +2574,7 @@ relativeError)); } static float MULReduceAllMasked(float[] a, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2551,20 +2588,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + float v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2573,7 +2605,7 @@ relativeError)); } static float MINReduce(float[] a, int idx) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.min(res, a[i]); } @@ -2582,7 +2614,7 @@ relativeError)); } static float MINReduceAll(float[] a) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduce(a, i)); } @@ -2594,20 +2626,15 @@ relativeError)); static void MINReduceFloat512VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + float v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2615,8 +2642,31 @@ relativeError)); Float512VectorTests::MINReduce, Float512VectorTests::MINReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MIN_IDENTITY; + + Assert.assertEquals((float) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.min(id, x), x); + Assert.assertEquals((float) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static float MINReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.min(res, a[i]); @@ -2626,7 +2676,7 @@ relativeError)); } static float MINReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2640,20 +2690,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + float v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2662,7 +2707,7 @@ relativeError)); } static float MAXReduce(float[] a, int idx) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.max(res, a[i]); } @@ -2671,7 +2716,7 @@ relativeError)); } static float MAXReduceAll(float[] a) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduce(a, i)); } @@ -2683,20 +2728,15 @@ relativeError)); static void MAXReduceFloat512VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + float v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2704,8 +2744,31 @@ relativeError)); Float512VectorTests::MAXReduce, Float512VectorTests::MAXReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MAX_IDENTITY; + + Assert.assertEquals((float) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.max(id, x), x); + Assert.assertEquals((float) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.max(res, a[i]); @@ -2715,7 +2778,7 @@ relativeError)); } static float MAXReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2729,20 +2792,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + float v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2751,7 +2809,7 @@ relativeError)); } static float FIRST_NONZEROReduce(float[] a, int idx) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2760,7 +2818,7 @@ relativeError)); } static float FIRST_NONZEROReduceAll(float[] a) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2772,20 +2830,15 @@ relativeError)); static void FIRST_NONZEROReduceFloat512VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2793,8 +2846,31 @@ relativeError)); Float512VectorTests::FIRST_NONZEROReduce, Float512VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2804,7 +2880,7 @@ relativeError)); } static float FIRST_NONZEROReduceAllMasked(float[] a, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2818,20 +2894,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Float64VectorTests.java b/test/jdk/jdk/incubator/vector/Float64VectorTests.java index 921264fc4e2..1b14cdb7791 100644 --- a/test/jdk/jdk/incubator/vector/Float64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Float64VectorTests.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 @@ -62,6 +62,12 @@ public class Float64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); + // Identity values for reduction operations + private static final float ADD_IDENTITY = (float)0; + private static final float FIRST_NONZERO_IDENTITY = (float)0; + private static final float MAX_IDENTITY = Float.NEGATIVE_INFINITY; + private static final float MIN_IDENTITY = Float.POSITIVE_INFINITY; + private static final float MUL_IDENTITY = (float)1; // for floating point addition reduction ops that may introduce rounding errors private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; @@ -2395,7 +2401,7 @@ relativeError)); } static float ADDReduce(float[] a, int idx) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2404,7 +2410,7 @@ relativeError)); } static float ADDReduceAll(float[] a) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2419,17 +2425,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + float v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2437,8 +2438,31 @@ relativeError)); Float64VectorTests::ADDReduce, Float64VectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "floatUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = ADD_IDENTITY; + + Assert.assertEquals((float) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id + x), x); + Assert.assertEquals((float) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2448,7 +2472,7 @@ relativeError)); } static float ADDReduceAllMasked(float[] a, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2465,17 +2489,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + float v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2484,7 +2503,7 @@ relativeError)); } static float MULReduce(float[] a, int idx) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2493,7 +2512,7 @@ relativeError)); } static float MULReduceAll(float[] a) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2505,20 +2524,15 @@ relativeError)); static void MULReduceFloat64VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + float v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2526,8 +2540,31 @@ relativeError)); Float64VectorTests::MULReduce, Float64VectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MUL_IDENTITY; + + Assert.assertEquals((float) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id * x), x); + Assert.assertEquals((float) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static float MULReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2537,7 +2574,7 @@ relativeError)); } static float MULReduceAllMasked(float[] a, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2551,20 +2588,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + float v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2573,7 +2605,7 @@ relativeError)); } static float MINReduce(float[] a, int idx) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.min(res, a[i]); } @@ -2582,7 +2614,7 @@ relativeError)); } static float MINReduceAll(float[] a) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduce(a, i)); } @@ -2594,20 +2626,15 @@ relativeError)); static void MINReduceFloat64VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + float v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2615,8 +2642,31 @@ relativeError)); Float64VectorTests::MINReduce, Float64VectorTests::MINReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MIN_IDENTITY; + + Assert.assertEquals((float) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.min(id, x), x); + Assert.assertEquals((float) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static float MINReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.min(res, a[i]); @@ -2626,7 +2676,7 @@ relativeError)); } static float MINReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2640,20 +2690,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + float v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2662,7 +2707,7 @@ relativeError)); } static float MAXReduce(float[] a, int idx) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.max(res, a[i]); } @@ -2671,7 +2716,7 @@ relativeError)); } static float MAXReduceAll(float[] a) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduce(a, i)); } @@ -2683,20 +2728,15 @@ relativeError)); static void MAXReduceFloat64VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + float v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2704,8 +2744,31 @@ relativeError)); Float64VectorTests::MAXReduce, Float64VectorTests::MAXReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MAX_IDENTITY; + + Assert.assertEquals((float) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.max(id, x), x); + Assert.assertEquals((float) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.max(res, a[i]); @@ -2715,7 +2778,7 @@ relativeError)); } static float MAXReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2729,20 +2792,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + float v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2751,7 +2809,7 @@ relativeError)); } static float FIRST_NONZEROReduce(float[] a, int idx) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2760,7 +2818,7 @@ relativeError)); } static float FIRST_NONZEROReduceAll(float[] a) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2772,20 +2830,15 @@ relativeError)); static void FIRST_NONZEROReduceFloat64VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2793,8 +2846,31 @@ relativeError)); Float64VectorTests::FIRST_NONZEROReduce, Float64VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2804,7 +2880,7 @@ relativeError)); } static float FIRST_NONZEROReduceAllMasked(float[] a, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2818,20 +2894,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java index 07c9b7d0ef9..53edb408035 100644 --- a/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/FloatMaxVectorTests.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 @@ -68,6 +68,13 @@ public class FloatMaxVectorTests extends AbstractVectorTest { private static final int Max = 256; // juts so we can do N/Max + // Identity values for reduction operations + private static final float ADD_IDENTITY = (float)0; + private static final float FIRST_NONZERO_IDENTITY = (float)0; + private static final float MAX_IDENTITY = Float.NEGATIVE_INFINITY; + private static final float MIN_IDENTITY = Float.POSITIVE_INFINITY; + private static final float MUL_IDENTITY = (float)1; + // for floating point addition reduction ops that may introduce rounding errors private static final float RELATIVE_ROUNDING_ERROR_FACTOR_ADD = (float)10.0; @@ -2400,7 +2407,7 @@ relativeError)); } static float ADDReduce(float[] a, int idx) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -2409,7 +2416,7 @@ relativeError)); } static float ADDReduceAll(float[] a) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -2424,17 +2431,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + float v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -2442,8 +2444,31 @@ relativeError)); FloatMaxVectorTests::ADDReduce, FloatMaxVectorTests::ADDReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_ADD); } + @Test(dataProvider = "floatUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = ADD_IDENTITY; + + Assert.assertEquals((float) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id + x), x); + Assert.assertEquals((float) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -2453,7 +2478,7 @@ relativeError)); } static float ADDReduceAllMasked(float[] a, boolean[] mask) { - float res = 0; + float res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -2470,17 +2495,12 @@ relativeError)); float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + float v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -2489,7 +2509,7 @@ relativeError)); } static float MULReduce(float[] a, int idx) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -2498,7 +2518,7 @@ relativeError)); } static float MULReduceAll(float[] a) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -2510,20 +2530,15 @@ relativeError)); static void MULReduceFloatMaxVectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + float v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -2531,8 +2546,31 @@ relativeError)); FloatMaxVectorTests::MULReduce, FloatMaxVectorTests::MULReduceAll, RELATIVE_ROUNDING_ERROR_FACTOR_MUL); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MUL_IDENTITY; + + Assert.assertEquals((float) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) (id * x), x); + Assert.assertEquals((float) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static float MULReduceMasked(float[] a, int idx, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -2542,7 +2580,7 @@ relativeError)); } static float MULReduceAllMasked(float[] a, boolean[] mask) { - float res = 1; + float res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -2556,20 +2594,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + float v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -2578,7 +2611,7 @@ relativeError)); } static float MINReduce(float[] a, int idx) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.min(res, a[i]); } @@ -2587,7 +2620,7 @@ relativeError)); } static float MINReduceAll(float[] a) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduce(a, i)); } @@ -2599,20 +2632,15 @@ relativeError)); static void MINReduceFloatMaxVectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + float v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2620,8 +2648,31 @@ relativeError)); FloatMaxVectorTests::MINReduce, FloatMaxVectorTests::MINReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MIN_IDENTITY; + + Assert.assertEquals((float) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.min(id, x), x); + Assert.assertEquals((float) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static float MINReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.min(res, a[i]); @@ -2631,7 +2682,7 @@ relativeError)); } static float MINReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.POSITIVE_INFINITY; + float res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -2645,20 +2696,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + float v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (float) Math.min(ra, v); } } @@ -2667,7 +2713,7 @@ relativeError)); } static float MAXReduce(float[] a, int idx) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (float) Math.max(res, a[i]); } @@ -2676,7 +2722,7 @@ relativeError)); } static float MAXReduceAll(float[] a) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduce(a, i)); } @@ -2688,20 +2734,15 @@ relativeError)); static void MAXReduceFloatMaxVectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + float v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2709,8 +2750,31 @@ relativeError)); FloatMaxVectorTests::MAXReduce, FloatMaxVectorTests::MAXReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = MAX_IDENTITY; + + Assert.assertEquals((float) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((float) Math.max(id, x), x); + Assert.assertEquals((float) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((float) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((float) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (float) Math.max(res, a[i]); @@ -2720,7 +2784,7 @@ relativeError)); } static float MAXReduceAllMasked(float[] a, boolean[] mask) { - float res = Float.NEGATIVE_INFINITY; + float res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (float) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -2734,20 +2798,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = (float) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + float v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (float) Math.max(ra, v); } } @@ -2756,7 +2815,7 @@ relativeError)); } static float FIRST_NONZEROReduce(float[] a, int idx) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -2765,7 +2824,7 @@ relativeError)); } static float FIRST_NONZEROReduceAll(float[] a) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -2777,20 +2836,15 @@ relativeError)); static void FIRST_NONZEROReduceFloatMaxVectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -2798,8 +2852,31 @@ relativeError)); FloatMaxVectorTests::FIRST_NONZEROReduce, FloatMaxVectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "floatUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + float[] a = fa.apply(SPECIES.length()); + float id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + float x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -2809,7 +2886,7 @@ relativeError)); } static float FIRST_NONZEROReduceAllMasked(float[] a, boolean[] mask) { - float res = (float) 0; + float res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -2823,20 +2900,15 @@ relativeError)); float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + float v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Int128VectorTests.java b/test/jdk/jdk/incubator/vector/Int128VectorTests.java index 93912243ecd..69bb0c84588 100644 --- a/test/jdk/jdk/incubator/vector/Int128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int128VectorTests.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 @@ -63,9 +63,21 @@ public class Int128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final int CONST_SHIFT = Integer.SIZE / 2; + // Identity values for reduction operations + private static final int ADD_IDENTITY = (int)0; + private static final int AND_IDENTITY = (int)-1; + private static final int FIRST_NONZERO_IDENTITY = (int)0; + private static final int MAX_IDENTITY = Integer.MIN_VALUE; + private static final int MIN_IDENTITY = Integer.MAX_VALUE; + private static final int MUL_IDENTITY = (int)1; + private static final int OR_IDENTITY = (int)0; + private static final int SUADD_IDENTITY = (int)0; + private static final int UMAX_IDENTITY = (int)0; // Minimum unsigned value + private static final int UMIN_IDENTITY = (int)-1; // Maximum unsigned value + private static final int XOR_IDENTITY = (int)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); static void assertArraysStrictlyEquals(int[] r, int[] a) { @@ -3655,7 +3667,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ANDReduce(int[] a, int idx) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3664,7 +3676,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ANDReduceAll(int[] a) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3676,20 +3688,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void ANDReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + int v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3697,8 +3704,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::ANDReduce, Int128VectorTests::ANDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = AND_IDENTITY; + + Assert.assertEquals((int) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id & x), x); + Assert.assertEquals((int) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3708,7 +3738,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ANDReduceAllMasked(int[] a, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3722,20 +3752,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + int v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3744,7 +3769,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ORReduce(int[] a, int idx) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3753,7 +3778,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ORReduceAll(int[] a) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3768,17 +3793,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + int v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3786,8 +3806,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::ORReduce, Int128VectorTests::ORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = OR_IDENTITY; + + Assert.assertEquals((int) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id | x), x); + Assert.assertEquals((int) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static int ORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3797,7 +3840,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3814,17 +3857,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + int v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3833,7 +3871,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int XORReduce(int[] a, int idx) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3842,7 +3880,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int XORReduceAll(int[] a) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3857,17 +3895,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + int v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3875,8 +3908,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::XORReduce, Int128VectorTests::XORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = XOR_IDENTITY; + + Assert.assertEquals((int) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id ^ x), x); + Assert.assertEquals((int) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static int XORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3886,7 +3942,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int XORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3903,17 +3959,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + int v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3922,7 +3973,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ADDReduce(int[] a, int idx) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3931,7 +3982,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ADDReduceAll(int[] a) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3946,17 +3997,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + int v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3964,8 +4010,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::ADDReduce, Int128VectorTests::ADDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = ADD_IDENTITY; + + Assert.assertEquals((int) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id + x), x); + Assert.assertEquals((int) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3975,7 +4044,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int ADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3992,17 +4061,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + int v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4011,7 +4075,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MULReduce(int[] a, int idx) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4020,7 +4084,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MULReduceAll(int[] a) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4032,20 +4096,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void MULReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + int v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4053,8 +4112,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::MULReduce, Int128VectorTests::MULReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MUL_IDENTITY; + + Assert.assertEquals((int) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id * x), x); + Assert.assertEquals((int) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static int MULReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4064,7 +4146,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MULReduceAllMasked(int[] a, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4078,20 +4160,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + int v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4100,7 +4177,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.min(res, a[i]); } @@ -4109,7 +4186,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduce(a, i)); } @@ -4121,20 +4198,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void MINReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + int v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4142,8 +4214,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::MINReduce, Int128VectorTests::MINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MIN_IDENTITY; + + Assert.assertEquals((int) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.min(id, x), x); + Assert.assertEquals((int) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static int MINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.min(res, a[i]); @@ -4153,7 +4248,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4167,20 +4262,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + int v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4189,7 +4279,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.max(res, a[i]); } @@ -4198,7 +4288,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduce(a, i)); } @@ -4210,20 +4300,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void MAXReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + int v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4231,8 +4316,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::MAXReduce, Int128VectorTests::MAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MAX_IDENTITY; + + Assert.assertEquals((int) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.max(id, x), x); + Assert.assertEquals((int) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.max(res, a[i]); @@ -4242,7 +4350,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int MAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4256,20 +4364,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + int v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4278,7 +4381,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int UMINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.minUnsigned(res, a[i]); } @@ -4287,7 +4390,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int UMINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4299,20 +4402,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void UMINReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + int v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4320,8 +4418,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::UMINReduce, Int128VectorTests::UMINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMIN_IDENTITY; + + Assert.assertEquals((int) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.minUnsigned(res, a[i]); @@ -4331,7 +4452,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int UMINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4345,20 +4466,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + int v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4367,7 +4483,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int UMAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.maxUnsigned(res, a[i]); } @@ -4376,7 +4492,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int UMAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4388,20 +4504,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void UMAXReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + int v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4409,8 +4520,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::UMAXReduce, Int128VectorTests::UMAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMAX_IDENTITY; + + Assert.assertEquals((int) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.maxUnsigned(res, a[i]); @@ -4420,7 +4554,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int UMAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4434,20 +4568,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + int v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4456,7 +4585,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduce(int[] a, int idx) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4465,7 +4594,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAll(int[] a) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4477,20 +4606,15 @@ public class Int128VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4498,8 +4622,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::FIRST_NONZEROReduce, Int128VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4509,7 +4656,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAllMasked(int[] a, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4523,20 +4670,15 @@ public class Int128VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4593,7 +4735,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int SUADDReduce(int[] a, int idx) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4602,7 +4744,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int SUADDReduceAll(int[] a) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4617,17 +4759,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + int v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4635,8 +4772,31 @@ public class Int128VectorTests extends AbstractVectorTest { Int128VectorTests::SUADDReduce, Int128VectorTests::SUADDReduceAll); } + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = SUADD_IDENTITY; + + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4646,7 +4806,7 @@ public class Int128VectorTests extends AbstractVectorTest { } static int SUADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4662,17 +4822,12 @@ public class Int128VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + int v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Int256VectorTests.java b/test/jdk/jdk/incubator/vector/Int256VectorTests.java index e8d6f3311ac..4e63de95b7b 100644 --- a/test/jdk/jdk/incubator/vector/Int256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int256VectorTests.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 @@ -63,9 +63,21 @@ public class Int256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final int CONST_SHIFT = Integer.SIZE / 2; + // Identity values for reduction operations + private static final int ADD_IDENTITY = (int)0; + private static final int AND_IDENTITY = (int)-1; + private static final int FIRST_NONZERO_IDENTITY = (int)0; + private static final int MAX_IDENTITY = Integer.MIN_VALUE; + private static final int MIN_IDENTITY = Integer.MAX_VALUE; + private static final int MUL_IDENTITY = (int)1; + private static final int OR_IDENTITY = (int)0; + private static final int SUADD_IDENTITY = (int)0; + private static final int UMAX_IDENTITY = (int)0; // Minimum unsigned value + private static final int UMIN_IDENTITY = (int)-1; // Maximum unsigned value + private static final int XOR_IDENTITY = (int)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); static void assertArraysStrictlyEquals(int[] r, int[] a) { @@ -3655,7 +3667,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ANDReduce(int[] a, int idx) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3664,7 +3676,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ANDReduceAll(int[] a) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3676,20 +3688,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void ANDReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + int v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3697,8 +3704,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::ANDReduce, Int256VectorTests::ANDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = AND_IDENTITY; + + Assert.assertEquals((int) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id & x), x); + Assert.assertEquals((int) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3708,7 +3738,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ANDReduceAllMasked(int[] a, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3722,20 +3752,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + int v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3744,7 +3769,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ORReduce(int[] a, int idx) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3753,7 +3778,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ORReduceAll(int[] a) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3768,17 +3793,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + int v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3786,8 +3806,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::ORReduce, Int256VectorTests::ORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = OR_IDENTITY; + + Assert.assertEquals((int) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id | x), x); + Assert.assertEquals((int) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static int ORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3797,7 +3840,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3814,17 +3857,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + int v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3833,7 +3871,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int XORReduce(int[] a, int idx) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3842,7 +3880,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int XORReduceAll(int[] a) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3857,17 +3895,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + int v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3875,8 +3908,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::XORReduce, Int256VectorTests::XORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = XOR_IDENTITY; + + Assert.assertEquals((int) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id ^ x), x); + Assert.assertEquals((int) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static int XORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3886,7 +3942,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int XORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3903,17 +3959,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + int v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3922,7 +3973,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ADDReduce(int[] a, int idx) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3931,7 +3982,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ADDReduceAll(int[] a) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3946,17 +3997,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + int v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3964,8 +4010,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::ADDReduce, Int256VectorTests::ADDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = ADD_IDENTITY; + + Assert.assertEquals((int) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id + x), x); + Assert.assertEquals((int) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3975,7 +4044,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int ADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3992,17 +4061,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + int v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4011,7 +4075,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MULReduce(int[] a, int idx) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4020,7 +4084,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MULReduceAll(int[] a) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4032,20 +4096,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void MULReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + int v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4053,8 +4112,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::MULReduce, Int256VectorTests::MULReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MUL_IDENTITY; + + Assert.assertEquals((int) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id * x), x); + Assert.assertEquals((int) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static int MULReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4064,7 +4146,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MULReduceAllMasked(int[] a, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4078,20 +4160,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + int v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4100,7 +4177,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.min(res, a[i]); } @@ -4109,7 +4186,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduce(a, i)); } @@ -4121,20 +4198,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void MINReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + int v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4142,8 +4214,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::MINReduce, Int256VectorTests::MINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MIN_IDENTITY; + + Assert.assertEquals((int) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.min(id, x), x); + Assert.assertEquals((int) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static int MINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.min(res, a[i]); @@ -4153,7 +4248,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4167,20 +4262,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + int v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4189,7 +4279,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.max(res, a[i]); } @@ -4198,7 +4288,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduce(a, i)); } @@ -4210,20 +4300,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void MAXReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + int v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4231,8 +4316,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::MAXReduce, Int256VectorTests::MAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MAX_IDENTITY; + + Assert.assertEquals((int) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.max(id, x), x); + Assert.assertEquals((int) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.max(res, a[i]); @@ -4242,7 +4350,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int MAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4256,20 +4364,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + int v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4278,7 +4381,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int UMINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.minUnsigned(res, a[i]); } @@ -4287,7 +4390,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int UMINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4299,20 +4402,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void UMINReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + int v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4320,8 +4418,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::UMINReduce, Int256VectorTests::UMINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMIN_IDENTITY; + + Assert.assertEquals((int) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.minUnsigned(res, a[i]); @@ -4331,7 +4452,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int UMINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4345,20 +4466,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + int v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4367,7 +4483,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int UMAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.maxUnsigned(res, a[i]); } @@ -4376,7 +4492,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int UMAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4388,20 +4504,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void UMAXReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + int v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4409,8 +4520,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::UMAXReduce, Int256VectorTests::UMAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMAX_IDENTITY; + + Assert.assertEquals((int) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.maxUnsigned(res, a[i]); @@ -4420,7 +4554,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int UMAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4434,20 +4568,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + int v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4456,7 +4585,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduce(int[] a, int idx) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4465,7 +4594,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAll(int[] a) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4477,20 +4606,15 @@ public class Int256VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4498,8 +4622,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::FIRST_NONZEROReduce, Int256VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4509,7 +4656,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAllMasked(int[] a, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4523,20 +4670,15 @@ public class Int256VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4593,7 +4735,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int SUADDReduce(int[] a, int idx) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4602,7 +4744,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int SUADDReduceAll(int[] a) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4617,17 +4759,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + int v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4635,8 +4772,31 @@ public class Int256VectorTests extends AbstractVectorTest { Int256VectorTests::SUADDReduce, Int256VectorTests::SUADDReduceAll); } + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = SUADD_IDENTITY; + + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4646,7 +4806,7 @@ public class Int256VectorTests extends AbstractVectorTest { } static int SUADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4662,17 +4822,12 @@ public class Int256VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + int v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Int512VectorTests.java b/test/jdk/jdk/incubator/vector/Int512VectorTests.java index 932d9ee4487..e2de7905a83 100644 --- a/test/jdk/jdk/incubator/vector/Int512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int512VectorTests.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 @@ -63,9 +63,21 @@ public class Int512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final int CONST_SHIFT = Integer.SIZE / 2; + // Identity values for reduction operations + private static final int ADD_IDENTITY = (int)0; + private static final int AND_IDENTITY = (int)-1; + private static final int FIRST_NONZERO_IDENTITY = (int)0; + private static final int MAX_IDENTITY = Integer.MIN_VALUE; + private static final int MIN_IDENTITY = Integer.MAX_VALUE; + private static final int MUL_IDENTITY = (int)1; + private static final int OR_IDENTITY = (int)0; + private static final int SUADD_IDENTITY = (int)0; + private static final int UMAX_IDENTITY = (int)0; // Minimum unsigned value + private static final int UMIN_IDENTITY = (int)-1; // Maximum unsigned value + private static final int XOR_IDENTITY = (int)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); static void assertArraysStrictlyEquals(int[] r, int[] a) { @@ -3655,7 +3667,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ANDReduce(int[] a, int idx) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3664,7 +3676,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ANDReduceAll(int[] a) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3676,20 +3688,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void ANDReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + int v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3697,8 +3704,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::ANDReduce, Int512VectorTests::ANDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = AND_IDENTITY; + + Assert.assertEquals((int) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id & x), x); + Assert.assertEquals((int) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3708,7 +3738,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ANDReduceAllMasked(int[] a, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3722,20 +3752,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + int v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3744,7 +3769,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ORReduce(int[] a, int idx) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3753,7 +3778,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ORReduceAll(int[] a) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3768,17 +3793,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + int v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3786,8 +3806,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::ORReduce, Int512VectorTests::ORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = OR_IDENTITY; + + Assert.assertEquals((int) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id | x), x); + Assert.assertEquals((int) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static int ORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3797,7 +3840,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3814,17 +3857,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + int v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3833,7 +3871,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int XORReduce(int[] a, int idx) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3842,7 +3880,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int XORReduceAll(int[] a) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3857,17 +3895,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + int v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3875,8 +3908,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::XORReduce, Int512VectorTests::XORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = XOR_IDENTITY; + + Assert.assertEquals((int) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id ^ x), x); + Assert.assertEquals((int) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static int XORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3886,7 +3942,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int XORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3903,17 +3959,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + int v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3922,7 +3973,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ADDReduce(int[] a, int idx) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3931,7 +3982,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ADDReduceAll(int[] a) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3946,17 +3997,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + int v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3964,8 +4010,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::ADDReduce, Int512VectorTests::ADDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = ADD_IDENTITY; + + Assert.assertEquals((int) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id + x), x); + Assert.assertEquals((int) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3975,7 +4044,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int ADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3992,17 +4061,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + int v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4011,7 +4075,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MULReduce(int[] a, int idx) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4020,7 +4084,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MULReduceAll(int[] a) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4032,20 +4096,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void MULReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + int v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4053,8 +4112,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::MULReduce, Int512VectorTests::MULReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MUL_IDENTITY; + + Assert.assertEquals((int) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id * x), x); + Assert.assertEquals((int) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static int MULReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4064,7 +4146,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MULReduceAllMasked(int[] a, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4078,20 +4160,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + int v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4100,7 +4177,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.min(res, a[i]); } @@ -4109,7 +4186,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduce(a, i)); } @@ -4121,20 +4198,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void MINReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + int v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4142,8 +4214,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::MINReduce, Int512VectorTests::MINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MIN_IDENTITY; + + Assert.assertEquals((int) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.min(id, x), x); + Assert.assertEquals((int) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static int MINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.min(res, a[i]); @@ -4153,7 +4248,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4167,20 +4262,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + int v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4189,7 +4279,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.max(res, a[i]); } @@ -4198,7 +4288,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduce(a, i)); } @@ -4210,20 +4300,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void MAXReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + int v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4231,8 +4316,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::MAXReduce, Int512VectorTests::MAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MAX_IDENTITY; + + Assert.assertEquals((int) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.max(id, x), x); + Assert.assertEquals((int) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.max(res, a[i]); @@ -4242,7 +4350,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int MAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4256,20 +4364,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + int v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4278,7 +4381,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int UMINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.minUnsigned(res, a[i]); } @@ -4287,7 +4390,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int UMINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4299,20 +4402,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void UMINReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + int v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4320,8 +4418,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::UMINReduce, Int512VectorTests::UMINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMIN_IDENTITY; + + Assert.assertEquals((int) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.minUnsigned(res, a[i]); @@ -4331,7 +4452,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int UMINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4345,20 +4466,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + int v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4367,7 +4483,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int UMAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.maxUnsigned(res, a[i]); } @@ -4376,7 +4492,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int UMAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4388,20 +4504,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void UMAXReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + int v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4409,8 +4520,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::UMAXReduce, Int512VectorTests::UMAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMAX_IDENTITY; + + Assert.assertEquals((int) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.maxUnsigned(res, a[i]); @@ -4420,7 +4554,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int UMAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4434,20 +4568,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + int v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4456,7 +4585,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduce(int[] a, int idx) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4465,7 +4594,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAll(int[] a) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4477,20 +4606,15 @@ public class Int512VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4498,8 +4622,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::FIRST_NONZEROReduce, Int512VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4509,7 +4656,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAllMasked(int[] a, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4523,20 +4670,15 @@ public class Int512VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4593,7 +4735,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int SUADDReduce(int[] a, int idx) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4602,7 +4744,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int SUADDReduceAll(int[] a) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4617,17 +4759,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + int v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4635,8 +4772,31 @@ public class Int512VectorTests extends AbstractVectorTest { Int512VectorTests::SUADDReduce, Int512VectorTests::SUADDReduceAll); } + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = SUADD_IDENTITY; + + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4646,7 +4806,7 @@ public class Int512VectorTests extends AbstractVectorTest { } static int SUADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4662,17 +4822,12 @@ public class Int512VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + int v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Int64VectorTests.java b/test/jdk/jdk/incubator/vector/Int64VectorTests.java index 3c00904a70e..d64db80b94d 100644 --- a/test/jdk/jdk/incubator/vector/Int64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Int64VectorTests.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 @@ -63,9 +63,21 @@ public class Int64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final int CONST_SHIFT = Integer.SIZE / 2; + // Identity values for reduction operations + private static final int ADD_IDENTITY = (int)0; + private static final int AND_IDENTITY = (int)-1; + private static final int FIRST_NONZERO_IDENTITY = (int)0; + private static final int MAX_IDENTITY = Integer.MIN_VALUE; + private static final int MIN_IDENTITY = Integer.MAX_VALUE; + private static final int MUL_IDENTITY = (int)1; + private static final int OR_IDENTITY = (int)0; + private static final int SUADD_IDENTITY = (int)0; + private static final int UMAX_IDENTITY = (int)0; // Minimum unsigned value + private static final int UMIN_IDENTITY = (int)-1; // Maximum unsigned value + private static final int XOR_IDENTITY = (int)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); static void assertArraysStrictlyEquals(int[] r, int[] a) { @@ -3655,7 +3667,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ANDReduce(int[] a, int idx) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3664,7 +3676,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ANDReduceAll(int[] a) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3676,20 +3688,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void ANDReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + int v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3697,8 +3704,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::ANDReduce, Int64VectorTests::ANDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = AND_IDENTITY; + + Assert.assertEquals((int) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id & x), x); + Assert.assertEquals((int) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3708,7 +3738,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ANDReduceAllMasked(int[] a, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3722,20 +3752,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + int v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3744,7 +3769,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ORReduce(int[] a, int idx) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3753,7 +3778,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ORReduceAll(int[] a) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3768,17 +3793,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + int v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3786,8 +3806,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::ORReduce, Int64VectorTests::ORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = OR_IDENTITY; + + Assert.assertEquals((int) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id | x), x); + Assert.assertEquals((int) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static int ORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3797,7 +3840,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3814,17 +3857,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + int v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3833,7 +3871,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int XORReduce(int[] a, int idx) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3842,7 +3880,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int XORReduceAll(int[] a) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3857,17 +3895,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + int v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3875,8 +3908,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::XORReduce, Int64VectorTests::XORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = XOR_IDENTITY; + + Assert.assertEquals((int) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id ^ x), x); + Assert.assertEquals((int) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static int XORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3886,7 +3942,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int XORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3903,17 +3959,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + int v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3922,7 +3973,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ADDReduce(int[] a, int idx) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3931,7 +3982,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ADDReduceAll(int[] a) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3946,17 +3997,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + int v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3964,8 +4010,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::ADDReduce, Int64VectorTests::ADDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = ADD_IDENTITY; + + Assert.assertEquals((int) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id + x), x); + Assert.assertEquals((int) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3975,7 +4044,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int ADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3992,17 +4061,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + int v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4011,7 +4075,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MULReduce(int[] a, int idx) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4020,7 +4084,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MULReduceAll(int[] a) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4032,20 +4096,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void MULReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + int v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4053,8 +4112,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::MULReduce, Int64VectorTests::MULReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MUL_IDENTITY; + + Assert.assertEquals((int) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id * x), x); + Assert.assertEquals((int) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static int MULReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4064,7 +4146,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MULReduceAllMasked(int[] a, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4078,20 +4160,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + int v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4100,7 +4177,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.min(res, a[i]); } @@ -4109,7 +4186,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduce(a, i)); } @@ -4121,20 +4198,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void MINReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + int v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4142,8 +4214,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::MINReduce, Int64VectorTests::MINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MIN_IDENTITY; + + Assert.assertEquals((int) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.min(id, x), x); + Assert.assertEquals((int) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static int MINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.min(res, a[i]); @@ -4153,7 +4248,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4167,20 +4262,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + int v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4189,7 +4279,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.max(res, a[i]); } @@ -4198,7 +4288,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduce(a, i)); } @@ -4210,20 +4300,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void MAXReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + int v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4231,8 +4316,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::MAXReduce, Int64VectorTests::MAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MAX_IDENTITY; + + Assert.assertEquals((int) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.max(id, x), x); + Assert.assertEquals((int) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.max(res, a[i]); @@ -4242,7 +4350,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int MAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4256,20 +4364,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + int v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4278,7 +4381,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int UMINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.minUnsigned(res, a[i]); } @@ -4287,7 +4390,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int UMINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4299,20 +4402,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void UMINReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + int v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4320,8 +4418,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::UMINReduce, Int64VectorTests::UMINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMIN_IDENTITY; + + Assert.assertEquals((int) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.minUnsigned(res, a[i]); @@ -4331,7 +4452,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int UMINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4345,20 +4466,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + int v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4367,7 +4483,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int UMAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.maxUnsigned(res, a[i]); } @@ -4376,7 +4492,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int UMAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4388,20 +4504,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void UMAXReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + int v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4409,8 +4520,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::UMAXReduce, Int64VectorTests::UMAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMAX_IDENTITY; + + Assert.assertEquals((int) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.maxUnsigned(res, a[i]); @@ -4420,7 +4554,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int UMAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4434,20 +4568,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + int v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4456,7 +4585,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduce(int[] a, int idx) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4465,7 +4594,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAll(int[] a) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4477,20 +4606,15 @@ public class Int64VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4498,8 +4622,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::FIRST_NONZEROReduce, Int64VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4509,7 +4656,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAllMasked(int[] a, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4523,20 +4670,15 @@ public class Int64VectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4593,7 +4735,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int SUADDReduce(int[] a, int idx) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4602,7 +4744,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int SUADDReduceAll(int[] a) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4617,17 +4759,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + int v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4635,8 +4772,31 @@ public class Int64VectorTests extends AbstractVectorTest { Int64VectorTests::SUADDReduce, Int64VectorTests::SUADDReduceAll); } + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = SUADD_IDENTITY; + + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4646,7 +4806,7 @@ public class Int64VectorTests extends AbstractVectorTest { } static int SUADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4662,17 +4822,12 @@ public class Int64VectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + int v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java b/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java index 42659bfe44e..7bf4dc48171 100644 --- a/test/jdk/jdk/incubator/vector/IntMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/IntMaxVectorTests.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 @@ -71,6 +71,19 @@ public class IntMaxVectorTests extends AbstractVectorTest { private static final int CONST_SHIFT = Integer.SIZE / 2; + // Identity values for reduction operations + private static final int ADD_IDENTITY = (int)0; + private static final int AND_IDENTITY = (int)-1; + private static final int FIRST_NONZERO_IDENTITY = (int)0; + private static final int MAX_IDENTITY = Integer.MIN_VALUE; + private static final int MIN_IDENTITY = Integer.MAX_VALUE; + private static final int MUL_IDENTITY = (int)1; + private static final int OR_IDENTITY = (int)0; + private static final int SUADD_IDENTITY = (int)0; + private static final int UMAX_IDENTITY = (int)0; // Minimum unsigned value + private static final int UMIN_IDENTITY = (int)-1; // Maximum unsigned value + private static final int XOR_IDENTITY = (int)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); static void assertArraysStrictlyEquals(int[] r, int[] a) { @@ -3660,7 +3673,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ANDReduce(int[] a, int idx) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3669,7 +3682,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ANDReduceAll(int[] a) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3681,20 +3694,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void ANDReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + int v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3702,8 +3710,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::ANDReduce, IntMaxVectorTests::ANDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = AND_IDENTITY; + + Assert.assertEquals((int) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id & x), x); + Assert.assertEquals((int) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3713,7 +3744,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ANDReduceAllMasked(int[] a, boolean[] mask) { - int res = -1; + int res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3727,20 +3758,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + int v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3749,7 +3775,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ORReduce(int[] a, int idx) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3758,7 +3784,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ORReduceAll(int[] a) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3773,17 +3799,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + int v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3791,8 +3812,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::ORReduce, IntMaxVectorTests::ORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = OR_IDENTITY; + + Assert.assertEquals((int) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id | x), x); + Assert.assertEquals((int) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static int ORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3802,7 +3846,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3819,17 +3863,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + int v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3838,7 +3877,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int XORReduce(int[] a, int idx) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3847,7 +3886,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int XORReduceAll(int[] a) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3862,17 +3901,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + int v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3880,8 +3914,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::XORReduce, IntMaxVectorTests::XORReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = XOR_IDENTITY; + + Assert.assertEquals((int) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id ^ x), x); + Assert.assertEquals((int) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static int XORReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3891,7 +3948,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int XORReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3908,17 +3965,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + int v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3927,7 +3979,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ADDReduce(int[] a, int idx) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3936,7 +3988,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ADDReduceAll(int[] a) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3951,17 +4003,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + int v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3969,8 +4016,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::ADDReduce, IntMaxVectorTests::ADDReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = ADD_IDENTITY; + + Assert.assertEquals((int) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id + x), x); + Assert.assertEquals((int) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3980,7 +4050,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int ADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3997,17 +4067,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + int v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4016,7 +4081,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MULReduce(int[] a, int idx) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4025,7 +4090,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MULReduceAll(int[] a) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4037,20 +4102,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void MULReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + int v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4058,8 +4118,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::MULReduce, IntMaxVectorTests::MULReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MUL_IDENTITY; + + Assert.assertEquals((int) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) (id * x), x); + Assert.assertEquals((int) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static int MULReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4069,7 +4152,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MULReduceAllMasked(int[] a, boolean[] mask) { - int res = 1; + int res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4083,20 +4166,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + int v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4105,7 +4183,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.min(res, a[i]); } @@ -4114,7 +4192,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduce(a, i)); } @@ -4126,20 +4204,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void MINReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + int v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4147,8 +4220,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::MINReduce, IntMaxVectorTests::MINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MIN_IDENTITY; + + Assert.assertEquals((int) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.min(id, x), x); + Assert.assertEquals((int) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static int MINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.min(res, a[i]); @@ -4158,7 +4254,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4172,20 +4268,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + int v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (int) Math.min(ra, v); } } @@ -4194,7 +4285,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) Math.max(res, a[i]); } @@ -4203,7 +4294,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduce(a, i)); } @@ -4215,20 +4306,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void MAXReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + int v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4236,8 +4322,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::MAXReduce, IntMaxVectorTests::MAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = MAX_IDENTITY; + + Assert.assertEquals((int) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) Math.max(id, x), x); + Assert.assertEquals((int) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) Math.max(res, a[i]); @@ -4247,7 +4356,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int MAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4261,20 +4370,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + int v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (int) Math.max(ra, v); } } @@ -4283,7 +4387,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int UMINReduce(int[] a, int idx) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.minUnsigned(res, a[i]); } @@ -4292,7 +4396,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int UMINReduceAll(int[] a) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4304,20 +4408,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void UMINReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + int v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4325,8 +4424,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::UMINReduce, IntMaxVectorTests::UMINReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMIN_IDENTITY; + + Assert.assertEquals((int) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.minUnsigned(res, a[i]); @@ -4336,7 +4458,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int UMINReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MAX_VALUE; + int res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4350,20 +4472,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + int v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (int) VectorMath.minUnsigned(ra, v); } } @@ -4372,7 +4489,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int UMAXReduce(int[] a, int idx) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.maxUnsigned(res, a[i]); } @@ -4381,7 +4498,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int UMAXReduceAll(int[] a) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4393,20 +4510,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void UMAXReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + int v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4414,8 +4526,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::UMAXReduce, IntMaxVectorTests::UMAXReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = UMAX_IDENTITY; + + Assert.assertEquals((int) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.maxUnsigned(res, a[i]); @@ -4425,7 +4560,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int UMAXReduceAllMasked(int[] a, boolean[] mask) { - int res = Integer.MIN_VALUE; + int res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4439,20 +4574,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + int v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (int) VectorMath.maxUnsigned(ra, v); } } @@ -4461,7 +4591,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduce(int[] a, int idx) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4470,7 +4600,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAll(int[] a) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4482,20 +4612,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4503,8 +4628,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::FIRST_NONZEROReduce, IntMaxVectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "intUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4514,7 +4662,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int FIRST_NONZEROReduceAllMasked(int[] a, boolean[] mask) { - int res = (int) 0; + int res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4528,20 +4676,15 @@ public class IntMaxVectorTests extends AbstractVectorTest { int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + int v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4598,7 +4741,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int SUADDReduce(int[] a, int idx) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4607,7 +4750,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int SUADDReduceAll(int[] a) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4622,17 +4765,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + int v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4640,8 +4778,31 @@ public class IntMaxVectorTests extends AbstractVectorTest { IntMaxVectorTests::SUADDReduce, IntMaxVectorTests::SUADDReduceAll); } + @Test(dataProvider = "intSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + int[] a = fa.apply(SPECIES.length()); + int id = SUADD_IDENTITY; + + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + int x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((int) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (int) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4651,7 +4812,7 @@ public class IntMaxVectorTests extends AbstractVectorTest { } static int SUADDReduceAllMasked(int[] a, boolean[] mask) { - int res = 0; + int res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (int) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4667,17 +4828,12 @@ public class IntMaxVectorTests extends AbstractVectorTest { int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - ra = (int) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + int v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (int) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Long128VectorTests.java b/test/jdk/jdk/incubator/vector/Long128VectorTests.java index 6c305a5c2b4..227f196ffdf 100644 --- a/test/jdk/jdk/incubator/vector/Long128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long128VectorTests.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 @@ -63,9 +63,21 @@ public class Long128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final long CONST_SHIFT = Long.SIZE / 2; + // Identity values for reduction operations + private static final long ADD_IDENTITY = (long)0; + private static final long AND_IDENTITY = (long)-1; + private static final long FIRST_NONZERO_IDENTITY = (long)0; + private static final long MAX_IDENTITY = Long.MIN_VALUE; + private static final long MIN_IDENTITY = Long.MAX_VALUE; + private static final long MUL_IDENTITY = (long)1; + private static final long OR_IDENTITY = (long)0; + private static final long SUADD_IDENTITY = (long)0; + private static final long UMAX_IDENTITY = (long)0; // Minimum unsigned value + private static final long UMIN_IDENTITY = (long)-1; // Maximum unsigned value + private static final long XOR_IDENTITY = (long)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); static void assertArraysStrictlyEquals(long[] r, long[] a) { @@ -3677,7 +3689,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ANDReduce(long[] a, int idx) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3686,7 +3698,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ANDReduceAll(long[] a) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3698,20 +3710,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void ANDReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + long v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3719,8 +3726,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::ANDReduce, Long128VectorTests::ANDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = AND_IDENTITY; + + Assert.assertEquals((long) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id & x), x); + Assert.assertEquals((long) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3730,7 +3760,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ANDReduceAllMasked(long[] a, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3744,20 +3774,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + long v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3766,7 +3791,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ORReduce(long[] a, int idx) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3775,7 +3800,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ORReduceAll(long[] a) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3790,17 +3815,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + long v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3808,8 +3828,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::ORReduce, Long128VectorTests::ORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = OR_IDENTITY; + + Assert.assertEquals((long) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id | x), x); + Assert.assertEquals((long) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static long ORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3819,7 +3862,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3836,17 +3879,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + long v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3855,7 +3893,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long XORReduce(long[] a, int idx) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3864,7 +3902,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long XORReduceAll(long[] a) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3879,17 +3917,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + long v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3897,8 +3930,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::XORReduce, Long128VectorTests::XORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = XOR_IDENTITY; + + Assert.assertEquals((long) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id ^ x), x); + Assert.assertEquals((long) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static long XORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3908,7 +3964,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long XORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3925,17 +3981,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + long v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3944,7 +3995,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ADDReduce(long[] a, int idx) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3953,7 +4004,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ADDReduceAll(long[] a) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3968,17 +4019,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + long v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3986,8 +4032,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::ADDReduce, Long128VectorTests::ADDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = ADD_IDENTITY; + + Assert.assertEquals((long) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id + x), x); + Assert.assertEquals((long) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3997,7 +4066,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long ADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -4014,17 +4083,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + long v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4033,7 +4097,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MULReduce(long[] a, int idx) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4042,7 +4106,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MULReduceAll(long[] a) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4054,20 +4118,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void MULReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + long v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4075,8 +4134,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::MULReduce, Long128VectorTests::MULReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MUL_IDENTITY; + + Assert.assertEquals((long) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id * x), x); + Assert.assertEquals((long) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static long MULReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4086,7 +4168,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MULReduceAllMasked(long[] a, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4100,20 +4182,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + long v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4122,7 +4199,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.min(res, a[i]); } @@ -4131,7 +4208,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduce(a, i)); } @@ -4143,20 +4220,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void MINReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + long v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4164,8 +4236,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::MINReduce, Long128VectorTests::MINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MIN_IDENTITY; + + Assert.assertEquals((long) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.min(id, x), x); + Assert.assertEquals((long) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static long MINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.min(res, a[i]); @@ -4175,7 +4270,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4189,20 +4284,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + long v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4211,7 +4301,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.max(res, a[i]); } @@ -4220,7 +4310,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduce(a, i)); } @@ -4232,20 +4322,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void MAXReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + long v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4253,8 +4338,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::MAXReduce, Long128VectorTests::MAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MAX_IDENTITY; + + Assert.assertEquals((long) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.max(id, x), x); + Assert.assertEquals((long) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.max(res, a[i]); @@ -4264,7 +4372,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long MAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4278,20 +4386,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + long v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4300,7 +4403,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long UMINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.minUnsigned(res, a[i]); } @@ -4309,7 +4412,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long UMINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4321,20 +4424,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void UMINReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + long v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4342,8 +4440,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::UMINReduce, Long128VectorTests::UMINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMIN_IDENTITY; + + Assert.assertEquals((long) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.minUnsigned(res, a[i]); @@ -4353,7 +4474,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long UMINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4367,20 +4488,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + long v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4389,7 +4505,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long UMAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.maxUnsigned(res, a[i]); } @@ -4398,7 +4514,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long UMAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4410,20 +4526,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void UMAXReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + long v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4431,8 +4542,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::UMAXReduce, Long128VectorTests::UMAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMAX_IDENTITY; + + Assert.assertEquals((long) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.maxUnsigned(res, a[i]); @@ -4442,7 +4576,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long UMAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4456,20 +4590,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + long v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4478,7 +4607,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduce(long[] a, int idx) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4487,7 +4616,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAll(long[] a) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4499,20 +4628,15 @@ public class Long128VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4520,8 +4644,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::FIRST_NONZEROReduce, Long128VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4531,7 +4678,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAllMasked(long[] a, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4545,20 +4692,15 @@ public class Long128VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4615,7 +4757,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long SUADDReduce(long[] a, int idx) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4624,7 +4766,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long SUADDReduceAll(long[] a) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4639,17 +4781,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + long v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4657,8 +4794,31 @@ public class Long128VectorTests extends AbstractVectorTest { Long128VectorTests::SUADDReduce, Long128VectorTests::SUADDReduceAll); } + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = SUADD_IDENTITY; + + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4668,7 +4828,7 @@ public class Long128VectorTests extends AbstractVectorTest { } static long SUADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4684,17 +4844,12 @@ public class Long128VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + long v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Long256VectorTests.java b/test/jdk/jdk/incubator/vector/Long256VectorTests.java index 3b276391cb7..c37e68e3728 100644 --- a/test/jdk/jdk/incubator/vector/Long256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long256VectorTests.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 @@ -63,9 +63,21 @@ public class Long256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final long CONST_SHIFT = Long.SIZE / 2; + // Identity values for reduction operations + private static final long ADD_IDENTITY = (long)0; + private static final long AND_IDENTITY = (long)-1; + private static final long FIRST_NONZERO_IDENTITY = (long)0; + private static final long MAX_IDENTITY = Long.MIN_VALUE; + private static final long MIN_IDENTITY = Long.MAX_VALUE; + private static final long MUL_IDENTITY = (long)1; + private static final long OR_IDENTITY = (long)0; + private static final long SUADD_IDENTITY = (long)0; + private static final long UMAX_IDENTITY = (long)0; // Minimum unsigned value + private static final long UMIN_IDENTITY = (long)-1; // Maximum unsigned value + private static final long XOR_IDENTITY = (long)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); static void assertArraysStrictlyEquals(long[] r, long[] a) { @@ -3677,7 +3689,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ANDReduce(long[] a, int idx) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3686,7 +3698,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ANDReduceAll(long[] a) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3698,20 +3710,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void ANDReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + long v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3719,8 +3726,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::ANDReduce, Long256VectorTests::ANDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = AND_IDENTITY; + + Assert.assertEquals((long) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id & x), x); + Assert.assertEquals((long) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3730,7 +3760,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ANDReduceAllMasked(long[] a, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3744,20 +3774,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + long v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3766,7 +3791,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ORReduce(long[] a, int idx) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3775,7 +3800,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ORReduceAll(long[] a) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3790,17 +3815,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + long v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3808,8 +3828,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::ORReduce, Long256VectorTests::ORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = OR_IDENTITY; + + Assert.assertEquals((long) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id | x), x); + Assert.assertEquals((long) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static long ORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3819,7 +3862,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3836,17 +3879,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + long v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3855,7 +3893,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long XORReduce(long[] a, int idx) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3864,7 +3902,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long XORReduceAll(long[] a) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3879,17 +3917,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + long v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3897,8 +3930,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::XORReduce, Long256VectorTests::XORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = XOR_IDENTITY; + + Assert.assertEquals((long) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id ^ x), x); + Assert.assertEquals((long) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static long XORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3908,7 +3964,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long XORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3925,17 +3981,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + long v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3944,7 +3995,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ADDReduce(long[] a, int idx) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3953,7 +4004,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ADDReduceAll(long[] a) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3968,17 +4019,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + long v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3986,8 +4032,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::ADDReduce, Long256VectorTests::ADDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = ADD_IDENTITY; + + Assert.assertEquals((long) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id + x), x); + Assert.assertEquals((long) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3997,7 +4066,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long ADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -4014,17 +4083,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + long v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4033,7 +4097,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MULReduce(long[] a, int idx) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4042,7 +4106,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MULReduceAll(long[] a) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4054,20 +4118,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void MULReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + long v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4075,8 +4134,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::MULReduce, Long256VectorTests::MULReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MUL_IDENTITY; + + Assert.assertEquals((long) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id * x), x); + Assert.assertEquals((long) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static long MULReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4086,7 +4168,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MULReduceAllMasked(long[] a, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4100,20 +4182,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + long v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4122,7 +4199,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.min(res, a[i]); } @@ -4131,7 +4208,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduce(a, i)); } @@ -4143,20 +4220,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void MINReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + long v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4164,8 +4236,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::MINReduce, Long256VectorTests::MINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MIN_IDENTITY; + + Assert.assertEquals((long) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.min(id, x), x); + Assert.assertEquals((long) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static long MINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.min(res, a[i]); @@ -4175,7 +4270,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4189,20 +4284,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + long v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4211,7 +4301,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.max(res, a[i]); } @@ -4220,7 +4310,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduce(a, i)); } @@ -4232,20 +4322,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void MAXReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + long v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4253,8 +4338,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::MAXReduce, Long256VectorTests::MAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MAX_IDENTITY; + + Assert.assertEquals((long) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.max(id, x), x); + Assert.assertEquals((long) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.max(res, a[i]); @@ -4264,7 +4372,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long MAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4278,20 +4386,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + long v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4300,7 +4403,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long UMINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.minUnsigned(res, a[i]); } @@ -4309,7 +4412,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long UMINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4321,20 +4424,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void UMINReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + long v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4342,8 +4440,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::UMINReduce, Long256VectorTests::UMINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMIN_IDENTITY; + + Assert.assertEquals((long) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.minUnsigned(res, a[i]); @@ -4353,7 +4474,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long UMINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4367,20 +4488,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + long v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4389,7 +4505,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long UMAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.maxUnsigned(res, a[i]); } @@ -4398,7 +4514,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long UMAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4410,20 +4526,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void UMAXReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + long v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4431,8 +4542,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::UMAXReduce, Long256VectorTests::UMAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMAX_IDENTITY; + + Assert.assertEquals((long) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.maxUnsigned(res, a[i]); @@ -4442,7 +4576,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long UMAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4456,20 +4590,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + long v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4478,7 +4607,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduce(long[] a, int idx) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4487,7 +4616,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAll(long[] a) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4499,20 +4628,15 @@ public class Long256VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4520,8 +4644,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::FIRST_NONZEROReduce, Long256VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4531,7 +4678,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAllMasked(long[] a, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4545,20 +4692,15 @@ public class Long256VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4615,7 +4757,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long SUADDReduce(long[] a, int idx) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4624,7 +4766,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long SUADDReduceAll(long[] a) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4639,17 +4781,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + long v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4657,8 +4794,31 @@ public class Long256VectorTests extends AbstractVectorTest { Long256VectorTests::SUADDReduce, Long256VectorTests::SUADDReduceAll); } + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = SUADD_IDENTITY; + + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4668,7 +4828,7 @@ public class Long256VectorTests extends AbstractVectorTest { } static long SUADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4684,17 +4844,12 @@ public class Long256VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + long v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Long512VectorTests.java b/test/jdk/jdk/incubator/vector/Long512VectorTests.java index 181116cf399..5f8abb5bdd5 100644 --- a/test/jdk/jdk/incubator/vector/Long512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long512VectorTests.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 @@ -63,9 +63,21 @@ public class Long512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final long CONST_SHIFT = Long.SIZE / 2; + // Identity values for reduction operations + private static final long ADD_IDENTITY = (long)0; + private static final long AND_IDENTITY = (long)-1; + private static final long FIRST_NONZERO_IDENTITY = (long)0; + private static final long MAX_IDENTITY = Long.MIN_VALUE; + private static final long MIN_IDENTITY = Long.MAX_VALUE; + private static final long MUL_IDENTITY = (long)1; + private static final long OR_IDENTITY = (long)0; + private static final long SUADD_IDENTITY = (long)0; + private static final long UMAX_IDENTITY = (long)0; // Minimum unsigned value + private static final long UMIN_IDENTITY = (long)-1; // Maximum unsigned value + private static final long XOR_IDENTITY = (long)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); static void assertArraysStrictlyEquals(long[] r, long[] a) { @@ -3677,7 +3689,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ANDReduce(long[] a, int idx) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3686,7 +3698,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ANDReduceAll(long[] a) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3698,20 +3710,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void ANDReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + long v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3719,8 +3726,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::ANDReduce, Long512VectorTests::ANDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = AND_IDENTITY; + + Assert.assertEquals((long) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id & x), x); + Assert.assertEquals((long) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3730,7 +3760,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ANDReduceAllMasked(long[] a, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3744,20 +3774,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + long v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3766,7 +3791,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ORReduce(long[] a, int idx) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3775,7 +3800,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ORReduceAll(long[] a) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3790,17 +3815,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + long v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3808,8 +3828,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::ORReduce, Long512VectorTests::ORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = OR_IDENTITY; + + Assert.assertEquals((long) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id | x), x); + Assert.assertEquals((long) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static long ORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3819,7 +3862,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3836,17 +3879,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + long v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3855,7 +3893,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long XORReduce(long[] a, int idx) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3864,7 +3902,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long XORReduceAll(long[] a) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3879,17 +3917,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + long v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3897,8 +3930,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::XORReduce, Long512VectorTests::XORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = XOR_IDENTITY; + + Assert.assertEquals((long) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id ^ x), x); + Assert.assertEquals((long) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static long XORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3908,7 +3964,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long XORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3925,17 +3981,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + long v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3944,7 +3995,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ADDReduce(long[] a, int idx) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3953,7 +4004,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ADDReduceAll(long[] a) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3968,17 +4019,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + long v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3986,8 +4032,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::ADDReduce, Long512VectorTests::ADDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = ADD_IDENTITY; + + Assert.assertEquals((long) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id + x), x); + Assert.assertEquals((long) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3997,7 +4066,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long ADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -4014,17 +4083,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + long v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4033,7 +4097,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MULReduce(long[] a, int idx) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4042,7 +4106,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MULReduceAll(long[] a) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4054,20 +4118,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void MULReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + long v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4075,8 +4134,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::MULReduce, Long512VectorTests::MULReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MUL_IDENTITY; + + Assert.assertEquals((long) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id * x), x); + Assert.assertEquals((long) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static long MULReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4086,7 +4168,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MULReduceAllMasked(long[] a, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4100,20 +4182,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + long v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4122,7 +4199,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.min(res, a[i]); } @@ -4131,7 +4208,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduce(a, i)); } @@ -4143,20 +4220,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void MINReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + long v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4164,8 +4236,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::MINReduce, Long512VectorTests::MINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MIN_IDENTITY; + + Assert.assertEquals((long) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.min(id, x), x); + Assert.assertEquals((long) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static long MINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.min(res, a[i]); @@ -4175,7 +4270,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4189,20 +4284,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + long v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4211,7 +4301,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.max(res, a[i]); } @@ -4220,7 +4310,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduce(a, i)); } @@ -4232,20 +4322,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void MAXReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + long v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4253,8 +4338,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::MAXReduce, Long512VectorTests::MAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MAX_IDENTITY; + + Assert.assertEquals((long) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.max(id, x), x); + Assert.assertEquals((long) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.max(res, a[i]); @@ -4264,7 +4372,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long MAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4278,20 +4386,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + long v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4300,7 +4403,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long UMINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.minUnsigned(res, a[i]); } @@ -4309,7 +4412,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long UMINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4321,20 +4424,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void UMINReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + long v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4342,8 +4440,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::UMINReduce, Long512VectorTests::UMINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMIN_IDENTITY; + + Assert.assertEquals((long) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.minUnsigned(res, a[i]); @@ -4353,7 +4474,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long UMINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4367,20 +4488,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + long v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4389,7 +4505,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long UMAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.maxUnsigned(res, a[i]); } @@ -4398,7 +4514,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long UMAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4410,20 +4526,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void UMAXReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + long v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4431,8 +4542,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::UMAXReduce, Long512VectorTests::UMAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMAX_IDENTITY; + + Assert.assertEquals((long) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.maxUnsigned(res, a[i]); @@ -4442,7 +4576,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long UMAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4456,20 +4590,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + long v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4478,7 +4607,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduce(long[] a, int idx) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4487,7 +4616,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAll(long[] a) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4499,20 +4628,15 @@ public class Long512VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4520,8 +4644,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::FIRST_NONZEROReduce, Long512VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4531,7 +4678,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAllMasked(long[] a, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4545,20 +4692,15 @@ public class Long512VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4615,7 +4757,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long SUADDReduce(long[] a, int idx) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4624,7 +4766,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long SUADDReduceAll(long[] a) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4639,17 +4781,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + long v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4657,8 +4794,31 @@ public class Long512VectorTests extends AbstractVectorTest { Long512VectorTests::SUADDReduce, Long512VectorTests::SUADDReduceAll); } + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = SUADD_IDENTITY; + + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4668,7 +4828,7 @@ public class Long512VectorTests extends AbstractVectorTest { } static long SUADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4684,17 +4844,12 @@ public class Long512VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + long v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Long64VectorTests.java b/test/jdk/jdk/incubator/vector/Long64VectorTests.java index cb052925778..5f8a9018384 100644 --- a/test/jdk/jdk/incubator/vector/Long64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Long64VectorTests.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 @@ -63,9 +63,21 @@ public class Long64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final long CONST_SHIFT = Long.SIZE / 2; + // Identity values for reduction operations + private static final long ADD_IDENTITY = (long)0; + private static final long AND_IDENTITY = (long)-1; + private static final long FIRST_NONZERO_IDENTITY = (long)0; + private static final long MAX_IDENTITY = Long.MIN_VALUE; + private static final long MIN_IDENTITY = Long.MAX_VALUE; + private static final long MUL_IDENTITY = (long)1; + private static final long OR_IDENTITY = (long)0; + private static final long SUADD_IDENTITY = (long)0; + private static final long UMAX_IDENTITY = (long)0; // Minimum unsigned value + private static final long UMIN_IDENTITY = (long)-1; // Maximum unsigned value + private static final long XOR_IDENTITY = (long)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); static void assertArraysStrictlyEquals(long[] r, long[] a) { @@ -3677,7 +3689,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ANDReduce(long[] a, int idx) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3686,7 +3698,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ANDReduceAll(long[] a) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3698,20 +3710,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void ANDReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + long v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3719,8 +3726,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::ANDReduce, Long64VectorTests::ANDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = AND_IDENTITY; + + Assert.assertEquals((long) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id & x), x); + Assert.assertEquals((long) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3730,7 +3760,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ANDReduceAllMasked(long[] a, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3744,20 +3774,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + long v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3766,7 +3791,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ORReduce(long[] a, int idx) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3775,7 +3800,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ORReduceAll(long[] a) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3790,17 +3815,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + long v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3808,8 +3828,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::ORReduce, Long64VectorTests::ORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = OR_IDENTITY; + + Assert.assertEquals((long) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id | x), x); + Assert.assertEquals((long) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static long ORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3819,7 +3862,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3836,17 +3879,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + long v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3855,7 +3893,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long XORReduce(long[] a, int idx) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3864,7 +3902,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long XORReduceAll(long[] a) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3879,17 +3917,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + long v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3897,8 +3930,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::XORReduce, Long64VectorTests::XORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = XOR_IDENTITY; + + Assert.assertEquals((long) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id ^ x), x); + Assert.assertEquals((long) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static long XORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3908,7 +3964,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long XORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3925,17 +3981,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + long v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3944,7 +3995,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ADDReduce(long[] a, int idx) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3953,7 +4004,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ADDReduceAll(long[] a) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3968,17 +4019,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + long v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3986,8 +4032,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::ADDReduce, Long64VectorTests::ADDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = ADD_IDENTITY; + + Assert.assertEquals((long) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id + x), x); + Assert.assertEquals((long) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3997,7 +4066,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long ADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -4014,17 +4083,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + long v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4033,7 +4097,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MULReduce(long[] a, int idx) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4042,7 +4106,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MULReduceAll(long[] a) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4054,20 +4118,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void MULReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + long v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4075,8 +4134,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::MULReduce, Long64VectorTests::MULReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MUL_IDENTITY; + + Assert.assertEquals((long) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id * x), x); + Assert.assertEquals((long) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static long MULReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4086,7 +4168,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MULReduceAllMasked(long[] a, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4100,20 +4182,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + long v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4122,7 +4199,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.min(res, a[i]); } @@ -4131,7 +4208,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduce(a, i)); } @@ -4143,20 +4220,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void MINReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + long v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4164,8 +4236,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::MINReduce, Long64VectorTests::MINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MIN_IDENTITY; + + Assert.assertEquals((long) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.min(id, x), x); + Assert.assertEquals((long) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static long MINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.min(res, a[i]); @@ -4175,7 +4270,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4189,20 +4284,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + long v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4211,7 +4301,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.max(res, a[i]); } @@ -4220,7 +4310,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduce(a, i)); } @@ -4232,20 +4322,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void MAXReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + long v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4253,8 +4338,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::MAXReduce, Long64VectorTests::MAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MAX_IDENTITY; + + Assert.assertEquals((long) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.max(id, x), x); + Assert.assertEquals((long) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.max(res, a[i]); @@ -4264,7 +4372,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long MAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4278,20 +4386,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + long v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4300,7 +4403,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long UMINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.minUnsigned(res, a[i]); } @@ -4309,7 +4412,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long UMINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4321,20 +4424,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void UMINReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + long v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4342,8 +4440,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::UMINReduce, Long64VectorTests::UMINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMIN_IDENTITY; + + Assert.assertEquals((long) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.minUnsigned(res, a[i]); @@ -4353,7 +4474,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long UMINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4367,20 +4488,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + long v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4389,7 +4505,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long UMAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.maxUnsigned(res, a[i]); } @@ -4398,7 +4514,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long UMAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4410,20 +4526,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void UMAXReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + long v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4431,8 +4542,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::UMAXReduce, Long64VectorTests::UMAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMAX_IDENTITY; + + Assert.assertEquals((long) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.maxUnsigned(res, a[i]); @@ -4442,7 +4576,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long UMAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4456,20 +4590,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + long v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4478,7 +4607,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduce(long[] a, int idx) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4487,7 +4616,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAll(long[] a) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4499,20 +4628,15 @@ public class Long64VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4520,8 +4644,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::FIRST_NONZEROReduce, Long64VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4531,7 +4678,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAllMasked(long[] a, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4545,20 +4692,15 @@ public class Long64VectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4615,7 +4757,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long SUADDReduce(long[] a, int idx) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4624,7 +4766,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long SUADDReduceAll(long[] a) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4639,17 +4781,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + long v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4657,8 +4794,31 @@ public class Long64VectorTests extends AbstractVectorTest { Long64VectorTests::SUADDReduce, Long64VectorTests::SUADDReduceAll); } + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = SUADD_IDENTITY; + + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4668,7 +4828,7 @@ public class Long64VectorTests extends AbstractVectorTest { } static long SUADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4684,17 +4844,12 @@ public class Long64VectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + long v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java b/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java index 6fe189867c2..17fee0a7765 100644 --- a/test/jdk/jdk/incubator/vector/LongMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/LongMaxVectorTests.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 @@ -71,6 +71,19 @@ public class LongMaxVectorTests extends AbstractVectorTest { private static final long CONST_SHIFT = Long.SIZE / 2; + // Identity values for reduction operations + private static final long ADD_IDENTITY = (long)0; + private static final long AND_IDENTITY = (long)-1; + private static final long FIRST_NONZERO_IDENTITY = (long)0; + private static final long MAX_IDENTITY = Long.MIN_VALUE; + private static final long MIN_IDENTITY = Long.MAX_VALUE; + private static final long MUL_IDENTITY = (long)1; + private static final long OR_IDENTITY = (long)0; + private static final long SUADD_IDENTITY = (long)0; + private static final long UMAX_IDENTITY = (long)0; // Minimum unsigned value + private static final long UMIN_IDENTITY = (long)-1; // Maximum unsigned value + private static final long XOR_IDENTITY = (long)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); static void assertArraysStrictlyEquals(long[] r, long[] a) { @@ -3682,7 +3695,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ANDReduce(long[] a, int idx) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3691,7 +3704,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ANDReduceAll(long[] a) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3703,20 +3716,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void ANDReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + long v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3724,8 +3732,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::ANDReduce, LongMaxVectorTests::ANDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = AND_IDENTITY; + + Assert.assertEquals((long) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id & x), x); + Assert.assertEquals((long) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3735,7 +3766,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ANDReduceAllMasked(long[] a, boolean[] mask) { - long res = -1; + long res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3749,20 +3780,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + long v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3771,7 +3797,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ORReduce(long[] a, int idx) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3780,7 +3806,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ORReduceAll(long[] a) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3795,17 +3821,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + long v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3813,8 +3834,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::ORReduce, LongMaxVectorTests::ORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = OR_IDENTITY; + + Assert.assertEquals((long) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id | x), x); + Assert.assertEquals((long) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static long ORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3824,7 +3868,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3841,17 +3885,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + long v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3860,7 +3899,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long XORReduce(long[] a, int idx) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3869,7 +3908,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long XORReduceAll(long[] a) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3884,17 +3923,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + long v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3902,8 +3936,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::XORReduce, LongMaxVectorTests::XORReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = XOR_IDENTITY; + + Assert.assertEquals((long) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id ^ x), x); + Assert.assertEquals((long) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static long XORReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3913,7 +3970,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long XORReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3930,17 +3987,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + long v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3949,7 +4001,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ADDReduce(long[] a, int idx) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3958,7 +4010,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ADDReduceAll(long[] a) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3973,17 +4025,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + long v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3991,8 +4038,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::ADDReduce, LongMaxVectorTests::ADDReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = ADD_IDENTITY; + + Assert.assertEquals((long) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id + x), x); + Assert.assertEquals((long) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -4002,7 +4072,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long ADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -4019,17 +4089,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + long v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -4038,7 +4103,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MULReduce(long[] a, int idx) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -4047,7 +4112,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MULReduceAll(long[] a) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -4059,20 +4124,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void MULReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + long v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4080,8 +4140,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::MULReduce, LongMaxVectorTests::MULReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MUL_IDENTITY; + + Assert.assertEquals((long) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) (id * x), x); + Assert.assertEquals((long) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static long MULReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4091,7 +4174,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MULReduceAllMasked(long[] a, boolean[] mask) { - long res = 1; + long res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4105,20 +4188,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + long v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4127,7 +4205,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.min(res, a[i]); } @@ -4136,7 +4214,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduce(a, i)); } @@ -4148,20 +4226,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void MINReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + long v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4169,8 +4242,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::MINReduce, LongMaxVectorTests::MINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MIN_IDENTITY; + + Assert.assertEquals((long) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.min(id, x), x); + Assert.assertEquals((long) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static long MINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.min(res, a[i]); @@ -4180,7 +4276,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4194,20 +4290,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + long v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (long) Math.min(ra, v); } } @@ -4216,7 +4307,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) Math.max(res, a[i]); } @@ -4225,7 +4316,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduce(a, i)); } @@ -4237,20 +4328,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void MAXReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + long v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4258,8 +4344,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::MAXReduce, LongMaxVectorTests::MAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = MAX_IDENTITY; + + Assert.assertEquals((long) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) Math.max(id, x), x); + Assert.assertEquals((long) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) Math.max(res, a[i]); @@ -4269,7 +4378,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long MAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4283,20 +4392,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + long v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (long) Math.max(ra, v); } } @@ -4305,7 +4409,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long UMINReduce(long[] a, int idx) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.minUnsigned(res, a[i]); } @@ -4314,7 +4418,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long UMINReduceAll(long[] a) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4326,20 +4430,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void UMINReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + long v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4347,8 +4446,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::UMINReduce, LongMaxVectorTests::UMINReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMIN_IDENTITY; + + Assert.assertEquals((long) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.minUnsigned(res, a[i]); @@ -4358,7 +4480,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long UMINReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MAX_VALUE; + long res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4372,20 +4494,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + long v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (long) VectorMath.minUnsigned(ra, v); } } @@ -4394,7 +4511,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long UMAXReduce(long[] a, int idx) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.maxUnsigned(res, a[i]); } @@ -4403,7 +4520,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long UMAXReduceAll(long[] a) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4415,20 +4532,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void UMAXReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + long v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4436,8 +4548,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::UMAXReduce, LongMaxVectorTests::UMAXReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = UMAX_IDENTITY; + + Assert.assertEquals((long) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.maxUnsigned(res, a[i]); @@ -4447,7 +4582,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long UMAXReduceAllMasked(long[] a, boolean[] mask) { - long res = Long.MIN_VALUE; + long res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4461,20 +4596,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + long v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (long) VectorMath.maxUnsigned(ra, v); } } @@ -4483,7 +4613,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduce(long[] a, int idx) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4492,7 +4622,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAll(long[] a) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4504,20 +4634,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4525,8 +4650,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::FIRST_NONZEROReduce, LongMaxVectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "longUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4536,7 +4684,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long FIRST_NONZEROReduceAllMasked(long[] a, boolean[] mask) { - long res = (long) 0; + long res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4550,20 +4698,15 @@ public class LongMaxVectorTests extends AbstractVectorTest { long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + long v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4620,7 +4763,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long SUADDReduce(long[] a, int idx) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4629,7 +4772,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long SUADDReduceAll(long[] a) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4644,17 +4787,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + long v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4662,8 +4800,31 @@ public class LongMaxVectorTests extends AbstractVectorTest { LongMaxVectorTests::SUADDReduce, LongMaxVectorTests::SUADDReduceAll); } + @Test(dataProvider = "longSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + long[] a = fa.apply(SPECIES.length()); + long id = SUADD_IDENTITY; + + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + long x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((long) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (long) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4673,7 +4834,7 @@ public class LongMaxVectorTests extends AbstractVectorTest { } static long SUADDReduceAllMasked(long[] a, boolean[] mask) { - long res = 0; + long res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (long) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4689,17 +4850,12 @@ public class LongMaxVectorTests extends AbstractVectorTest { long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - ra = (long) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + long v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (long) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Short128VectorTests.java b/test/jdk/jdk/incubator/vector/Short128VectorTests.java index be39a01a4e7..fb740fedfd4 100644 --- a/test/jdk/jdk/incubator/vector/Short128VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short128VectorTests.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 @@ -63,9 +63,21 @@ public class Short128VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final short CONST_SHIFT = Short.SIZE / 2; + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short AND_IDENTITY = (short)-1; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = Short.MIN_VALUE; + private static final short MIN_IDENTITY = Short.MAX_VALUE; + private static final short MUL_IDENTITY = (short)1; + private static final short OR_IDENTITY = (short)0; + private static final short SUADD_IDENTITY = (short)0; + private static final short UMAX_IDENTITY = (short)0; // Minimum unsigned value + private static final short UMIN_IDENTITY = (short)-1; // Maximum unsigned value + private static final short XOR_IDENTITY = (short)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 128); static void assertArraysStrictlyEquals(short[] r, short[] a) { @@ -3602,7 +3614,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ANDReduce(short[] a, int idx) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3611,7 +3623,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ANDReduceAll(short[] a) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3623,20 +3635,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void ANDReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + short v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3644,8 +3651,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::ANDReduce, Short128VectorTests::ANDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = AND_IDENTITY; + + Assert.assertEquals((short) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id & x), x); + Assert.assertEquals((short) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3655,7 +3685,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ANDReduceAllMasked(short[] a, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3669,20 +3699,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + short v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3691,7 +3716,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ORReduce(short[] a, int idx) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3700,7 +3725,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ORReduceAll(short[] a) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3715,17 +3740,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + short v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3733,8 +3753,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::ORReduce, Short128VectorTests::ORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = OR_IDENTITY; + + Assert.assertEquals((short) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id | x), x); + Assert.assertEquals((short) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static short ORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3744,7 +3787,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3761,17 +3804,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + short v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3780,7 +3818,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short XORReduce(short[] a, int idx) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3789,7 +3827,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short XORReduceAll(short[] a) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3804,17 +3842,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + short v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3822,8 +3855,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::XORReduce, Short128VectorTests::XORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = XOR_IDENTITY; + + Assert.assertEquals((short) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id ^ x), x); + Assert.assertEquals((short) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static short XORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3833,7 +3889,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short XORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3850,17 +3906,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + short v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3869,7 +3920,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ADDReduce(short[] a, int idx) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3878,7 +3929,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ADDReduceAll(short[] a) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3893,17 +3944,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3911,8 +3957,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::ADDReduce, Short128VectorTests::ADDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + Assert.assertEquals((short) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id + x), x); + Assert.assertEquals((short) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3922,7 +3991,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short ADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3939,17 +4008,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3958,7 +4022,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MULReduce(short[] a, int idx) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3967,7 +4031,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MULReduceAll(short[] a) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3979,20 +4043,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void MULReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4000,8 +4059,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::MULReduce, Short128VectorTests::MULReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + Assert.assertEquals((short) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id * x), x); + Assert.assertEquals((short) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4011,7 +4093,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MULReduceAllMasked(short[] a, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4025,20 +4107,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4047,7 +4124,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.min(res, a[i]); } @@ -4056,7 +4133,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduce(a, i)); } @@ -4068,20 +4145,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void MINReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4089,8 +4161,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::MINReduce, Short128VectorTests::MINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + Assert.assertEquals((short) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) Math.min(id, x), x); + Assert.assertEquals((short) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.min(res, a[i]); @@ -4100,7 +4195,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4114,20 +4209,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4136,7 +4226,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.max(res, a[i]); } @@ -4145,7 +4235,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduce(a, i)); } @@ -4157,20 +4247,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void MAXReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4178,8 +4263,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::MAXReduce, Short128VectorTests::MAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + Assert.assertEquals((short) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) Math.max(id, x), x); + Assert.assertEquals((short) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.max(res, a[i]); @@ -4189,7 +4297,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short MAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4203,20 +4311,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4225,7 +4328,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short UMINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.minUnsigned(res, a[i]); } @@ -4234,7 +4337,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short UMINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4246,20 +4349,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void UMINReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + short v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4267,8 +4365,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::UMINReduce, Short128VectorTests::UMINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMIN_IDENTITY; + + Assert.assertEquals((short) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.minUnsigned(res, a[i]); @@ -4278,7 +4399,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short UMINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4292,20 +4413,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + short v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4314,7 +4430,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short UMAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.maxUnsigned(res, a[i]); } @@ -4323,7 +4439,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short UMAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4335,20 +4451,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void UMAXReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + short v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4356,8 +4467,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::UMAXReduce, Short128VectorTests::UMAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMAX_IDENTITY; + + Assert.assertEquals((short) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.maxUnsigned(res, a[i]); @@ -4367,7 +4501,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short UMAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4381,20 +4515,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + short v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4403,7 +4532,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduce(short[] a, int idx) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4412,7 +4541,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAll(short[] a) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4424,20 +4553,15 @@ public class Short128VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 0; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4445,8 +4569,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::FIRST_NONZEROReduce, Short128VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4456,7 +4603,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4470,20 +4617,15 @@ public class Short128VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 0; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4540,7 +4682,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short SUADDReduce(short[] a, int idx) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4549,7 +4691,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short SUADDReduceAll(short[] a) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4564,17 +4706,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + short v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4582,8 +4719,31 @@ public class Short128VectorTests extends AbstractVectorTest { Short128VectorTests::SUADDReduce, Short128VectorTests::SUADDReduceAll); } + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = SUADD_IDENTITY; + + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4593,7 +4753,7 @@ public class Short128VectorTests extends AbstractVectorTest { } static short SUADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4609,17 +4769,12 @@ public class Short128VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + short v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Short256VectorTests.java b/test/jdk/jdk/incubator/vector/Short256VectorTests.java index 0b6ae53710e..cd6aa113b84 100644 --- a/test/jdk/jdk/incubator/vector/Short256VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short256VectorTests.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 @@ -63,9 +63,21 @@ public class Short256VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final short CONST_SHIFT = Short.SIZE / 2; + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short AND_IDENTITY = (short)-1; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = Short.MIN_VALUE; + private static final short MIN_IDENTITY = Short.MAX_VALUE; + private static final short MUL_IDENTITY = (short)1; + private static final short OR_IDENTITY = (short)0; + private static final short SUADD_IDENTITY = (short)0; + private static final short UMAX_IDENTITY = (short)0; // Minimum unsigned value + private static final short UMIN_IDENTITY = (short)-1; // Maximum unsigned value + private static final short XOR_IDENTITY = (short)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 256); static void assertArraysStrictlyEquals(short[] r, short[] a) { @@ -3602,7 +3614,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ANDReduce(short[] a, int idx) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3611,7 +3623,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ANDReduceAll(short[] a) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3623,20 +3635,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void ANDReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + short v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3644,8 +3651,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::ANDReduce, Short256VectorTests::ANDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = AND_IDENTITY; + + Assert.assertEquals((short) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id & x), x); + Assert.assertEquals((short) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3655,7 +3685,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ANDReduceAllMasked(short[] a, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3669,20 +3699,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + short v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3691,7 +3716,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ORReduce(short[] a, int idx) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3700,7 +3725,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ORReduceAll(short[] a) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3715,17 +3740,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + short v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3733,8 +3753,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::ORReduce, Short256VectorTests::ORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = OR_IDENTITY; + + Assert.assertEquals((short) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id | x), x); + Assert.assertEquals((short) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static short ORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3744,7 +3787,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3761,17 +3804,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + short v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3780,7 +3818,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short XORReduce(short[] a, int idx) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3789,7 +3827,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short XORReduceAll(short[] a) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3804,17 +3842,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + short v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3822,8 +3855,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::XORReduce, Short256VectorTests::XORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = XOR_IDENTITY; + + Assert.assertEquals((short) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id ^ x), x); + Assert.assertEquals((short) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static short XORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3833,7 +3889,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short XORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3850,17 +3906,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + short v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3869,7 +3920,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ADDReduce(short[] a, int idx) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3878,7 +3929,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ADDReduceAll(short[] a) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3893,17 +3944,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3911,8 +3957,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::ADDReduce, Short256VectorTests::ADDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + Assert.assertEquals((short) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id + x), x); + Assert.assertEquals((short) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3922,7 +3991,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short ADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3939,17 +4008,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3958,7 +4022,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MULReduce(short[] a, int idx) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3967,7 +4031,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MULReduceAll(short[] a) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3979,20 +4043,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void MULReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4000,8 +4059,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::MULReduce, Short256VectorTests::MULReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + Assert.assertEquals((short) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id * x), x); + Assert.assertEquals((short) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4011,7 +4093,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MULReduceAllMasked(short[] a, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4025,20 +4107,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4047,7 +4124,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.min(res, a[i]); } @@ -4056,7 +4133,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduce(a, i)); } @@ -4068,20 +4145,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void MINReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4089,8 +4161,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::MINReduce, Short256VectorTests::MINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + Assert.assertEquals((short) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) Math.min(id, x), x); + Assert.assertEquals((short) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.min(res, a[i]); @@ -4100,7 +4195,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4114,20 +4209,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4136,7 +4226,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.max(res, a[i]); } @@ -4145,7 +4235,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduce(a, i)); } @@ -4157,20 +4247,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void MAXReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4178,8 +4263,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::MAXReduce, Short256VectorTests::MAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + Assert.assertEquals((short) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) Math.max(id, x), x); + Assert.assertEquals((short) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.max(res, a[i]); @@ -4189,7 +4297,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short MAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4203,20 +4311,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4225,7 +4328,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short UMINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.minUnsigned(res, a[i]); } @@ -4234,7 +4337,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short UMINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4246,20 +4349,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void UMINReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + short v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4267,8 +4365,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::UMINReduce, Short256VectorTests::UMINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMIN_IDENTITY; + + Assert.assertEquals((short) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.minUnsigned(res, a[i]); @@ -4278,7 +4399,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short UMINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4292,20 +4413,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + short v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4314,7 +4430,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short UMAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.maxUnsigned(res, a[i]); } @@ -4323,7 +4439,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short UMAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4335,20 +4451,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void UMAXReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + short v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4356,8 +4467,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::UMAXReduce, Short256VectorTests::UMAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMAX_IDENTITY; + + Assert.assertEquals((short) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.maxUnsigned(res, a[i]); @@ -4367,7 +4501,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short UMAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4381,20 +4515,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + short v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4403,7 +4532,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduce(short[] a, int idx) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4412,7 +4541,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAll(short[] a) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4424,20 +4553,15 @@ public class Short256VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 0; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4445,8 +4569,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::FIRST_NONZEROReduce, Short256VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4456,7 +4603,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4470,20 +4617,15 @@ public class Short256VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 0; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4540,7 +4682,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short SUADDReduce(short[] a, int idx) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4549,7 +4691,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short SUADDReduceAll(short[] a) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4564,17 +4706,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + short v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4582,8 +4719,31 @@ public class Short256VectorTests extends AbstractVectorTest { Short256VectorTests::SUADDReduce, Short256VectorTests::SUADDReduceAll); } + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = SUADD_IDENTITY; + + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4593,7 +4753,7 @@ public class Short256VectorTests extends AbstractVectorTest { } static short SUADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4609,17 +4769,12 @@ public class Short256VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + short v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Short512VectorTests.java b/test/jdk/jdk/incubator/vector/Short512VectorTests.java index d4568068b6a..722f826f3e9 100644 --- a/test/jdk/jdk/incubator/vector/Short512VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short512VectorTests.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 @@ -63,9 +63,21 @@ public class Short512VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final short CONST_SHIFT = Short.SIZE / 2; + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short AND_IDENTITY = (short)-1; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = Short.MIN_VALUE; + private static final short MIN_IDENTITY = Short.MAX_VALUE; + private static final short MUL_IDENTITY = (short)1; + private static final short OR_IDENTITY = (short)0; + private static final short SUADD_IDENTITY = (short)0; + private static final short UMAX_IDENTITY = (short)0; // Minimum unsigned value + private static final short UMIN_IDENTITY = (short)-1; // Maximum unsigned value + private static final short XOR_IDENTITY = (short)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 512); static void assertArraysStrictlyEquals(short[] r, short[] a) { @@ -3602,7 +3614,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ANDReduce(short[] a, int idx) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3611,7 +3623,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ANDReduceAll(short[] a) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3623,20 +3635,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void ANDReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + short v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3644,8 +3651,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::ANDReduce, Short512VectorTests::ANDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = AND_IDENTITY; + + Assert.assertEquals((short) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id & x), x); + Assert.assertEquals((short) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3655,7 +3685,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ANDReduceAllMasked(short[] a, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3669,20 +3699,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + short v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3691,7 +3716,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ORReduce(short[] a, int idx) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3700,7 +3725,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ORReduceAll(short[] a) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3715,17 +3740,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + short v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3733,8 +3753,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::ORReduce, Short512VectorTests::ORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = OR_IDENTITY; + + Assert.assertEquals((short) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id | x), x); + Assert.assertEquals((short) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static short ORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3744,7 +3787,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3761,17 +3804,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + short v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3780,7 +3818,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short XORReduce(short[] a, int idx) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3789,7 +3827,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short XORReduceAll(short[] a) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3804,17 +3842,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + short v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3822,8 +3855,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::XORReduce, Short512VectorTests::XORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = XOR_IDENTITY; + + Assert.assertEquals((short) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id ^ x), x); + Assert.assertEquals((short) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static short XORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3833,7 +3889,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short XORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3850,17 +3906,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + short v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3869,7 +3920,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ADDReduce(short[] a, int idx) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3878,7 +3929,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ADDReduceAll(short[] a) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3893,17 +3944,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3911,8 +3957,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::ADDReduce, Short512VectorTests::ADDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + Assert.assertEquals((short) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id + x), x); + Assert.assertEquals((short) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3922,7 +3991,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short ADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3939,17 +4008,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3958,7 +4022,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MULReduce(short[] a, int idx) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3967,7 +4031,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MULReduceAll(short[] a) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3979,20 +4043,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void MULReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4000,8 +4059,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::MULReduce, Short512VectorTests::MULReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + Assert.assertEquals((short) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id * x), x); + Assert.assertEquals((short) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4011,7 +4093,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MULReduceAllMasked(short[] a, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4025,20 +4107,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4047,7 +4124,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.min(res, a[i]); } @@ -4056,7 +4133,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduce(a, i)); } @@ -4068,20 +4145,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void MINReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4089,8 +4161,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::MINReduce, Short512VectorTests::MINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + Assert.assertEquals((short) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) Math.min(id, x), x); + Assert.assertEquals((short) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.min(res, a[i]); @@ -4100,7 +4195,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4114,20 +4209,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4136,7 +4226,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.max(res, a[i]); } @@ -4145,7 +4235,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduce(a, i)); } @@ -4157,20 +4247,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void MAXReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4178,8 +4263,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::MAXReduce, Short512VectorTests::MAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + Assert.assertEquals((short) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) Math.max(id, x), x); + Assert.assertEquals((short) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.max(res, a[i]); @@ -4189,7 +4297,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short MAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4203,20 +4311,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4225,7 +4328,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short UMINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.minUnsigned(res, a[i]); } @@ -4234,7 +4337,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short UMINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4246,20 +4349,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void UMINReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + short v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4267,8 +4365,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::UMINReduce, Short512VectorTests::UMINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMIN_IDENTITY; + + Assert.assertEquals((short) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.minUnsigned(res, a[i]); @@ -4278,7 +4399,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short UMINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4292,20 +4413,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + short v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4314,7 +4430,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short UMAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.maxUnsigned(res, a[i]); } @@ -4323,7 +4439,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short UMAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4335,20 +4451,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void UMAXReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + short v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4356,8 +4467,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::UMAXReduce, Short512VectorTests::UMAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMAX_IDENTITY; + + Assert.assertEquals((short) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.maxUnsigned(res, a[i]); @@ -4367,7 +4501,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short UMAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4381,20 +4515,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + short v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4403,7 +4532,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduce(short[] a, int idx) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4412,7 +4541,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAll(short[] a) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4424,20 +4553,15 @@ public class Short512VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 0; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4445,8 +4569,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::FIRST_NONZEROReduce, Short512VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4456,7 +4603,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4470,20 +4617,15 @@ public class Short512VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 0; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4540,7 +4682,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short SUADDReduce(short[] a, int idx) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4549,7 +4691,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short SUADDReduceAll(short[] a) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4564,17 +4706,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + short v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4582,8 +4719,31 @@ public class Short512VectorTests extends AbstractVectorTest { Short512VectorTests::SUADDReduce, Short512VectorTests::SUADDReduceAll); } + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = SUADD_IDENTITY; + + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4593,7 +4753,7 @@ public class Short512VectorTests extends AbstractVectorTest { } static short SUADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4609,17 +4769,12 @@ public class Short512VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + short v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/Short64VectorTests.java b/test/jdk/jdk/incubator/vector/Short64VectorTests.java index 83d5cfd18e8..9ec8ac08789 100644 --- a/test/jdk/jdk/incubator/vector/Short64VectorTests.java +++ b/test/jdk/jdk/incubator/vector/Short64VectorTests.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 @@ -63,9 +63,21 @@ public class Short64VectorTests extends AbstractVectorTest { static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - private static final short CONST_SHIFT = Short.SIZE / 2; + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short AND_IDENTITY = (short)-1; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = Short.MIN_VALUE; + private static final short MIN_IDENTITY = Short.MAX_VALUE; + private static final short MUL_IDENTITY = (short)1; + private static final short OR_IDENTITY = (short)0; + private static final short SUADD_IDENTITY = (short)0; + private static final short UMAX_IDENTITY = (short)0; // Minimum unsigned value + private static final short UMIN_IDENTITY = (short)-1; // Maximum unsigned value + private static final short XOR_IDENTITY = (short)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / 64); static void assertArraysStrictlyEquals(short[] r, short[] a) { @@ -3602,7 +3614,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ANDReduce(short[] a, int idx) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3611,7 +3623,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ANDReduceAll(short[] a) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3623,20 +3635,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void ANDReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + short v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3644,8 +3651,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::ANDReduce, Short64VectorTests::ANDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = AND_IDENTITY; + + Assert.assertEquals((short) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id & x), x); + Assert.assertEquals((short) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3655,7 +3685,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ANDReduceAllMasked(short[] a, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3669,20 +3699,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + short v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3691,7 +3716,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ORReduce(short[] a, int idx) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3700,7 +3725,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ORReduceAll(short[] a) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3715,17 +3740,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + short v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3733,8 +3753,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::ORReduce, Short64VectorTests::ORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = OR_IDENTITY; + + Assert.assertEquals((short) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id | x), x); + Assert.assertEquals((short) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static short ORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3744,7 +3787,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3761,17 +3804,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + short v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3780,7 +3818,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short XORReduce(short[] a, int idx) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3789,7 +3827,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short XORReduceAll(short[] a) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3804,17 +3842,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + short v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3822,8 +3855,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::XORReduce, Short64VectorTests::XORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = XOR_IDENTITY; + + Assert.assertEquals((short) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id ^ x), x); + Assert.assertEquals((short) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static short XORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3833,7 +3889,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short XORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3850,17 +3906,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + short v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3869,7 +3920,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ADDReduce(short[] a, int idx) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3878,7 +3929,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ADDReduceAll(short[] a) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3893,17 +3944,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3911,8 +3957,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::ADDReduce, Short64VectorTests::ADDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + Assert.assertEquals((short) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id + x), x); + Assert.assertEquals((short) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3922,7 +3991,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short ADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3939,17 +4008,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3958,7 +4022,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MULReduce(short[] a, int idx) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3967,7 +4031,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MULReduceAll(short[] a) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3979,20 +4043,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void MULReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4000,8 +4059,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::MULReduce, Short64VectorTests::MULReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + Assert.assertEquals((short) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id * x), x); + Assert.assertEquals((short) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4011,7 +4093,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MULReduceAllMasked(short[] a, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4025,20 +4107,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4047,7 +4124,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.min(res, a[i]); } @@ -4056,7 +4133,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduce(a, i)); } @@ -4068,20 +4145,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void MINReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4089,8 +4161,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::MINReduce, Short64VectorTests::MINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + Assert.assertEquals((short) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) Math.min(id, x), x); + Assert.assertEquals((short) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.min(res, a[i]); @@ -4100,7 +4195,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4114,20 +4209,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4136,7 +4226,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.max(res, a[i]); } @@ -4145,7 +4235,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduce(a, i)); } @@ -4157,20 +4247,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void MAXReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4178,8 +4263,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::MAXReduce, Short64VectorTests::MAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + Assert.assertEquals((short) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) Math.max(id, x), x); + Assert.assertEquals((short) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.max(res, a[i]); @@ -4189,7 +4297,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short MAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4203,20 +4311,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4225,7 +4328,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short UMINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.minUnsigned(res, a[i]); } @@ -4234,7 +4337,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short UMINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4246,20 +4349,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void UMINReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + short v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4267,8 +4365,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::UMINReduce, Short64VectorTests::UMINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMIN_IDENTITY; + + Assert.assertEquals((short) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.minUnsigned(res, a[i]); @@ -4278,7 +4399,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short UMINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4292,20 +4413,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + short v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4314,7 +4430,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short UMAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.maxUnsigned(res, a[i]); } @@ -4323,7 +4439,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short UMAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4335,20 +4451,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void UMAXReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + short v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4356,8 +4467,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::UMAXReduce, Short64VectorTests::UMAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMAX_IDENTITY; + + Assert.assertEquals((short) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.maxUnsigned(res, a[i]); @@ -4367,7 +4501,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short UMAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4381,20 +4515,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + short v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4403,7 +4532,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduce(short[] a, int idx) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4412,7 +4541,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAll(short[] a) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4424,20 +4553,15 @@ public class Short64VectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 0; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4445,8 +4569,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::FIRST_NONZEROReduce, Short64VectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4456,7 +4603,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4470,20 +4617,15 @@ public class Short64VectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 0; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4540,7 +4682,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short SUADDReduce(short[] a, int idx) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4549,7 +4691,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short SUADDReduceAll(short[] a) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4564,17 +4706,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + short v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4582,8 +4719,31 @@ public class Short64VectorTests extends AbstractVectorTest { Short64VectorTests::SUADDReduce, Short64VectorTests::SUADDReduceAll); } + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = SUADD_IDENTITY; + + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4593,7 +4753,7 @@ public class Short64VectorTests extends AbstractVectorTest { } static short SUADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4609,17 +4769,12 @@ public class Short64VectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + short v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java b/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java index 45ba8db51c1..ad2efd3575d 100644 --- a/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.java +++ b/test/jdk/jdk/incubator/vector/ShortMaxVectorTests.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 @@ -71,6 +71,19 @@ public class ShortMaxVectorTests extends AbstractVectorTest { private static final short CONST_SHIFT = Short.SIZE / 2; + // Identity values for reduction operations + private static final short ADD_IDENTITY = (short)0; + private static final short AND_IDENTITY = (short)-1; + private static final short FIRST_NONZERO_IDENTITY = (short)0; + private static final short MAX_IDENTITY = Short.MIN_VALUE; + private static final short MIN_IDENTITY = Short.MAX_VALUE; + private static final short MUL_IDENTITY = (short)1; + private static final short OR_IDENTITY = (short)0; + private static final short SUADD_IDENTITY = (short)0; + private static final short UMAX_IDENTITY = (short)0; // Minimum unsigned value + private static final short UMIN_IDENTITY = (short)-1; // Maximum unsigned value + private static final short XOR_IDENTITY = (short)0; + static final int BUFFER_REPS = Integer.getInteger("jdk.incubator.vector.test.buffer-vectors", 25000 / Max); static void assertArraysStrictlyEquals(short[] r, short[] a) { @@ -3607,7 +3620,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ANDReduce(short[] a, int idx) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res &= a[i]; } @@ -3616,7 +3629,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ANDReduceAll(short[] a) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduce(a, i); } @@ -3628,20 +3641,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void ANDReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND); + short v = av.reduceLanes(VectorOperators.AND); + r[i] = v; + ra &= v; } } @@ -3649,8 +3657,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::ANDReduce, ShortMaxVectorTests::ANDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ANDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = AND_IDENTITY; + + Assert.assertEquals((short) (id & id), id, + "AND(AND_IDENTITY, AND_IDENTITY) != AND_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id & x), x); + Assert.assertEquals((short) (x & id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id & x), x, + "AND(AND_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x & id), x, + "AND(" + x + ", AND_IDENTITY) != " + x); + } + } + static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res &= a[i]; @@ -3660,7 +3691,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ANDReduceAllMasked(short[] a, boolean[] mask) { - short res = -1; + short res = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res &= ANDReduceMasked(a, i, mask); } @@ -3674,20 +3705,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = AND_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra &= av.reduceLanes(VectorOperators.AND, vmask); + short v = av.reduceLanes(VectorOperators.AND, vmask); + r[i] = v; + ra &= v; } } @@ -3696,7 +3722,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ORReduce(short[] a, int idx) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res |= a[i]; } @@ -3705,7 +3731,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ORReduceAll(short[] a) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduce(a, i); } @@ -3720,17 +3746,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR); + short v = av.reduceLanes(VectorOperators.OR); + r[i] = v; + ra |= v; } } @@ -3738,8 +3759,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::ORReduce, ShortMaxVectorTests::ORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = OR_IDENTITY; + + Assert.assertEquals((short) (id | id), id, + "OR(OR_IDENTITY, OR_IDENTITY) != OR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id | x), x); + Assert.assertEquals((short) (x | id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id | x), x, + "OR(OR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x | id), x, + "OR(" + x + ", OR_IDENTITY) != " + x); + } + } + static short ORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res |= a[i]; @@ -3749,7 +3793,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res |= ORReduceMasked(a, i, mask); } @@ -3766,17 +3810,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = OR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.OR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra |= av.reduceLanes(VectorOperators.OR, vmask); + short v = av.reduceLanes(VectorOperators.OR, vmask); + r[i] = v; + ra |= v; } } @@ -3785,7 +3824,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short XORReduce(short[] a, int idx) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res ^= a[i]; } @@ -3794,7 +3833,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short XORReduceAll(short[] a) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduce(a, i); } @@ -3809,17 +3848,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR); + short v = av.reduceLanes(VectorOperators.XOR); + r[i] = v; + ra ^= v; } } @@ -3827,8 +3861,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::XORReduce, ShortMaxVectorTests::XORReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void XORReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = XOR_IDENTITY; + + Assert.assertEquals((short) (id ^ id), id, + "XOR(XOR_IDENTITY, XOR_IDENTITY) != XOR_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id ^ x), x); + Assert.assertEquals((short) (x ^ id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id ^ x), x, + "XOR(XOR_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x ^ id), x, + "XOR(" + x + ", XOR_IDENTITY) != " + x); + } + } + static short XORReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res ^= a[i]; @@ -3838,7 +3895,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short XORReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res ^= XORReduceMasked(a, i, mask); } @@ -3855,17 +3912,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = XOR_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.XOR, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra ^= av.reduceLanes(VectorOperators.XOR, vmask); + short v = av.reduceLanes(VectorOperators.XOR, vmask); + r[i] = v; + ra ^= v; } } @@ -3874,7 +3926,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ADDReduce(short[] a, int idx) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res += a[i]; } @@ -3883,7 +3935,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ADDReduceAll(short[] a) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduce(a, i); } @@ -3898,17 +3950,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD); + short v = av.reduceLanes(VectorOperators.ADD); + r[i] = v; + ra += v; } } @@ -3916,8 +3963,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::ADDReduce, ShortMaxVectorTests::ADDReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void ADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = ADD_IDENTITY; + + Assert.assertEquals((short) (id + id), id, + "ADD(ADD_IDENTITY, ADD_IDENTITY) != ADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id + x), x); + Assert.assertEquals((short) (x + id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id + x), x, + "ADD(ADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x + id), x, + "ADD(" + x + ", ADD_IDENTITY) != " + x); + } + } + static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res += a[i]; @@ -3927,7 +3997,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short ADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res += ADDReduceMasked(a, i, mask); } @@ -3944,17 +4014,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = ADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.ADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra += av.reduceLanes(VectorOperators.ADD, vmask); + short v = av.reduceLanes(VectorOperators.ADD, vmask); + r[i] = v; + ra += v; } } @@ -3963,7 +4028,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MULReduce(short[] a, int idx) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res *= a[i]; } @@ -3972,7 +4037,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MULReduceAll(short[] a) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduce(a, i); } @@ -3984,20 +4049,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void MULReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL); + short v = av.reduceLanes(VectorOperators.MUL); + r[i] = v; + ra *= v; } } @@ -4005,8 +4065,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::MULReduce, ShortMaxVectorTests::MULReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MULReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MUL_IDENTITY; + + Assert.assertEquals((short) (id * id), id, + "MUL(MUL_IDENTITY, MUL_IDENTITY) != MUL_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) (id * x), x); + Assert.assertEquals((short) (x * id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) (id * x), x, + "MUL(MUL_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) (x * id), x, + "MUL(" + x + ", MUL_IDENTITY) != " + x); + } + } + static short MULReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res *= a[i]; @@ -4016,7 +4099,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MULReduceAllMasked(short[] a, boolean[] mask) { - short res = 1; + short res = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res *= MULReduceMasked(a, i, mask); } @@ -4030,20 +4113,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MUL_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra *= av.reduceLanes(VectorOperators.MUL, vmask); + short v = av.reduceLanes(VectorOperators.MUL, vmask); + r[i] = v; + ra *= v; } } @@ -4052,7 +4130,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.min(res, a[i]); } @@ -4061,7 +4139,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduce(a, i)); } @@ -4073,20 +4151,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void MINReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN)); + short v = av.reduceLanes(VectorOperators.MIN); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4094,8 +4167,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::MINReduce, ShortMaxVectorTests::MINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MIN_IDENTITY; + + Assert.assertEquals((short) Math.min(id, id), id, + "MIN(MIN_IDENTITY, MIN_IDENTITY) != MIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) Math.min(id, x), x); + Assert.assertEquals((short) Math.min(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.min(id, x), x, + "MIN(MIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.min(x, id), x, + "MIN(" + x + ", MIN_IDENTITY) != " + x); + } + } + static short MINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.min(res, a[i]); @@ -4105,7 +4201,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.min(res, MINReduceMasked(a, i, mask)); } @@ -4119,20 +4215,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.min(ra, av.reduceLanes(VectorOperators.MIN, vmask)); + short v = av.reduceLanes(VectorOperators.MIN, vmask); + r[i] = v; + ra = (short) Math.min(ra, v); } } @@ -4141,7 +4232,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) Math.max(res, a[i]); } @@ -4150,7 +4241,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduce(a, i)); } @@ -4162,20 +4253,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void MAXReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX)); + short v = av.reduceLanes(VectorOperators.MAX); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4183,8 +4269,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::MAXReduce, ShortMaxVectorTests::MAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void MAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = MAX_IDENTITY; + + Assert.assertEquals((short) Math.max(id, id), id, + "MAX(MAX_IDENTITY, MAX_IDENTITY) != MAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) Math.max(id, x), x); + Assert.assertEquals((short) Math.max(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) Math.max(id, x), x, + "MAX(MAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) Math.max(x, id), x, + "MAX(" + x + ", MAX_IDENTITY) != " + x); + } + } + static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) Math.max(res, a[i]); @@ -4194,7 +4303,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short MAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) Math.max(res, MAXReduceMasked(a, i, mask)); } @@ -4208,20 +4317,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = MAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) Math.max(ra, av.reduceLanes(VectorOperators.MAX, vmask)); + short v = av.reduceLanes(VectorOperators.MAX, vmask); + r[i] = v; + ra = (short) Math.max(ra, v); } } @@ -4230,7 +4334,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short UMINReduce(short[] a, int idx) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.minUnsigned(res, a[i]); } @@ -4239,7 +4343,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short UMINReduceAll(short[] a) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduce(a, i)); } @@ -4251,20 +4355,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void UMINReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN)); + short v = av.reduceLanes(VectorOperators.UMIN); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4272,8 +4371,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::UMINReduce, ShortMaxVectorTests::UMINReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMINReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMIN_IDENTITY; + + Assert.assertEquals((short) VectorMath.minUnsigned(id, id), id, + "UMIN(UMIN_IDENTITY, UMIN_IDENTITY) != UMIN_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.minUnsigned(id, x), x, + "UMIN(UMIN_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.minUnsigned(x, id), x, + "UMIN(" + x + ", UMIN_IDENTITY) != " + x); + } + } + static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.minUnsigned(res, a[i]); @@ -4283,7 +4405,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short UMINReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MAX_VALUE; + short res = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.minUnsigned(res, UMINReduceMasked(a, i, mask)); } @@ -4297,20 +4419,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMIN_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.minUnsigned(ra, av.reduceLanes(VectorOperators.UMIN, vmask)); + short v = av.reduceLanes(VectorOperators.UMIN, vmask); + r[i] = v; + ra = (short) VectorMath.minUnsigned(ra, v); } } @@ -4319,7 +4436,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short UMAXReduce(short[] a, int idx) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.maxUnsigned(res, a[i]); } @@ -4328,7 +4445,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short UMAXReduceAll(short[] a) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduce(a, i)); } @@ -4340,20 +4457,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void UMAXReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX)); + short v = av.reduceLanes(VectorOperators.UMAX); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4361,8 +4473,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::UMAXReduce, ShortMaxVectorTests::UMAXReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void UMAXReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = UMAX_IDENTITY; + + Assert.assertEquals((short) VectorMath.maxUnsigned(id, id), id, + "UMAX(UMAX_IDENTITY, UMAX_IDENTITY) != UMAX_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.maxUnsigned(id, x), x, + "UMAX(UMAX_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.maxUnsigned(x, id), x, + "UMAX(" + x + ", UMAX_IDENTITY) != " + x); + } + } + static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.maxUnsigned(res, a[i]); @@ -4372,7 +4507,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short UMAXReduceAllMasked(short[] a, boolean[] mask) { - short res = Short.MIN_VALUE; + short res = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.maxUnsigned(res, UMAXReduceMasked(a, i, mask)); } @@ -4386,20 +4521,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = UMAX_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.maxUnsigned(ra, av.reduceLanes(VectorOperators.UMAX, vmask)); + short v = av.reduceLanes(VectorOperators.UMAX, vmask); + r[i] = v; + ra = (short) VectorMath.maxUnsigned(ra, v); } } @@ -4408,7 +4538,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduce(short[] a, int idx) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = firstNonZero(res, a[i]); } @@ -4417,7 +4547,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAll(short[] a) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduce(a, i)); } @@ -4429,20 +4559,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { static void FIRST_NONZEROReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 0; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4450,8 +4575,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::FIRST_NONZEROReduce, ShortMaxVectorTests::FIRST_NONZEROReduceAll); } + @Test(dataProvider = "shortUnaryOpProvider") + static void FIRST_NONZEROReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = FIRST_NONZERO_IDENTITY; + + Assert.assertEquals(firstNonZero(id, id), id, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, FIRST_NONZERO_IDENTITY) != FIRST_NONZERO_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(firstNonZero(id, x), x); + Assert.assertEquals(firstNonZero(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(firstNonZero(id, x), x, + "FIRST_NONZERO(FIRST_NONZERO_IDENTITY, " + x + ") != " + x); + Assert.assertEquals(firstNonZero(x, id), x, + "FIRST_NONZERO(" + x + ", FIRST_NONZERO_IDENTITY) != " + x); + } + } + static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = firstNonZero(res, a[i]); @@ -4461,7 +4609,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short FIRST_NONZEROReduceAllMasked(short[] a, boolean[] mask) { - short res = (short) 0; + short res = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = firstNonZero(res, FIRST_NONZEROReduceMasked(a, i, mask)); } @@ -4475,20 +4623,15 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 0; + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = FIRST_NONZERO_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = firstNonZero(ra, av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask)); + short v = av.reduceLanes(VectorOperators.FIRST_NONZERO, vmask); + r[i] = v; + ra = firstNonZero(ra, v); } } @@ -4545,7 +4688,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short SUADDReduce(short[] a, int idx) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); } @@ -4554,7 +4697,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short SUADDReduceAll(short[] a) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduce(a, i)); } @@ -4569,17 +4712,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD)); + short v = av.reduceLanes(VectorOperators.SUADD); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } @@ -4587,8 +4725,31 @@ public class ShortMaxVectorTests extends AbstractVectorTest { ShortMaxVectorTests::SUADDReduce, ShortMaxVectorTests::SUADDReduceAll); } + @Test(dataProvider = "shortSaturatingUnaryOpProvider") + static void SUADDReduceIdentityValueTests(IntFunction fa) { + short[] a = fa.apply(SPECIES.length()); + short id = SUADD_IDENTITY; + + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, id), id, + "SUADD(SUADD_IDENTITY, SUADD_IDENTITY) != SUADD_IDENTITY"); + + short x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(id, x), x, + "SUADD(SUADD_IDENTITY, " + x + ") != " + x); + Assert.assertEquals((short) VectorMath.addSaturatingUnsigned(x, id), x, + "SUADD(" + x + ", SUADD_IDENTITY) != " + x); + } + } + static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = idx; i < (idx + SPECIES.length()); i++) { if (mask[i % SPECIES.length()]) res = (short) VectorMath.addSaturatingUnsigned(res, a[i]); @@ -4598,7 +4759,7 @@ public class ShortMaxVectorTests extends AbstractVectorTest { } static short SUADDReduceAllMasked(short[] a, boolean[] mask) { - short res = 0; + short res = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { res = (short) VectorMath.addSaturatingUnsigned(res, SUADDReduceMasked(a, i, mask)); } @@ -4614,17 +4775,12 @@ public class ShortMaxVectorTests extends AbstractVectorTest { short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { + ra = SUADD_IDENTITY; for (int i = 0; i < a.length; i += SPECIES.length()) { ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.SUADD, vmask); - } - } - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 0; - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - ra = (short) VectorMath.addSaturatingUnsigned(ra, av.reduceLanes(VectorOperators.SUADD, vmask)); + short v = av.reduceLanes(VectorOperators.SUADD, vmask); + r[i] = v; + ra = (short) VectorMath.addSaturatingUnsigned(ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/gen-template.sh b/test/jdk/jdk/incubator/vector/gen-template.sh index d9bae116da2..d2528b13b78 100644 --- a/test/jdk/jdk/incubator/vector/gen-template.sh +++ b/test/jdk/jdk/incubator/vector/gen-template.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# 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 @@ -511,23 +511,23 @@ gen_binary_bcst_op_no_masked "MAX+max" "Math.max(a, b)" gen_saturating_binary_op_associative "SUADD" "VectorMath.addSaturatingUnsigned(a, b)" "BITWISE" # Reductions. -gen_reduction_op "AND" "\&" "BITWISE" "-1" -gen_reduction_op "OR" "|" "BITWISE" "0" -gen_reduction_op "XOR" "^" "BITWISE" "0" -gen_reduction_op "ADD" "+" "" "0" -gen_reduction_op "MUL" "*" "" "1" -gen_reduction_op_func "MIN" "(\$type\$) Math.min" "" "\$Wideboxtype\$.\$MaxValue\$" -gen_reduction_op_func "MAX" "(\$type\$) Math.max" "" "\$Wideboxtype\$.\$MinValue\$" -gen_reduction_op_func "UMIN" "(\$type\$) VectorMath.minUnsigned" "BITWISE" "\$Wideboxtype\$.\$MaxValue\$" -gen_reduction_op_func "UMAX" "(\$type\$) VectorMath.maxUnsigned" "BITWISE" "\$Wideboxtype\$.\$MinValue\$" -gen_reduction_op_func "FIRST_NONZERO" "firstNonZero" "" "(\$type\$) 0" +gen_reduction_op "AND" "\&" "BITWISE" "AND_IDENTITY" +gen_reduction_op "OR" "|" "BITWISE" "OR_IDENTITY" +gen_reduction_op "XOR" "^" "BITWISE" "XOR_IDENTITY" +gen_reduction_op "ADD" "+" "" "ADD_IDENTITY" +gen_reduction_op "MUL" "*" "" "MUL_IDENTITY" +gen_reduction_op_func "MIN" "(\$type\$) Math.min" "" "MIN_IDENTITY" +gen_reduction_op_func "MAX" "(\$type\$) Math.max" "" "MAX_IDENTITY" +gen_reduction_op_func "UMIN" "(\$type\$) VectorMath.minUnsigned" "BITWISE" "UMIN_IDENTITY" +gen_reduction_op_func "UMAX" "(\$type\$) VectorMath.maxUnsigned" "BITWISE" "UMAX_IDENTITY" +gen_reduction_op_func "FIRST_NONZERO" "firstNonZero" "" "FIRST_NONZERO_IDENTITY" # Boolean reductions. gen_bool_reduction_op "anyTrue" "|" "BITWISE" "false" gen_bool_reduction_op "allTrue" "\&" "BITWISE" "true" # Saturating reductions. -gen_saturating_reduction_op "SUADD" "(\$type\$) VectorMath.addSaturatingUnsigned" "BITWISE" "0" +gen_saturating_reduction_op "SUADD" "(\$type\$) VectorMath.addSaturatingUnsigned" "BITWISE" "SUADD_IDENTITY" #Insert gen_with_op "withLane" "" "" "" diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op-func.template b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op-func.template index bf03a4d0430..73e02bf68dd 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op-func.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op-func.template @@ -2,20 +2,15 @@ $type$[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask<$Wideboxtype$> vmask = VectorMask.fromArray(SPECIES, mask, 0); - $type$ ra = [[TEST_INIT]]; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.[[TEST]], vmask); - } - } + $type$ ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { ra = [[TEST_INIT]]; for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - ra = [[TEST_OP]](ra, av.reduceLanes(VectorOperators.[[TEST]], vmask)); + $type$ v = av.reduceLanes(VectorOperators.[[TEST]], vmask); + r[i] = v; + ra = [[TEST_OP]](ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op.template b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op.template index f108491523e..82e20d594b6 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-Masked-op.template @@ -2,20 +2,15 @@ $type$[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask<$Wideboxtype$> vmask = VectorMask.fromArray(SPECIES, mask, 0); - $type$ ra = [[TEST_INIT]]; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.[[TEST]], vmask); - } - } + $type$ ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { ra = [[TEST_INIT]]; for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - ra [[TEST_OP]]= av.reduceLanes(VectorOperators.[[TEST]], vmask); + $type$ v = av.reduceLanes(VectorOperators.[[TEST]], vmask); + r[i] = v; + ra [[TEST_OP]]= v; } } diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op-func.template b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op-func.template index 7082ff0795e..94fae420cdd 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op-func.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op-func.template @@ -1,19 +1,14 @@ $type$[] a = fa.apply(SPECIES.length()); $type$[] r = fr.apply(SPECIES.length()); - $type$ ra = [[TEST_INIT]]; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.[[TEST]]); - } - } + $type$ ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { ra = [[TEST_INIT]]; for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - ra = [[TEST_OP]](ra, av.reduceLanes(VectorOperators.[[TEST]])); + $type$ v = av.reduceLanes(VectorOperators.[[TEST]]); + r[i] = v; + ra = [[TEST_OP]](ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op.template b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op.template index 66250c09213..3edc2b27e3e 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-Reduction-op.template @@ -1,19 +1,14 @@ $type$[] a = fa.apply(SPECIES.length()); $type$[] r = fr.apply(SPECIES.length()); - $type$ ra = [[TEST_INIT]]; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.[[TEST]]); - } - } + $type$ ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { ra = [[TEST_INIT]]; for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - ra [[TEST_OP]]= av.reduceLanes(VectorOperators.[[TEST]]); + $type$ v = av.reduceLanes(VectorOperators.[[TEST]]); + r[i] = v; + ra [[TEST_OP]]= v; } } diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-Masked-op.template b/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-Masked-op.template index bf03a4d0430..73e02bf68dd 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-Masked-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-Masked-op.template @@ -2,20 +2,15 @@ $type$[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask<$Wideboxtype$> vmask = VectorMask.fromArray(SPECIES, mask, 0); - $type$ ra = [[TEST_INIT]]; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.[[TEST]], vmask); - } - } + $type$ ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { ra = [[TEST_INIT]]; for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - ra = [[TEST_OP]](ra, av.reduceLanes(VectorOperators.[[TEST]], vmask)); + $type$ v = av.reduceLanes(VectorOperators.[[TEST]], vmask); + r[i] = v; + ra = [[TEST_OP]](ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-op.template b/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-op.template index 7082ff0795e..94fae420cdd 100644 --- a/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Kernel-SaturatingReduction-op.template @@ -1,19 +1,14 @@ $type$[] a = fa.apply(SPECIES.length()); $type$[] r = fr.apply(SPECIES.length()); - $type$ ra = [[TEST_INIT]]; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.[[TEST]]); - } - } + $type$ ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { ra = [[TEST_INIT]]; for (int i = 0; i < a.length; i += SPECIES.length()) { $abstractvectortype$ av = $abstractvectortype$.fromArray(SPECIES, a, i); - ra = [[TEST_OP]](ra, av.reduceLanes(VectorOperators.[[TEST]])); + $type$ v = av.reduceLanes(VectorOperators.[[TEST]]); + r[i] = v; + ra = [[TEST_OP]](ra, v); } } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op-func.template b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op-func.template index e6bcdb83c2e..c797ad907fb 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op-func.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op-func.template @@ -5,3 +5,26 @@ assertReductionArraysEquals(r, ra, a, $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll); } + + @Test(dataProvider = "$type$UnaryOpProvider") + static void [[TEST]]ReduceIdentityValueTests(IntFunction<$type$[]> fa) { + $type$[] a = fa.apply(SPECIES.length()); + $type$ id = [[TEST_INIT]]; + + Assert.assertEquals([[TEST_OP]](id, id), id, + "[[TEST]]([[TEST_INIT]], [[TEST_INIT]]) != [[TEST_INIT]]"); + + $type$ x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals([[TEST_OP]](id, x), x); + Assert.assertEquals([[TEST_OP]](x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals([[TEST_OP]](id, x), x, + "[[TEST]]([[TEST_INIT]], " + x + ") != " + x); + Assert.assertEquals([[TEST_OP]](x, id), x, + "[[TEST]](" + x + ", [[TEST_INIT]]) != " + x); + } + } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template index 5638940045d..f8438fa58c8 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Reduction-op.template @@ -9,3 +9,26 @@ $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll); #end[FP] } + + @Test(dataProvider = "$type$UnaryOpProvider") + static void [[TEST]]ReduceIdentityValueTests(IntFunction<$type$[]> fa) { + $type$[] a = fa.apply(SPECIES.length()); + $type$ id = [[TEST_INIT]]; + + Assert.assertEquals(($type$) (id [[TEST_OP]] id), id, + "[[TEST]]([[TEST_INIT]], [[TEST_INIT]]) != [[TEST_INIT]]"); + + $type$ x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals(($type$) (id [[TEST_OP]] x), x); + Assert.assertEquals(($type$) (x [[TEST_OP]] id), x); + } + } catch (AssertionError e) { + Assert.assertEquals(($type$) (id [[TEST_OP]] x), x, + "[[TEST]]([[TEST_INIT]], " + x + ") != " + x); + Assert.assertEquals(($type$) (x [[TEST_OP]] id), x, + "[[TEST]](" + x + ", [[TEST_INIT]]) != " + x); + } + } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-op.template b/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-op.template index fdd9e47167e..d961a29e5c8 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-op.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-SaturatingReduction-op.template @@ -5,3 +5,26 @@ assertReductionArraysEquals(r, ra, a, $vectorteststype$::[[TEST]]Reduce, $vectorteststype$::[[TEST]]ReduceAll); } + + @Test(dataProvider = "$type$SaturatingUnaryOpProvider") + static void [[TEST]]ReduceIdentityValueTests(IntFunction<$type$[]> fa) { + $type$[] a = fa.apply(SPECIES.length()); + $type$ id = [[TEST_INIT]]; + + Assert.assertEquals([[TEST_OP]](id, id), id, + "[[TEST]]([[TEST_INIT]], [[TEST_INIT]]) != [[TEST_INIT]]"); + + $type$ x = 0; + try { + for (int i = 0; i < a.length; i++) { + x = a[i]; + Assert.assertEquals([[TEST_OP]](id, x), x); + Assert.assertEquals([[TEST_OP]](x, id), x); + } + } catch (AssertionError e) { + Assert.assertEquals([[TEST_OP]](id, x), x, + "[[TEST]]([[TEST_INIT]], " + x + ") != " + x); + Assert.assertEquals([[TEST_OP]](x, id), x, + "[[TEST]](" + x + ", [[TEST_INIT]]) != " + x); + } + } diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-header.template b/test/jdk/jdk/incubator/vector/templates/Unit-header.template index 7e00cb1678c..e1ec6624022 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-header.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-header.template @@ -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 @@ -86,19 +86,37 @@ public class $vectorteststype$ extends AbstractVectorTest { #end[MaxBit] static final int INVOC_COUNT = Integer.getInteger("jdk.incubator.vector.test.loop-iterations", 100); - #if[MaxBit] + static VectorShape getMaxBit() { return VectorShape.S_Max_BIT; } private static final int Max = 256; // juts so we can do N/$bits$ #end[MaxBit] - #if[BITWISE] + private static final $type$ CONST_SHIFT = $Boxtype$.SIZE / 2; #end[BITWISE] + + // Identity values for reduction operations + private static final $type$ ADD_IDENTITY = ($type$)0; +#if[BITWISE] + private static final $type$ AND_IDENTITY = ($type$)-1; +#end[BITWISE] + private static final $type$ FIRST_NONZERO_IDENTITY = ($type$)0; + private static final $type$ MAX_IDENTITY = $Wideboxtype$.$MinValue$; + private static final $type$ MIN_IDENTITY = $Wideboxtype$.$MaxValue$; + private static final $type$ MUL_IDENTITY = ($type$)1; +#if[BITWISE] + private static final $type$ OR_IDENTITY = ($type$)0; + private static final $type$ SUADD_IDENTITY = ($type$)0; + private static final $type$ UMAX_IDENTITY = ($type$)0; // Minimum unsigned value + private static final $type$ UMIN_IDENTITY = ($type$)-1; // Maximum unsigned value + private static final $type$ XOR_IDENTITY = ($type$)0; +#end[BITWISE] #if[FP] + // for floating point addition reduction ops that may introduce rounding errors private static final $type$ RELATIVE_ROUNDING_ERROR_FACTOR_ADD = ($type$)10.0; From 624d7144f757c39215ae3dfed1b78cdd3b3e4f8e Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Wed, 14 Jan 2026 07:09:38 +0000 Subject: [PATCH 093/204] 8374435: assert(addp->is_AddP()) failed: must be AddP during EA with -XX:-UseCompressedOops Reviewed-by: chagedorn, thartmann --- src/hotspot/share/opto/escape.cpp | 35 ++++++++++- .../TestSplitLoadThroughPhiDuringEA.java | 58 +++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/compiler/escapeAnalysis/TestSplitLoadThroughPhiDuringEA.java diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 7b5c3855a9e..792384b1ec1 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.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 @@ -1058,6 +1058,39 @@ void ConnectionGraph::updates_after_load_split(Node* data_phi, Node* previous_lo // "new_load" might actually be a constant, parameter, etc. if (new_load->is_Load()) { Node* new_addp = new_load->in(MemNode::Address); + + // If new_load is a Load but not from an AddP, it means that the load is folded into another + // load. And since this load is not from a field, we cannot create a unique type for it. + // For example: + // + // if (b) { + // Holder h1 = new Holder(); + // Object o = ...; + // h.o = o.getClass(); + // } else { + // Holder h2 = ...; + // } + // Holder h = Phi(h1, h2); + // Object r = h.o; + // + // Then, splitting r through the merge point results in: + // + // if (b) { + // Holder h1 = new Holder(); + // Object o = ...; + // h.o = o.getClass(); + // Object o1 = h.o; + // } else { + // Holder h2 = ...; + // Object o2 = h2.o; + // } + // Object r = Phi(o1, o2); + // + // In this case, o1 is folded to o.getClass() which is a Load but not from an AddP, but from + // an OopHandle that is loaded from the Klass of o. + if (!new_addp->is_AddP()) { + continue; + } Node* base = get_addp_base(new_addp); // The base might not be something that we can create an unique diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestSplitLoadThroughPhiDuringEA.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestSplitLoadThroughPhiDuringEA.java new file mode 100644 index 00000000000..1221c1b8064 --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestSplitLoadThroughPhiDuringEA.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. + */ +package compiler.escapeAnalysis; + +import java.util.Objects; + +/* + * @test + * @bug 8374435 + * @summary assert during escape analysis when splitting a Load through a Phi because the input of + * the result Phi is a Load not from an AddP + * @run main/othervm -XX:-UseOnStackReplacement -XX:-UseCompressedOops ${test.main.class} + */ +public class TestSplitLoadThroughPhiDuringEA { + static class Holder { + Object o; + } + + public static void main(String[] args) { + Object o = new Object(); + Holder h = new Holder(); + for (int i = 0; i < 20000; i++) { + test(true, h, o); + test(false, h, o); + } + } + + private static Object test(boolean b, Holder h, Object o) { + h = Objects.requireNonNull(h); + if (b) { + h = new Holder(); + // This access has the pattern LoadP -> LoadP, which upsets the compiler because the + // pointer input of a LoadP is not an AddP + h.o = o.getClass(); + } + return h.o; + } +} From 1b6c2bdd7b57891ed35e3c067871d2c0bf282824 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 14 Jan 2026 07:21:25 +0000 Subject: [PATCH 094/204] 8375055: C2: Better dead loop detection printout Reviewed-by: chagedorn, qamai --- src/hotspot/share/opto/phaseX.cpp | 46 +++++++++++++++++++------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 1e4cd42e09a..c4bdc5e8903 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -762,26 +762,36 @@ bool PhaseGVN::is_dominator_helper(Node *d, Node *n, bool linear_only) { //------------------------------dead_loop_check-------------------------------- // Check for a simple dead loop when a data node references itself directly // or through an other data node excluding cons and phis. -void PhaseGVN::dead_loop_check( Node *n ) { - // Phi may reference itself in a loop - if (n != nullptr && !n->is_dead_loop_safe() && !n->is_CFG()) { - // Do 2 levels check and only data inputs. - bool no_dead_loop = true; - uint cnt = n->req(); - for (uint i = 1; i < cnt && no_dead_loop; i++) { - Node *in = n->in(i); - if (in == n) { - no_dead_loop = false; - } else if (in != nullptr && !in->is_dead_loop_safe()) { - uint icnt = in->req(); - for (uint j = 1; j < icnt && no_dead_loop; j++) { - if (in->in(j) == n || in->in(j) == in) - no_dead_loop = false; - } +void PhaseGVN::dead_loop_check(Node* n) { + // Phi may reference itself in a loop. + if (n == nullptr || n->is_dead_loop_safe() || n->is_CFG()) { + return; + } + + // Do 2 levels check and only data inputs. + for (uint i = 1; i < n->req(); i++) { + Node* in = n->in(i); + if (in == n) { + n->dump_bfs(100, nullptr, ""); + fatal("Dead loop detected, node references itself: %s (%d)", + n->Name(), n->_idx); + } + + if (in == nullptr || in->is_dead_loop_safe()) { + continue; + } + for (uint j = 1; j < in->req(); j++) { + if (in->in(j) == n) { + n->dump_bfs(100, nullptr, ""); + fatal("Dead loop detected, node input references current node: %s (%d) -> %s (%d)", + in->Name(), in->_idx, n->Name(), n->_idx); + } + if (in->in(j) == in) { + n->dump_bfs(100, nullptr, ""); + fatal("Dead loop detected, node input references itself: %s (%d)", + in->Name(), in->_idx); } } - if (!no_dead_loop) { n->dump_bfs(100, nullptr, ""); } - assert(no_dead_loop, "dead loop detected"); } } From 703665c13f754f3ba7858c4bb2549c76cbc22a62 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Wed, 14 Jan 2026 13:46:40 +0000 Subject: [PATCH 095/204] 8356684: jpackage error messages are not helpful Reviewed-by: almatvee --- .../jdk/jpackage/internal/Executor.java | 16 +- .../jdk/jpackage/internal/cli/Main.java | 64 +++++-- .../jpackage/internal/cli/StandardOption.java | 3 + .../jdk/jpackage/internal/cli/Utils.java | 4 +- ...xecutableAttributesWithCapturedOutput.java | 53 ++++++ .../internal/model/JPackageException.java | 3 +- .../model/SelfContainedException.java | 42 ++++ .../resources/MainResources.properties | 5 + .../internal/util/CommandOutputControl.java | 83 ++++++-- .../helpers/jdk/jpackage/test/Executor.java | 15 +- .../test/mock/CommandActionSpecs.java | 7 + .../jdk/jpackage/internal/cli/MainTest.java | 180 +++++++++++++++++- .../util/CommandOutputControlTest.java | 39 +++- 13 files changed, 440 insertions(+), 74 deletions(-) create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ExecutableAttributesWithCapturedOutput.java create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/SelfContainedException.java diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java index ca7a630b6d1..53c587ab37c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Executor.java @@ -41,6 +41,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.UnaryOperator; import java.util.spi.ToolProvider; import java.util.stream.Stream; +import jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput; import jdk.jpackage.internal.util.CommandLineFormat; import jdk.jpackage.internal.util.CommandOutputControl; import jdk.jpackage.internal.util.CommandOutputControl.ProcessAttributes; @@ -211,17 +212,11 @@ final class Executor { throw new IllegalStateException("No target to execute"); } - PrintableOutputBuilder printableOutputBuilder; - if (dumpOutput()) { - printableOutputBuilder = new PrintableOutputBuilder(coc); - } else { - printableOutputBuilder = null; - } - if (dumpOutput()) { Log.verbose(String.format("Running %s", CommandLineFormat.DEFAULT.apply(List.of(commandLine().getFirst())))); } + var printableOutputBuilder = new PrintableOutputBuilder(coc); Result result; try { if (timeout == null) { @@ -233,11 +228,12 @@ final class Executor { throw ExceptionBox.toUnchecked(ex); } + var printableOutput = printableOutputBuilder.create(); if (dumpOutput()) { - log(result, printableOutputBuilder.create()); + log(result, printableOutput); } - return result; + return ExecutableAttributesWithCapturedOutput.augmentResultWithOutput(result, printableOutput); } Result executeExpectSuccess() throws IOException { @@ -309,7 +305,7 @@ final class Executor { pid.ifPresent(p -> { sb.append(" [PID: ").append(p).append("]"); }); - sb.append(":\n ").append(result.execAttrs()); + sb.append(":\n ").append(result.execAttrs().printableCommandLine()); Log.verbose(sb.toString()); if (!printableOutput.isEmpty()) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java index 270ba0c927f..562a0d2d3c1 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java @@ -29,10 +29,12 @@ import static jdk.jpackage.internal.cli.StandardOption.HELP; import static jdk.jpackage.internal.cli.StandardOption.VERBOSE; import static jdk.jpackage.internal.cli.StandardOption.VERSION; +import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; +import java.io.StringReader; import java.io.UncheckedIOException; import java.nio.file.NoSuchFileException; import java.util.Collection; @@ -48,7 +50,11 @@ import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.Globals; import jdk.jpackage.internal.Log; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput; import jdk.jpackage.internal.model.JPackageException; +import jdk.jpackage.internal.model.SelfContainedException; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedExitCodeException; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedResultException; import jdk.jpackage.internal.util.Slot; import jdk.jpackage.internal.util.function.ExceptionBox; @@ -239,44 +245,60 @@ public final class Main { reportError(ex.getCause()); } else if (t instanceof UncheckedIOException ex) { reportError(ex.getCause()); + } else if (t instanceof UnexpectedResultException ex) { + printExternalCommandError(ex); } else { printError(t, Optional.empty()); } } - private void printError(Throwable t, Optional advice) { - var isAlienException = isAlienExceptionType(t); + private void printExternalCommandError(UnexpectedResultException ex) { + var result = ex.getResult(); + var commandOutput = ((ExecutableAttributesWithCapturedOutput)result.execAttrs()).printableOutput(); + var printableCommandLine = result.execAttrs().printableCommandLine(); - if (isAlienException || verbose) { + if (verbose) { + stackTracePrinter.accept(ex); + } + + String msg; + if (ex instanceof UnexpectedExitCodeException) { + msg = I18N.format("error.command-failed-unexpected-exit-code", result.getExitCode(), printableCommandLine); + } else if (result.exitCode().isPresent()) { + msg = I18N.format("error.command-failed-unexpected-output", printableCommandLine); + } else { + msg = I18N.format("error.command-failed-timed-out", printableCommandLine); + } + + messagePrinter.accept(I18N.format("message.error-header", msg)); + if (!verbose) { + messagePrinter.accept(I18N.format("message.failed-command-output-header")); + try (var lines = new BufferedReader(new StringReader(commandOutput)).lines()) { + lines.forEach(messagePrinter); + } + } + } + + private void printError(Throwable t, Optional advice) { + var isSelfContained = isSelfContained(t); + + if (!isSelfContained || verbose) { stackTracePrinter.accept(t); } String msg; - if (isAlienException) { - msg = t.toString(); - } else { + if (isSelfContained) { msg = t.getMessage(); + } else { + msg = t.toString(); } messagePrinter.accept(I18N.format("message.error-header", msg)); advice.ifPresent(v -> messagePrinter.accept(I18N.format("message.advice-header", v))); } - private static boolean isAlienExceptionType(Throwable t) { - switch (t) { - case JPackageException _ -> { - return false; - } - case Utils.ParseException _ -> { - return false; - } - case StandardOption.AddLauncherIllegalArgumentException _ -> { - return false; - } - default -> { - return true; - } - } + private static boolean isSelfContained(Throwable t) { + return t.getClass().getAnnotation(SelfContainedException.class) != null; } } 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 9c828705a4d..fadd9bacb1c 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 @@ -61,6 +61,8 @@ import jdk.jpackage.internal.model.BundlingOperationDescriptor; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; +import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.model.SelfContainedException; import jdk.jpackage.internal.util.SetBuilder; /** @@ -701,6 +703,7 @@ public final class StandardOption { } + @SelfContainedException static class AddLauncherIllegalArgumentException extends IllegalArgumentException { AddLauncherIllegalArgumentException(String message) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Utils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Utils.java index 0565a8f1235..a94de355284 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Utils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Utils.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 @@ -32,6 +32,7 @@ import java.util.function.IntPredicate; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; import jdk.jpackage.internal.model.BundlingEnvironment; +import jdk.jpackage.internal.model.SelfContainedException; final class Utils { @@ -98,6 +99,7 @@ final class Utils { }); } + @SelfContainedException static final class ParseException extends RuntimeException { ParseException(String msg) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ExecutableAttributesWithCapturedOutput.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ExecutableAttributesWithCapturedOutput.java new file mode 100644 index 00000000000..faed9d24d01 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/ExecutableAttributesWithCapturedOutput.java @@ -0,0 +1,53 @@ +/* + * 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.model; + +import java.util.List; +import java.util.Objects; +import jdk.jpackage.internal.util.CommandOutputControl.ExecutableAttributes; +import jdk.jpackage.internal.util.CommandOutputControl.Result; + +/** + * {@link ExecutableAttributes} augmented with printable command output. + */ +public record ExecutableAttributesWithCapturedOutput(ExecutableAttributes execAttrs, String printableOutput) + implements ExecutableAttributes { + + public ExecutableAttributesWithCapturedOutput { + Objects.requireNonNull(execAttrs); + Objects.requireNonNull(printableOutput); + } + + @Override + public List commandLine() { + return execAttrs.commandLine(); + } + + public static Result augmentResultWithOutput(Result result, String output) { + var execAttrs = new ExecutableAttributesWithCapturedOutput(result.execAttrs(), output); + return result.copyWithExecutableAttributes(execAttrs); + } +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/JPackageException.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/JPackageException.java index 9727f21d2d7..3d24d293f18 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/JPackageException.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/JPackageException.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. * 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,7 @@ import java.util.Objects; /** * Generic jpackage exception with non-null message. */ +@SelfContainedException public class JPackageException extends RuntimeException { public JPackageException(String msg) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/SelfContainedException.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/SelfContainedException.java new file mode 100644 index 00000000000..c7d1b3016ac --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/SelfContainedException.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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.internal.model; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for exceptions with self-contained error messages that don't + * require an additional context, like exception class name or a stack trace. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface SelfContainedException { +} 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 588a3702839..07460a18fe8 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 @@ -55,6 +55,11 @@ message.module-version=Using version "{0}" from module "{1}" as application vers message.error-header=Error: {0} message.advice-header=Advice to fix: {0} +message.failed-command-output-header=Command output: + +error.command-failed-unexpected-output=Unexpected output from executing the command {0} +error.command-failed-unexpected-exit-code=Unexpected exit code {0} from executing the command {1} +error.command-failed-timed-out=Timed-out command {0} error.version-string-empty=Version may not be empty string error.version-string-zero-length-component=Version [{0}] contains a zero length component diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java index e35439815b1..00fbdd71919 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/CommandOutputControl.java @@ -796,6 +796,10 @@ public final class CommandOutputControl { public interface ExecutableAttributes { List commandLine(); + + default String printableCommandLine() { + return CommandLineFormat.DEFAULT.apply(commandLine()); + } } public sealed interface Executable { @@ -812,11 +816,6 @@ public final class CommandOutputControl { Objects.requireNonNull(pid); commandLine.forEach(Objects::requireNonNull); } - - @Override - public String toString() { - return CommandLineFormat.DEFAULT.apply(commandLine()); - } } public record ToolProviderAttributes(String name, List args) implements ExecutableAttributes { @@ -825,11 +824,6 @@ public final class CommandOutputControl { args.forEach(Objects::requireNonNull); } - @Override - public String toString() { - return CommandLineFormat.DEFAULT.apply(commandLine()); - } - @Override public List commandLine() { return Stream.concat(Stream.of(name), args.stream()).toList(); @@ -837,14 +831,9 @@ public final class CommandOutputControl { } public static ExecutableAttributes EMPTY_EXECUTABLE_ATTRIBUTES = new ExecutableAttributes() { - @Override - public String toString() { - return ""; - } - @Override public List commandLine() { - return List.of(); + return List.of(""); } }; @@ -869,6 +858,51 @@ public final class CommandOutputControl { Objects.requireNonNull(execAttrs); } + public static Builder build() { + return new Builder(); + } + + public static final class Builder { + + public Result create() { + return new Result( + exitCode, + Optional.ofNullable(content).map(List::copyOf).map(StringListContent::new).map(c -> { + return new CommandOutput>(Optional.of(c), c.size(), true); + }), + Optional.empty(), + Optional.ofNullable(execAttrs).orElse(EMPTY_EXECUTABLE_ATTRIBUTES)); + } + + public Builder exitCode(int v) { + exitCode = Optional.of(v); + return this; + } + + public Builder noExitCode() { + exitCode = Optional.empty(); + return this; + } + + public Builder execAttrs(ExecutableAttributes v) { + execAttrs = v; + return this; + } + + public Builder content(List v) { + content = v; + return this; + } + + public Builder content(String... v) { + return content(List.of(v)); + } + + private ExecutableAttributes execAttrs; + private List content; + private Optional exitCode = Optional.empty(); + } + public Result(int exitCode) { this(Optional.of(exitCode), Optional.empty(), Optional.empty(), EMPTY_EXECUTABLE_ATTRIBUTES); } @@ -1019,7 +1053,8 @@ public final class CommandOutputControl { } private UnexpectedResultException(Result value) { - this(value, String.format("Unexpected result from executing the command %s", value.execAttrs())); + this(value, String.format("Unexpected result from executing the command %s", + value.execAttrs().printableCommandLine())); } public Result getResult() { @@ -1034,11 +1069,21 @@ public final class CommandOutputControl { public static final class UnexpectedExitCodeException extends UnexpectedResultException { public UnexpectedExitCodeException(Result value, String message) { - super(value, message); + super(requireExitCode(value), message); } public UnexpectedExitCodeException(Result value) { - this(value, String.format("Unexpected exit code %d from executing the command %s", value.getExitCode(), value.execAttrs())); + this(value, String.format("Unexpected exit code %d from executing the command %s", + requireExitCode(value).getExitCode(), value.execAttrs().printableCommandLine())); + } + + private static Result requireExitCode(Result v) { + Objects.requireNonNull(v); + if (v.exitCode().isPresent()) { + return v; + } else { + throw new IllegalArgumentException(); + } } private static final long serialVersionUID = 1L; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java index d4833eb9736..b508d4f5ffe 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java @@ -283,11 +283,11 @@ public final class Executor extends CommandArguments { long expectedExitCode = expectedExitCodes.getFirst(); TKit.assertEquals(expectedExitCode, getExitCode(), String.format( "Check command %s exited with %d code", - base.execAttrs(), expectedExitCode)); + base.execAttrs().printableCommandLine(), expectedExitCode)); } default -> { TKit.assertTrue(expectedExitCodes.contains(getExitCode()), String.format( "Check command %s exited with one of %s codes", - base.execAttrs(), expectedExitCodes.stream().sorted().toList())); + base.execAttrs().printableCommandLine(), expectedExitCodes.stream().sorted().toList())); } } return this; @@ -302,7 +302,7 @@ public final class Executor extends CommandArguments { } public String getPrintableCommandLine() { - return base.execAttrs().toString(); + return base.execAttrs().printableCommandLine(); } } @@ -515,21 +515,16 @@ public final class Executor extends CommandArguments { return String.format(format, CommandLineFormat.DEFAULT.apply(cmdline), cmdline.size()); } - private record ExecutableAttributes(CommandOutputControl.ExecutableAttributes base, String toStringValue) + private record ExecutableAttributes(CommandOutputControl.ExecutableAttributes base, String printableCommandLine) implements CommandOutputControl.ExecutableAttributes { ExecutableAttributes { Objects.requireNonNull(base); - if (toStringValue.isBlank()) { + if (printableCommandLine.isBlank()) { throw new IllegalArgumentException(); } } - @Override - public String toString() { - return toStringValue; - } - @Override public List commandLine() { return base.commandLine(); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java index e89e458c02d..ce3aa6f80e1 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/mock/CommandActionSpecs.java @@ -115,6 +115,13 @@ public record CommandActionSpecs(List specs) { return printToStderr(List.of(str)); } + public Builder argsListener(Consumer> listener) { + Objects.requireNonNull(listener); + return action(CommandActionSpec.create("args-listener", context -> { + listener.accept(context.args()); + })); + } + public Builder exit(int exitCode) { return action(CommandActionSpec.create(String.format("exit(%d)", exitCode), () -> { return exitCode; diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java index 365a8a50b33..79648260274 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.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 @@ -22,6 +22,7 @@ */ package jdk.jpackage.internal.cli; +import static jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput.augmentResultWithOutput; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -43,19 +44,31 @@ import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.UnaryOperator; +import java.util.spi.ToolProvider; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; +import jdk.jpackage.internal.Globals; +import jdk.jpackage.internal.MockUtils; import jdk.jpackage.internal.model.ConfigException; +import jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput; import jdk.jpackage.internal.model.JPackageException; +import jdk.jpackage.internal.util.CommandOutputControl; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedExitCodeException; +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedResultException; import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.test.Annotations; +import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JUnitAdapter; import jdk.jpackage.test.TKit; +import jdk.jpackage.test.mock.CommandActionSpecs; +import jdk.jpackage.test.mock.Script; +import jdk.jpackage.test.mock.VerbatimCommandMock; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; -public class MainTest extends JUnitAdapter.TestSrcInitializer { +public class MainTest extends JUnitAdapter { @ParameterizedTest @MethodSource @@ -78,6 +91,71 @@ public class MainTest extends JUnitAdapter.TestSrcInitializer { assertNotEquals(str, msg); } + /** + * Run app image packaging and simulate jlink failure. + *

      + * The test verifies that jpackage prints an error message with jlink exit code, + * command line, and output. + */ + @Annotations.Test + public void testFailedCommandOutput() { + + var jlinkMockExitCode = 17; + + var jlinkArgs = new ArrayList(); + + var jlinkMock = CommandActionSpecs.build() + .stdout("It").stderr("fell").stdout("apart") + .argsListener(jlinkArgs::addAll) + .exit(jlinkMockExitCode).create().toCommandMockBuilder().name("jlink-mock").create(); + + var script = Script.build() + // Replace jlink with the mock. + .map(Script.cmdlineStartsWith("jlink"), jlinkMock) + // Don't mock other external commands. + .map(_ -> true, VerbatimCommandMock.INSTANCE) + .createLoop(); + + var jpackageExitCode = 1; + + var jpackageToolProviderMock = new ToolProvider() { + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + var globalsMutator = MockUtils.buildJPackage().script(script).createGlobalsMutator(); + return Globals.main(() -> { + globalsMutator.accept(Globals.instance()); + + var result = ExecutionResult.create(args); + + var jlinkMockAttrs = new CommandOutputControl.ToolProviderAttributes(jlinkMock.name(), jlinkArgs); + + result.stdout().forEach(out::println); + result.stderr().forEach(err::println); + + assertEquals(jpackageExitCode, result.exitCode()); + assertEquals(List.of(), result.stdout()); + assertEquals(List.of( + I18N.format("message.error-header", I18N.format("error.command-failed-unexpected-exit-code", + jlinkMockExitCode, jlinkMockAttrs.printableCommandLine())), + I18N.format("message.failed-command-output-header"), + "It", "fell", "apart"), result.stderr()); + + return jpackageExitCode; + }); + } + + @Override + public String name() { + return "jpackage-mock"; + } + }; + + JPackageCommand.helloAppImage() + .ignoreDefaultVerbose(true) + .useToolProvider(jpackageToolProviderMock) + .execute(jpackageExitCode); + } + private static Collection testOutput() { return Stream.of( // Print the tool version @@ -165,6 +243,49 @@ public class MainTest extends JUnitAdapter.TestSrcInitializer { data.add(new ErrorReporterTestSpec(cause, expect.getKey(), verbose, expectedOutput)); } } + + var execAttrs = new CommandOutputControl.ProcessAttributes(Optional.of(12345L), List.of("foo", "--bar")); + for (var makeCause : List.>of( + ex -> ex, + ExceptionBox::toUnchecked + )) { + + for (var expect : List.of( + Map.entry( + augmentResultWithOutput( + CommandOutputControl.Result.build().exitCode(135).execAttrs(execAttrs).create(), + "The quick brown fox\njumps over the lazy dog" + ).unexpected("Kaput!"), + ExceptionFormatter.FAILED_COMMAND_UNEXPECTED_OUTPUT_MESSAGE + ), + Map.entry( + new UnexpectedExitCodeException(augmentResultWithOutput( + CommandOutputControl.Result.build().exitCode(135).create(), + "The quick brown fox\njumps" + )), + ExceptionFormatter.FAILED_COMMAND_UNEXPECTED_EXIT_CODE_MESSAGE + ), + Map.entry( + augmentResultWithOutput( + CommandOutputControl.Result.build().create(), + "The quick brown fox\njumps" + ).unexpected("Timed out!"), + ExceptionFormatter.FAILED_COMMAND_TIMEDOUT_MESSAGE + ) + )) { + var cause = makeCause.apply(expect.getKey()); + var expectedOutput = new ArrayList(); + if (verbose) { + expectedOutput.add(ExceptionFormatter.STACK_TRACE); + } + expectedOutput.add(expect.getValue()); + if (!verbose) { + expectedOutput.add(ExceptionFormatter.FAILED_COMMAND_OUTPUT); + } + data.add(new ErrorReporterTestSpec(cause, expect.getKey(), verbose, expectedOutput)); + } + } + } return data; @@ -350,7 +471,24 @@ public class MainTest extends JUnitAdapter.TestSrcInitializer { ex.printStackTrace(pw); } return sink.toString(); - }) + }), + FAILED_COMMAND_OUTPUT(ex -> { + var result = failedCommandResult(ex); + var commandOutput = ((ExecutableAttributesWithCapturedOutput)result.execAttrs()).printableOutput(); + + var sink = new StringWriter(); + var pw = new PrintWriter(sink); + + pw.println(I18N.format("message.failed-command-output-header")); + try (var lines = new BufferedReader(new StringReader(commandOutput)).lines()) { + lines.forEach(pw::println); + } + + return sink.toString(); + }), + FAILED_COMMAND_UNEXPECTED_OUTPUT_MESSAGE(errorMessage(CommandFailureType.UNEXPECTED_OUTPUT::getMessage)), + FAILED_COMMAND_UNEXPECTED_EXIT_CODE_MESSAGE(errorMessage(CommandFailureType.UNEXPECTED_EXIT_CODE::getMessage)), + FAILED_COMMAND_TIMEDOUT_MESSAGE(errorMessage(CommandFailureType.TIMEDOUT::getMessage)), ; ExceptionFormatter(Function formatter) { @@ -369,6 +507,42 @@ public class MainTest extends JUnitAdapter.TestSrcInitializer { }; } + private static CommandOutputControl.Result failedCommandResult(Exception ex) { + Objects.requireNonNull(ex); + if (ex instanceof UnexpectedResultException urex) { + return urex.getResult(); + } else { + throw new IllegalArgumentException(); + } + } + + private enum CommandFailureType { + UNEXPECTED_OUTPUT, + UNEXPECTED_EXIT_CODE, + TIMEDOUT, + ; + + String getMessage(Exception ex) { + var result = failedCommandResult(ex); + var printableCommandLine = result.execAttrs().printableCommandLine(); + switch (this) { + case TIMEDOUT -> { + return I18N.format("error.command-failed-timed-out", printableCommandLine); + } + case UNEXPECTED_EXIT_CODE -> { + return I18N.format("error.command-failed-unexpected-exit-code", result.getExitCode(), printableCommandLine); + } + case UNEXPECTED_OUTPUT -> { + return I18N.format("error.command-failed-unexpected-output", printableCommandLine); + } + default -> { + // Unreachable + throw ExceptionBox.reachedUnreachable(); + } + } + } + } + private final Function formatter; } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java index f1d8c142eb9..d71cf7c4d41 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/CommandOutputControlTest.java @@ -203,11 +203,11 @@ public class CommandOutputControlTest { exec = coc.createExecutable(new ProcessBuilder("runme", "--foo", "--baz=10")); } - assertEquals("runme --foo --baz=10", exec.attributes().toString()); + assertEquals("runme --foo --baz=10", exec.attributes().printableCommandLine()); } @Test - public void test_Result_no_args_ctor() { + public void test_Result_single_arg_ctor() { var result = new CommandOutputControl.Result(7); assertFalse(result.findContent().isPresent()); assertFalse(result.findStdout().isPresent()); @@ -216,6 +216,18 @@ public class CommandOutputControlTest { assertSame(Objects.requireNonNull(CommandOutputControl.EMPTY_EXECUTABLE_ATTRIBUTES), result.execAttrs()); } + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_Result_unexpected(boolean customMessage) throws IOException { + var result = new CommandOutputControl.Result(7); + + if (customMessage) { + assertEquals("Kaput!", result.unexpected("Kaput!").getMessage()); + } else { + assertEquals("Unexpected result from executing the command ", result.unexpected().getMessage()); + } + } + @Test public void test_Result_expectExitCode() throws IOException { var result = new CommandOutputControl.Result(7); @@ -276,14 +288,9 @@ public class CommandOutputControlTest { var empty = new CommandOutputControl.Result(0); var execAttrs = new CommandOutputControl.ExecutableAttributes() { - @Override - public String toString() { - return "foo"; - } - @Override public List commandLine() { - return List.of(); + return List.of("foo"); } }; @@ -295,6 +302,20 @@ public class CommandOutputControlTest { assertSame(execAttrs, copy.execAttrs()); } + @Test + public void test_UnexpectedExitCodeException_no_exit_code() { + + var resultWithoutExitCode = CommandOutputControl.Result.build().create(); + + assertThrowsExactly(IllegalArgumentException.class, () -> { + new CommandOutputControl.UnexpectedExitCodeException(resultWithoutExitCode); + }); + + assertThrowsExactly(IllegalArgumentException.class, () -> { + new CommandOutputControl.UnexpectedExitCodeException(resultWithoutExitCode, "Kaput!"); + }); + } + @ParameterizedTest @EnumSource(ExecutableType.class) public void test_timeout_expires(ExecutableType mode) throws InterruptedException, IOException { @@ -1781,7 +1802,7 @@ public class CommandOutputControlTest { } } - private final static class InterruptibleToolProvider implements ToolProvider { + private static final class InterruptibleToolProvider implements ToolProvider { InterruptibleToolProvider(ToolProvider impl) { this.impl = impl; From 20bd178b997b8bbf895877774d55d1a9e87c3038 Mon Sep 17 00:00:00 2001 From: Roger Calnan Date: Wed, 14 Jan 2026 14:08:21 +0000 Subject: [PATCH 096/204] 8373836: add anchors to the java options in the java man page Reviewed-by: jwilhelm, iris --- src/java.base/share/man/java.md | 494 ++++++++++++++++---------------- 1 file changed, 247 insertions(+), 247 deletions(-) diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index 30661a3f387..4343df663e6 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved. # 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 @@ To launch a source-file program: : Specifies the name of the class to be launched. Command-line entries following `classname` are the arguments for the main method. -`-jar` *jarfile* +[`-jar`]{#-jar} *jarfile* : Executes a program encapsulated in a JAR file. The *jarfile* argument is the name of a JAR file with a manifest that contains a line in the form `Main-Class:`*classname* that defines the class with the @@ -70,7 +70,7 @@ To launch a source-file program: is the source of all user classes, and other class path settings are ignored. If you're using JAR files, then see [jar](jar.html). -`-m` or `--module` *module*\[`/`*mainclass*\] +[`-m`]{#-m} or `--module` *module*\[`/`*mainclass*\] : Executes the main class in a module specified by *mainclass* if it is given, or, if it is not given, the value in the *module*. In other words, *mainclass* can be used when it is not specified by the module, or to @@ -370,7 +370,7 @@ the JVM. > **Note:** To specify an argument for a long option, you can use either `--`*name*`=`*value* or `--`*name* *value*. -`-agentlib:`*libname*\[`=`*options*\] +[`-agentlib:`]{#-agentlib}*libname*\[`=`*options*\] : Loads the specified native agent library. After the library name, a comma-separated list of options specific to the library can be used. If the option `-agentlib:foo` is specified, then the JVM attempts to @@ -393,12 +393,12 @@ the JVM. > `-agentlib:jdwp=transport=dt_socket,server=y,address=8000` -`-agentpath:`*pathname*\[`=`*options*\] +[`-agentpath:`]{#-agentpath}*pathname*\[`=`*options*\] : Loads the native agent library specified by the absolute path name. This option is equivalent to `-agentlib` but uses the full path and file name of the library. -`--class-path` *classpath*, `-classpath` *classpath*, or `-cp` *classpath* +[`--class-path`]{#--class-path} *classpath*, `-classpath` *classpath*, or `-cp` *classpath* : Specifies a list of directories, JAR files, and ZIP archives to search for class files. @@ -424,15 +424,15 @@ the JVM. expanded except by querying the environment, such as by calling `System.getenv("CLASSPATH")`. -`--disable-@files` +[`--disable-@files`]{#--disable-@files} : Can be used anywhere on the command line, including in an argument file, to prevent further `@filename` expansion. This option stops expanding `@`-argfiles after the option. -`--enable-preview` +[`--enable-preview`]{#--enable-preview} : Allows classes to depend on [preview features](https://docs.oracle.com/en/java/javase/12/language/index.html#JSLAN-GUID-5A82FE0E-0CA4-4F1F-B075-564874FE2823) of the release. -`--enable-native-access` *module*\[`,`*module*...\] +[`--enable-native-access`]{#--enable-native-access} *module*\[`,`*module*...\] : Native access involves access to code or data outside the Java runtime. This is generally unsafe and, if done incorrectly, might crash the JVM or result in memory corruption. Native access can occur as a result of calling a method that @@ -465,7 +465,7 @@ the JVM. run it with `--illegal-native-access=deny` along with any necessary `--enable-native-access` options. -`--enable-final-field-mutation` *module*\[,*module*...\] +[`--enable-final-field-mutation`]{#--enable-final-field-mutation} *module*\[,*module*...\] : Mutation of final fields is possible with the reflection API of the Java Platform. However, it compromises safety and performance in all programs. This option allows code in the specified modules to mutate final fields by reflection. @@ -498,13 +498,13 @@ the JVM. run it with `--illegal-final-field-mutation=deny` along with any necessary `--enable-final-field-mutation` options. -`--finalization=`*value* +[`--finalization=`]{#--finalization}*value* : Controls whether the JVM performs finalization of objects. Valid values are "enabled" and "disabled". Finalization is enabled by default, so the value "enabled" does nothing. The value "disabled" disables finalization, so that no finalizers are invoked. -`--module-path` *modulepath*... or `-p` *modulepath* +[`--module-path`]{#--module-path} *modulepath*... or `-p` *modulepath* : Specifies where to find application modules with a list of path elements. The elements of a module path can be a file path to a module or a directory containing modules. Each module is either a modular JAR or an @@ -513,7 +513,7 @@ the JVM. On Windows, semicolons (`;`) separate path elements in this list; on other platforms it is a colon (`:`). -`--upgrade-module-path` *modulepath*... +[`--upgrade-module-path`]{#--upgrade-module-path} *modulepath*... : Specifies where to find module replacements of upgradeable modules in the runtime image with a list of path elements. The elements of a module path can be a file path to a module or a directory @@ -523,33 +523,33 @@ the JVM. On Windows, semicolons (`;`) separate path elements in this list; on other platforms it is a colon (`:`). -`--add-modules` *module*\[`,`*module*...\] +[`--add-modules`]{#--add-modules} *module*\[`,`*module*...\] : Specifies the root modules to resolve in addition to the initial module. *module* can also be `ALL-DEFAULT`, `ALL-SYSTEM`, and `ALL-MODULE-PATH`. -`--list-modules` +[`--list-modules`]{#--list-modules} : Lists the observable modules and then exits. -`-d` *module\_name* or `--describe-module` *module\_name* +[`-d`]{#-d} *module\_name* or `--describe-module` *module\_name* : Describes a specified module and then exits. -`--dry-run` +[`--dry-run`]{#--dry-run} : Creates the VM but doesn't execute the main method. This `--dry-run` option might be useful for validating the command-line options such as the module system configuration. -`--validate-modules` +[`--validate-modules`]{#--validate-modules} : Validates all modules and exit. This option is helpful for finding conflicts and other errors with modules on the module path. -`-D`*property*`=`*value* +[`-D`]{#-D}*property*`=`*value* : Sets a system property value. The *property* variable is a string with no spaces that represents the name of the property. The *value* variable is a string that represents the value of the property. If *value* is a string with spaces, then enclose it in quotation marks (for example `-Dfoo="foo bar"`). -`-disableassertions`\[`:`\[*packagename*\]...\|`:`*classname*\] or `-da`\[`:`\[*packagename*\]...\|`:`*classname*\] +[`-disableassertions`]{#-disableassertions}\[`:`\[*packagename*\]...\|`:`*classname*\] or `-da`\[`:`\[*packagename*\]...\|`:`*classname*\] : Disables assertions. By default, assertions are disabled in all packages and classes. With no arguments, `-disableassertions` (`-da`) disables assertions in all packages and classes. With the *packagename* argument @@ -574,10 +574,10 @@ the JVM. > `java -ea:com.wombat.fruitbat... -da:com.wombat.fruitbat.Brickbat MyClass` -`-disablesystemassertions` or `-dsa` +[`-disablesystemassertions`]{#-disablesystemassertions} or `-dsa` : Disables assertions in all system classes. -`-enableassertions`\[`:`\[*packagename*\]...\|`:`*classname*\] or `-ea`\[`:`\[*packagename*\]...\|`:`*classname*\] +[`-enableassertions`]{#-enableassertions}\[`:`\[*packagename*\]...\|`:`*classname*\] or `-ea`\[`:`\[*packagename*\]...\|`:`*classname*\] : Enables assertions. By default, assertions are disabled in all packages and classes. With no arguments, `-enableassertions` (`-ea`) enables assertions in all packages and classes. With the *packagename* argument ending in @@ -604,28 +604,28 @@ the JVM. > `java -ea:com.wombat.fruitbat... -da:com.wombat.fruitbat.Brickbat MyClass` -`-enablesystemassertions` or `-esa` +[`-enablesystemassertions`]{#-enablesystemassertions} or `-esa` : Enables assertions in all system classes. -`-help`, `-h`, or `-?` +[`-help`]{#-help}, `-h`, or `-?` : Prints the help message to the error stream. -`--help` +[`--help`]{#--help} : Prints the help message to the output stream. -`-javaagent:`*jarpath*\[`=`*options*\] +[`-javaagent:`]{#-javaagent_}*jarpath*\[`=`*options*\] : Loads the specified Java programming language agent. See `java.lang.instrument`. -`--show-version` +[`--show-version`]{#--show-version} : Prints the product version to the output stream and continues. -`-showversion` +[`-showversion`]{#-showversion} : Prints the product version to the error stream and continues. -`--show-module-resolution` +[`--show-module-resolution`]{#--show-module-resolution} : Shows module resolution output during startup. -`-splash:`*imagepath* +[`-splash:`]{#-splash_}*imagepath* : Shows the splash screen with the image specified by *imagepath*. HiDPI scaled images are automatically supported and used if available. The unscaled image file name, such as `image.ext`, should always be passed as @@ -639,29 +639,29 @@ the JVM. See the SplashScreen API documentation for more information. -`-verbose:class` +[`-verbose:class`]{#-verbose_class} : Displays information about each loaded class. -`-verbose:gc` +[`-verbose:gc`]{#-verbose_gc} : Displays information about each garbage collection (GC) event. -`-verbose:jni` +[`-verbose:jni`]{#-verbose_jni} : Displays information about the use of native methods and other Java Native Interface (JNI) activity. -`-verbose:module` +[`-verbose:module`]{#-verbose_module} : Displays information about the modules in use. -`--version` +[`--version`]{#--version} : Prints product version to the output stream and exits. -`-version` +[`-version`]{#-version} : Prints product version to the error stream and exits. -`-X` +[`-X`]{#-X} : Prints the help on extra options to the error stream. -`--help-extra` +[`--help-extra`]{#--help-extra} : Prints the help on extra options to the output stream. `@`*argfile* @@ -688,7 +688,7 @@ the JVM. The following `java` options are general purpose options that are specific to the Java HotSpot Virtual Machine. -`-Xbatch` +[`-Xbatch`]{#-Xbatch} : Disables background compilation. By default, the JVM compiles the method as a background task, running the method in interpreter mode until the background compilation is finished. The `-Xbatch` flag disables background @@ -696,14 +696,14 @@ the Java HotSpot Virtual Machine. task until completed. This option is equivalent to `-XX:-BackgroundCompilation`. -`-Xbootclasspath/a:`*directories*\|*zip*\|*JAR-files* +[`-Xbootclasspath/a:`]{#-Xbootclasspath}*directories*\|*zip*\|*JAR-files* : Specifies a list of directories, JAR files, and ZIP archives to append to the end of the default bootstrap class path. On Windows, semicolons (`;`) separate entities in this list; on other platforms it is a colon (`:`). -`-Xcheck:jni` +[`-Xcheck:jni`]{#-Xcheck_jni} : Performs additional checks for Java Native Interface (JNI) functions. The following checks are considered indicative of significant problems @@ -739,22 +739,22 @@ the Java HotSpot Virtual Machine. Expect a performance degradation when this option is used. -`-Xcomp` +[`-Xcomp`]{#-Xcomp} : Testing mode to exercise JIT compilers. This option should not be used in production environments. -`-Xdebug` +[`-Xdebug`]{#-Xdebug} : Does nothing; deprecated for removal in a future release. -`-Xdiag` +[`-Xdiag`]{#-Xdiag} : Shows additional diagnostic messages. -`-Xint` +[`-Xint`]{#-Xint} : Runs the application in interpreted-only mode. Compilation to native code is disabled, and all bytecode is executed by the interpreter. The performance benefits offered by the just-in-time (JIT) compiler aren't present in this mode. -`-Xinternalversion` +[`-Xinternalversion`]{#-Xinternalversion} : Displays more detailed JVM version information than the `-version` option, and then exits. @@ -763,11 +763,11 @@ the Java HotSpot Virtual Machine. logging framework. See [Enable Logging with the JVM Unified Logging Framework]. -`-Xmixed` +[`-Xmixed`]{#-Xmixed} : Executes all bytecode by the interpreter except for hot methods, which are compiled to native code. On by default. Use `-Xint` to switch off. -`-Xmn` *size* +[`-Xmn`]{#-Xmn} *size* : Sets the initial and maximum size (in bytes) of the heap for the young generation (nursery) in the generational collectors. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or @@ -793,7 +793,7 @@ the Java HotSpot Virtual Machine. the heap for the young generation, you can use `-XX:NewSize` to set the initial size and `-XX:MaxNewSize` to set the maximum size. -`-Xms` *size* +[`-Xms`]{#-Xms} *size* : Sets the minimum and the initial size (in bytes) of the heap. This value must be a multiple of 1024 and greater than 1 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` @@ -815,7 +815,7 @@ the Java HotSpot Virtual Machine. initial heap size. If it appears after `-Xms` on the command line, then the initial heap size gets set to the value specified with `-XX:InitialHeapSize`. -`-Xmx` *size* +[`-Xmx`]{#-Xmx} *size* : Specifies the maximum size (in bytes) of the heap. This value must be a multiple of 1024 and greater than 2 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` @@ -832,7 +832,7 @@ the Java HotSpot Virtual Machine. The `-Xmx` option is equivalent to `-XX:MaxHeapSize`. -`-Xnoclassgc` +[`-Xnoclassgc`]{#-Xnoclassgc} : Disables garbage collection (GC) of classes. This can save some GC time, which shortens interruptions during the application run. When you specify `-Xnoclassgc` at startup, the class objects in the application are left @@ -840,7 +840,7 @@ the Java HotSpot Virtual Machine. more memory being permanently occupied which, if not used carefully, throws an out-of-memory exception. -`-Xrs` +[`-Xrs`]{#-Xrs} : Reduces the use of operating system signals by the JVM. Shutdown hooks enable the orderly shutdown of a Java application by running user cleanup code (such as closing database connections) at shutdown, even if the JVM @@ -890,7 +890,7 @@ the Java HotSpot Virtual Machine. User code is responsible for causing shutdown hooks to run, for example, by calling `System.exit()` when the JVM is to be terminated. -`-Xshare:`*mode* +[`-Xshare:`]{#-Xshare_}*mode* : Sets the class data sharing (CDS) mode. Possible *mode* arguments for this option include the following: @@ -910,10 +910,10 @@ the Java HotSpot Virtual Machine. `off` : Do not attempt to use shared class data. -`-XshowSettings` +[`-XshowSettings`]{#-XshowSettings} : Shows all settings and then continues. -`-XshowSettings:`*category* +[`-XshowSettings:`]{#-XshowSettings_}*category* : Shows settings and continues. Possible *category* arguments for this option include the following: @@ -942,7 +942,7 @@ the Java HotSpot Virtual Machine. `system` : **Linux only:** Shows host system or container configuration and continues. -`-Xss` *size* +[`-Xss`]{#-Xss} *size* : Sets the thread stack size (in bytes). Append the letter `k` or `K` to indicate KB, `m` or `M` to indicate MB, or `g` or `G` to indicate GB. The actual size may be rounded up to a multiple of the system page size as @@ -970,32 +970,32 @@ the Java HotSpot Virtual Machine. This option is similar to `-XX:ThreadStackSize`. -`--add-reads` *module*`=`*target-module*(`,`*target-module*)\* +[`--add-reads`]{#--add-reads} *module*`=`*target-module*(`,`*target-module*)\* : Updates *module* to read the *target-module*, regardless of the module declaration. *target-module* can be `ALL-UNNAMED` to read all unnamed modules. -`--add-exports` *module*`/`*package*`=`*target-module*(`,`*target-module*)\* +[`--add-exports`]{#--add-exports} *module*`/`*package*`=`*target-module*(`,`*target-module*)\* : Updates *module* to export *package* to *target-module*, regardless of module declaration. *target-module* can be `ALL-UNNAMED` to export to all unnamed modules. -`--add-opens` *module*`/`*package*`=`*target-module*(`,`*target-module*)\* +[`--add-opens`]{#--add-opens} *module*`/`*package*`=`*target-module*(`,`*target-module*)\* : Updates *module* to open *package* to *target-module*, regardless of module declaration. -`--limit-modules` *module*\[`,`*module*...\] +[`--limit-modules`]{#--limit-modules} *module*\[`,`*module*...\] : Specifies the limit of the universe of observable modules. -`--patch-module` *module*`=`*file*(`;`*file*)\* +[`--patch-module`]{#--patch-module} *module*`=`*file*(`;`*file*)\* : Overrides or augments a module with classes and resources in JAR files or directories. -`--source` *version* +[`--source`]{#--source} *version* : Sets the version of the source in source-file mode. -`--sun-misc-unsafe-memory-access=` *value* +[`--sun-misc-unsafe-memory-access=`]{#--sun-misc-unsafe-memory-access} *value* : Allow or deny usage of unsupported API `sun.misc.Unsafe`. *value* is one of: `allow` @@ -1021,20 +1021,20 @@ the Java HotSpot Virtual Machine. The following extra options are macOS specific. -`-XstartOnFirstThread` +[`-XstartOnFirstThread`]{#-XstartOnFirstThread} : Runs the `main()` method on the first (AppKit) thread. -`-Xdock:name=`*application\_name* +[`-Xdock:name=`]{#-Xdock_name}*application\_name* : Overrides the default application name displayed in dock. -`-Xdock:icon=`*path\_to\_icon\_file* +[`-Xdock:icon=`]{#-Xdock_icon}*path\_to\_icon\_file* : Overrides the default icon displayed in dock. ## Advanced Options for Java These `java` options can be used to enable other advanced options. -`-XX:+UnlockDiagnosticVMOptions` +[`-XX:+UnlockDiagnosticVMOptions`]{#-XX__UnlockDiagnosticVMOptions} : Unlocks the options intended for diagnosing the JVM. By default, this option is disabled and diagnostic options aren't available. @@ -1046,7 +1046,7 @@ These `java` options can be used to enable other advanced options. of these options may be removed or their behavior changed without any warning. -`-XX:+UnlockExperimentalVMOptions` +[`-XX:+UnlockExperimentalVMOptions`]{#-XX__UnlockExperimentalVMOptions} : Unlocks the options that provide experimental features in the JVM. By default, this option is disabled and experimental features aren't available. @@ -1054,7 +1054,7 @@ These `java` options can be used to enable other advanced options. These `java` options control the runtime behavior of the Java HotSpot VM. -`-XX:ActiveProcessorCount=`*x* +[`-XX:ActiveProcessorCount=`]{#-XX_ActiveProcessorCount}*x* : Overrides the number of CPUs that the VM will use to calculate the size of thread pools it will use for various operations such as Garbage Collection and ForkJoinPool. @@ -1066,7 +1066,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. `-XX:-UseContainerSupport` for a description of enabling and disabling container support. -`-XX:AllocateHeapAt=`*path* +[`-XX:AllocateHeapAt=`]{#-XX_AllocateHeapAt}*path* : Takes a path to the file system and uses memory mapping to allocate the object heap on the memory device. Using this option enables the HotSpot VM to allocate the Java object heap on an alternative memory device, such as @@ -1084,7 +1084,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. The existing heap related flags (such as `-Xmx` and `-Xms`) and garbage-collection related flags continue to work as before. -`-XX:-CompactStrings` +[`-XX:-CompactStrings`]{#-XX__CompactStrings} : Disables the Compact Strings feature. By default, this option is enabled. When this option is enabled, Java Strings containing only single-byte characters are internally represented and stored as @@ -1107,7 +1107,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. In both of these scenarios, disabling Compact Strings makes sense. -`-XX:ErrorFile=`*filename* +[`-XX:ErrorFile=`]{#-XX_ErrorFile}*filename* : Specifies the path and file name to which error data is written when an irrecoverable error occurs. By default, this file is created in the current working directory and named `hs_err_pid`*pid*`.log` where *pid* is the @@ -1139,7 +1139,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. `TMP` environment variable; if that environment variable isn't defined, then the value of the `TEMP` environment variable is used. -`-XX:+ExtensiveErrorReports` +[`-XX:+ExtensiveErrorReports`]{#-XX__ExtensiveErrorReports} : Enables the reporting of more extensive error information in the `ErrorFile`. This option can be turned on in environments where maximal information is desired - even if the resulting logs may be quite large and/or contain @@ -1147,7 +1147,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. from release to release, and across different platforms. By default this option is disabled. -`-XX:FlightRecorderOptions=`*parameter*`=`*value* (or) `-XX:FlightRecorderOptions:`*parameter*`=`*value* +[`-XX:FlightRecorderOptions=`]{#-XX_FlightRecorderOptions}*parameter*`=`*value* (or) `-XX:FlightRecorderOptions:`*parameter*`=`*value* : Sets the parameters that control the behavior of JFR. Multiple parameters can be specified by separating them with a comma. @@ -1207,7 +1207,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. 4 kilobytes. Overriding this parameter could reduce performance and is not recommended. -`-XX:LargePageSizeInBytes=`*size* +[`-XX:LargePageSizeInBytes=`]{#-XX_LargePageSizeInBytes}*size* : Sets the maximum large page size (in bytes) used by the JVM. The *size* argument must be a valid page size supported by the environment to have any effect. Append the letter `k` or `K` to indicate kilobytes, @@ -1221,7 +1221,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. > `-XX:LargePageSizeInBytes=1g` -`-XX:MaxDirectMemorySize=`*size* +[`-XX:MaxDirectMemorySize=`]{#-XX_MaxDirectMemorySize}*size* : Sets the maximum total size (in bytes) of the `java.nio` package, direct-buffer allocations. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate @@ -1237,14 +1237,14 @@ These `java` options control the runtime behavior of the Java HotSpot VM. -XX:MaxDirectMemorySize=1048576 ``` -`-XX:-MaxFDLimit` +[`-XX:-MaxFDLimit`]{#-XX__MaxFDLimit} : Disables the attempt to set the soft limit for the number of open file descriptors to the hard limit. By default, this option is enabled on all platforms, but is ignored on Windows. The only time that you may need to disable this is on macOS, where its use imposes a maximum of 10240, which is lower than the actual system maximum. -`-XX:NativeMemoryTracking=`*mode* +[`-XX:NativeMemoryTracking=`]{#-XX_NativeMemoryTracking}*mode* : Specifies the mode for tracking JVM native memory usage. Possible *mode* arguments for this option include the following: @@ -1261,7 +1261,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. usage by individual `CallSite`, individual virtual memory region and its committed regions. -`-XX:TrimNativeHeapInterval=`*millis* +[`-XX:TrimNativeHeapInterval=`]{#-XX_TrimNativeHeapInterval}*millis* : Interval, in ms, at which the JVM will trim the native heap. Lower values will reclaim memory more eagerly at the cost of higher overhead. A value of 0 (default) disables native heap trimming. @@ -1269,7 +1269,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. This option is only supported on Linux with GNU C Library (glibc). -`-XX:ObjectAlignmentInBytes=`*alignment* +[`-XX:ObjectAlignmentInBytes=`]{#-XX_ObjectAlignmentInBytes}*alignment* : Sets the memory alignment of Java objects (in bytes). By default, the value is set to 8 bytes. The specified value should be a power of 2, and must be within the range of 8 and 256 (inclusive). This option makes it possible to @@ -1283,7 +1283,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. increases. As a result, you may not realize any benefits from using compressed pointers with large Java heap sizes. -`-XX:OnError=`*string* +[`-XX:OnError=`]{#-XX_OnError}*string* : Sets a custom command or a series of semicolon-separated commands to run when an irrecoverable error occurs. If the string contains spaces, then it must be enclosed in quotation marks. @@ -1304,7 +1304,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. > `-XX:OnError="userdump.exe %p"` -`-XX:OnOutOfMemoryError=`*string* +[`-XX:OnOutOfMemoryError=`]{#-XX_OnOutOfMemoryError}*string* : Sets a custom command or a series of semicolon-separated commands to run when an `OutOfMemoryError` exception is first thrown by the JVM. If the string @@ -1316,31 +1316,31 @@ These `java` options control the runtime behavior of the Java HotSpot VM. directly from Java code, nor by the JVM for other types of resource exhaustion (such as native thread creation errors). -`-XX:+PrintCommandLineFlags` +[`-XX:+PrintCommandLineFlags`]{#-XX__PrintCommandLineFlags} : Enables printing of ergonomically selected JVM flags that appeared on the command line. It can be useful to know the ergonomic values set by the JVM, such as the heap space size and the selected garbage collector. By default, this option is disabled and flags aren't printed. -`-XX:+PreserveFramePointer` +[`-XX:+PreserveFramePointer`]{#-XX__PreserveFramePointer} : Selects between using the RBP register as a general purpose register (`-XX:-PreserveFramePointer`) and using the RBP register to hold the frame pointer of the currently executing method (`-XX:+PreserveFramePointer`). If the frame pointer is available, then external profiling tools (for example, Linux perf) can construct more accurate stack traces. -`-XX:+PrintNMTStatistics` +[`-XX:+PrintNMTStatistics`]{#-XX__PrintNMTStatistics} : Enables printing of collected native memory tracking data at JVM exit when native memory tracking is enabled (see `-XX:NativeMemoryTracking`). By default, this option is disabled and native memory tracking data isn't printed. -`-XX:SharedArchiveFile=`*path* +[`-XX:SharedArchiveFile=`]{#-XX_SharedArchiveFile}*path* : Specifies the path and name of the class data sharing (CDS) archive file See [Application Class Data Sharing]. -`-XX:+VerifySharedSpaces` +[`-XX:+VerifySharedSpaces`]{#-XX__VerifySharedSpaces} : If this option is specified, the JVM will load a CDS archive file only if it passes an integrity check based on CRC32 checksums. The purpose of this flag is to check for unintentional damage to CDS archive files in transmission or storage. @@ -1348,10 +1348,10 @@ These `java` options control the runtime behavior of the Java HotSpot VM. ensure that the CDS archive files used by Java applications cannot be modified without proper authorization. -`-XX:SharedArchiveConfigFile=`*shared\_config\_file* +[`-XX:SharedArchiveConfigFile=`]{#-XX_SharedArchiveConfigFile}*shared\_config\_file* : Specifies additional shared data added to the archive file. -`-XX:SharedClassListFile=`*file\_name* +[`-XX:SharedClassListFile=`]{#-XX_SharedClassListFile}*file\_name* : Specifies the text file that contains the names of the classes to store in the class data sharing (CDS) archive. This file contains the full name of one class per line, except slashes (`/`) replace dots (`.`). For example, @@ -1369,7 +1369,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. See [Application Class Data Sharing]. -`-XX:+ShowCodeDetailsInExceptionMessages` +[`-XX:+ShowCodeDetailsInExceptionMessages`]{#-XX__ShowCodeDetailsInExceptionMessages} : Enables printing of improved `NullPointerException` messages. When an application throws a `NullPointerException`, the option enables the JVM to analyze the program's bytecode instructions to determine precisely which reference is `null`, @@ -1378,13 +1378,13 @@ These `java` options control the runtime behavior of the Java HotSpot VM. and will be printed as the exception message along with the method, filename, and line number. By default, this option is enabled. -`-XX:+ShowMessageBoxOnError` +[`-XX:+ShowMessageBoxOnError`]{#-XX__ShowMessageBoxOnError} : Enables the display of a dialog box when the JVM experiences an irrecoverable error. This prevents the JVM from exiting and keeps the process active so that you can attach a debugger to it to investigate the cause of the error. By default, this option is disabled. -`-XX:StartFlightRecording:`*parameter*`=`*value* +[`-XX:StartFlightRecording:`]{#-XX_StartFlightRecording_}*parameter*`=`*value* : Starts a JFR recording for the Java application. This option is equivalent to the `JFR.start` diagnostic command that starts a recording during runtime. `-XX:StartFlightRecording:help` prints available options and @@ -1503,7 +1503,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. To only see warnings and errors from JFR during startup set -Xlog:jfr+startup=warning. -`-XX:ThreadStackSize=`*size* +[`-XX:ThreadStackSize=`]{#-XX_ThreadStackSize}*size* : Sets the Java thread stack size (in kilobytes). Use of a scaling suffix, such as `k`, results in the scaling of the kilobytes value so that `-XX:ThreadStackSize=1k` sets the Java thread stack size to 1024\*1024 @@ -1529,7 +1529,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. This option is similar to `-Xss`. -`-XX:+UseCompactObjectHeaders` +[`-XX:+UseCompactObjectHeaders`]{#-XX__UseCompactObjectHeaders} : Enables compact object headers. By default, this option is disabled. Enabling this option reduces memory footprint in the Java heap by 4 bytes per object (on average) and often improves performance. @@ -1538,7 +1538,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. In a future release it is expected to be enabled by default, and eventually will be the only mode of operation. -`-XX:-UseCompressedOops` +[`-XX:-UseCompressedOops`]{#-XX__UseCompressedOops} : Disables the use of compressed pointers. By default, this option is enabled, and compressed pointers are used. This will automatically limit the maximum ergonomically determined Java heap size to the maximum amount @@ -1553,7 +1553,7 @@ These `java` options control the runtime behavior of the Java HotSpot VM. It's possible to use compressed pointers with Java heap sizes greater than 32 GB. See the `-XX:ObjectAlignmentInBytes` option. -`-XX:-UseContainerSupport` +[`-XX:-UseContainerSupport`]{#-XX__UseContainerSupport} : **Linux only:** The VM now provides automatic container detection support, which allows the VM to determine the amount of memory and number of processors that are available to a Java process running in docker containers. It uses this @@ -1568,28 +1568,28 @@ These `java` options control the runtime behavior of the Java HotSpot VM. information. See [Enable Logging with the JVM Unified Logging Framework] for a description of using Unified Logging. -`-XX:+UseLargePages` +[`-XX:+UseLargePages`]{#-XX__UseLargePages} : Enables the use of large page memory. By default, this option is disabled and large page memory isn't used. See [Large Pages]. -`-XX:+UseTransparentHugePages` +[`-XX:+UseTransparentHugePages`]{#-XX__UseTransparentHugePages} : **Linux only:** Enables the use of large pages that can dynamically grow or shrink. This option is disabled by default. You may encounter performance problems with transparent huge pages as the OS moves other pages around to create huge pages; this option is made available for experimentation. -`-XX:+AllowUserSignalHandlers` +[`-XX:+AllowUserSignalHandlers`]{#-XX__AllowUserSignalHandlers} : **Non-Windows:** Enables installation of signal handlers by the application. By default, this option is disabled and the application isn't allowed to install signal handlers. -`-XX:VMOptionsFile=`*filename* +[`-XX:VMOptionsFile=`]{#-XX_VMOptionsFile}*filename* : Allows user to specify VM options in a file, for example, `java -XX:VMOptionsFile=/var/my_vm_options HelloWorld`. -`-XX:UseBranchProtection=`*mode* +[`-XX:UseBranchProtection=`]{#-XX_UseBranchProtection}*mode* : **Linux AArch64 only:** Specifies the branch protection mode. All options other than `none` require the VM to have been built with branch protection @@ -1613,14 +1613,14 @@ These `java` options control the runtime behavior of the Java HotSpot VM. These `java` options control the dynamic just-in-time (JIT) compilation performed by the Java HotSpot VM. -`-XX:AllocateInstancePrefetchLines=`*lines* +[`-XX:AllocateInstancePrefetchLines=`]{#-XX_AllocateInstancePrefetchLines}*lines* : Sets the number of lines to prefetch ahead of the instance allocation pointer. By default, the number of lines to prefetch is set to 1: > `-XX:AllocateInstancePrefetchLines=1` -`-XX:AllocatePrefetchDistance=`*size* +[`-XX:AllocatePrefetchDistance=`]{#-XX_AllocatePrefetchDistance}*size* : Sets the size (in bytes) of the prefetch distance for object allocation. Memory about to be written with the value of new objects is prefetched up to this distance starting from the address of the last allocated object. @@ -1636,7 +1636,7 @@ performed by the Java HotSpot VM. > `-XX:AllocatePrefetchDistance=1024` -`-XX:AllocatePrefetchInstr=`*instruction* +[`-XX:AllocatePrefetchInstr=`]{#-XX_AllocatePrefetchInstr}*instruction* : Sets the prefetch instruction to prefetch ahead of the allocation pointer. Possible values are from 0 to 3. The actual instructions behind the values depend on the platform. By default, the prefetch instruction is set to 0: @@ -1644,7 +1644,7 @@ performed by the Java HotSpot VM. > `-XX:AllocatePrefetchInstr=0` -`-XX:AllocatePrefetchLines=`*lines* +[`-XX:AllocatePrefetchLines=`]{#-XX_AllocatePrefetchLines}*lines* : Sets the number of cache lines to load after the last object allocation by using the prefetch instructions generated in compiled code. The default value is 1 if the last allocated object was an instance, and 3 if it was an @@ -1656,7 +1656,7 @@ performed by the Java HotSpot VM. > `-XX:AllocatePrefetchLines=5` -`-XX:AllocatePrefetchStepSize=`*size* +[`-XX:AllocatePrefetchStepSize=`]{#-XX_AllocatePrefetchStepSize}*size* : Sets the step size (in bytes) for sequential prefetch instructions. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, `g` or `G` to indicate gigabytes. By default, the step size is @@ -1665,7 +1665,7 @@ performed by the Java HotSpot VM. > `-XX:AllocatePrefetchStepSize=16` -`-XX:AllocatePrefetchStyle=`*style* +[`-XX:AllocatePrefetchStyle=`]{#-XX_AllocatePrefetchStyle}*style* : Sets the generated code style for prefetch instructions. The *style* argument is an integer from 0 to 3: @@ -1684,12 +1684,12 @@ performed by the Java HotSpot VM. : Generate one prefetch instruction per cache line. -`-XX:+BackgroundCompilation` +[`-XX:+BackgroundCompilation`]{#-XX__BackgroundCompilation} : Enables background compilation. This option is enabled by default. To disable background compilation, specify `-XX:-BackgroundCompilation` (this is equivalent to specifying `-Xbatch`). -`-XX:CICompilerCount=`*threads* +[`-XX:CICompilerCount=`]{#-XX_CICompilerCount}*threads* : Sets the number of compiler threads to use for compilation. By default, the number of compiler threads is selected automatically depending on the number of CPUs and memory available for compiled code. @@ -1697,11 +1697,11 @@ performed by the Java HotSpot VM. > `-XX:CICompilerCount=2` -`-XX:+UseDynamicNumberOfCompilerThreads` +[`-XX:+UseDynamicNumberOfCompilerThreads`]{#-XX__UseDynamicNumberOfCompilerThreads} : Dynamically create compiler thread up to the limit specified by `-XX:CICompilerCount`. This option is enabled by default. -`-XX:CompileCommand=`*command*`,`*method*\[`,`*option*\] +[`-XX:CompileCommand=`]{#-XX_CompileCommand}*command*`,`*method*\[`,`*option*\] : Specifies a *command* to perform on a *method*. For example, to exclude the `indexOf()` method of the `String` class from being compiled, use the following: @@ -1800,7 +1800,7 @@ performed by the Java HotSpot VM. You can suppress this by specifying the `-XX:CompileCommand=quiet` option before other `-XX:CompileCommand` options. -`-XX:CompileCommandFile=`*filename* +[`-XX:CompileCommandFile=`]{#-XX_CompileCommandFile}*filename* : Sets the file from which JIT compiler commands are read. By default, the `.hotspot_compiler` file is used to store commands performed by the JIT compiler. @@ -1814,7 +1814,7 @@ performed by the Java HotSpot VM. If you're using commands for the JIT compiler to perform on methods, then see the `-XX:CompileCommand` option. -`-XX:CompilerDirectivesFile=`*file* +[`-XX:CompilerDirectivesFile=`]{#-XX_CompilerDirectivesFile}*file* : Adds directives from a file to the directives stack when a program starts. See [Compiler Control](https://docs.oracle.com/en/java/javase/12/vm/compiler-control1.html#GUID-94AD8194-786A-4F19-BFFF-278F8E237F3A). @@ -1822,14 +1822,14 @@ performed by the Java HotSpot VM. `-XX:UnlockDiagnosticVMOptions` option that unlocks diagnostic JVM options. -`-XX:+CompilerDirectivesPrint` +[`-XX:+CompilerDirectivesPrint`]{#-XX__CompilerDirectivesPrint} : Prints the directives stack when the program starts or when a new directive is added. The `-XX:+CompilerDirectivesPrint` option has to be used together with the `-XX:UnlockDiagnosticVMOptions` option that unlocks diagnostic JVM options. -`-XX:CompileOnly=`*methods* +[`-XX:CompileOnly=`]{#-XX_CompileOnly}*methods* : Sets the list of methods (separated by commas) to which compilation should be restricted. Only the specified methods are compiled. @@ -1841,7 +1841,7 @@ performed by the Java HotSpot VM. -XX:CompileCommand=compileonly,methodN ``` -`-XX:CompileThresholdScaling=`*scale* +[`-XX:CompileThresholdScaling=`]{#-XX_CompileThresholdScaling}*scale* : Provides unified control of first compilation. This option controls when methods are first compiled for both the tiered and the nontiered modes of operation. The `CompileThresholdScaling` option has a floating point value @@ -1851,11 +1851,11 @@ performed by the Java HotSpot VM. compilation while values greater than 1.0 delay compilation. Setting `CompileThresholdScaling` to 0 is equivalent to disabling compilation. -`-XX:+DoEscapeAnalysis` +[`-XX:+DoEscapeAnalysis`]{#-XX__DoEscapeAnalysis} : Enables the use of escape analysis. This option is enabled by default. To disable the use of escape analysis, specify `-XX:-DoEscapeAnalysis`. -`-XX:InitialCodeCacheSize=`*size* +[`-XX:InitialCodeCacheSize=`]{#-XX_InitialCodeCacheSize}*size* : Sets the initial code cache size (in bytes). Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate gigabytes. The default value depends on the platform. The initial code @@ -1865,11 +1865,11 @@ performed by the Java HotSpot VM. > `-XX:InitialCodeCacheSize=32k` -`-XX:+Inline` +[`-XX:+Inline`]{#-XX__Inline} : Enables method inlining. This option is enabled by default to increase performance. To disable method inlining, specify `-XX:-Inline`. -`-XX:InlineSmallCode=`*size* +[`-XX:InlineSmallCode=`]{#-XX_InlineSmallCode}*size* : Sets the maximum code size (in bytes) for already compiled methods that may be inlined. This flag only applies to the C2 compiler. Append the letter `k` or `K` to indicate kilobytes, @@ -1879,7 +1879,7 @@ performed by the Java HotSpot VM. > `-XX:InlineSmallCode=1000` -`-XX:+LogCompilation` +[`-XX:+LogCompilation`]{#-XX__LogCompilation} : Enables logging of compilation activity to a file named `hotspot.log` in the current working directory. You can specify a different log file path and name using the `-XX:LogFile` option. @@ -1893,7 +1893,7 @@ performed by the Java HotSpot VM. `-XX:+PrintCompilation` option. -`-XX:FreqInlineSize=`*size* +[`-XX:FreqInlineSize=`]{#-XX_FreqInlineSize}*size* : Sets the maximum bytecode size (in bytes) of a hot method to be inlined. This flag only applies to the C2 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate @@ -1903,7 +1903,7 @@ performed by the Java HotSpot VM. > `-XX:FreqInlineSize=325` -`-XX:MaxInlineSize=`*size* +[`-XX:MaxInlineSize=`]{#-XX_MaxInlineSize}*size* : Sets the maximum bytecode size (in bytes) of a cold method to be inlined. This flag only applies to the C2 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate @@ -1912,7 +1912,7 @@ performed by the Java HotSpot VM. > `-XX:MaxInlineSize=35` -`-XX:C1MaxInlineSize=`*size* +[`-XX:C1MaxInlineSize=`]{#-XX_C1MaxInlineSize}*size* : Sets the maximum bytecode size (in bytes) of a cold method to be inlined. This flag only applies to the C1 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate @@ -1921,7 +1921,7 @@ performed by the Java HotSpot VM. > `-XX:MaxInlineSize=35` -`-XX:MaxTrivialSize=`*size* +[`-XX:MaxTrivialSize=`]{#-XX_MaxTrivialSize}*size* : Sets the maximum bytecode size (in bytes) of a trivial method to be inlined. This flag only applies to the C2 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to @@ -1930,7 +1930,7 @@ performed by the Java HotSpot VM. > `-XX:MaxTrivialSize=6` -`-XX:C1MaxTrivialSize=`*size* +[`-XX:C1MaxTrivialSize=`]{#-XX_C1MaxTrivialSize}*size* : Sets the maximum bytecode size (in bytes) of a trivial method to be inlined. This flag only applies to the C1 compiler. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to @@ -1939,14 +1939,14 @@ performed by the Java HotSpot VM. > `-XX:MaxTrivialSize=6` -`-XX:MaxNodeLimit=`*nodes* +[`-XX:MaxNodeLimit=`]{#-XX_MaxNodeLimit}*nodes* : Sets the maximum number of nodes to be used during single method compilation. By default the value depends on the features enabled. In the following example the maximum number of nodes is set to 100,000: > `-XX:MaxNodeLimit=100000` -`-XX:NonNMethodCodeHeapSize=`*size* +[`-XX:NonNMethodCodeHeapSize=`]{#-XX_NonNMethodCodeHeapSize}*size* : Sets the size in bytes of the code segment containing nonmethod code. A nonmethod code segment containing nonmethod code, such as compiler @@ -1954,16 +1954,16 @@ performed by the Java HotSpot VM. cache forever. This flag is used only if `-XX:SegmentedCodeCache` is enabled. -`-XX:NonProfiledCodeHeapSize=`*size* +[`-XX:NonProfiledCodeHeapSize=`]{#-XX_NonProfiledCodeHeapSize}*size* : Sets the size in bytes of the code segment containing nonprofiled methods. This flag is used only if `-XX:SegmentedCodeCache` is enabled. -`-XX:+OptimizeStringConcat` +[`-XX:+OptimizeStringConcat`]{#-XX__OptimizeStringConcat} : Enables the optimization of `String` concatenation operations. This option is enabled by default. To disable the optimization of `String` concatenation operations, specify `-XX:-OptimizeStringConcat`. -`-XX:+PrintAssembly` +[`-XX:+PrintAssembly`]{#-XX__PrintAssembly} : Enables printing of assembly code for bytecoded and native methods by using the external `hsdis-.so` or `.dll` library. For 64-bit VM on Windows, it's `hsdis-amd64.dll`. This lets you to see the generated code, which may @@ -1973,11 +1973,11 @@ performed by the Java HotSpot VM. `-XX:+PrintAssembly` option has to be used together with the `-XX:UnlockDiagnosticVMOptions` option that unlocks diagnostic JVM options. -`-XX:ProfiledCodeHeapSize=`*size* +[`-XX:ProfiledCodeHeapSize=`]{#-XX_ProfiledCodeHeapSize}*size* : Sets the size in bytes of the code segment containing profiled methods. This flag is used only if `-XX:SegmentedCodeCache` is enabled. -`-XX:+PrintCompilation` +[`-XX:+PrintCompilation`]{#-XX__PrintCompilation} : Enables verbose diagnostic output from the JVM by printing a message to the console every time a method is compiled. This lets you to see which methods actually get compiled. By default, this option is disabled and diagnostic @@ -1986,7 +1986,7 @@ performed by the Java HotSpot VM. You can also log compilation activity to a file by using the `-XX:+LogCompilation` option. -`-XX:+PrintInlining` +[`-XX:+PrintInlining`]{#-XX__PrintInlining} : Enables printing of inlining decisions. This let's you see which methods are getting inlined. @@ -1995,7 +1995,7 @@ performed by the Java HotSpot VM. `-XX:+UnlockDiagnosticVMOptions` option that unlocks diagnostic JVM options. -`-XX:ReservedCodeCacheSize=`*size* +[`-XX:ReservedCodeCacheSize=`]{#-XX_ReservedCodeCacheSize}*size* : Sets the maximum code cache size (in bytes) for JIT-compiled code. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate gigabytes. The default maximum code @@ -2005,7 +2005,7 @@ performed by the Java HotSpot VM. size shouldn't be less than the initial code cache size; see the option `-XX:InitialCodeCacheSize`. -`-XX:+SegmentedCodeCache` +[`-XX:+SegmentedCodeCache`]{#-XX__SegmentedCodeCache} : Enables segmentation of the code cache, without which the code cache consists of one large segment. With `-XX:+SegmentedCodeCache`, separate segments will be used for non-method, profiled method, and non-profiled @@ -2018,29 +2018,29 @@ performed by the Java HotSpot VM. (`-XX:+TieredCompilation` ) and the reserved code cache size (`-XX:ReservedCodeCacheSize`) is at least 240 MB. -`-XX:StartAggressiveSweepingAt=`*percent* +[`-XX:StartAggressiveSweepingAt=`]{#-XX_StartAggressiveSweepingAt}*percent* : Forces stack scanning of active methods to aggressively remove unused code when only the given percentage of the code cache is free. The default value is 10%. -`-XX:-TieredCompilation` +[`-XX:-TieredCompilation`]{#-XX__TieredCompilation} : Disables the use of tiered compilation. By default, this option is enabled. -`-XX:UseSSE=`*version* +[`-XX:UseSSE=`]{#-XX_UseSSE}*version* : Enables the use of SSE instruction set of a specified version. Is set by default to the highest supported version available (x86 only). -`-XX:UseAVX=`*version* +[`-XX:UseAVX=`]{#-XX_UseAVX}*version* : Enables the use of AVX instruction set of a specified version. Is set by default to the highest supported version available (x86 only). -`-XX:+UseAES` +[`-XX:+UseAES`]{#-XX__UseAES} : Enables hardware-based AES intrinsics for hardware that supports it. This option is on by default on hardware that has the necessary instructions. The `-XX:+UseAES` is used in conjunction with `UseAESIntrinsics`. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseAESIntrinsics` +[`-XX:+UseAESIntrinsics`]{#-XX__UseAESIntrinsics} : Enables AES intrinsics. Specifying `-XX:+UseAESIntrinsics` is equivalent to also enabling `-XX:+UseAES`. To disable hardware-based AES intrinsics, specify `-XX:-UseAES -XX:-UseAESIntrinsics`. For example, to enable hardware @@ -2051,47 +2051,47 @@ performed by the Java HotSpot VM. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseAESCTRIntrinsics` +[`-XX:+UseAESCTRIntrinsics`]{#-XX__UseAESCTRIntrinsics} : Analogous to `-XX:+UseAESIntrinsics` enables AES/CTR intrinsics. -`-XX:+UseGHASHIntrinsics` +[`-XX:+UseGHASHIntrinsics`]{#-XX__UseGHASHIntrinsics} : Controls the use of GHASH intrinsics. Enabled by default on platforms that support the corresponding instructions. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseChaCha20Intrinsics` +[`-XX:+UseChaCha20Intrinsics`]{#-XX__UseChaCha20Intrinsics} : Enable ChaCha20 intrinsics. This option is on by default for supported platforms. To disable ChaCha20 intrinsics, specify `-XX:-UseChaCha20Intrinsics`. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UsePoly1305Intrinsics` +[`-XX:+UsePoly1305Intrinsics`]{#-XX__UsePoly1305Intrinsics} : Enable Poly1305 intrinsics. This option is on by default for supported platforms. To disable Poly1305 intrinsics, specify `-XX:-UsePoly1305Intrinsics`. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseBASE64Intrinsics` +[`-XX:+UseBASE64Intrinsics`]{#-XX__UseBASE64Intrinsics} : Controls the use of accelerated BASE64 encoding routines for `java.util.Base64`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseAdler32Intrinsics` +[`-XX:+UseAdler32Intrinsics`]{#-XX__UseAdler32Intrinsics} : Controls the use of Adler32 checksum algorithm intrinsic for `java.util.zip.Adler32`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseCRC32Intrinsics` +[`-XX:+UseCRC32Intrinsics`]{#-XX__UseCRC32Intrinsics} : Controls the use of CRC32 intrinsics for `java.util.zip.CRC32`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseCRC32CIntrinsics` +[`-XX:+UseCRC32CIntrinsics`]{#-XX__UseCRC32CIntrinsics} : Controls the use of CRC32C intrinsics for `java.util.zip.CRC32C`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseSHA` +[`-XX:+UseSHA`]{#-XX__UseSHA} : Enables hardware-based intrinsics for SHA crypto hash functions for some hardware. The `UseSHA` option is used in conjunction with the `UseSHA1Intrinsics`, `UseSHA256Intrinsics`, and `UseSHA512Intrinsics` @@ -2108,26 +2108,26 @@ performed by the Java HotSpot VM. disable only a particular SHA intrinsic, use the appropriate corresponding option. For example: `-XX:-UseSHA256Intrinsics`. -`-XX:+UseSHA1Intrinsics` +[`-XX:+UseSHA1Intrinsics`]{#-XX__UseSHA1Intrinsics} : Enables intrinsics for SHA-1 crypto hash function. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseSHA256Intrinsics` +[`-XX:+UseSHA256Intrinsics`]{#-XX__UseSHA256Intrinsics} : Enables intrinsics for SHA-224 and SHA-256 crypto hash functions. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseSHA512Intrinsics` +[`-XX:+UseSHA512Intrinsics`]{#-XX__UseSHA512Intrinsics} : Enables intrinsics for SHA-384 and SHA-512 crypto hash functions. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseMathExactIntrinsics` +[`-XX:+UseMathExactIntrinsics`]{#-XX__UseMathExactIntrinsics} : Enables intrinsification of various `java.lang.Math.*Exact()` functions. Enabled by default. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseMultiplyToLenIntrinsic` +[`-XX:+UseMultiplyToLenIntrinsic`]{#-XX__UseMultiplyToLenIntrinsic} : Enables intrinsification of `BigInteger.multiplyToLen()`. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. @@ -2152,44 +2152,44 @@ performed by the Java HotSpot VM. Enabled by default on platforms that support it. Flags that control intrinsics now require the option `-XX:+UnlockDiagnosticVMOptions`. -`-XX:+UseCMoveUnconditionally` +[`-XX:+UseCMoveUnconditionally`]{#-XX__UseCMoveUnconditionally} : Generates CMove (scalar and vector) instructions regardless of profitability analysis. -`-XX:+UseCodeCacheFlushing` +[`-XX:+UseCodeCacheFlushing`]{#-XX__UseCodeCacheFlushing} : Enables flushing of the code cache before shutting down the compiler. This option is enabled by default. To disable flushing of the code cache before shutting down the compiler, specify `-XX:-UseCodeCacheFlushing`. -`-XX:+UseCondCardMark` +[`-XX:+UseCondCardMark`]{#-XX__UseCondCardMark} : Enables checking if the card is already marked before updating the card table. This option is disabled by default. It should be used only on machines with multiple sockets, where it increases the performance of Java applications that rely on concurrent operations. -`-XX:+UseCountedLoopSafepoints` +[`-XX:+UseCountedLoopSafepoints`]{#-XX__UseCountedLoopSafepoints} : Keeps safepoints in counted loops. Its default value depends on whether the selected garbage collector requires low latency safepoints. -`-XX:LoopStripMiningIter=`*number_of_iterations* +[`-XX:LoopStripMiningIter=`]{#-XX_LoopStripMiningIter}*number_of_iterations* : Controls the number of iterations in the inner strip mined loop. Strip mining transforms counted loops into two level nested loops. Safepoints are kept in the outer loop while the inner loop can execute at full speed. This option controls the maximum number of iterations in the inner loop. The default value is 1,000. -`-XX:LoopStripMiningIterShortLoop=`*number_of_iterations* +[`-XX:LoopStripMiningIterShortLoop=`]{#-XX_LoopStripMiningIterShortLoop}*number_of_iterations* : Controls loop strip mining optimization. Loops with the number of iterations less than specified will not have safepoints in them. Default value is 1/10th of `-XX:LoopStripMiningIter`. -`-XX:+UseFMA` +[`-XX:+UseFMA`]{#-XX__UseFMA} : Enables hardware-based FMA intrinsics for hardware where FMA instructions are available (such as, Intel and ARM64). FMA intrinsics are generated for the `java.lang.Math.fma(`*a*`,` *b*`,` *c*`)` methods that calculate the value of `(` *a* `*` *b* `+` *c* `)` expressions. -`-XX:+UseSuperWord` +[`-XX:+UseSuperWord`]{#-XX__UseSuperWord} : Enables the transformation of scalar operations into superword operations. Superword is a vectorization optimization. This option is enabled by default. To disable the transformation of scalar operations into superword @@ -2200,7 +2200,7 @@ performed by the Java HotSpot VM. These `java` options provide the ability to gather system information and perform extensive debugging. -`-XX:+DisableAttachMechanism` +[`-XX:+DisableAttachMechanism`]{#-XX__DisableAttachMechanism} : Disables the mechanism that lets tools attach to the JVM. By default, this option is disabled, meaning that the attach mechanism is enabled and you can use diagnostics and troubleshooting tools such as `jcmd`, `jstack`, @@ -2211,17 +2211,17 @@ perform extensive debugging. supported when using the tools from one JDK version to troubleshoot a different JDK version. -`-XX:+DTraceAllocProbes` +[`-XX:+DTraceAllocProbes`]{#-XX__DTraceAllocProbes} : **Linux and macOS:** Enable `dtrace` tool probes for object allocation. -`-XX:+DTraceMethodProbes` +[`-XX:+DTraceMethodProbes`]{#-XX__DTraceMethodProbes} : **Linux and macOS:** Enable `dtrace` tool probes for method-entry and method-exit. -`-XX:+DTraceMonitorProbes` +[`-XX:+DTraceMonitorProbes`]{#-XX__DTraceMonitorProbes} : **Linux and macOS:** Enable `dtrace` tool probes for monitor events. -`-XX:+HeapDumpOnOutOfMemoryError` +[`-XX:+HeapDumpOnOutOfMemoryError`]{#-XX__HeapDumpOnOutOfMemoryError} : Enables the dumping of the Java heap to a file in the current directory by using the heap profiler (HPROF) when a `java.lang.OutOfMemoryError` exception is thrown by the JVM. You can explicitly set the heap dump file path and @@ -2233,7 +2233,7 @@ perform extensive debugging. directly from Java code, nor by the JVM for other types of resource exhaustion (such as native thread creation errors). -`-XX:HeapDumpPath=`*path* +[`-XX:HeapDumpPath=`]{#-XX_HeapDumpPath}*path* : Sets the path and file name for writing the heap dump provided by the heap profiler (HPROF) when the `-XX:+HeapDumpOnOutOfMemoryError` option is set. By default, the file is created in the current working directory, and it's @@ -2253,7 +2253,7 @@ perform extensive debugging. > `-XX:HeapDumpPath=C:/log/java/java_heapdump.log` -`-XX:LogFile=`*path* +[`-XX:LogFile=`]{#-XX_LogFile}*path* : Sets the path and file name to where log data is written. By default, the file is created in the current working directory, and it's named `hotspot.log`. @@ -2268,7 +2268,7 @@ perform extensive debugging. > `-XX:LogFile=C:/log/java/hotspot.log` -`-XX:+PrintClassHistogram` +[`-XX:+PrintClassHistogram`]{#-XX__PrintClassHistogram} : Enables printing of a class instance histogram after one of the following events: @@ -2282,7 +2282,7 @@ perform extensive debugging. the `jcmd` *pid* `GC.class_histogram` command, where *pid* is the current Java process identifier. -`-XX:+PrintConcurrentLocks` +[`-XX:+PrintConcurrentLocks`]{#-XX__PrintConcurrentLocks} : Enables printing of `java.util.concurrent` locks after one of the following events: @@ -2296,12 +2296,12 @@ perform extensive debugging. `jcmd` *pid* `Thread.print -l` command, where *pid* is the current Java process identifier. -`-XX:+PrintFlagsRanges` +[`-XX:+PrintFlagsRanges`]{#-XX__PrintFlagsRanges} : Prints the range specified and allows automatic testing of the values. See [Validate Java Virtual Machine Flag Arguments]. -`-XX:+PerfDataSaveToFile` +[`-XX:+PerfDataSaveToFile`]{#-XX__PerfDataSaveToFile} : If enabled, saves [jstat](jstat.html) binary data when the Java application exits. This binary data is saved in a file named `hsperfdata_`*pid*, where *pid* is the process identifier of the Java application that you ran. Use @@ -2312,7 +2312,7 @@ perform extensive debugging. > `jstat -gc file:///`*path*`/hsperfdata_`*pid* -`-XX:+UsePerfData` +[`-XX:+UsePerfData`]{#-XX__UsePerfData} : Enables the `perfdata` feature. This option is enabled by default to allow JVM monitoring and performance testing. Disabling it suppresses the creation of the `hsperfdata_userid` directories. To disable the `perfdata` @@ -2323,13 +2323,13 @@ perform extensive debugging. These `java` options control how garbage collection (GC) is performed by the Java HotSpot VM. -`-XX:+AlwaysPreTouch` +[`-XX:+AlwaysPreTouch`]{#-XX__AlwaysPreTouch} : Requests the VM to touch every page on the Java heap after requesting it from the operating system and before handing memory out to the application. By default, this option is disabled and all pages are committed as the application uses the heap space. -`-XX:ConcGCThreads=`*threads* +[`-XX:ConcGCThreads=`]{#-XX_ConcGCThreads}*threads* : Sets the number of threads used for concurrent GC. Sets *`threads`* to approximately 1/4 of the number of parallel garbage collection threads. The default value depends on the number of CPUs available to the JVM. @@ -2339,24 +2339,24 @@ Java HotSpot VM. > `-XX:ConcGCThreads=2` -`-XX:+DisableExplicitGC` +[`-XX:+DisableExplicitGC`]{#-XX__DisableExplicitGC} : Enables the option that disables processing of calls to the `System.gc()` method. This option is disabled by default, meaning that calls to `System.gc()` are processed. If processing of calls to `System.gc()` is disabled, then the JVM still performs GC when necessary. -`-XX:+ExplicitGCInvokesConcurrent` +[`-XX:+ExplicitGCInvokesConcurrent`]{#-XX__ExplicitGCInvokesConcurrent} : Enables invoking of concurrent GC by using the `System.gc()` request. This option is disabled by default and can be enabled only with the `-XX:+UseG1GC` option. -`-XX:G1AdaptiveIHOPNumInitialSamples=`*number* +[`-XX:G1AdaptiveIHOPNumInitialSamples=`]{#-XX_G1AdaptiveIHOPNumInitialSamples}*number* : When `-XX:UseAdaptiveIHOP` is enabled, this option sets the number of completed marking cycles used to gather samples until G1 adaptively determines the optimum value of `-XX:InitiatingHeapOccupancyPercent`. Before, G1 uses the value of `-XX:InitiatingHeapOccupancyPercent` directly for this purpose. The default value is 3. -`-XX:G1HeapRegionSize=`*size* +[`-XX:G1HeapRegionSize=`]{#-XX_G1HeapRegionSize}*size* : Sets the size of the regions into which the Java heap is subdivided when using the garbage-first (G1) collector. The value is a power of 2 and can range from 1 MB to 32 MB. The default region size is determined @@ -2367,44 +2367,44 @@ Java HotSpot VM. > `-XX:G1HeapRegionSize=16m` -`-XX:G1HeapWastePercent=`*percent* +[`-XX:G1HeapWastePercent=`]{#-XX_G1HeapWastePercent}*percent* : Sets the percentage of heap that you're willing to waste. The Java HotSpot VM doesn't initiate the mixed garbage collection cycle when the reclaimable percentage is less than the heap waste percentage. The default is 5 percent. -`-XX:G1MaxNewSizePercent=`*percent* +[`-XX:G1MaxNewSizePercent=`]{#-XX_G1MaxNewSizePercent}*percent* : Sets the percentage of the heap size to use as the maximum for the young generation size. The default value is 60 percent of your Java heap. This is an experimental flag. This setting replaces the `-XX:DefaultMaxNewGenPercent` setting. -`-XX:G1MixedGCCountTarget=`*number* +[`-XX:G1MixedGCCountTarget=`]{#-XX_G1MixedGCCountTarget}*number* : Sets the target number of mixed garbage collections after a marking cycle to collect old regions with at most `G1MixedGCLIveThresholdPercent` live data. The default is 8 mixed garbage collections. The goal for mixed collections is to be within this target number. -`-XX:G1MixedGCLiveThresholdPercent=`*percent* +[`-XX:G1MixedGCLiveThresholdPercent=`]{#-XX_G1MixedGCLiveThresholdPercent}*percent* : Sets the occupancy threshold for an old region to be included in a mixed garbage collection cycle. The default occupancy is 85 percent. This is an experimental flag. This setting replaces the `-XX:G1OldCSetRegionLiveThresholdPercent` setting. -`-XX:G1NewSizePercent=`*percent* +[`-XX:G1NewSizePercent=`]{#-XX_G1NewSizePercent}*percent* : Sets the percentage of the heap to use as the minimum for the young generation size. The default value is 5 percent of your Java heap. This is an experimental flag. This setting replaces the `-XX:DefaultMinNewGenPercent` setting. -`-XX:G1OldCSetRegionThresholdPercent=`*percent* +[`-XX:G1OldCSetRegionThresholdPercent=`]{#-XX_G1OldCSetRegionThresholdPercent}*percent* : Sets an upper limit on the number of old regions to be collected during a mixed garbage collection cycle. The default is 10 percent of the Java heap. -`-XX:G1ReservePercent=`*percent* +[`-XX:G1ReservePercent=`]{#-XX_G1ReservePercent}*percent* : Sets the percentage of the heap (0 to 50) that's reserved as a false ceiling to reduce the possibility of promotion failure for the G1 collector. When you increase or decrease the percentage, ensure that you @@ -2415,7 +2415,7 @@ Java HotSpot VM. > `-XX:G1ReservePercent=20` -`-XX:+G1UseAdaptiveIHOP` +[`-XX:+G1UseAdaptiveIHOP`]{#-XX__G1UseAdaptiveIHOP} : Controls adaptive calculation of the old generation occupancy to start background work preparing for an old generation collection. If enabled, G1 uses `-XX:InitiatingHeapOccupancyPercent` for the first few times as @@ -2428,7 +2428,7 @@ Java HotSpot VM. The default is enabled. -`-XX:InitialHeapSize=`*size* +[`-XX:InitialHeapSize=`]{#-XX_InitialHeapSize}*size* : Sets the initial size (in bytes) of the memory allocation pool. This value must be either 0, or a multiple of 1024 and greater than 1 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, @@ -2452,7 +2452,7 @@ Java HotSpot VM. command line, then the initial heap size gets set to the value specified with `-Xms`. -`-XX:InitialRAMPercentage=`*percent* +[`-XX:InitialRAMPercentage=`]{#-XX_InitialRAMPercentage}*percent* : Sets the initial amount of memory that the JVM will use for the Java heap before applying ergonomics heuristics as a percentage of the maximum amount determined as described in the `-XX:MaxRAM` option. @@ -2462,7 +2462,7 @@ Java HotSpot VM. > `-XX:InitialRAMPercentage=5` -`-XX:InitialSurvivorRatio=`*ratio* +[`-XX:InitialSurvivorRatio=`]{#-XX_InitialSurvivorRatio}*ratio* : Sets the initial survivor space ratio used by the throughput garbage collector (which is enabled by the `-XX:+UseParallelGC` option). Adaptive sizing is enabled by default with the throughput garbage collector by @@ -2491,7 +2491,7 @@ Java HotSpot VM. > `-XX:InitialSurvivorRatio=4` -`-XX:InitiatingHeapOccupancyPercent=`*percent* +[`-XX:InitiatingHeapOccupancyPercent=`]{#-XX_InitiatingHeapOccupancyPercent}*percent* : Sets the percentage of the old generation occupancy (0 to 100) at which to start the first few concurrent marking cycles for the G1 garbage collector. @@ -2506,7 +2506,7 @@ Java HotSpot VM. > `-XX:InitiatingHeapOccupancyPercent=75` -`-XX:MaxGCPauseMillis=`*time* +[`-XX:MaxGCPauseMillis=`]{#-XX_MaxGCPauseMillis}*time* : Sets a target for the maximum GC pause time (in milliseconds). This is a soft goal, and the JVM will make its best effort to achieve it. Only G1 and Parallel support a maximum GC pause time target. For G1, the default @@ -2517,7 +2517,7 @@ Java HotSpot VM. > `-XX:MaxGCPauseMillis=500` -`-XX:MaxHeapSize=`*size* +[`-XX:MaxHeapSize=`]{#-XX_MaxHeapSize}*size* : Sets the maximum size (in byes) of the memory allocation pool. This value must be a multiple of 1024 and greater than 2 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` @@ -2537,7 +2537,7 @@ Java HotSpot VM. The `-XX:MaxHeapSize` option is equivalent to `-Xmx`. -`-XX:MaxHeapFreeRatio=`*percent* +[`-XX:MaxHeapFreeRatio=`]{#-XX_MaxHeapFreeRatio}*percent* : Sets the maximum allowed percentage of free heap space (0 to 100) after a GC event. If free heap space expands above this value, then the heap is shrunk. By default, this value is set to 70%. @@ -2558,7 +2558,7 @@ Java HotSpot VM. description of using this option to keep the Java heap small by reducing the dynamic footprint for embedded applications. -`-XX:MaxMetaspaceSize=`*size* +[`-XX:MaxMetaspaceSize=`]{#-XX_MaxMetaspaceSize}*size* : Sets the maximum amount of native memory that can be allocated for class metadata. By default, the size isn't limited. The amount of metadata for an application depends on the application itself, other running applications, @@ -2569,11 +2569,11 @@ Java HotSpot VM. > `-XX:MaxMetaspaceSize=256m` -`-XX:MaxNewSize=`*size* +[`-XX:MaxNewSize=`]{#-XX_MaxNewSize}*size* : Sets the maximum size (in bytes) of the heap for the young generation (nursery). The default value is set ergonomically. -`-XX:MaxRAMPercentage=`*percent* +[`-XX:MaxRAMPercentage=`]{#-XX_MaxRAMPercentage}*percent* : Sets the maximum amount of memory that the JVM may use for the Java heap before applying ergonomics heuristics as a percentage of the maximum amount determined as described in the `-XX:MaxRAM` option. The default value is 25 @@ -2589,7 +2589,7 @@ Java HotSpot VM. > `-XX:MaxRAMPercentage=75` -`-XX:MinRAMPercentage=`*percent* +[`-XX:MinRAMPercentage=`]{#-XX_MinRAMPercentage}*percent* : Sets the maximum amount of memory that the JVM may use for the Java heap before applying ergonomics heuristics as a percentage of the maximum amount determined as described in the `-XX:MaxRAM` option for small heaps. A small @@ -2600,7 +2600,7 @@ Java HotSpot VM. > `-XX:MinRAMPercentage=75` -`-XX:MaxTenuringThreshold=`*threshold* +[`-XX:MaxTenuringThreshold=`]{#-XX_MaxTenuringThreshold}*threshold* : Sets the maximum tenuring threshold for use in adaptive GC sizing. The largest value is 15. The default value is 15 for the parallel (throughput) collector. @@ -2610,13 +2610,13 @@ Java HotSpot VM. > `-XX:MaxTenuringThreshold=10` -`-XX:MetaspaceSize=`*size* +[`-XX:MetaspaceSize=`]{#-XX_MetaspaceSize}*size* : Sets the size of the allocated class metadata space that triggers a garbage collection the first time it's exceeded. This threshold for a garbage collection is increased or decreased depending on the amount of metadata used. The default size depends on the platform. -`-XX:MinHeapFreeRatio=`*percent* +[`-XX:MinHeapFreeRatio=`]{#-XX_MinHeapFreeRatio}*percent* : Sets the minimum allowed percentage of free heap space (0 to 100) after a GC event. If free heap space falls below this value, then the heap is expanded. By default, this value is set to 40%. @@ -2637,7 +2637,7 @@ Java HotSpot VM. description of using this option to keep the Java heap small by reducing the dynamic footprint for embedded applications. -`-XX:MinHeapSize=`*size* +[`-XX:MinHeapSize=`]{#-XX_MinHeapSize}*size* : Sets the minimum size (in bytes) of the memory allocation pool. This value must be either 0, or a multiple of 1024 and greater than 1 MB. Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, @@ -2656,14 +2656,14 @@ Java HotSpot VM. If you set this option to 0, then the minimum size is set to the same value as the initial size. -`-XX:NewRatio=`*ratio* +[`-XX:NewRatio=`]{#-XX_NewRatio}*ratio* : Sets the ratio between young and old generation sizes. By default, this option is set to 2. The following example shows how to set the young-to-old ratio to 1: > `-XX:NewRatio=1` -`-XX:NewSize=`*size* +[`-XX:NewSize=`]{#-XX_NewSize}*size* : Sets the initial size (in bytes) of the heap for the young generation (nursery). Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate gigabytes. @@ -2687,7 +2687,7 @@ Java HotSpot VM. The `-XX:NewSize` option is equivalent to `-Xmn`. -`-XX:ParallelGCThreads=`*threads* +[`-XX:ParallelGCThreads=`]{#-XX_ParallelGCThreads}*threads* : Sets the number of the stop-the-world (STW) worker threads. The default value depends on the number of CPUs available to the JVM and the garbage collector selected. @@ -2697,11 +2697,11 @@ Java HotSpot VM. > `-XX:ParallelGCThreads=2` -`-XX:+PrintAdaptiveSizePolicy` +[`-XX:+PrintAdaptiveSizePolicy`]{#-XX__PrintAdaptiveSizePolicy} : Enables printing of information about adaptive-generation sizing. By default, this option is disabled. -`-XX:SoftRefLRUPolicyMSPerMB=`*time* +[`-XX:SoftRefLRUPolicyMSPerMB=`]{#-XX_SoftRefLRUPolicyMSPerMB}*time* : Sets the amount of time (in milliseconds) a softly reachable object is kept active on the heap after the last time it was referenced. The default value is one second of lifetime per free megabyte in the heap. The @@ -2718,7 +2718,7 @@ Java HotSpot VM. `-XX:SoftRefLRUPolicyMSPerMB=2500` -`-XX:-ShrinkHeapInSteps` +[`-XX:-ShrinkHeapInSteps`]{#-XX__ShrinkHeapInSteps} : Incrementally reduces the Java heap to the target size, specified by the option `-XX:MaxHeapFreeRatio`. This option is enabled by default. If disabled, then it immediately reduces the Java heap to the target size @@ -2730,7 +2730,7 @@ Java HotSpot VM. `MaxHeapFreeRatio` option to keep the Java heap small by reducing the dynamic footprint for embedded applications. -`-XX:StringDeduplicationAgeThreshold=`*threshold* +[`-XX:StringDeduplicationAgeThreshold=`]{#-XX_StringDeduplicationAgeThreshold}*threshold* : Identifies `String` objects reaching the specified age that are considered candidates for deduplication. An object's age is a measure of how many times it has survived garbage collection. This is sometimes referred to as @@ -2741,14 +2741,14 @@ Java HotSpot VM. default value for this option is `3`. See the `-XX:+UseStringDeduplication` option. -`-XX:SurvivorRatio=`*ratio* +[`-XX:SurvivorRatio=`]{#-XX_SurvivorRatio}*ratio* : Sets the ratio between eden space size and survivor space size. By default, this option is set to 8. The following example shows how to set the eden/survivor space ratio to 4: > `-XX:SurvivorRatio=4` -`-XX:TargetSurvivorRatio=`*percent* +[`-XX:TargetSurvivorRatio=`]{#-XX_TargetSurvivorRatio}*percent* : Sets the desired percentage of survivor space (0 to 100) used after young garbage collection. By default, this option is set to 50%. @@ -2757,7 +2757,7 @@ Java HotSpot VM. > `-XX:TargetSurvivorRatio=30` -`-XX:TLABSize=`*size* +[`-XX:TLABSize=`]{#-XX_TLABSize}*size* : Sets the initial size (in bytes) of a thread-local allocation buffer (TLAB). Append the letter `k` or `K` to indicate kilobytes, `m` or `M` to indicate megabytes, or `g` or `G` to indicate gigabytes. If this option is @@ -2767,13 +2767,13 @@ Java HotSpot VM. > `-XX:TLABSize=512k` -`-XX:+UseAdaptiveSizePolicy` +[`-XX:+UseAdaptiveSizePolicy`]{#-XX__UseAdaptiveSizePolicy} : Enables the use of adaptive generation sizing. This option is enabled by default. To disable adaptive generation sizing, specify `-XX:-UseAdaptiveSizePolicy` and set the size of the memory allocation pool explicitly. See the `-XX:SurvivorRatio` option. -`-XX:+UseG1GC` +[`-XX:+UseG1GC`]{#-XX__UseG1GC} : Enables the use of the garbage-first (G1) garbage collector. It's a server-style garbage collector, targeted for multiprocessor machines with a large amount of RAM. This option meets GC pause time goals with high @@ -2783,7 +2783,7 @@ Java HotSpot VM. pause time below 0.5 seconds). By default, this option is enabled and G1 is used as the default garbage collector. -`-XX:+UseGCOverheadLimit` +[`-XX:+UseGCOverheadLimit`]{#-XX__UseGCOverheadLimit} : Enables the use of a policy that limits the proportion of time spent by the JVM on GC before an `OutOfMemoryError` exception is thrown. This option is enabled, by default, and the parallel GC will throw an `OutOfMemoryError` @@ -2793,26 +2793,26 @@ Java HotSpot VM. little or no progress. To disable this option, specify the option `-XX:-UseGCOverheadLimit`. -`-XX:+UseNUMA` +[`-XX:+UseNUMA`]{#-XX__UseNUMA} : Enables performance optimization of an application on a machine with nonuniform memory architecture (NUMA) by increasing the application's use of lower latency memory. The default value for this option depends on the garbage collector. -`-XX:+UseParallelGC` +[`-XX:+UseParallelGC`]{#-XX__UseParallelGC} : Enables the use of the parallel scavenge garbage collector (also known as the throughput collector) to improve the performance of your application by leveraging multiple processors. By default, this option is disabled and the default collector is used. -`-XX:+UseSerialGC` +[`-XX:+UseSerialGC`]{#-XX__UseSerialGC} : Enables the use of the serial garbage collector. This is generally the best choice for small and simple applications that don't require any special functionality from garbage collection. By default, this option is disabled and the default collector is used. -`-XX:+UseStringDeduplication` +[`-XX:+UseStringDeduplication`]{#-XX__UseStringDeduplication} : Enables string deduplication. By default, this option is disabled. To use this option, you must enable the garbage-first (G1) garbage collector. @@ -2822,34 +2822,34 @@ Java HotSpot VM. character array, identical `String` objects can point to and share the same character array. -`-XX:+UseTLAB` +[`-XX:+UseTLAB`]{#-XX__UseTLAB} : Enables the use of thread-local allocation blocks (TLABs) in the young generation space. This option is enabled by default. To disable the use of TLABs, specify the option `-XX:-UseTLAB`. -`-XX:+UseZGC` +[`-XX:+UseZGC`]{#-XX__UseZGC} : Enables the use of the Z garbage collector (ZGC). This is a low latency garbage collector, providing max pause times of a few milliseconds, at some throughput cost. Pause times are independent of what heap size is used. Supports heap sizes from 8MB to 16TB. -`-XX:ZAllocationSpikeTolerance=`*factor* +[`-XX:ZAllocationSpikeTolerance=`]{#-XX_ZAllocationSpikeTolerance}*factor* : Sets the allocation spike tolerance for ZGC. By default, this option is set to 2.0. This factor describes the level of allocation spikes to expect. For example, using a factor of 3.0 means the current allocation rate can be expected to triple at any time. -`-XX:ZCollectionInterval=`*seconds* +[`-XX:ZCollectionInterval=`]{#-XX_ZCollectionInterval}*seconds* : Sets the maximum interval (in seconds) between two GC cycles when using ZGC. By default, this option is set to 0 (disabled). -`-XX:ZFragmentationLimit=`*percent* +[`-XX:ZFragmentationLimit=`]{#-XX_ZFragmentationLimit}*percent* : Sets the maximum acceptable heap fragmentation (in percent) for ZGC. By default, this option is set to 25. Using a lower value will cause the heap to be compacted more aggressively, to reclaim more memory at the cost of using more CPU time. -`-XX:+ZProactive` +[`-XX:+ZProactive`]{#-XX__ZProactive} : Enables proactive GC cycles when using ZGC. By default, this option is enabled. ZGC will start a proactive GC cycle if doing so is expected to have minimal impact on the running application. This is useful if the @@ -2857,27 +2857,27 @@ Java HotSpot VM. want to keep the heap size down and allow reference processing to happen even when there are a lot of free space on the heap. -`-XX:+ZUncommit` +[`-XX:+ZUncommit`]{#-XX__ZUncommit} : Enables uncommitting of unused heap memory when using ZGC. By default, this option is enabled. Uncommitting unused heap memory will lower the memory footprint of the JVM, and make that memory available for other processes to use. -`-XX:ZUncommitDelay=`*seconds* +[`-XX:ZUncommitDelay=`]{#-XX_ZUncommitDelay}*seconds* : Sets the amount of time (in seconds) that heap memory must have been unused before being uncommitted. By default, this option is set to 300 (5 minutes). Committing and uncommitting memory are relatively expensive operations. Using a lower value will cause heap memory to be uncommitted earlier, at the risk of soon having to commit it again. -`-XX:+UseShenandoahGC` +[`-XX:+UseShenandoahGC`]{#-XX__UseShenandoahGC} : Enables the use of the Shenandoah garbage collector. This is a low pause time, concurrent garbage collector. Its pause times are not proportional to the size of the heap. Shenandoah garbage collector can work with compressed pointers. See `-XX:UseCompressedOops` for further information about compressed pointers. -`-XX:ShenandoahGCMode=`*mode* +[`-XX:ShenandoahGCMode=`]{#-XX_ShenandoahGCMode}*mode* : Sets the GC mode for Shenandoah GC to use. By default, this option is set to `satb`. Among other things, this defines which barriers are in use. Possible mode values include the following: @@ -2891,7 +2891,7 @@ Java HotSpot VM. generational. Please see [JEP 404](https://openjdk.org/jeps/404) and [JEP 521](https://openjdk.org/jeps/521) for its advantages and risks. -`-XX:ShenandoahGCHeuristics=`*heuristics* +[`-XX:ShenandoahGCHeuristics=`]{#-XX_ShenandoahGCHeuristics}*heuristics* : Sets the heuristics for Shenandoah GC to use. By default, this option is set to `adaptive`. This fine-tunes the GC mode selected, by choosing when to start the GC, how much to process on each cycle, and what other features @@ -2916,7 +2916,7 @@ These `java` options are deprecated and might be removed in a future JDK release. They're still accepted and acted upon, but a warning is issued when they're used. -`-Xloggc:`*filename* +[`-Xloggc:`]{#-Xloggc_}*filename* : Sets the file to which verbose GC events information should be redirected for logging. The `-Xloggc` option overrides `-verbose:gc` if both are given with the same java command. `-Xloggc:`*filename* is replaced by @@ -2927,11 +2927,11 @@ they're used. `-Xlog:gc:garbage-collection.log` -`-XX:+FlightRecorder` +[`-XX:+FlightRecorder`]{#-XX__FlightRecorder} : Enables the use of Java Flight Recorder (JFR) during the runtime of the application. Since JDK 8u40 this option has not been required to use JFR. -`-XX:+ParallelRefProcEnabled` +[`-XX:+ParallelRefProcEnabled`]{#-XX__ParallelRefProcEnabled} : Enables parallel reference processing. By default, collectors employing multiple threads perform parallel reference processing if the number of parallel threads to use is larger than one. @@ -2939,7 +2939,7 @@ they're used. (`-XX:+UseParallelGC` or `-XX:+UseG1GC`). Other collectors employing multiple threads always perform reference processing in parallel. -`-XX:MaxRAM=`*size* +[`-XX:MaxRAM=`]{#-XX_MaxRAM}*size* : Sets the maximum amount of memory that the JVM may use for the Java heap before applying ergonomics heuristics. The default value is the amount of available memory to the JVM process. @@ -2958,13 +2958,13 @@ they're used. > `-XX:MaxRAM=2G` -`-XX:+AggressiveHeap` +[`-XX:+AggressiveHeap`]{#-XX__AggressiveHeap} : Enables Java heap optimization. This sets various parameters to be optimal for long-running jobs with intensive memory allocation, based on the configuration of the computer (RAM and CPU). By default, the option is disabled and the heap sizes are configured less aggressively. -`-XX:+NeverActAsServerClassMachine` +[`-XX:+NeverActAsServerClassMachine`]{#-XX__NeverActAsServerClassMachine} : Enable the "Client VM emulation" mode which only uses the C1 JIT compiler, a 32Mb CodeCache and the Serial GC. The maximum amount of memory that the JVM may use (controlled by the `-XX:MaxRAM=n` flag) is set to 1GB by default. @@ -2989,7 +2989,7 @@ they're used. These `java` options are still accepted but ignored, and a warning is issued when they're used. -`--illegal-access=`*parameter* +[`--illegal-access=`]{#--illegal-access}*parameter* : Controlled _relaxed strong encapsulation_, as defined in [JEP 261](https://openjdk.org/jeps/261#Relaxed-strong-encapsulation). This option was deprecated in JDK 16 by [JEP @@ -3283,7 +3283,7 @@ Flags to Xlog]. The following provides quick reference to the `-Xlog` command and syntax for options: -`-Xlog` +[`-Xlog`]{#-Xlog} : Enables JVM logging on an `info` level. `-Xlog:help` @@ -3570,7 +3570,7 @@ Legacy Runtime Flag Xlog Configuration Comment The following are `-Xlog` examples. -`-Xlog` +[`-Xlog`]{#-Xlog} : Logs all messages by using the `info` level to `stdout` with `uptime`, `levels`, and `tags` decorations. This is equivalent to using: @@ -4075,7 +4075,7 @@ The deployment of the AOT cache is divided into three phases: The AOT cache can be used with the following command-line options: -`-XX:AOTCache=`*cachefile* +[`-XX:AOTCache=`]{#-XX_AOTCache}*cachefile* : Specifies the location of the AOT cache. The standard extension for *cachefile* is `.aot`. This option cannot be used together with `-XX:AOTCacheOutput`. @@ -4084,13 +4084,13 @@ The AOT cache can be used with the following command-line options: The *cachefile* is written by AOT mode `create`. In that case, this option is equivalent to `-XX:AOTCacheOutput=`*cachefile*. -`-XX:AOTCacheOutput=`*cachefile* +[`-XX:AOTCacheOutput=`]{#-XX_AOTCacheOutput}*cachefile* : Specifies the location of the AOT cache to write. The standard extension for *cachefile* is `.aot`. This option cannot be used together with `-XX:AOTCache`. This option is compatible with `AOTMode` settings of `record`, `create`, or `auto` (the default). -`-XX:AOTConfiguration=`*configfile* +[`-XX:AOTConfiguration=`]{#-XX_AOTConfiguration}*configfile* : Specifies the AOT Configuration file for the JVM to write to or read from. The standard extension for *configfile* is `.aotconfig`. @@ -4098,7 +4098,7 @@ The AOT cache can be used with the following command-line options: The *configfile* is read by AOT mode `create`, and written by the other applicable modes. If the AOT mode is `auto`, then `AOTCacheOutput` must also be present. -`-XX:AOTMode=`*mode* +[`-XX:AOTMode=`]{#-XX_AOTMode}*mode* : Specifies the AOT Mode for this run. *mode* must be one of the following: `auto`, `off`, `record`, `create`, or `on`. @@ -4170,7 +4170,7 @@ The AOT cache can be used with the following command-line options: options are compatible with the AOT cache. An alternative is to run your application with `-XX:AOTMode=auto -Xlog:aot` to see if the AOT cache can be used or not. -`-XX:+AOTClassLinking` +[`-XX:+AOTClassLinking`]{#-XX__AOTClassLinking} : If this option is enabled, the JVM will perform more advanced optimizations (such as ahead-of-time resolution of invokedynamic instructions) when creating the AOT cache. As a result, the application will see further improvements From 56545328f849c3ebf062e3ff601224084fa3b46e Mon Sep 17 00:00:00 2001 From: Jonas Norlinder Date: Wed, 14 Jan 2026 16:54:24 +0000 Subject: [PATCH 097/204] 8375297: ZGC: Remove obsolete O_CLOEXEC definition Reviewed-by: tschatzl, eosterlund --- src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp b/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp index 25ffd0b8078..28159ae4801 100644 --- a/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp +++ b/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp @@ -66,9 +66,6 @@ #endif // open(2) flags -#ifndef O_CLOEXEC -#define O_CLOEXEC 02000000 -#endif #ifndef O_TMPFILE #define O_TMPFILE (020000000 | O_DIRECTORY) #endif From 60fbaf5b26d7d359b1258898d4c4dfd86010b8a5 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 14 Jan 2026 18:53:10 +0000 Subject: [PATCH 098/204] 8374828: Save load_barrier_on_oop_field_preloaded in aot CodeCache Reviewed-by: adinn, iklam, shade --- src/hotspot/share/code/aotCodeCache.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 0314c5227d2..f51c068f1e7 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -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 @@ -1371,6 +1371,7 @@ void AOTCodeAddressTable::init_extrs() { SET_ADDRESS(_extrs, ShenandoahRuntime::load_reference_barrier_phantom_narrow); #endif #if INCLUDE_ZGC + SET_ADDRESS(_extrs, ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr()); SET_ADDRESS(_extrs, ZBarrierSetRuntime::load_barrier_on_phantom_oop_field_preloaded_addr()); #if defined(AMD64) SET_ADDRESS(_extrs, &ZPointerLoadShift); From a7507ffa1dda403110a61c4b61143b76e8a7911e Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Wed, 14 Jan 2026 19:26:45 +0000 Subject: [PATCH 099/204] 8375237: Document existing exceptional behavior of divideUnsigned and remainderUnsigned Reviewed-by: rgiulietti --- src/java.base/share/classes/java/lang/Integer.java | 4 +++- src/java.base/share/classes/java/lang/Long.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index a9da1c32490..85ca80735f8 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1448,6 +1448,7 @@ public final class Integer extends Number * @param divisor the value doing the dividing * @return the unsigned quotient of the first argument divided by * the second argument + * @throws ArithmeticException if the divisor is zero * @see #remainderUnsigned * @since 1.8 */ @@ -1466,6 +1467,7 @@ public final class Integer extends Number * @param divisor the value doing the dividing * @return the unsigned remainder of the first argument divided by * the second argument + * @throws ArithmeticException if the divisor is zero * @see #divideUnsigned * @since 1.8 */ diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index c5cd9650f2d..5fa1b8fc2ea 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1411,6 +1411,7 @@ public final class Long extends Number * @param divisor the value doing the dividing * @return the unsigned quotient of the first argument divided by * the second argument + * @throws ArithmeticException if the divisor is zero * @see #remainderUnsigned * @since 1.8 */ @@ -1434,6 +1435,7 @@ public final class Long extends Number * @param divisor the value doing the dividing * @return the unsigned remainder of the first argument divided by * the second argument + * @throws ArithmeticException if the divisor is zero * @see #divideUnsigned * @since 1.8 */ From 3007365b73d400ee6a5ea9a9041899bb81cf357a Mon Sep 17 00:00:00 2001 From: Roger Riggs Date: Wed, 14 Jan 2026 19:27:10 +0000 Subject: [PATCH 100/204] 8373913: Refactor serialization tests to use JUnit Reviewed-by: jlu, naoto --- .../Serializable/GetField/ReadFieldsCNF.java | 34 ++-- .../class/NonSerializableTest.java | 75 +++++---- .../Serializable/records/RecordClassTest.java | 10 +- .../records/SerialVersionUIDTest.java | 12 +- .../serialFilter/CheckArrayTest.java | 28 ++-- .../serialFilter/CheckInputOrderTest.java | 23 +-- .../serialFilter/GlobalFilterTest.java | 71 ++++---- .../serialFilter/InvalidGlobalFilterTest.java | 40 ++--- .../serialFilter/MixedFiltersTest.java | 52 +++--- .../serialFilter/SerialFactoryExample.java | 33 ++-- .../serialFilter/SerialFactoryFaults.java | 52 +++--- .../serialFilter/SerialFilterFactoryTest.java | 76 +++++---- .../SerialFilterFunctionTest.java | 51 +++--- .../serialFilter/SerialFilterTest.java | 151 ++++++++---------- 14 files changed, 355 insertions(+), 353 deletions(-) diff --git a/test/jdk/java/io/Serializable/GetField/ReadFieldsCNF.java b/test/jdk/java/io/Serializable/GetField/ReadFieldsCNF.java index 7b91222d737..2b3f1c6aa45 100644 --- a/test/jdk/java/io/Serializable/GetField/ReadFieldsCNF.java +++ b/test/jdk/java/io/Serializable/GetField/ReadFieldsCNF.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,8 @@ * @summary Verify that ObjectInputStream ReadFields correctly reports ClassNotFoundException * while getting the field value. The test uses Vector that calls ReadFields from its readObject. * @library /test/lib - * @run testng ReadFieldsCNF - * @run testng/othervm -Djdk.serialGetFieldCnfeReturnsNull=true ReadFieldsCNF + * @run junit ReadFieldsCNF + * @run junit/othervm -Djdk.serialGetFieldCnfeReturnsNull=true ReadFieldsCNF */ import java.io.ByteArrayInputStream; @@ -41,12 +41,12 @@ import java.io.StreamCorruptedException; import java.nio.charset.StandardCharsets; import java.util.Vector; -import org.testng.annotations.Test; -import org.testng.Assert; - import jdk.test.lib.hexdump.HexPrinter; import jdk.test.lib.hexdump.ObjectStreamPrinter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + public class ReadFieldsCNF { private static final boolean GETFIELD_CNFE_RETURNS_NULL = @@ -58,7 +58,7 @@ public class ReadFieldsCNF { * @throws IOException If any other exception occurs */ @Test - private static void testVectorWithRole() throws IOException { + void testVectorWithRole() throws IOException { System.out.println("Property GETFIELD_CNFE_RETURNS_NULL: " + GETFIELD_CNFE_RETURNS_NULL); Role role = new Role(); @@ -75,7 +75,7 @@ public class ReadFieldsCNF { System.out.printf("Role offset: %d (0x%x) : %s%n", off, off, Role.class.getName()); if (off < 0) { HexPrinter.simple().formatter(ObjectStreamPrinter.formatter()).format(bytes); - Assert.fail("classname not found"); + Assertions.fail("classname not found"); } bytes[off] = (byte) 'X'; // replace R with X -> Class not found @@ -85,18 +85,18 @@ public class ReadFieldsCNF { try { Object obj = in.readObject(); System.out.println("Read: " + obj); - Assert.fail("Should not reach here, an exception should always occur"); + Assertions.fail("Should not reach here, an exception should always occur"); } catch (ClassNotFoundException cnfe) { // Expected ClassNotFoundException String expected = "XeadFieldsCNF$Role"; - Assert.assertEquals(expected, cnfe.getMessage(), "Wrong classname"); + Assertions.assertEquals(expected, cnfe.getMessage(), "Wrong classname"); if (GETFIELD_CNFE_RETURNS_NULL) { - Assert.fail("Expected IOException got ClassNotFoundException", cnfe); + Assertions.fail("Expected IOException got ClassNotFoundException", cnfe); } System.out.println("Normal: OIS.readObject: " + cnfe); } catch (StreamCorruptedException ioe) { if (!GETFIELD_CNFE_RETURNS_NULL) { - Assert.fail("Expected ClassNotFoundException got StreamCorruptedException ", ioe); + Assertions.fail("Expected ClassNotFoundException got StreamCorruptedException ", ioe); } System.out.println("Normal: " + ioe); } @@ -108,7 +108,7 @@ public class ReadFieldsCNF { * @throws IOException If any other exception occurs */ @Test - private static void testHolderWithRole() throws IOException { + void testHolderWithRole() throws IOException { System.out.println("Property GETFIELD_CNFE_RETURNS_NULL: " + GETFIELD_CNFE_RETURNS_NULL); Role role = new Role(); Holder holder = new Holder(role); @@ -123,7 +123,7 @@ public class ReadFieldsCNF { System.out.printf("Role offset: %d (0x%x)%n", off, off); if (off < 0) { HexPrinter.simple().formatter(ObjectStreamPrinter.formatter()).format(bytes); - Assert.fail("classname found at index: " + off + " (0x" + Integer.toHexString(off) + ")"); + Assertions.fail("classname found at index: " + off + " (0x" + Integer.toHexString(off) + ")"); } bytes[off] = (byte) 'X'; // replace R with X -> Class not found @@ -133,15 +133,15 @@ public class ReadFieldsCNF { try { Holder obj = (Holder)in.readObject(); System.out.println("Read: " + obj); - Assert.fail("Should not reach here, an exception should always occur"); + Assertions.fail("Should not reach here, an exception should always occur"); } catch (ClassNotFoundException cnfe) { // Expected ClassNotFoundException String expected = "XeadFieldsCNF$Role"; - Assert.assertEquals(expected, cnfe.getMessage(), "Wrong classname"); + Assertions.assertEquals(expected, cnfe.getMessage(), "Wrong classname"); System.out.println("Normal: OIS.readObject: " + cnfe); } catch (StreamCorruptedException ioe) { if (!GETFIELD_CNFE_RETURNS_NULL) { - Assert.fail("Expected ClassNotFoundException got StreamCorruptedException ", ioe); + Assertions.fail("Expected ClassNotFoundException got StreamCorruptedException ", ioe); } System.out.println("Normal: " + ioe); } diff --git a/test/jdk/java/io/Serializable/class/NonSerializableTest.java b/test/jdk/java/io/Serializable/class/NonSerializableTest.java index fa81b3e3ce7..2a36380aae7 100644 --- a/test/jdk/java/io/Serializable/class/NonSerializableTest.java +++ b/test/jdk/java/io/Serializable/class/NonSerializableTest.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 @@ -32,79 +32,84 @@ * jdk.test.lib.JDKToolLauncher * jdk.test.lib.Platform * jdk.test.lib.process.* - * @run testng/timeout=300 NonSerializableTest + * @run junit/timeout=300 NonSerializableTest * @summary Enable serialize of nonSerializable Class descriptor. */ import java.nio.file.Paths; import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import jdk.test.lib.compiler.CompilerUtils; import jdk.test.lib.process.ProcessTools; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + public class NonSerializableTest { - @BeforeClass - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { boolean b = CompilerUtils.compile( Paths.get(System.getProperty("test.src"), "TestEntry.java"), Paths.get(System.getProperty("user.dir"))); assertTrue(b, "Compilation failed"); } - @DataProvider - public Object[][] provider() { - return new String[][][] { + // Test cases to compile and run + public static Stream> provider() { + return Stream.of( // Write NonSerial1, Read NonSerial1 - {{"NonSerialA_1", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"NonSerialA_1", "-cp", ".", "TestEntry", "-d"}}, + List.of("NonSerialA_1", "-cp", ".", "TestEntry", "-s", "A"), + List.of("NonSerialA_1", "-cp", ".", "TestEntry", "-d"), // Write NonSerial1, Read NonSerial2 - {{"NonSerialA_1", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"NonSerialA_2", "-cp", ".", "TestEntry", "-d"}}, + List.of("NonSerialA_1", "-cp", ".", "TestEntry", "-s", "A"), + List.of("NonSerialA_2", "-cp", ".", "TestEntry", "-d"), // Write NonSerial1, Read Serial1 - {{"NonSerialA_1", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"SerialA_1", "-cp", ".", "TestEntry", "-d"}}, + List.of("NonSerialA_1", "-cp", ".", "TestEntry", "-s", "A"), + List.of("SerialA_1", "-cp", ".", "TestEntry", "-d"), // Write Serial1, Read NonSerial1 - {{"SerialA_1", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"NonSerialA_1", "-cp", ".", "TestEntry", "-doe"}}, + List.of("SerialA_1", "-cp", ".", "TestEntry", "-s", "A"), + List.of("NonSerialA_1", "-cp", ".", "TestEntry", "-doe"), // Write Serial1, Read Serial2 - {{"SerialA_1", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"SerialA_2", "-cp", ".", "TestEntry", "-d"}}, + List.of("SerialA_1", "-cp", ".", "TestEntry", "-s", "A"), + List.of("SerialA_2", "-cp", ".", "TestEntry", "-d"), // Write Serial2, Read Serial1 - {{"SerialA_2", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"SerialA_1", "-cp", ".", "TestEntry", "-d"}}, + List.of("SerialA_2", "-cp", ".", "TestEntry", "-s", "A"), + List.of("SerialA_1", "-cp", ".", "TestEntry", "-d"), // Write Serial1, Read Serial3 - {{"SerialA_1", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"SerialA_3", "-cp", ".", "TestEntry", "-de"}}, + List.of("SerialA_1", "-cp", ".", "TestEntry", "-s", "A"), + List.of("SerialA_3", "-cp", ".", "TestEntry", "-de"), // Write Serial3, Read Serial1 - {{"SerialA_3", "-cp", ".", "TestEntry", "-s", "A"}}, - {{"SerialA_1", "-cp", ".", "TestEntry", "-de"}}, - }; + List.of("SerialA_3", "-cp", ".", "TestEntry", "-s", "A"), + List.of("SerialA_1", "-cp", ".", "TestEntry", "-de")); } - @Test(dataProvider="provider") - public void test(String[] args) throws Exception { + @ParameterizedTest + @MethodSource("provider") + public void test(List argList) throws Exception { + String[] args = argList.toArray(new String[0]); boolean b = CompilerUtils.compile(Paths.get(System.getProperty("test.src"), args[0]), Paths.get(System.getProperty("user.dir"))); assertTrue(b, "Compilation failed"); - String params[] = Arrays.copyOfRange(args, 1, args.length); + String[] params = Arrays.copyOfRange(args, 1, args.length); ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(params); - Process p = ProcessTools.startProcess("Serializable Test", pb); - int exitValue = p.waitFor(); - assertEquals(exitValue, 0, "Test failed"); + try (Process p = ProcessTools.startProcess("Serializable Test", pb)) { + int exitValue = p.waitFor(); + assertEquals(0, exitValue, "Test failed"); + } } } diff --git a/test/jdk/java/io/Serializable/records/RecordClassTest.java b/test/jdk/java/io/Serializable/records/RecordClassTest.java index ba920ce92e5..951dd2f44dc 100644 --- a/test/jdk/java/io/Serializable/records/RecordClassTest.java +++ b/test/jdk/java/io/Serializable/records/RecordClassTest.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 @@ -37,6 +37,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; +import java.io.Serial; import java.io.Serializable; import static java.lang.System.out; @@ -49,12 +50,12 @@ import org.junit.jupiter.params.provider.MethodSource; /** * Serializes and deserializes record classes. Ensures that the SUID is 0. */ -@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class RecordClassTest { record Foo () implements Serializable { } record Bar (int x) implements Serializable { + @Serial private static final long serialVersionUID = 987654321L; } @@ -70,6 +71,7 @@ public class RecordClassTest { } record Wibble () implements ThrowingExternalizable { + @Serial private static final long serialVersionUID = 12345678L; } @@ -77,7 +79,7 @@ public class RecordClassTest { record Wubble (Wobble wobble, Wibble wibble, String s) implements ThrowingExternalizable { } - public Object[][] recordClasses() { + public static Object[][] recordClasses() { return new Object[][] { new Object[] { Foo.class , 0L }, new Object[] { Bar.class , 987654321L }, @@ -124,7 +126,7 @@ public class RecordClassTest { record NotSerializable3(T t) { } - public Object[][] notSerRecordClasses() { + public static Object[][] notSerRecordClasses() { return new Object[][] { new Object[] { NotSerializable1.class }, new Object[] { NotSerializable2.class }, diff --git a/test/jdk/java/io/Serializable/records/SerialVersionUIDTest.java b/test/jdk/java/io/Serializable/records/SerialVersionUIDTest.java index 2c133392dcb..f313e8a44a3 100644 --- a/test/jdk/java/io/Serializable/records/SerialVersionUIDTest.java +++ b/test/jdk/java/io/Serializable/records/SerialVersionUIDTest.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 @@ -35,6 +35,7 @@ import java.io.DataOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.Serial; import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -44,18 +45,18 @@ import static java.lang.System.out; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class SerialVersionUIDTest { record R1 () implements Serializable { + @Serial private static final long serialVersionUID = 1L; } record R2 (int x, int y) implements Serializable { + @Serial private static final long serialVersionUID = 0L; } @@ -64,10 +65,11 @@ public class SerialVersionUIDTest { record R4 (String s) implements Serializable { } record R5 (long l) implements Serializable { + @Serial private static final long serialVersionUID = 5678L; } - public Object[][] recordObjects() { + public static Object[][] recordObjects() { return new Object[][] { new Object[] { new R1(), 1L }, new Object[] { new R2(1, 2), 0L }, @@ -103,7 +105,7 @@ public class SerialVersionUIDTest { assertEquals(expectedUID, dis.readLong()); } - public Object[][] recordClasses() { + public static Object[][] recordClasses() { List list = new ArrayList<>(); List> recordClasses = List.of(R1.class, R2.class, R3.class, R4.class, R5.class); LongStream.of(0L, 1L, 100L, 10_000L, 1_000_000L).forEach(suid -> diff --git a/test/jdk/java/io/Serializable/serialFilter/CheckArrayTest.java b/test/jdk/java/io/Serializable/serialFilter/CheckArrayTest.java index d00c670bd15..2081375ca59 100644 --- a/test/jdk/java/io/Serializable/serialFilter/CheckArrayTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/CheckArrayTest.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,15 @@ import java.io.InvalidClassException; import jdk.internal.access.SharedSecrets; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.testng.Assert; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* @test * @build CheckArrayTest SerialFilterTest * @bug 8203368 * @modules java.base/jdk.internal.access - * @run testng CheckArrayTest + * @run junit CheckArrayTest * * @summary Test the SharedSecret access to ObjectInputStream.checkArray works * with overridden subclasses. @@ -53,8 +53,8 @@ import org.testng.Assert; */ public class CheckArrayTest { - @DataProvider(name = "Patterns") - Object[][] patterns() { + // Test patterns for arrays + private static Object[][] patterns() { return new Object[][]{ new Object[]{"maxarray=10", 10, new String[10]}, // successful new Object[]{"maxarray=10", 11, new String[11]}, // exception expected @@ -64,7 +64,8 @@ public class CheckArrayTest { /** * Test SharedSecrets checkArray with unmodified ObjectInputStream. */ - @Test(dataProvider = "Patterns") + @ParameterizedTest + @MethodSource("patterns") public void normalOIS(String pattern, int arraySize, Object[] array) throws IOException { ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); byte[] bytes = SerialFilterTest.writeObjects(array); @@ -75,10 +76,10 @@ public class CheckArrayTest { ois.setObjectInputFilter(filter); SharedSecrets.getJavaObjectInputStreamAccess() .checkArray(ois, array.getClass(), arraySize); - Assert.assertTrue(array.length >= arraySize, + Assertions.assertTrue(array.length >= arraySize, "Should have thrown InvalidClassException due to array size"); } catch (InvalidClassException ice) { - Assert.assertFalse(array.length > arraySize, + Assertions.assertFalse(array.length > arraySize, "Should NOT have thrown InvalidClassException due to array size"); } } @@ -88,7 +89,8 @@ public class CheckArrayTest { * Test SharedSecrets checkArray with an ObjectInputStream subclassed to * handle all input stream functions. */ - @Test(dataProvider = "Patterns") + @ParameterizedTest + @MethodSource("patterns") public void subclassedOIS(String pattern, int arraySize, Object[] array) throws IOException { byte[] bytes = SerialFilterTest.writeObjects(array); try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); @@ -98,10 +100,10 @@ public class CheckArrayTest { ois.setObjectInputFilter(filter); SharedSecrets.getJavaObjectInputStreamAccess() .checkArray(ois, array.getClass(), arraySize); - Assert.assertTrue(array.length >= arraySize, + Assertions.assertTrue(array.length >= arraySize, "Should have thrown InvalidClassException due to array size"); } catch (InvalidClassException ice) { - Assert.assertFalse(array.length > arraySize, + Assertions.assertFalse(array.length > arraySize, "Should NOT have thrown InvalidClassException due to array size"); } } diff --git a/test/jdk/java/io/Serializable/serialFilter/CheckInputOrderTest.java b/test/jdk/java/io/Serializable/serialFilter/CheckInputOrderTest.java index 0230eb653ce..d824c947ece 100644 --- a/test/jdk/java/io/Serializable/serialFilter/CheckInputOrderTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/CheckInputOrderTest.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 @@ -25,28 +25,28 @@ import java.io.ByteArrayInputStream; import java.io.InvalidClassException; import java.io.ObjectInputFilter; import java.io.ObjectInputStream; +import java.io.Serial; import java.io.Serializable; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* @test * @build CheckInputOrderTest SerialFilterTest - * @run testng/othervm CheckInputOrderTest + * @run junit/othervm CheckInputOrderTest * * @summary Test that when both global filter and specific filter are set, * global filter will not affect specific filter. */ public class CheckInputOrderTest implements Serializable { + @Serial private static final long serialVersionUID = 12345678901L; - @DataProvider(name="Patterns") - Object[][] patterns() { + // Test cases for serial filter strings + static Object[][] patterns() { return new Object[][] { new Object[] { SerialFilterTest.genTestObject("maxarray=1", true), "java.**;java.lang.*;java.lang.Long;maxarray=0", false }, new Object[] { SerialFilterTest.genTestObject("maxarray=1", true), "java.**;java.lang.*;java.lang.Long", true }, @@ -75,7 +75,8 @@ public class CheckInputOrderTest implements Serializable { * "global filter reject" + "specific ObjectInputStream filter is empty" => should reject * "global filter reject" + "specific ObjectInputStream filter allow" => should allow */ - @Test(dataProvider="Patterns") + @ParameterizedTest + @MethodSource("patterns") public void testRejectedInGlobal(Object toDeserialized, String pattern, boolean allowed) throws Exception { byte[] bytes = SerialFilterTest.writeObjects(toDeserialized); ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); diff --git a/test/jdk/java/io/Serializable/serialFilter/GlobalFilterTest.java b/test/jdk/java/io/Serializable/serialFilter/GlobalFilterTest.java index 9e38d6c499d..18bbf265e43 100644 --- a/test/jdk/java/io/Serializable/serialFilter/GlobalFilterTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/GlobalFilterTest.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 @@ -21,9 +21,8 @@ * questions. */ -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertSame; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; import java.io.EOFException; @@ -35,39 +34,40 @@ import java.io.ObjectInputStream; import java.security.Security; import java.util.Objects; -import org.testng.Assert; -import org.testng.annotations.Test; -import org.testng.annotations.DataProvider; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIf; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* @test * @bug 8231422 * @build GlobalFilterTest SerialFilterTest - * @run testng/othervm GlobalFilterTest - * @run testng/othervm -Djdk.serialFilter=java.** + * @run junit/othervm GlobalFilterTest + * @run junit/othervm -Djdk.serialFilter=java.** * -Dexpected-jdk.serialFilter=java.** GlobalFilterTest * @summary Test Global Filters */ -@Test public class GlobalFilterTest { private static final String serialPropName = "jdk.serialFilter"; private static final String badSerialFilter = "java.lang.StringBuffer;!*"; private static final String origSerialFilterProperty = System.setProperty(serialPropName, badSerialFilter); + private static final String EXPECTED_GLOBAL_FILTER = System.getProperty("expected-" + serialPropName, + Security.getProperty(serialPropName)); + + static boolean hasGlobalFilter() { + return EXPECTED_GLOBAL_FILTER != null && !EXPECTED_GLOBAL_FILTER.isEmpty(); + } + /** * DataProvider of patterns and objects derived from the configured process-wide filter. * @return Array of arrays of pattern, object, allowed boolean, and API factory */ - @DataProvider(name="globalPatternElements") - Object[][] globalPatternElements() { - String globalFilter = - System.getProperty("expected-" + serialPropName, - Security.getProperty(serialPropName)); - if (globalFilter == null) { - return new Object[0][]; - } + static Object[][] globalPatternElements() { - String[] patterns = globalFilter.split(";"); + String[] patterns = EXPECTED_GLOBAL_FILTER.split(";"); Object[][] objects = new Object[patterns.length][]; for (int i = 0; i < patterns.length; i++) { @@ -83,7 +83,7 @@ public class GlobalFilterTest { ? SerialFilterTest.genTestObject(pattern, true) : SerialFilterTest.genTestObject(pattern.substring(1), false); - Assert.assertNotNull(o, "fail generation failed"); + Assertions.assertNotNull(o, "fail generation failed"); } objects[i] = new Object[3]; objects[i][0] = pattern; @@ -98,13 +98,13 @@ public class GlobalFilterTest { * and has the toString matching the configured pattern. */ @Test() - static void globalFilter() { + void globalFilter() { ObjectInputFilter filter = ObjectInputFilter.Config.getSerialFilter(); // Check that the System.setProperty(jdk.serialFilter) DOES NOT affect the filter. String asSetSystemProp = System.getProperty(serialPropName, Security.getProperty(serialPropName)); - Assert.assertNotEquals(Objects.toString(filter, null), asSetSystemProp, + Assertions.assertNotEquals(asSetSystemProp, Objects.toString(filter, null), "System.setProperty(\"jdk.serialfilter\", ...) should not change filter: " + asSetSystemProp); @@ -112,7 +112,7 @@ public class GlobalFilterTest { System.getProperty("expected-" + serialPropName, Security.getProperty(serialPropName)); System.out.printf("global pattern: %s, filter: %s%n", pattern, filter); - Assert.assertEquals(Objects.toString(filter, null), pattern, + assertEquals(pattern, Objects.toString(filter, null), "process-wide filter pattern does not match"); } @@ -120,16 +120,15 @@ public class GlobalFilterTest { * If the Global filter is already set, it should always refuse to be * set again. */ - @Test() - @SuppressWarnings("removal") - static void setGlobalFilter() { + @Test + void setGlobalFilter() { ObjectInputFilter filter = new SerialFilterTest.Validator(); ObjectInputFilter global = ObjectInputFilter.Config.getSerialFilter(); if (global != null) { // once set, can never be re-set try { ObjectInputFilter.Config.setSerialFilter(filter); - Assert.fail("set only once process-wide filter"); + Assertions.fail("set only once process-wide filter"); } catch (IllegalStateException ise) { // Normal, once set can never be re-set } @@ -141,7 +140,7 @@ public class GlobalFilterTest { try { // Try to set it again, expecting it to throw ObjectInputFilter.Config.setSerialFilter(filter); - Assert.fail("set only once process-wide filter"); + Assertions.fail("set only once process-wide filter"); } catch (IllegalStateException ise) { // Normal case } @@ -154,8 +153,10 @@ public class GlobalFilterTest { * * @param pattern a pattern extracted from the configured global pattern */ - @Test(dataProvider = "globalPatternElements") - static void globalFilterElements(String pattern, boolean allowed,Object obj) { + @ParameterizedTest + @EnabledIf("hasGlobalFilter") + @MethodSource("globalPatternElements") + void globalFilterElements(String pattern, boolean allowed,Object obj) { testGlobalPattern(pattern, obj, allowed); } @@ -177,15 +178,15 @@ public class GlobalFilterTest { } catch (EOFException eof) { // normal completion } catch (ClassNotFoundException cnf) { - Assert.fail("Deserializing", cnf); + Assertions.fail("Deserializing", cnf); } - Assert.assertTrue(allowed, "filter should have thrown an exception"); + assertTrue(allowed, "filter should have thrown an exception"); } catch (IllegalArgumentException iae) { - Assert.fail("bad format pattern", iae); + Assertions.fail("bad format pattern", iae); } catch (InvalidClassException ice) { - Assert.assertFalse(allowed, "filter should not have thrown an exception: " + ice); + Assertions.assertFalse(allowed, "filter should not have thrown an exception: " + ice); } catch (IOException ioe) { - Assert.fail("Unexpected IOException", ioe); + Assertions.fail("Unexpected IOException", ioe); } } } diff --git a/test/jdk/java/io/Serializable/serialFilter/InvalidGlobalFilterTest.java b/test/jdk/java/io/Serializable/serialFilter/InvalidGlobalFilterTest.java index a017354b103..b924a96c86c 100644 --- a/test/jdk/java/io/Serializable/serialFilter/InvalidGlobalFilterTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/InvalidGlobalFilterTest.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,9 +21,6 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -31,18 +28,22 @@ import java.io.ObjectInputFilter; import java.io.ObjectInputStream; import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + /* * @test * @bug 8278087 * @summary Test that an invalid pattern value for the jdk.serialFilter system property causes an * exception to be thrown when an attempt is made to use the filter or deserialize. * A subset of invalid filter patterns is tested. - * @run testng/othervm -Djdk.serialFilter=.* InvalidGlobalFilterTest - * @run testng/othervm -Djdk.serialFilter=! InvalidGlobalFilterTest - * @run testng/othervm -Djdk.serialFilter=/ InvalidGlobalFilterTest + * @run junit/othervm -Djdk.serialFilter=.* InvalidGlobalFilterTest + * @run junit/othervm -Djdk.serialFilter=! InvalidGlobalFilterTest + * @run junit/othervm -Djdk.serialFilter=/ InvalidGlobalFilterTest * */ -@Test public class InvalidGlobalFilterTest { private static final String serialPropName = "jdk.serialFilter"; private static final String serialFilter = System.getProperty(serialPropName); @@ -64,13 +65,13 @@ public class InvalidGlobalFilterTest { "java.base/", "Invalid jdk.serialFilter: class or package missing in: \"java.base/\"", "/", "Invalid jdk.serialFilter: module name is missing in: \"/\""); - @DataProvider(name = "MethodsToCall") - private Object[][] cases() { + // Test cases for exceptions + private static Object[][] cases() { return new Object[][] { - {serialFilter, "getSerialFilter", (Assert.ThrowingRunnable) () -> ObjectInputFilter.Config.getSerialFilter()}, - {serialFilter, "setSerialFilter", (Assert.ThrowingRunnable) () -> ObjectInputFilter.Config.setSerialFilter(new NoopFilter())}, - {serialFilter, "new ObjectInputStream(is)", (Assert.ThrowingRunnable) () -> new ObjectInputStream(new ByteArrayInputStream(new byte[0]))}, - {serialFilter, "new OISSubclass()", (Assert.ThrowingRunnable) () -> new OISSubclass()}, + {serialFilter, "getSerialFilter", (Executable) () -> ObjectInputFilter.Config.getSerialFilter()}, + {serialFilter, "setSerialFilter", (Executable) () -> ObjectInputFilter.Config.setSerialFilter(new NoopFilter())}, + {serialFilter, "new ObjectInputStream(is)", (Executable) () -> new ObjectInputStream(new ByteArrayInputStream(new byte[0]))}, + {serialFilter, "new OISSubclass()", (Executable) () -> new OISSubclass()}, }; } @@ -78,18 +79,19 @@ public class InvalidGlobalFilterTest { * Test each method that should throw IllegalStateException based on * the invalid arguments it was launched with. */ - @Test(dataProvider = "MethodsToCall") - public void initFaultTest(String pattern, String method, Assert.ThrowingRunnable runnable) { + @ParameterizedTest + @MethodSource("cases") + public void initFaultTest(String pattern, String method, Executable runnable) { - IllegalStateException ex = Assert.expectThrows(IllegalStateException.class, + IllegalStateException ex = Assertions.assertThrows(IllegalStateException.class, runnable); String expected = invalidMessages.get(serialFilter); if (expected == null) { - Assert.fail("No expected message for filter: " + serialFilter); + Assertions.fail("No expected message for filter: " + serialFilter); } System.out.println(ex.getMessage()); - Assert.assertEquals(ex.getMessage(), expected, "wrong message"); + Assertions.assertEquals(expected, ex.getMessage(), "wrong message"); } private static class NoopFilter implements ObjectInputFilter { diff --git a/test/jdk/java/io/Serializable/serialFilter/MixedFiltersTest.java b/test/jdk/java/io/Serializable/serialFilter/MixedFiltersTest.java index 8e24f27f598..5af5f4d15ed 100644 --- a/test/jdk/java/io/Serializable/serialFilter/MixedFiltersTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/MixedFiltersTest.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 @@ -25,20 +25,21 @@ import java.io.ByteArrayInputStream; import java.io.InvalidClassException; import java.io.ObjectInputFilter; import java.io.ObjectInputStream; +import java.io.Serial; import java.io.Serializable; import java.security.Security; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.condition.DisabledIf; +import org.junit.jupiter.api.condition.EnabledIf; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* @test * @build MixedFiltersTest SerialFilterTest - * @run testng/othervm -Djdk.serialFilter=!java.**;!java.lang.Long;maxdepth=5;maxarray=5;maxbytes=90;maxrefs=5 MixedFiltersTest - * @run testng/othervm -Djdk.serialFilter=java.**;java.lang.Long;maxdepth=1000;maxarray=1000;maxbytes=1000;maxrefs=1000 MixedFiltersTest + * @run junit/othervm -Djdk.serialFilter=!java.**;!java.lang.Long;maxdepth=5;maxarray=5;maxbytes=90;maxrefs=5 MixedFiltersTest + * @run junit/othervm -Djdk.serialFilter=java.**;java.lang.Long;maxdepth=1000;maxarray=1000;maxbytes=1000;maxrefs=1000 MixedFiltersTest * * @summary Test that when both global filter and specific filter are set, * global filter will not affect specific filter. @@ -46,23 +47,17 @@ import static org.testng.Assert.fail; public class MixedFiltersTest implements Serializable { + @Serial private static final long serialVersionUID = 1234567890L; + private static final String JDK_SERIAL_FILTER = System.getProperty("jdk.serialFilter", + Security.getProperty("jdk.serialFilter")); - boolean globalRejected; - - @BeforeClass - public void setup() { - String pattern = System.getProperty("jdk.serialFilter", - Security.getProperty("jdk.serialFilter")); - globalRejected = pattern.startsWith("!"); + private static boolean globalRejected() { + return JDK_SERIAL_FILTER.startsWith("!"); } - @DataProvider(name="RejectedInGlobal") - Object[][] rejectedInGlobal() { - if (!globalRejected) { - return new Object[0][]; - } + static Object[][] rejectedInGlobal() { return new Object[][] { new Object[] { Long.MAX_VALUE, "java.**" }, new Object[] { Long.MAX_VALUE, "java.lang.Long" }, @@ -79,7 +74,9 @@ public class MixedFiltersTest implements Serializable { * "global filter reject" + "specific ObjectInputStream filter is empty" => should reject * "global filter reject" + "specific ObjectInputStream filter allow" => should allow */ - @Test(dataProvider="RejectedInGlobal") + @ParameterizedTest + @EnabledIf("globalRejected") + @MethodSource("rejectedInGlobal") public void testRejectedInGlobal(Object toDeserialized, String pattern) throws Exception { byte[] bytes = SerialFilterTest.writeObjects(toDeserialized); try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); @@ -96,12 +93,7 @@ public class MixedFiltersTest implements Serializable { } } - @DataProvider(name="AllowedInGlobal") - Object[][] allowedInGlobal() { - if (globalRejected) { - return new Object[0][]; - } - + static Object[][] allowedInGlobal() { return new Object[][] { new Object[] { Long.MAX_VALUE, "!java.**" }, new Object[] { Long.MAX_VALUE, "!java.lang.Long" }, @@ -118,7 +110,9 @@ public class MixedFiltersTest implements Serializable { * "global filter allow" + "specific ObjectInputStream filter is empty" => should allow * "global filter allow" + "specific ObjectInputStream filter reject" => should reject */ - @Test(dataProvider="AllowedInGlobal") + @ParameterizedTest + @DisabledIf("globalRejected") + @MethodSource("allowedInGlobal") public void testAllowedInGlobal(Object toDeserialized, String pattern) throws Exception { byte[] bytes = SerialFilterTest.writeObjects(toDeserialized); try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); diff --git a/test/jdk/java/io/Serializable/serialFilter/SerialFactoryExample.java b/test/jdk/java/io/Serializable/serialFilter/SerialFactoryExample.java index 45b1872bae0..271c22b917c 100644 --- a/test/jdk/java/io/Serializable/serialFilter/SerialFactoryExample.java +++ b/test/jdk/java/io/Serializable/serialFilter/SerialFactoryExample.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.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -46,9 +42,13 @@ import static java.io.ObjectInputFilter.Status.ALLOWED; import static java.io.ObjectInputFilter.Status.REJECTED; import static java.io.ObjectInputFilter.Status.UNDECIDED; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + /* @test - * @run testng/othervm SerialFactoryExample - * @run testng/othervm -Djdk.serialFilterFactory=SerialFactoryExample$FilterInThread SerialFactoryExample + * @run junit/othervm SerialFactoryExample + * @run junit/othervm -Djdk.serialFilterFactory=SerialFactoryExample$FilterInThread SerialFactoryExample * @summary Test SerialFactoryExample */ @@ -76,10 +76,9 @@ import static java.io.ObjectInputFilter.Status.UNDECIDED; * * The `doWithSerialFilter` calls can be nested. When nested, the filters are concatenated. */ -@Test public class SerialFactoryExample { - @DataProvider(name = "Examples") + // Test cases for filters static Object[][] examples() { return new Object[][]{ {new Point(1, 2), null, @@ -108,7 +107,8 @@ public class SerialFactoryExample { } - @Test(dataProvider = "Examples") + @ParameterizedTest + @MethodSource("examples") void examples(Serializable obj, ObjectInputFilter filter, Status expected) { // Establish FilterInThread as the application-wide filter factory FilterInThread filterInThread; @@ -128,11 +128,11 @@ public class SerialFactoryExample { Object o = deserializeObject(bytes); }); if (expected.equals(REJECTED)) - Assert.fail("IllegalClassException should have occurred"); + Assertions.fail("IllegalClassException should have occurred"); } catch (UncheckedIOException uioe) { IOException ioe = uioe.getCause(); - Assert.assertEquals(ioe.getClass(), InvalidClassException.class, "Wrong exception"); - Assert.assertEquals(REJECTED, expected, "Exception should not have occurred"); + Assertions.assertEquals(InvalidClassException.class, ioe.getClass(), "Wrong exception"); + Assertions.assertEquals(expected, REJECTED, "Exception should not have occurred"); } } @@ -142,7 +142,8 @@ public class SerialFactoryExample { * @param filter a filter * @param expected status */ - @Test(dataProvider = "Examples") + @ParameterizedTest + @MethodSource("examples") void checkStatus(Serializable obj, ObjectInputFilter filter, Status expected) { // Establish FilterInThread as the application-wide filter factory FilterInThread filterInThread; @@ -166,12 +167,12 @@ public class SerialFactoryExample { System.out.println(" filter in effect: " + filterInThread.currFilter); if (compositeFilter != null) { Status actualStatus = compositeFilter.checkInput(info); - Assert.assertEquals(actualStatus, expected, "Wrong Status"); + Assertions.assertEquals(expected, actualStatus, "Wrong Status"); } }); } catch (Exception ex) { - Assert.fail("unexpected exception", ex); + Assertions.fail("unexpected exception", ex); } } diff --git a/test/jdk/java/io/Serializable/serialFilter/SerialFactoryFaults.java b/test/jdk/java/io/Serializable/serialFilter/SerialFactoryFaults.java index 1d35306a426..add9f3557da 100644 --- a/test/jdk/java/io/Serializable/serialFilter/SerialFactoryFaults.java +++ b/test/jdk/java/io/Serializable/serialFilter/SerialFactoryFaults.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,9 +21,6 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -32,15 +29,19 @@ import java.io.ObjectInputFilter.Config; import java.io.ObjectInputStream; import java.util.function.BinaryOperator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + /* @test - * @run testng/othervm -Djdk.serialFilterFactory=ForcedError_NoSuchClass SerialFactoryFaults - * @run testng/othervm -Djdk.serialFilterFactory=SerialFactoryFaults$NoPublicConstructor SerialFactoryFaults - * @run testng/othervm -Djdk.serialFilterFactory=SerialFactoryFaults$ConstructorThrows SerialFactoryFaults - * @run testng/othervm -Djdk.serialFilterFactory=SerialFactoryFaults$FactorySetsFactory SerialFactoryFaults + * @run junit/othervm -Djdk.serialFilterFactory=ForcedError_NoSuchClass SerialFactoryFaults + * @run junit/othervm -Djdk.serialFilterFactory=SerialFactoryFaults$NoPublicConstructor SerialFactoryFaults + * @run junit/othervm -Djdk.serialFilterFactory=SerialFactoryFaults$ConstructorThrows SerialFactoryFaults + * @run junit/othervm -Djdk.serialFilterFactory=SerialFactoryFaults$FactorySetsFactory SerialFactoryFaults * @summary Check cases where the Filter Factory initialization from properties fails */ -@Test public class SerialFactoryFaults { // Sample the serial factory class name @@ -52,13 +53,13 @@ public class SerialFactoryFaults { System.getProperty("test.src", ".") + "/logging.properties"); } - @DataProvider(name = "MethodsToCall") - private Object[][] cases() { + // Test cases of faults + private static Object[][] cases() { return new Object[][] { - {"getSerialFilterFactory", (Assert.ThrowingRunnable) () -> Config.getSerialFilterFactory()}, - {"setSerialFilterFactory", (Assert.ThrowingRunnable) () -> Config.setSerialFilterFactory(new NoopFactory())}, - {"new ObjectInputStream(is)", (Assert.ThrowingRunnable) () -> new ObjectInputStream(new ByteArrayInputStream(new byte[0]))}, - {"new OISSubclass()", (Assert.ThrowingRunnable) () -> new OISSubclass()}, + {"getSerialFilterFactory", (Executable) () -> Config.getSerialFilterFactory()}, + {"setSerialFilterFactory", (Executable) () -> Config.setSerialFilterFactory(new NoopFactory())}, + {"new ObjectInputStream(is)", (Executable) () -> new ObjectInputStream(new ByteArrayInputStream(new byte[0]))}, + {"new OISSubclass()", (Executable) () -> new OISSubclass()}, }; } @@ -66,26 +67,23 @@ public class SerialFactoryFaults { * Test each method that should throw IllegalStateException based on * the invalid arguments it was launched with. */ - @Test(dataProvider = "MethodsToCall") - public void initFaultTest(String name, Assert.ThrowingRunnable runnable) { - IllegalStateException ex = Assert.expectThrows(IllegalStateException.class, + @ParameterizedTest + @MethodSource("cases") + public void initFaultTest(String name, Executable runnable) { + IllegalStateException ex = Assertions.assertThrows(IllegalStateException.class, runnable); final String msg = ex.getMessage(); if (factoryName.equals("ForcedError_NoSuchClass")) { - Assert.assertEquals(msg, - "invalid jdk.serialFilterFactory: ForcedError_NoSuchClass: java.lang.ClassNotFoundException: ForcedError_NoSuchClass", "wrong exception"); + Assertions.assertEquals("invalid jdk.serialFilterFactory: ForcedError_NoSuchClass: java.lang.ClassNotFoundException: ForcedError_NoSuchClass", msg, "wrong exception"); } else if (factoryName.equals("SerialFactoryFaults$NoPublicConstructor")) { - Assert.assertEquals(msg, - "invalid jdk.serialFilterFactory: SerialFactoryFaults$NoPublicConstructor: java.lang.NoSuchMethodException: SerialFactoryFaults$NoPublicConstructor.()", "wrong exception"); + Assertions.assertEquals("invalid jdk.serialFilterFactory: SerialFactoryFaults$NoPublicConstructor: java.lang.NoSuchMethodException: SerialFactoryFaults$NoPublicConstructor.()", msg, "wrong exception"); } else if (factoryName.equals("SerialFactoryFaults$ConstructorThrows")) { - Assert.assertEquals(msg, - "invalid jdk.serialFilterFactory: SerialFactoryFaults$ConstructorThrows: java.lang.RuntimeException: constructor throwing a runtime exception", "wrong exception"); + Assertions.assertEquals("invalid jdk.serialFilterFactory: SerialFactoryFaults$ConstructorThrows: java.lang.RuntimeException: constructor throwing a runtime exception", msg, "wrong exception"); } else if (factoryName.equals("SerialFactoryFaults$FactorySetsFactory")) { - Assert.assertEquals(msg, - "invalid jdk.serialFilterFactory: SerialFactoryFaults$FactorySetsFactory: java.lang.IllegalStateException: Serial filter factory initialization incomplete", "wrong exception"); + Assertions.assertEquals("invalid jdk.serialFilterFactory: SerialFactoryFaults$FactorySetsFactory: java.lang.IllegalStateException: Serial filter factory initialization incomplete", msg, "wrong exception"); } else { - Assert.fail("No test for filter factory: " + factoryName); + Assertions.fail("No test for filter factory: " + factoryName); } } diff --git a/test/jdk/java/io/Serializable/serialFilter/SerialFilterFactoryTest.java b/test/jdk/java/io/Serializable/serialFilter/SerialFilterFactoryTest.java index 6842eabe9b0..f34f95a72b3 100644 --- a/test/jdk/java/io/Serializable/serialFilter/SerialFilterFactoryTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/SerialFilterFactoryTest.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 @@ -22,9 +22,6 @@ */ -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -38,17 +35,26 @@ import java.io.Serial; import java.io.Serializable; import java.util.function.BinaryOperator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.condition.EnabledIf; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + /* @test * @build SerialFilterFactoryTest - * @run testng/othervm SerialFilterFactoryTest - * @run testng/othervm -Djdk.serialFilterFactory=SerialFilterFactoryTest$PropertyFilterFactory + * @run junit/othervm SerialFilterFactoryTest + * @run junit/othervm -Djdk.serialFilterFactory=SerialFilterFactoryTest$PropertyFilterFactory * -Djava.util.logging.config.file=${test.src}/logging.properties SerialFilterFactoryTest - * @run testng/othervm -Djdk.serialFilterFactory=SerialFilterFactoryTest$NotMyFilterFactory + * @run junit/othervm -Djdk.serialFilterFactory=SerialFilterFactoryTest$NotMyFilterFactory * -Djava.util.logging.config.file=${test.src}/logging.properties SerialFilterFactoryTest * * @summary Test Context-specific Deserialization Filters */ -@Test +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class SerialFilterFactoryTest { // A stream with just the header, enough to create a OIS @@ -70,7 +76,7 @@ public class SerialFilterFactoryTest { ois.writeObject(new Dummy("Here")); return boas.toByteArray(); } catch (IOException ioe) { - Assert.fail("unexpected IOE", ioe); + Assertions.fail("unexpected IOE", ioe); } throw new RuntimeException("should not reach here"); } @@ -109,7 +115,7 @@ public class SerialFilterFactoryTest { return !(ObjectInputFilter.Config.getSerialFilterFactory() instanceof NotMyFilterFactory); } - @DataProvider(name="FilterCases") + // Test cases for filter factory static Object[][] filterCases() { if (isValidFilterFactory()) { return new Object[][]{ @@ -124,9 +130,11 @@ public class SerialFilterFactoryTest { } // Setting the filter factory to null is not allowed. - @Test(expectedExceptions=NullPointerException.class) + @Test void testNull() { - Config.setSerialFilterFactory(null); + Assertions.assertThrows(NullPointerException.class, () -> { + Config.setSerialFilterFactory(null); + }); } /** @@ -136,7 +144,6 @@ public class SerialFilterFactoryTest { * Try to set it again, the second should throw. */ @Test - @SuppressWarnings("removal") void testSecondSetShouldThrow() { var currFF = Config.getSerialFilterFactory(); if (currFF.getClass().getClassLoader() == null) { @@ -145,14 +152,14 @@ public class SerialFilterFactoryTest { Config.setSerialFilterFactory(contextFilterFactory); currFF = contextFilterFactory; } catch (IllegalStateException ise) { - Assert.fail("First setSerialFilterFactory should not throw"); + Assertions.fail("First setSerialFilterFactory should not throw"); } } // Setting it again will throw - Assert.expectThrows(IllegalStateException.class, + Assertions.assertThrows(IllegalStateException.class, () -> Config.setSerialFilterFactory(new MyFilterFactory("f11"))); var resetFF = Config.getSerialFilterFactory(); - Assert.assertEquals(resetFF, currFF, "Setting again should not change filter factory"); + Assertions.assertEquals(currFF, resetFF, "Setting again should not change filter factory"); } /** @@ -167,8 +174,10 @@ public class SerialFilterFactoryTest { * @throws IOException if an I/O error occurs (should not occur) * @throws ClassNotFoundException for class not found (should not occur) */ - @Test(dataProvider="FilterCases") - @SuppressWarnings("removal") + @ParameterizedTest + @EnabledIf("isValidFilterFactory") + @MethodSource("filterCases") + @Order(1) void testCase(MyFilterFactory dynFilterFactory, Validator dynFilter, Validator streamFilter) throws IOException, ClassNotFoundException { @@ -182,32 +191,32 @@ public class SerialFilterFactoryTest { InputStream is = new ByteArrayInputStream(simpleStream); ObjectInputStream ois = new ObjectInputStream(is); - Assert.assertNull(factory.current(), "initially current should be null"); - Assert.assertEquals(factory.next(), configFilter, "initially next should be the configured filter"); + Assertions.assertNull(factory.current(), "initially current should be null"); + Assertions.assertEquals(configFilter, factory.next(), "initially next should be the configured filter"); var currFilter = ois.getObjectInputFilter(); if (currFilter != null && currFilter.getClass().getClassLoader() == null) { // Builtin loader; defaults to configured filter - Assert.assertEquals(currFilter, configFilter, "getObjectInputFilter should be configured filter"); + Assertions.assertEquals(configFilter, currFilter, "getObjectInputFilter should be configured filter"); } else { - Assert.assertEquals(currFilter, configFilter, "getObjectInputFilter should be null"); + Assertions.assertEquals(configFilter, currFilter, "getObjectInputFilter should be null"); } if (streamFilter != null) { ois.setObjectInputFilter(streamFilter); // MyFilterFactory is called when the stream filter is changed; verify values passed it - Assert.assertEquals(factory.current(), currFilter, "when setObjectInputFilter, current should be current filter"); - Assert.assertEquals(factory.next(), streamFilter, "next should be stream specific filter"); + Assertions.assertEquals(currFilter, factory.current(), "when setObjectInputFilter, current should be current filter"); + Assertions.assertEquals(streamFilter, factory.next(), "next should be stream specific filter"); // Check the OIS filter after the factory has updated it. currFilter = ois.getObjectInputFilter(); - Assert.assertEquals(currFilter, streamFilter, "getObjectInputFilter should be set"); + Assertions.assertEquals(streamFilter, currFilter, "getObjectInputFilter should be set"); // Verify that it can not be set again - Assert.assertThrows(IllegalStateException.class, () -> ois.setObjectInputFilter(streamFilter)); + Assertions.assertThrows(IllegalStateException.class, () -> ois.setObjectInputFilter(streamFilter)); } if (currFilter instanceof Validator validator) { validator.reset(); Object o = ois.readObject(); // Invoke only for the side effect of calling the Filter - Assert.assertEquals(validator.count, 1, "Wrong number of calls to the stream filter"); + Assertions.assertEquals(1, validator.count, "Wrong number of calls to the stream filter"); } else { Object o = ois.readObject(); // Invoke only for the side effect of calling the filter } @@ -217,18 +226,19 @@ public class SerialFilterFactoryTest { @Test void testPropertyFilterFactory() { if (jdkSerialFilterFactoryProp != null) { - Assert.assertEquals(jdkSerialFilterFactory.getClass().getName(), jdkSerialFilterFactoryProp, + Assertions.assertEquals(jdkSerialFilterFactoryProp, jdkSerialFilterFactory.getClass().getName(), "jdk.serialFilterFactory property classname mismatch"); } } // Test that setting the filter factory after any deserialization (any testCase) // throws IllegalStateException with the specific message - @Test(dependsOnMethods="testCase") + @Test + @Order(99) void testSetFactoryAfterDeserialization() { BinaryOperator factory = Config.getSerialFilterFactory(); - IllegalStateException ise = Assert.expectThrows(IllegalStateException.class, () -> Config.setSerialFilterFactory(factory)); - Assert.assertTrue(ise.getMessage().startsWith("Cannot replace filter factory: ")); + IllegalStateException ise = Assertions.assertThrows(IllegalStateException.class, () -> Config.setSerialFilterFactory(factory)); + Assertions.assertTrue(ise.getMessage().startsWith("Cannot replace filter factory: ")); } @@ -243,11 +253,11 @@ public class SerialFilterFactoryTest { // Try to set the filter to null ois.setObjectInputFilter(null); if (curr != null) { - Assert.fail("setting filter to null after a non-null filter should throw"); + Assertions.fail("setting filter to null after a non-null filter should throw"); } } catch (IllegalStateException ise) { if (curr == null) { - Assert.fail("setting filter to null after a null filter should not throw"); + Assertions.fail("setting filter to null after a null filter should not throw"); } } } diff --git a/test/jdk/java/io/Serializable/serialFilter/SerialFilterFunctionTest.java b/test/jdk/java/io/Serializable/serialFilter/SerialFilterFunctionTest.java index 41832d583c2..8930c2dfa9d 100644 --- a/test/jdk/java/io/Serializable/serialFilter/SerialFilterFunctionTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/SerialFilterFunctionTest.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,11 +21,6 @@ * questions. */ - -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - import java.io.ObjectInputFilter; import java.io.ObjectInputFilter.FilterInfo; import java.util.function.Predicate; @@ -35,12 +30,16 @@ import static java.io.ObjectInputFilter.Status.ALLOWED; import static java.io.ObjectInputFilter.Status.REJECTED; import static java.io.ObjectInputFilter.Status.UNDECIDED; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + /* @test - * @run testng/othervm -Djava.util.logging.config.file=${test.src}/logging.properties + * @run junit/othervm -Djava.util.logging.config.file=${test.src}/logging.properties * SerialFilterFunctionTest * @summary ObjectInputFilter.Config Function Tests */ -@Test public class SerialFilterFunctionTest { @Test @@ -53,10 +52,10 @@ public class SerialFilterFunctionTest { ObjectInputFilter filter2 = getFilter(st2); ObjectInputFilter f = ObjectInputFilter.merge(filter1, filter2); Status r = f.checkInput(info); - Assert.assertEquals(merge(st1, st2), r, "merge"); + Assertions.assertEquals(r, merge(st1, st2), "merge"); } - Assert.assertSame(ObjectInputFilter.merge(filter1, null), filter1, "merge with null fail"); - Assert.assertThrows(NullPointerException.class, () -> ObjectInputFilter.merge(null, filter1)); + Assertions.assertSame(ObjectInputFilter.merge(filter1, null), filter1, "merge with null fail"); + Assertions.assertThrows(NullPointerException.class, () -> ObjectInputFilter.merge(null, filter1)); } } @@ -84,7 +83,7 @@ public class SerialFilterFunctionTest { return (cl) -> cl.equals(Integer.class); } - @DataProvider(name = "AllowPredicateCases") + // Test cases of filter strings static Object[][] allowPredicateCases() { return new Object[][]{ { Integer.class, isInteger(), REJECTED, ALLOWED}, @@ -95,18 +94,17 @@ public class SerialFilterFunctionTest { }; } - @Test(dataProvider = "AllowPredicateCases") + @ParameterizedTest + @MethodSource("allowPredicateCases") void testAllowPredicates(Class clazz, Predicate> predicate, Status otherStatus, Status expected) { ObjectInputFilter.FilterInfo info = new SerialInfo(clazz); if (predicate == null || expected == null) { - Assert.assertThrows(NullPointerException.class, () -> ObjectInputFilter.allowFilter(predicate, expected)); + Assertions.assertThrows(NullPointerException.class, () -> ObjectInputFilter.allowFilter(predicate, expected)); } else { - Assert.assertEquals(ObjectInputFilter.allowFilter(predicate, otherStatus).checkInput(info), - expected, "Predicate result"); + Assertions.assertEquals( expected, ObjectInputFilter.allowFilter(predicate, otherStatus).checkInput(info), "Predicate result"); } } - @DataProvider(name = "RejectPredicateCases") static Object[][] rejectPredicateCases() { return new Object[][]{ { Integer.class, isInteger(), REJECTED, REJECTED}, @@ -117,14 +115,15 @@ public class SerialFilterFunctionTest { }; } - @Test(dataProvider = "RejectPredicateCases") + @ParameterizedTest + @MethodSource("rejectPredicateCases") void testRejectPredicates(Class clazz, Predicate> predicate, Status otherStatus, Status expected) { ObjectInputFilter.FilterInfo info = new SerialInfo(clazz); if (predicate == null || expected == null) { - Assert.assertThrows(NullPointerException.class, () -> ObjectInputFilter.allowFilter(predicate, expected)); + Assertions.assertThrows(NullPointerException.class, () -> ObjectInputFilter.allowFilter(predicate, expected)); } else { - Assert.assertEquals(ObjectInputFilter.rejectFilter(predicate, otherStatus) - .checkInput(info), expected, "Predicate result"); + Assertions.assertEquals(expected, ObjectInputFilter.rejectFilter(predicate, otherStatus) + .checkInput(info), "Predicate result"); } } @@ -133,11 +132,11 @@ public class SerialFilterFunctionTest { FilterInfo info = new SerialInfo(Object.class); // an info structure, unused ObjectInputFilter undecided = getFilter(UNDECIDED); - Assert.assertEquals(ObjectInputFilter.rejectUndecidedClass(undecided).checkInput(info), REJECTED, "undecided -> rejected"); + Assertions.assertEquals(REJECTED, ObjectInputFilter.rejectUndecidedClass(undecided).checkInput(info), "undecided -> rejected"); ObjectInputFilter allowed = getFilter(ALLOWED); - Assert.assertEquals(ObjectInputFilter.rejectUndecidedClass(allowed).checkInput(info), ALLOWED, "allowed -> rejected"); + Assertions.assertEquals(ALLOWED, ObjectInputFilter.rejectUndecidedClass(allowed).checkInput(info), "allowed -> rejected"); ObjectInputFilter rejected = getFilter(REJECTED); - Assert.assertEquals(ObjectInputFilter.rejectUndecidedClass(rejected).checkInput(info), REJECTED, "rejected -> rejected"); + Assertions.assertEquals(REJECTED, ObjectInputFilter.rejectUndecidedClass(rejected).checkInput(info), "rejected -> rejected"); // Specific cases of Classes the result in allowed, rejected, and undecided status ObjectInputFilter numberFilter = ObjectInputFilter.Config.createFilter("java.lang.Integer;!java.lang.Double"); @@ -164,9 +163,9 @@ public class SerialFilterFunctionTest { while (clazz.isArray()) clazz = clazz.getComponentType(); Status expected = (clazz.isPrimitive()) ? UNDECIDED : REJECTED; - Assert.assertEquals(st, expected, "Wrong status for class: " + obj.getClass()); + Assertions.assertEquals(expected, st, "Wrong status for class: " + obj.getClass()); } else { - Assert.assertEquals(rawSt, st, "raw filter and rejectUndecided filter disagree"); + Assertions.assertEquals(st, rawSt, "raw filter and rejectUndecided filter disagree"); } } } diff --git a/test/jdk/java/io/Serializable/serialFilter/SerialFilterTest.java b/test/jdk/java/io/Serializable/serialFilter/SerialFilterTest.java index fb02cd02268..3b2ecc3d1b1 100644 --- a/test/jdk/java/io/Serializable/serialFilter/SerialFilterTest.java +++ b/test/jdk/java/io/Serializable/serialFilter/SerialFilterTest.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 @@ -46,20 +46,20 @@ import java.util.concurrent.atomic.LongAdder; import javax.net.ssl.SSLEngineResult; -import org.testng.Assert; -import org.testng.annotations.Test; -import org.testng.annotations.DataProvider; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* @test * @bug 8234836 * @build SerialFilterTest - * @run testng/othervm -Djava.util.logging.config.file=${test.src}/logging.properties + * @run junit/othervm -Djava.util.logging.config.file=${test.src}/logging.properties * SerialFilterTest - * @run testng/othervm -Djdk.serialSetFilterAfterRead=true SerialFilterTest + * @run junit/othervm -Djdk.serialSetFilterAfterRead=true SerialFilterTest * * @summary Test ObjectInputFilters using Builtin Filter Factory */ -@Test public class SerialFilterTest implements Serializable { @Serial @@ -89,9 +89,8 @@ public class SerialFilterTest implements Serializable { * Expand the patterns into cases for each of the Std and Compatibility APIs. * @return an array of arrays of the parameters including factories */ - @DataProvider(name="Patterns") static Object[][] patterns() { - Object[][] patterns = new Object[][]{ + return new Object[][]{ {"java.util.Hashtable"}, {"java.util.Hash*"}, {"javax.net.ssl.*"}, @@ -105,10 +104,8 @@ public class SerialFilterTest implements Serializable { {"maxbytes=+1024"}, {"java.base/java.util.Hashtable"}, }; - return patterns; } - @DataProvider(name="InvalidPatterns") static Object[][] invalidPatterns() { return new Object [][] { {".*"}, @@ -120,7 +117,6 @@ public class SerialFilterTest implements Serializable { }; } - @DataProvider(name="Limits") static Object[][] limits() { // The numbers are arbitrary > 1 return new Object[][] { @@ -133,7 +129,6 @@ public class SerialFilterTest implements Serializable { }; } - @DataProvider(name="InvalidLimits") static Object[][] invalidLimits() { return new Object[][] { {"maxrefs=-1"}, @@ -157,7 +152,6 @@ public class SerialFilterTest implements Serializable { * available to the filter. * @return Arrays of parameters with objects */ - @DataProvider(name="Objects") static Object[][] objects() { byte[] byteArray = new byte[0]; Object[] objArray = new Object[7]; @@ -168,7 +162,7 @@ public class SerialFilterTest implements Serializable { try { serClass = Class.forName(className); } catch (Exception e) { - Assert.fail("missing class: " + className, e); + Assertions.fail("missing class: " + className, e); } Class[] interfaces = {Runnable.class}; @@ -213,7 +207,6 @@ public class SerialFilterTest implements Serializable { return objects; } - @DataProvider(name="Arrays") static Object[][] arrays() { return new Object[][]{ {new Object[16], 16}, @@ -244,21 +237,22 @@ public class SerialFilterTest implements Serializable { * @param classes the expected (unique) classes * @throws IOException */ - @Test(dataProvider="Objects") + @ParameterizedTest + @MethodSource("objects") void t1(Object object, long count, long maxArray, long maxRefs, long maxDepth, long maxBytes, List> classes) throws IOException { byte[] bytes = writeObjects(object); Validator validator = new Validator(); validate(bytes, validator); - System.out.printf("v: %s%n", validator); + System.err.printf("v: %s%n", validator); - Assert.assertEquals(validator.count, count, "callback count wrong"); - Assert.assertEquals(validator.classes, classes, "classes mismatch"); - Assert.assertEquals(validator.maxArray, maxArray, "maxArray mismatch"); - Assert.assertEquals(validator.maxRefs, maxRefs, "maxRefs wrong"); - Assert.assertEquals(validator.maxDepth, maxDepth, "depth wrong"); - Assert.assertEquals(validator.maxBytes, maxBytes, "maxBytes wrong"); + Assertions.assertEquals(count, validator.count, "callback count wrong"); + Assertions.assertEquals(classes, validator.classes, "classes mismatch"); + Assertions.assertEquals(maxArray, validator.maxArray, "maxArray mismatch"); + Assertions.assertEquals(maxRefs, validator.maxRefs, "maxRefs wrong"); + Assertions.assertEquals(maxDepth, validator.maxDepth, "depth wrong"); + Assertions.assertEquals(maxBytes, validator.maxBytes, "maxBytes wrong"); } /** @@ -269,7 +263,8 @@ public class SerialFilterTest implements Serializable { * * @param pattern a pattern */ - @Test(dataProvider="Patterns") + @ParameterizedTest + @MethodSource("patterns") void testPatterns(String pattern) { evalPattern(pattern, (p, o, neg) -> testPatterns(p, o, neg)); } @@ -297,23 +292,23 @@ public class SerialFilterTest implements Serializable { // Check the initial filter is the global filter; may be null ObjectInputFilter global = ObjectInputFilter.Config.getSerialFilter(); ObjectInputFilter initial = ois.getObjectInputFilter(); - Assert.assertEquals(global, initial, "initial filter should be the global filter"); + Assertions.assertEquals(initial, global, "initial filter should be the global filter"); ois.setObjectInputFilter(validator); Object o = ois.readObject(); try { ois.setObjectInputFilter(validator2); - Assert.fail("Should not be able to set filter twice"); + Assertions.fail("Should not be able to set filter twice"); } catch (IllegalStateException ise) { // success, the exception was expected } } catch (EOFException eof) { - Assert.fail("Should not reach end-of-file", eof); + Assertions.fail("Should not reach end-of-file", eof); } catch (ClassNotFoundException cnf) { - Assert.fail("Deserializing", cnf); + Assertions.fail("Deserializing", cnf); } } catch (IOException ex) { - Assert.fail("Unexpected IOException", ex); + Assertions.fail("Unexpected IOException", ex); } } } @@ -336,7 +331,7 @@ public class SerialFilterTest implements Serializable { try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bais)) { Object actual1 = toggle ? ois.readObject() : ois.readUnshared(); - Assert.assertEquals(actual1, expected1, "unexpected string"); + Assertions.assertEquals(expected1, actual1, "unexpected string"); // Attempt to set filter ois.setObjectInputFilter(new ObjectInputFilter() { @Override @@ -345,14 +340,14 @@ public class SerialFilterTest implements Serializable { } }); if (!SET_FILTER_AFTER_READ) - Assert.fail("Should not be able to set filter after readObject has been called"); + Assertions.fail("Should not be able to set filter after readObject has been called"); } catch (IllegalStateException ise) { // success, the exception was expected if (SET_FILTER_AFTER_READ) - Assert.fail("With jdk.serialSetFilterAfterRead property set = true; " + + Assertions.fail("With jdk.serialSetFilterAfterRead property set = true; " + "should be able to set the filter after a read"); } catch (EOFException eof) { - Assert.fail("Should not reach end-of-file", eof); + Assertions.fail("Should not reach end-of-file", eof); } } } @@ -362,12 +357,13 @@ public class SerialFilterTest implements Serializable { * that the callback to the filter includes the proper array length. * @throws IOException if an error occurs */ - @Test(dataProvider="Arrays") + @ParameterizedTest + @MethodSource("arrays") void testReadResolveToArray(Object array, int length) throws IOException { ReadResolveToArray object = new ReadResolveToArray(array, length); byte[] bytes = writeObjects(object); Object o = validate(bytes, object); // the object is its own filter - Assert.assertEquals(o.getClass(), array.getClass(), "Filter not called with the array"); + Assertions.assertEquals(array.getClass(), o.getClass(), "Filter not called with the array"); } @@ -379,18 +375,17 @@ public class SerialFilterTest implements Serializable { * @param name the name of the limit to test * @param value a test value */ - @Test(dataProvider="Limits") + @ParameterizedTest + @MethodSource("limits") void testLimits(String name, long value) { Class arrayClass = new int[0].getClass(); String pattern = String.format("%s=%d;%s=%d", name, value, name, value - 1); ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); - Assert.assertEquals( + Assertions.assertEquals(ObjectInputFilter.Status.REJECTED, filter.checkInput(new FilterValues(arrayClass, value, value, value, value)), - ObjectInputFilter.Status.REJECTED, "last limit value not used: " + filter); - Assert.assertEquals( + Assertions.assertEquals(ObjectInputFilter.Status.UNDECIDED, filter.checkInput(new FilterValues(arrayClass, value-1, value-1, value-1, value-1)), - ObjectInputFilter.Status.UNDECIDED, "last limit value not used: " + filter); } @@ -399,46 +394,36 @@ public class SerialFilterTest implements Serializable { * Construct a filter with the limit, it should throw IllegalArgumentException. * @param pattern a pattern to test */ - @Test(dataProvider="InvalidLimits", expectedExceptions=java.lang.IllegalArgumentException.class) + @ParameterizedTest + @MethodSource("invalidLimits") void testInvalidLimits(String pattern) { - try { - ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); - } catch (IllegalArgumentException iae) { - System.out.printf(" success exception: %s%n", iae); - throw iae; - } + var iae = Assertions.assertThrows(IllegalArgumentException.class, + () -> ObjectInputFilter.Config.createFilter(pattern)); + System.err.printf(" success exception: %s%n", iae); } /** * Test that returning null from a filter causes deserialization to fail. */ - @Test(expectedExceptions=InvalidClassException.class) + @Test void testNullStatus() throws IOException { - byte[] bytes = writeObjects(0); // an Integer - try { - Object o = validate(bytes, new ObjectInputFilter() { - public ObjectInputFilter.Status checkInput(ObjectInputFilter.FilterInfo f) { - return null; - } - }); - } catch (InvalidClassException ice) { - System.out.printf(" success exception: %s%n", ice); - throw ice; - } + var ice = Assertions.assertThrows(InvalidClassException.class, () -> { + byte[] bytes = writeObjects(0); // an Integer + validate(bytes, f -> null); + }); + System.err.printf(" success exception: %s%n", ice); } /** * Verify that malformed patterns throw IAE. * @param pattern pattern from the data source */ - @Test(dataProvider="InvalidPatterns", expectedExceptions=IllegalArgumentException.class) + @ParameterizedTest + @MethodSource("invalidPatterns") void testInvalidPatterns(String pattern) { - try { - ObjectInputFilter.Config.createFilter(pattern); - } catch (IllegalArgumentException iae) { - System.out.printf(" success exception: %s%n", iae); - throw iae; - } + var iae = Assertions.assertThrows(IllegalArgumentException.class, + () -> ObjectInputFilter.Config.createFilter(pattern)); + System.err.printf(" success exception: %s%n", iae); } /** @@ -447,10 +432,10 @@ public class SerialFilterTest implements Serializable { @Test() void testEmptyPattern() { ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(""); - Assert.assertNull(filter, "empty pattern did not return null"); + Assertions.assertNull(filter, "empty pattern did not return null"); filter = ObjectInputFilter.Config.createFilter(";;;;"); - Assert.assertNull(filter, "pattern with only delimiters did not return null"); + Assertions.assertNull(filter, "pattern with only delimiters did not return null"); } /** @@ -472,7 +457,7 @@ public class SerialFilterTest implements Serializable { } catch (EOFException eof) { // normal completion } catch (ClassNotFoundException cnf) { - Assert.fail("Deserializing", cnf); + Assertions.fail("Deserializing", cnf); } return null; } @@ -514,7 +499,7 @@ public class SerialFilterTest implements Serializable { @Override public ObjectInputFilter.Status checkInput(FilterInfo filter) { Class serialClass = filter.serialClass(); - System.out.printf(" checkInput: class: %s, arrayLen: %d, refs: %d, depth: %d, bytes; %d%n", + System.err.printf(" checkInput: class: %s, arrayLen: %d, refs: %d, depth: %d, bytes; %d%n", serialClass, filter.arrayLength(), filter.references(), filter.depth(), filter.streamBytes()); count++; @@ -561,13 +546,13 @@ public class SerialFilterTest implements Serializable { byte[] bytes = SerialFilterTest.writeObjects(object); ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(pattern); validate(bytes, filter); - Assert.assertTrue(allowed, "filter should have thrown an exception"); + Assertions.assertTrue(allowed, "filter should have thrown an exception"); } catch (IllegalArgumentException iae) { - Assert.fail("bad format pattern", iae); + Assertions.fail("bad format pattern", iae); } catch (InvalidClassException ice) { - Assert.assertFalse(allowed, "filter should not have thrown an exception: " + ice); + Assertions.assertFalse(allowed, "filter should not have thrown an exception: " + ice); } catch (IOException ioe) { - Assert.fail("Unexpected IOException", ioe); + Assertions.fail("Unexpected IOException", ioe); } } @@ -578,12 +563,12 @@ public class SerialFilterTest implements Serializable { */ static void evalPattern(String pattern, TriConsumer action) { Object o = genTestObject(pattern, true); - Assert.assertNotNull(o, "success generation failed"); + Assertions.assertNotNull(o, "success generation failed"); action.accept(pattern, o, true); // Test the negative pattern o = genTestObject(pattern, false); - Assert.assertNotNull(o, "fail generation failed"); + Assertions.assertNotNull(o, "fail generation failed"); String negPattern = pattern.contains("=") ? pattern : "!" + pattern; action.accept(negPattern, o, false); } @@ -616,12 +601,12 @@ public class SerialFilterTest implements Serializable { Constructor cons = clazz.getConstructor(); return cons.newInstance(); } catch (ClassNotFoundException ex) { - Assert.fail("no such class available: " + pattern); + Assertions.fail("no such class available: " + pattern); } catch (InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException ex1) { - Assert.fail("newInstance: " + ex1); + Assertions.fail("newInstance: " + ex1); } } return null; @@ -661,7 +646,7 @@ public class SerialFilterTest implements Serializable { return new Hashtable(); } } - Assert.fail("Object could not be generated for pattern: " + Assertions.fail("Object could not be generated for pattern: " + pattern + ", allowed: " + allowed); return null; @@ -677,7 +662,7 @@ public class SerialFilterTest implements Serializable { */ static Object genTestLimit(String pattern, boolean allowed) { int ndx = pattern.indexOf('='); - Assert.assertNotEquals(ndx, -1, "missing value in limit"); + Assertions.assertNotEquals(-1, ndx, "missing value in limit"); long value = Long.parseUnsignedLong(pattern.substring(ndx+1)); if (pattern.startsWith("maxdepth=")) { // Return an object with the requested depth (or 1 greater) @@ -703,7 +688,7 @@ public class SerialFilterTest implements Serializable { } else if (pattern.startsWith("maxarray=")) { return allowed ? new int[(int)value] : new int[(int)value+1]; } - Assert.fail("Object could not be generated for pattern: " + Assertions.fail("Object could not be generated for pattern: " + pattern + ", allowed: " + allowed); return null; @@ -736,7 +721,7 @@ public class SerialFilterTest implements Serializable { os.flush(); actualSize = baos.size(); } catch (IOException ie) { - Assert.fail("exception generating stream", ie); + Assertions.fail("exception generating stream", ie); } } while (actualSize != desiredSize); return holder; From 6ad9f4ef6826bb031db7840ba3f689b0bde47775 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Wed, 14 Jan 2026 21:27:34 +0000 Subject: [PATCH 101/204] 8374493: Add missing @Override annotations in "com.sun.java.swing.plaf.motif" package Reviewed-by: tr, prr, aivanov --- .../java/swing/plaf/motif/MotifBorders.java | 21 ++++++++- .../swing/plaf/motif/MotifButtonListener.java | 3 +- .../java/swing/plaf/motif/MotifButtonUI.java | 9 +++- .../plaf/motif/MotifCheckBoxMenuItemUI.java | 13 +++++- .../swing/plaf/motif/MotifCheckBoxUI.java | 5 ++- .../swing/plaf/motif/MotifComboBoxUI.java | 20 ++++++++- .../swing/plaf/motif/MotifDesktopIconUI.java | 32 ++++++++++++- .../swing/plaf/motif/MotifDesktopPaneUI.java | 12 ++++- .../swing/plaf/motif/MotifEditorPaneUI.java | 3 +- .../swing/plaf/motif/MotifFileChooserUI.java | 45 ++++++++++++++++++- .../swing/plaf/motif/MotifIconFactory.java | 17 ++++++- .../motif/MotifInternalFrameTitlePane.java | 38 +++++++++++++++- .../plaf/motif/MotifInternalFrameUI.java | 19 +++++++- .../swing/plaf/motif/MotifLookAndFeel.java | 22 ++++++++- .../swing/plaf/motif/MotifMenuItemUI.java | 13 +++++- .../plaf/motif/MotifMenuMouseListener.java | 6 ++- .../motif/MotifMenuMouseMotionListener.java | 4 +- .../java/swing/plaf/motif/MotifMenuUI.java | 12 ++++- .../swing/plaf/motif/MotifOptionPaneUI.java | 8 +++- .../plaf/motif/MotifPasswordFieldUI.java | 3 +- .../plaf/motif/MotifPopupMenuSeparatorUI.java | 4 +- .../swing/plaf/motif/MotifPopupMenuUI.java | 5 ++- .../motif/MotifRadioButtonMenuItemUI.java | 13 +++++- .../swing/plaf/motif/MotifRadioButtonUI.java | 5 ++- .../plaf/motif/MotifScrollBarButton.java | 7 ++- .../swing/plaf/motif/MotifScrollBarUI.java | 7 ++- .../java/swing/plaf/motif/MotifSliderUI.java | 10 ++++- .../plaf/motif/MotifSplitPaneDivider.java | 8 +++- .../swing/plaf/motif/MotifSplitPaneUI.java | 3 +- .../swing/plaf/motif/MotifTabbedPaneUI.java | 12 ++++- .../swing/plaf/motif/MotifTextAreaUI.java | 3 +- .../swing/plaf/motif/MotifTextFieldUI.java | 3 +- .../swing/plaf/motif/MotifTextPaneUI.java | 3 +- .../java/swing/plaf/motif/MotifTextUI.java | 6 ++- .../swing/plaf/motif/MotifToggleButtonUI.java | 5 ++- .../plaf/motif/MotifTreeCellRenderer.java | 5 ++- .../java/swing/plaf/motif/MotifTreeUI.java | 10 ++++- 37 files changed, 377 insertions(+), 37 deletions(-) diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java index c61b1258894..6a97dd22d75 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -67,6 +67,7 @@ public class MotifBorders { this.lightShadow = lightShadow; } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { g.setColor((isRaised) ? lightShadow : darkShadow); g.drawLine(x, y, x+w-1, y); // top @@ -77,6 +78,7 @@ public class MotifBorders { g.drawLine(x+w-1, y+h-1, x+w-1, y+1); // right } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.set(1, 1, 1, 1); return insets; @@ -99,6 +101,7 @@ public class MotifBorders { this.focus = focus; } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { if (c.hasFocus()) { g.setColor(focus); @@ -109,6 +112,7 @@ public class MotifBorders { } } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.set(1, 1, 1, 1); return insets; @@ -130,6 +134,7 @@ public class MotifBorders { this.focus = focus; } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { boolean isPressed = false; boolean hasFocus = false; @@ -187,6 +192,7 @@ public class MotifBorders { g.drawLine(bx1+1, by2, bx2, by2); } + @Override public Insets getBorderInsets(Component c, Insets insets) { int thickness = (c instanceof JButton && ((JButton)c).isDefaultCapable())? 8 : 2; insets.set(thickness, thickness, thickness, thickness); @@ -202,6 +208,7 @@ public class MotifBorders { super(shadow, highlight, darkShadow, focus); } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { if (c instanceof AbstractButton) { @@ -223,6 +230,7 @@ public class MotifBorders { } } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.set(2, 2, 3, 3); return insets; @@ -236,6 +244,7 @@ public class MotifBorders { super(shadow, highlight, darkShadow, focus); } + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { if (!(c instanceof JMenuBar)) { return; @@ -249,6 +258,7 @@ public class MotifBorders { } } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.set(6, 6, 6, 6); return insets; @@ -297,6 +307,7 @@ public class MotifBorders { return frameShadow; } + @Override public Insets getBorderInsets(Component c, Insets newInsets) { newInsets.set(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE); return newInsets; @@ -424,6 +435,7 @@ public class MotifBorders { * drawTitleBar, drawLeftBorder, drawRightBorder and * drawBottomBorder. */ + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { if (isActiveFrame()) { @@ -487,6 +499,7 @@ public class MotifBorders { /** Draws the InternalFrameBorder's top border. */ + @Override protected boolean drawTopBorder(Component c, Graphics g, int x, int y, int width, int height) { if (super.drawTopBorder(c, g, x, y, width, height) && @@ -506,6 +519,7 @@ public class MotifBorders { /** Draws the InternalFrameBorder's left border. */ + @Override protected boolean drawLeftBorder(Component c, Graphics g, int x, int y, int width, int height) { if (super.drawLeftBorder(c, g, x, y, width, height) && @@ -525,6 +539,7 @@ public class MotifBorders { /** Draws the InternalFrameBorder's right border. */ + @Override protected boolean drawRightBorder(Component c, Graphics g, int x, int y, int width, int height) { if (super.drawRightBorder(c, g, x, y, width, height) && @@ -545,6 +560,7 @@ public class MotifBorders { /** Draws the InternalFrameBorder's bottom border. */ + @Override protected boolean drawBottomBorder(Component c, Graphics g, int x, int y, int width, int height) { if (super.drawBottomBorder(c, g, x, y, width, height) && @@ -567,6 +583,7 @@ public class MotifBorders { } // Returns true if the associated internal frame has focus. + @Override protected boolean isActiveFrame() { return frame.isSelected(); } @@ -667,6 +684,7 @@ public class MotifBorders { * @param width the width of the painted border * @param height the height of the painted border */ + @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { if (!(c instanceof JPopupMenu)) { return; @@ -713,6 +731,7 @@ public class MotifBorders { * @param c the component for which this border insets value applies * @param insets the object to be reinitialized */ + @Override public Insets getBorderInsets(Component c, Insets insets) { if (!(c instanceof JPopupMenu)) { return insets; diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonListener.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonListener.java index a1a74f1230f..65754efed0d 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonListener.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2001, 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,6 +42,7 @@ public class MotifButtonListener extends BasicButtonListener { super(b); } + @Override protected void checkOpacity(AbstractButton b) { b.setOpaque( false ); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java index 08017a0b2c8..0e4c2e526ac 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -74,6 +74,7 @@ public class MotifButtonUI extends BasicButtonUI { // ******************************** // Create Listeners // ******************************** + @Override protected BasicButtonListener createButtonListener(AbstractButton b){ return new MotifButtonListener(b); } @@ -81,6 +82,7 @@ public class MotifButtonUI extends BasicButtonUI { // ******************************** // Install Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -90,6 +92,7 @@ public class MotifButtonUI extends BasicButtonUI { LookAndFeel.installProperty(b, "opaque", Boolean.FALSE); } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; @@ -106,12 +109,14 @@ public class MotifButtonUI extends BasicButtonUI { // ******************************** // Paint Methods // ******************************** + @Override public void paint(Graphics g, JComponent c) { fillContentArea( g, (AbstractButton)c , c.getBackground() ); super.paint(g,c); } // Overridden to ensure we don't paint icon over button borders. + @Override protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect) { Shape oldClip = g.getClip(); Rectangle newClip = @@ -127,10 +132,12 @@ public class MotifButtonUI extends BasicButtonUI { g.setClip(oldClip); } + @Override protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, Rectangle textRect, Rectangle iconRect){ // focus painting is handled by the border } + @Override protected void paintButtonPressed(Graphics g, AbstractButton b) { fillContentArea( g, b , selectColor ); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java index bcaf72cbe26..17d66a74bad 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2006, 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,12 +50,14 @@ public class MotifCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI return new MotifCheckBoxMenuItemUI(); } + @Override protected void installListeners() { super.installListeners(); changeListener = createChangeListener(menuItem); menuItem.addChangeListener(changeListener); } + @Override protected void uninstallListeners() { super.uninstallListeners(); menuItem.removeChangeListener(changeListener); @@ -66,23 +68,28 @@ public class MotifCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI } protected class ChangeHandler implements ChangeListener { + @Override public void stateChanged(ChangeEvent e) { JMenuItem c = (JMenuItem)e.getSource(); LookAndFeel.installProperty(c, "borderPainted", c.isArmed()); } } + @Override protected MouseInputListener createMouseInputListener(JComponent c) { return new MouseInputHandler(); } protected class MouseInputHandler implements MouseInputListener { + @Override public void mouseClicked(MouseEvent e) {} + @Override public void mousePressed(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); manager.setSelectedPath(getPath()); } + @Override public void mouseReleased(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); @@ -99,11 +106,15 @@ public class MotifCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI manager.processMouseEvent(e); } } + @Override public void mouseEntered(MouseEvent e) {} + @Override public void mouseExited(MouseEvent e) {} + @Override public void mouseDragged(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseMoved(MouseEvent e) { } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java index fa06aa6bd02..9e9f0008b07 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -60,6 +60,7 @@ public class MotifCheckBoxUI extends MotifRadioButtonUI { return motifCheckBoxUI; } + @Override public String getPropertyPrefix() { return propertyPrefix; } @@ -67,6 +68,7 @@ public class MotifCheckBoxUI extends MotifRadioButtonUI { // ******************************** // Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -75,6 +77,7 @@ public class MotifCheckBoxUI extends MotifRadioButtonUI { } } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifComboBoxUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifComboBoxUI.java index d0cea2ec35b..8a6cb918ae4 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifComboBoxUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifComboBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -65,6 +65,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { return new MotifComboBoxUI(); } + @Override public void installUI(JComponent c) { super.installUI(c); arrowIcon = new MotifComboBoxArrowIcon(UIManager.getColor("controlHighlight"), @@ -72,6 +73,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { UIManager.getColor("control")); } + @Override public Dimension getMinimumSize( JComponent c ) { if ( !isMinimumSizeDirty ) { return new Dimension( cachedMinimumSize ); @@ -89,6 +91,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { return size; } + @Override protected ComboPopup createPopup() { return new MotifComboPopup( comboBox ); } @@ -106,10 +109,12 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { /** * Motif combo popup should not track the mouse in the list. */ + @Override public MouseMotionListener createListMouseMotionListener() { return new MouseMotionAdapter() {}; } + @Override public KeyListener createKeyListener() { return super.createKeyListener(); } @@ -121,6 +126,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { } } + @Override protected void installComponents() { if ( comboBox.isEditable() ) { addEditor(); @@ -129,11 +135,13 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { comboBox.add( currentValuePane ); } + @Override protected void uninstallComponents() { removeEditor(); comboBox.removeAll(); } + @Override public void paint(Graphics g, JComponent c) { boolean hasFocus = comboBox.hasFocus(); Rectangle r; @@ -180,6 +188,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { currentValuePane.removeAll(); } + @Override public void paintCurrentValue(Graphics g,Rectangle bounds,boolean hasFocus) { ListCellRenderer renderer = comboBox.getRenderer(); Component c; @@ -226,6 +235,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { return b; } + @Override protected Rectangle rectangleForCurrentValue() { int width = comboBox.getWidth(); int height = comboBox.getHeight(); @@ -251,11 +261,13 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { return arrowIcon.getIconWidth() + (3 * HORIZ_MARGIN) + 2; } + @Override public void configureEditor() { super.configureEditor(); editor.setBackground( UIManager.getColor( "text" ) ); } + @Override protected LayoutManager createLayoutManager() { return new ComboBoxLayoutManager(); } @@ -273,6 +285,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { public ComboBoxLayoutManager() { MotifComboBoxUI.this.super(); } + @Override public void layoutContainer(Container parent) { if ( motifGetEditor() != null ) { Rectangle cvb = rectangleForCurrentValue(); @@ -298,6 +311,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { } + @Override public void paintIcon(Component c, Graphics g, int xo, int yo) { int w = getIconWidth(); int h = getIconHeight(); @@ -322,10 +336,12 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { } + @Override public int getIconWidth() { return 11; } + @Override public int getIconHeight() { return 11; } @@ -336,6 +352,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { * * @since 1.6 */ + @Override protected PropertyChangeListener createPropertyChangeListener() { return new MotifPropertyChangeListener(); } @@ -345,6 +362,7 @@ public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable { */ private class MotifPropertyChangeListener extends BasicComboBoxUI.PropertyChangeHandler { + @Override public void propertyChange(PropertyChangeEvent e) { super.propertyChange(e); String propertyName = e.getPropertyName(); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopIconUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopIconUI.java index b71363d1c0e..872d4992d91 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopIconUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopIconUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, 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 @@ -74,6 +74,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI public MotifDesktopIconUI() { } + @Override protected void installDefaults(){ super.installDefaults(); setDefaultIcon(UIManager.getIcon("DesktopIcon.icon")); @@ -94,12 +95,15 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI JLayeredPane.putLayer(desktopIcon, JLayeredPane.getLayer(frame)); } + @Override protected void installComponents(){ } + @Override protected void uninstallComponents(){ } + @Override protected void installListeners(){ super.installListeners(); desktopIconActionListener = createDesktopIconActionListener(); @@ -149,6 +153,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI return new DesktopIconMouseListener(); } + @Override protected void uninstallDefaults(){ super.uninstallDefaults(); desktopIcon.setLayout(null); @@ -156,6 +161,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI desktopIcon.remove(iconLabel); } + @Override protected void uninstallListeners(){ super.uninstallListeners(); iconButton.removeActionListener(desktopIconActionListener); @@ -163,6 +169,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI sysMenuTitlePane.uninstallListeners(); } + @Override public Dimension getMinimumSize(JComponent c) { JInternalFrame iframe = desktopIcon.getInternalFrame(); @@ -180,10 +187,12 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI return new Dimension(w, h); } + @Override public Dimension getPreferredSize(JComponent c) { return getMinimumSize(c); } + @Override public Dimension getMaximumSize(JComponent c){ return getMinimumSize(c); } @@ -213,26 +222,33 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI // Forward mouse events to titlebar for moves. addMouseMotionListener(new MouseMotionListener() { + @Override public void mouseDragged(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseMoved(MouseEvent e) { forwardEventToParent(e); } }); addMouseListener(new MouseListener() { + @Override public void mouseClicked(MouseEvent e) { forwardEventToParent(e); } + @Override public void mousePressed(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseReleased(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseEntered(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseExited(MouseEvent e) { forwardEventToParent(e); } @@ -251,16 +267,19 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI getParent().dispatchEvent(newEvent); } + @Override @SuppressWarnings("deprecation") public boolean isFocusTraversable() { return false; } + @Override public Dimension getMinimumSize() { return new Dimension(defaultIcon.getIconWidth() + 1, LABEL_HEIGHT + LABEL_DIVIDER); } + @Override public Dimension getPreferredSize() { String title = frame.getTitle(); FontMetrics fm = frame.getFontMetrics(defaultTitleFont); @@ -271,6 +290,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI return new Dimension(w, LABEL_HEIGHT + LABEL_DIVIDER); } + @Override public void paint(Graphics g) { super.paint(g); @@ -308,28 +328,35 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI this.icon = icon; // Forward mouse events to titlebar for moves. addMouseMotionListener(new MouseMotionListener() { + @Override public void mouseDragged(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseMoved(MouseEvent e) { forwardEventToParent(e); } }); addMouseListener(new MouseListener() { + @Override public void mouseClicked(MouseEvent e) { forwardEventToParent(e); } + @Override public void mousePressed(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseReleased(MouseEvent e) { if (!systemMenu.isShowing()) { forwardEventToParent(e); } } + @Override public void mouseEntered(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseExited(MouseEvent e) { forwardEventToParent(e); } @@ -347,6 +374,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI getParent().dispatchEvent(newEvent); } + @Override @SuppressWarnings("deprecation") public boolean isFocusTraversable() { return false; @@ -355,6 +383,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI protected class DesktopIconActionListener implements ActionListener { + @Override public void actionPerformed(ActionEvent e){ systemMenu.show(iconButton, 0, getDesktopIcon().getHeight()); } @@ -362,6 +391,7 @@ public class MotifDesktopIconUI extends BasicDesktopIconUI protected class DesktopIconMouseListener extends MouseAdapter { // if we drag or move we should deengage the popup + @Override public void mousePressed(MouseEvent e){ if (e.getClickCount() > 1) { try { diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopPaneUI.java index 50563a971da..66f253be8c1 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, 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 @@ -59,6 +59,7 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU public MotifDesktopPaneUI() { } + @Override protected void installDesktopManager() { desktopManager = desktop.getDesktopManager(); if(desktopManager == null) { @@ -75,6 +76,7 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU //////////////////////////////////////////////////////////////////////////////////// @SuppressWarnings("serial") // Superclass is not serializable across versions private static class DragPane extends JComponent { + @Override public void paint(Graphics g) { g.setColor(Color.darkGray); g.drawRect(0, 0, getWidth()-1, getHeight()-1); @@ -92,6 +94,7 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU int iconWidth, iconHeight; // PENDING(klobad) this should be optimized + @Override public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) { if(!usingDragPane) { @@ -112,6 +115,7 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU } } + @Override public void beginDraggingFrame(JComponent f) { usingDragPane = false; if(f.getParent() instanceof JLayeredPane) { @@ -125,10 +129,12 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU } } + @Override public void dragFrame(JComponent f, int newX, int newY) { setBoundsForFrame(f, newX, newY, f.getWidth(), f.getHeight()); } + @Override public void endDraggingFrame(JComponent f) { if(usingDragPane) { layeredPaneForDragPane.remove(dragPane); @@ -143,6 +149,7 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU } } + @Override public void beginResizingFrame(JComponent f, int direction) { usingDragPane = false; if(f.getParent() instanceof JLayeredPane) { @@ -157,11 +164,13 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU } } + @Override public void resizeFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) { setBoundsForFrame(f, newX, newY, newWidth, newHeight); } + @Override public void endResizingFrame(JComponent f) { if(usingDragPane) { JLayeredPane p = (JLayeredPane)f.getParent(); @@ -172,6 +181,7 @@ public class MotifDesktopPaneUI extends javax.swing.plaf.basic.BasicDesktopPaneU } } + @Override public void iconifyFrame(JInternalFrame f) { JInternalFrame.JDesktopIcon icon = f.getDesktopIcon(); Point p = icon.getLocation(); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifEditorPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifEditorPaneUI.java index 3367b36f0fd..e0be55c9be7 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifEditorPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifEditorPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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,6 +55,7 @@ public class MotifEditorPaneUI extends BasicEditorPaneUI { * * @return the caret object */ + @Override protected Caret createCaret() { return MotifTextUI.createCaret(); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifFileChooserUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifFileChooserUI.java index 7d820930415..10a193b1271 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifFileChooserUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifFileChooserUI.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 @@ -141,6 +141,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { super(filechooser); } + @Override public String getFileName() { if(filenameTextField != null) { return filenameTextField.getText(); @@ -149,30 +150,37 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override public void setFileName(String filename) { if(filenameTextField != null) { filenameTextField.setText(filename); } } + @Override public String getDirectoryName() { return pathField.getText(); } + @Override public void setDirectoryName(String dirname) { pathField.setText(dirname); } + @Override public void ensureFileIsVisible(JFileChooser fc, File f) { // PENDING(jeff) } + @Override public void rescanCurrentDirectory(JFileChooser fc) { getModel().validateFileCache(); } + @Override public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) { return new PropertyChangeListener() { + @Override public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); if(prop.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) { @@ -266,10 +274,12 @@ public class MotifFileChooserUI extends BasicFileChooserUI { return new MotifFileChooserUI((JFileChooser)c); } + @Override public void installUI(JComponent c) { super.installUI(c); } + @Override public void uninstallUI(JComponent c) { c.removePropertyChangeListener(filterComboBoxModel); approveButton.removeActionListener(getApproveSelectionAction()); @@ -277,11 +287,13 @@ public class MotifFileChooserUI extends BasicFileChooserUI { super.uninstallUI(c); } + @Override public void installComponents(JFileChooser fc) { fc.setLayout(new BorderLayout(10, 10)); fc.setAlignmentX(JComponent.CENTER_ALIGNMENT); JPanel interior = new JPanel() { + @Override public Insets getInsets() { return insets; } @@ -305,6 +317,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } JTextField tmp1 = new JTextField(curDirName, 35) { + @Override public Dimension getMaximumSize() { Dimension d = super.getMaximumSize(); d.height = getPreferredSize().height; @@ -340,6 +353,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { leftPanel.add(l); JComboBox tmp2 = new JComboBox() { + @Override public Dimension getMaximumSize() { Dimension d = super.getMaximumSize(); d.height = getPreferredSize().height; @@ -417,6 +431,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { interior.add(fileNameLabel); JTextField tmp3 = new JTextField(35) { + @Override public Dimension getMaximumSize() { Dimension d = super.getMaximumSize(); d.height = getPreferredSize().height; @@ -441,6 +456,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { buttonPanel.add(Box.createGlue()); JButton tmp4 = new JButton(getApproveButtonText(fc)) { + @Override public Dimension getMaximumSize() { return new Dimension(MAX_SIZE.width, this.getPreferredSize().height); } @@ -456,6 +472,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { buttonPanel.add(Box.createGlue()); JButton updateButton = new JButton(updateButtonText) { + @Override public Dimension getMaximumSize() { return new Dimension(MAX_SIZE.width, this.getPreferredSize().height); } @@ -470,6 +487,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { buttonPanel.add(Box.createGlue()); JButton cancelButton = new JButton(cancelButtonText) { + @Override public Dimension getMaximumSize() { return new Dimension(MAX_SIZE.width, this.getPreferredSize().height); } @@ -484,6 +502,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { buttonPanel.add(Box.createGlue()); JButton helpButton = new JButton(helpButtonText) { + @Override public Dimension getMaximumSize() { return new Dimension(MAX_SIZE.width, this.getPreferredSize().height); } @@ -520,6 +539,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override public void uninstallComponents(JFileChooser fc) { fc.removeAll(); bottomPanel = null; @@ -528,6 +548,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override protected void installStrings(JFileChooser fc) { super.installStrings(fc); @@ -555,11 +576,13 @@ public class MotifFileChooserUI extends BasicFileChooserUI { return SwingUtilities2.getUIDefaultsInt(key, l); } + @Override protected void installIcons(JFileChooser fc) { // Since motif doesn't have button icons, leave this empty // which overrides the supertype icon loading } + @Override protected void uninstallIcons(JFileChooser fc) { // Since motif doesn't have button icons, leave this empty // which overrides the supertype icon loading @@ -580,6 +603,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { fileList.addListSelectionListener(createListSelectionListener(getFileChooser())); fileList.addMouseListener(createDoubleClickListener(getFileChooser(), fileList)); fileList.addMouseListener(new MouseAdapter() { + @Override public void mouseClicked(MouseEvent e) { JFileChooser chooser = getFileChooser(); if (SwingUtilities.isLeftMouseButton(e) && !chooser.isMultiSelectionEnabled()) { @@ -650,6 +674,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { @SuppressWarnings("serial") // Superclass is not serializable across versions protected class FileCellRenderer extends DefaultListCellRenderer { + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { @@ -665,6 +690,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { @SuppressWarnings("serial") // Superclass is not serializable across versions protected class DirectoryCellRenderer extends DefaultListCellRenderer { + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { @@ -684,18 +710,22 @@ public class MotifFileChooserUI extends BasicFileChooserUI { getModel().addListDataListener(this); } + @Override public int getSize() { return getModel().getDirectories().size(); } + @Override public File getElementAt(int index) { return getModel().getDirectories().elementAt(index); } + @Override public void intervalAdded(ListDataEvent e) { fireIntervalAdded(this, e.getIndex0(), e.getIndex1()); } + @Override public void intervalRemoved(ListDataEvent e) { fireIntervalRemoved(this, e.getIndex0(), e.getIndex1()); } @@ -709,6 +739,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { // PENDING(jeff) - fire the correct interval changed - currently sending // out that everything has changed + @Override public void contentsChanged(ListDataEvent e) { fireContentsChanged(); } @@ -721,6 +752,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { getModel().addListDataListener(this); } + @Override public int getSize() { return getModel().getFiles().size(); } @@ -733,14 +765,17 @@ public class MotifFileChooserUI extends BasicFileChooserUI { return getModel().getFiles().indexOf(o); } + @Override public File getElementAt(int index) { return getModel().getFiles().elementAt(index); } + @Override public void intervalAdded(ListDataEvent e) { fireIntervalAdded(this, e.getIndex0(), e.getIndex1()); } + @Override public void intervalRemoved(ListDataEvent e) { fireIntervalRemoved(this, e.getIndex0(), e.getIndex1()); } @@ -753,6 +788,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } // PENDING(jeff) - fire the interval changed + @Override public void contentsChanged(ListDataEvent e) { fireContentsChanged(); } @@ -779,6 +815,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { */ @SuppressWarnings("serial") // Superclass is not serializable across versions public class FilterComboBoxRenderer extends DefaultListCellRenderer { + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { @@ -805,6 +842,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { filters = getFileChooser().getChoosableFileFilters(); } + @Override public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); if(prop.equals(JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY)) { @@ -815,6 +853,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override public void setSelectedItem(Object filter) { if(filter != null) { getFileChooser().setFileFilter((FileFilter) filter); @@ -822,6 +861,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override public Object getSelectedItem() { // Ensure that the current filter is in the list. // NOTE: we shouldn't have to do this, since JFileChooser adds @@ -843,6 +883,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { return getFileChooser().getFileFilter(); } + @Override public int getSize() { if(filters != null) { return filters.length; @@ -851,6 +892,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override public FileFilter getElementAt(int index) { if(index > getSize() - 1) { // This shouldn't happen. Try to recover gracefully. @@ -864,6 +906,7 @@ public class MotifFileChooserUI extends BasicFileChooserUI { } } + @Override protected JButton getApproveButton(JFileChooser fc) { return approveButton; } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifIconFactory.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifIconFactory.java index dac6514f0c4..748c4dc6155 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifIconFactory.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifIconFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -93,6 +93,7 @@ public class MotifIconFactory implements Serializable private Color highlight = UIManager.getColor("controlHighlight"); private Color lightShadow = UIManager.getColor("controlLightShadow"); + @Override public void paintIcon(Component c, Graphics g, int x, int y) { AbstractButton b = (AbstractButton) c; ButtonModel model = b.getModel(); @@ -158,10 +159,12 @@ public class MotifIconFactory implements Serializable } } + @Override public int getIconWidth() { return csize; } + @Override public int getIconHeight() { return csize; } @@ -258,6 +261,7 @@ public class MotifIconFactory implements Serializable private Color highlight = UIManager.getColor("controlHighlight"); private Color shadow = UIManager.getColor("controlShadow"); + @Override public void paintIcon(Component c, Graphics g, int x, int y) { // fill interior AbstractButton b = (AbstractButton) c; @@ -303,10 +307,12 @@ public class MotifIconFactory implements Serializable } } + @Override public int getIconWidth() { return 14; } + @Override public int getIconHeight() { return 14; } @@ -315,10 +321,13 @@ public class MotifIconFactory implements Serializable @SuppressWarnings("serial") // Same-version serialization only private static class MenuItemCheckIcon implements Icon, UIResource, Serializable { + @Override public void paintIcon(Component c,Graphics g, int x, int y) { } + @Override public int getIconWidth() { return 0; } + @Override public int getIconHeight() { return 0; } } // end class MenuItemCheckIcon @@ -326,10 +335,13 @@ public class MotifIconFactory implements Serializable @SuppressWarnings("serial") // Same-version serialization only private static class MenuItemArrowIcon implements Icon, UIResource, Serializable { + @Override public void paintIcon(Component c,Graphics g, int x, int y) { } + @Override public int getIconWidth() { return 0; } + @Override public int getIconHeight() { return 0; } } // end class MenuItemArrowIcon @@ -340,6 +352,7 @@ public class MotifIconFactory implements Serializable private Color shadow = UIManager.getColor("controlShadow"); private Color highlight = UIManager.getColor("controlHighlight"); + @Override public void paintIcon(Component c, Graphics g, int x, int y) { AbstractButton b = (AbstractButton) c; ButtonModel model = b.getModel(); @@ -414,7 +427,9 @@ public class MotifIconFactory implements Serializable } } + @Override public int getIconWidth() { return 10; } + @Override public int getIconHeight() { return 10; } } // End class MenuArrowIcon } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameTitlePane.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameTitlePane.java index b68200c1924..474bd707ce4 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameTitlePane.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameTitlePane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, 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 @@ -66,20 +66,24 @@ public class MotifInternalFrameTitlePane super(frame); } + @Override protected void installDefaults() { setFont(UIManager.getFont("InternalFrame.titleFont")); setPreferredSize(new Dimension(100, BUTTON_SIZE)); } + @Override protected void uninstallListeners() { // Get around protected method in superclass super.uninstallListeners(); } + @Override protected PropertyChangeListener createPropertyChangeListener() { return this; } + @Override protected LayoutManager createLayout() { return this; } @@ -88,6 +92,7 @@ public class MotifInternalFrameTitlePane return systemMenu; } + @Override protected void assembleSystemMenu() { systemMenu = new JPopupMenu(); JMenuItem mi = systemMenu.add(restoreAction); @@ -106,12 +111,14 @@ public class MotifInternalFrameTitlePane systemButton = new SystemButton(); systemButton.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { systemMenu.show(systemButton, 0, BUTTON_SIZE); } }); systemButton.addMouseListener(new MouseAdapter() { + @Override public void mousePressed(MouseEvent evt) { try { frame.setSelected(true); @@ -137,6 +144,7 @@ public class MotifInternalFrameTitlePane } } + @Override protected void createButtons() { minimizeButton = new MinimizeButton(); minimizeButton.addActionListener(iconifyAction); @@ -146,6 +154,7 @@ public class MotifInternalFrameTitlePane } + @Override protected void addSubComponents() { title = new Title(frame.getTitle()); title.setFont(getFont()); @@ -156,6 +165,7 @@ public class MotifInternalFrameTitlePane add(maximizeButton); } + @Override public void paintComponent(Graphics g) { } @@ -165,9 +175,11 @@ public class MotifInternalFrameTitlePane shadow = s; } + @Override public void actionPerformed(ActionEvent e) { } + @Override public void propertyChange(PropertyChangeEvent evt) { String prop = evt.getPropertyName(); JInternalFrame f = (JInternalFrame)evt.getSource(); @@ -194,16 +206,21 @@ public class MotifInternalFrameTitlePane enableActions(); } + @Override public void addLayoutComponent(String name, Component c) {} + @Override public void removeLayoutComponent(Component c) {} + @Override public Dimension preferredLayoutSize(Container c) { return minimumLayoutSize(c); } + @Override public Dimension minimumLayoutSize(Container c) { return new Dimension(100, BUTTON_SIZE); } + @Override public void layoutContainer(Container c) { int w = getWidth(); systemButton.setBounds(0, 0, BUTTON_SIZE, BUTTON_SIZE); @@ -226,6 +243,7 @@ public class MotifInternalFrameTitlePane title.setBounds(BUTTON_SIZE, 0, x, BUTTON_SIZE); } + @Override protected void showSystemMenu(){ systemMenu.show(systemButton, 0, BUTTON_SIZE); } @@ -245,23 +263,28 @@ public class MotifInternalFrameTitlePane setBorderPainted(false); } + @Override @SuppressWarnings("deprecation") public boolean isFocusTraversable() { return false; } + @Override public void requestFocus() { // ignore request. } + @Override public Dimension getMinimumSize() { return buttonDimension; } + @Override public Dimension getPreferredSize() { return buttonDimension; } + @Override public void paintComponent(Graphics g) { Dimension d = getSize(); int maxX = d.width - 1; @@ -284,6 +307,7 @@ public class MotifInternalFrameTitlePane @SuppressWarnings("serial") // Superclass is not serializable across versions private class MinimizeButton extends FrameButton { + @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(highlight); @@ -297,6 +321,7 @@ public class MotifInternalFrameTitlePane @SuppressWarnings("serial") // Superclass is not serializable across versions private class MaximizeButton extends FrameButton { + @Override public void paintComponent(Graphics g) { super.paintComponent(g); int max = BUTTON_SIZE - 5; @@ -312,9 +337,12 @@ public class MotifInternalFrameTitlePane @SuppressWarnings("serial") // Superclass is not serializable across versions private class SystemButton extends FrameButton { + @Override public boolean isFocusTraversable() { return false; } + @Override public void requestFocus() {} + @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(highlight); @@ -339,26 +367,33 @@ public class MotifInternalFrameTitlePane // Forward mouse events to titlebar for moves. addMouseMotionListener(new MouseMotionListener() { + @Override public void mouseDragged(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseMoved(MouseEvent e) { forwardEventToParent(e); } }); addMouseListener(new MouseListener() { + @Override public void mouseClicked(MouseEvent e) { forwardEventToParent(e); } + @Override public void mousePressed(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseReleased(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseEntered(MouseEvent e) { forwardEventToParent(e); } + @Override public void mouseExited(MouseEvent e) { forwardEventToParent(e); } @@ -377,6 +412,7 @@ public class MotifInternalFrameTitlePane getParent().dispatchEvent(newEvent); } + @Override public void paintComponent(Graphics g) { super.paintComponent(g); if (frame.isSelected()) { diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameUI.java index 1179df181ab..678686a9b17 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameUI.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 @@ -81,11 +81,13 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { super(w); } + @Override public void installUI(JComponent c) { super.installUI(c); setColors((JInternalFrame)c); } + @Override protected void installDefaults() { Border frameBorder = frame.getBorder(); frame.setLayout(internalFrameLayout = createLayoutManager()); @@ -95,6 +97,7 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { } + @Override protected void installKeyboardActions(){ super.installKeyboardActions(); // We replace the @@ -103,6 +106,7 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { } + @Override protected void uninstallDefaults() { LookAndFeel.uninstallBorder(frame); frame.setLayout(null); @@ -113,15 +117,18 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { return frame; } + @Override public JComponent createNorthPane(JInternalFrame w) { titlePane = new MotifInternalFrameTitlePane(w); return titlePane; } + @Override public Dimension getMaximumSize(JComponent x) { return Toolkit.getDefaultToolkit().getScreenSize(); } + @Override protected void uninstallKeyboardActions(){ super.uninstallKeyboardActions(); if (isKeyBindingRegistered()){ @@ -132,6 +139,7 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { } } + @Override protected void setupMenuOpenKey(){ super.setupMenuOpenKey(); ActionMap map = SwingUtilities.getUIActionMap(frame); @@ -141,9 +149,11 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { // titlePane ivar in BasicInternalFrameUI, making supers action throw // an NPE for us. map.put("showSystemMenu", new AbstractAction(){ + @Override public void actionPerformed(ActionEvent e){ titlePane.showSystemMenu(); } + @Override public boolean isEnabled(){ return isKeyBindingActive(); } @@ -151,13 +161,16 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { } } + @Override protected void setupMenuCloseKey(){ ActionMap map = SwingUtilities.getUIActionMap(frame); if (map != null) { map.put("hideSystemMenu", new AbstractAction(){ + @Override public void actionPerformed(ActionEvent e){ titlePane.hideSystemMenu(); } + @Override public boolean isEnabled(){ return isKeyBindingActive(); } @@ -184,6 +197,7 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { if (diActionMap == null) { diActionMap = new ActionMapUIResource(); diActionMap.put("hideSystemMenu", new AbstractAction(){ + @Override public void actionPerformed(ActionEvent e){ JInternalFrame.JDesktopIcon icon = getFrame(). getDesktopIcon(); @@ -191,6 +205,7 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { getUI(); micon.hideSystemMenu(); } + @Override public boolean isEnabled(){ return isKeyBindingActive(); } @@ -201,12 +216,14 @@ public class MotifInternalFrameUI extends BasicInternalFrameUI { /** This method is called when the frame becomes selected. */ + @Override protected void activateFrame(JInternalFrame f) { super.activateFrame(f); setColors(f); } /** This method is called when the frame is no longer selected. */ + @Override protected void deactivateFrame(JInternalFrame f) { setColors(f); super.deactivateFrame(f); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java index aaeb0243ce7..d254443b8d1 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.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 @@ -56,24 +56,29 @@ import sun.swing.SwingUtilities2; @Deprecated(since="13", forRemoval=true) public class MotifLookAndFeel extends BasicLookAndFeel { + @Override public String getName() { return "CDE/Motif"; } + @Override public String getID() { return "Motif"; } + @Override public String getDescription() { return "The CDE/Motif Look and Feel"; } + @Override public boolean isNativeLookAndFeel() { return false; } + @Override public boolean isSupportedLookAndFeel() { return true; } @@ -87,6 +92,7 @@ public class MotifLookAndFeel extends BasicLookAndFeel * values, otherwise we create color objects whose values match * the default CDE/Motif colors. */ + @Override protected void initSystemColorDefaults(UIDefaults table) { String[] defaultSystemColors = { @@ -123,6 +129,7 @@ public class MotifLookAndFeel extends BasicLookAndFeel } + @Override protected void initClassDefaults(UIDefaults table) { super.initClassDefaults(table); @@ -178,6 +185,7 @@ public class MotifLookAndFeel extends BasicLookAndFeel } + @Override protected void initComponentDefaults(UIDefaults table) { super.initComponentDefaults(table); @@ -255,36 +263,42 @@ public class MotifLookAndFeel extends BasicLookAndFeel )); Object menuItemCheckIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifIconFactory.getMenuItemCheckIcon(); } }; Object menuItemArrowIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifIconFactory.getMenuItemArrowIcon(); } }; Object menuArrowIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifIconFactory.getMenuArrowIcon(); } }; Object checkBoxIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifIconFactory.getCheckBoxIcon(); } }; Object radioButtonIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifIconFactory.getRadioButtonIcon(); } }; Object unselectedTabBackground = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { Color c = table.getColor("control"); return new ColorUIResource(Math.max((int)(c.getRed()*.85),0), @@ -294,6 +308,7 @@ public class MotifLookAndFeel extends BasicLookAndFeel }; Object unselectedTabForeground = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { Color c = table.getColor("controlText"); return new ColorUIResource(Math.max((int)(c.getRed()*.85),0), @@ -303,6 +318,7 @@ public class MotifLookAndFeel extends BasicLookAndFeel }; Object unselectedTabShadow = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { Color c = table.getColor("control"); Color base = new Color(Math.max((int)(c.getRed()*.85),0), @@ -313,6 +329,7 @@ public class MotifLookAndFeel extends BasicLookAndFeel }; Object unselectedTabHighlight = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { Color c = table.getColor("control"); Color base = new Color(Math.max((int)(c.getRed()*.85),0), @@ -462,18 +479,21 @@ public class MotifLookAndFeel extends BasicLookAndFeel "icons/TreeClosed.gif"); Object treeLeafIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifTreeCellRenderer.loadLeafIcon(); } }; Object treeExpandedIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifTreeUI.MotifExpandedIcon.createExpandedIcon(); } }; Object treeCollapsedIcon = new UIDefaults.LazyValue() { + @Override public Object createValue(UIDefaults table) { return MotifTreeUI.MotifCollapsedIcon.createCollapsedIcon(); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuItemUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuItemUI.java index 52116aaf588..50ea704c73a 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuItemUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2004, 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,12 +50,14 @@ public class MotifMenuItemUI extends BasicMenuItemUI return new MotifMenuItemUI(); } + @Override protected void installListeners() { super.installListeners(); changeListener = createChangeListener(menuItem); menuItem.addChangeListener(changeListener); } + @Override protected void uninstallListeners() { super.uninstallListeners(); menuItem.removeChangeListener(changeListener); @@ -65,12 +67,14 @@ public class MotifMenuItemUI extends BasicMenuItemUI return new ChangeHandler(); } + @Override protected MouseInputListener createMouseInputListener(JComponent c) { return new MouseInputHandler(); } protected class ChangeHandler implements ChangeListener { + @Override public void stateChanged(ChangeEvent e) { JMenuItem c = (JMenuItem)e.getSource(); LookAndFeel.installProperty(c, "borderPainted", @@ -79,11 +83,14 @@ public class MotifMenuItemUI extends BasicMenuItemUI } protected class MouseInputHandler implements MouseInputListener { + @Override public void mouseClicked(MouseEvent e) {} + @Override public void mousePressed(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); manager.setSelectedPath(getPath()); } + @Override public void mouseReleased(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); @@ -97,11 +104,15 @@ public class MotifMenuItemUI extends BasicMenuItemUI manager.processMouseEvent(e); } } + @Override public void mouseEntered(MouseEvent e) {} + @Override public void mouseExited(MouseEvent e) {} + @Override public void mouseDragged(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseMoved(MouseEvent e) { } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseListener.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseListener.java index 93f4d2c731c..c3f8e7c9ece 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseListener.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, 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,15 +33,19 @@ import javax.swing.MenuSelectionManager; * @author Arnaud Weber */ class MotifMenuMouseListener extends MouseAdapter { + @Override public void mousePressed(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseReleased(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseEntered(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseExited(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseMotionListener.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseMotionListener.java index 22fcd908a11..8171ad8934d 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseMotionListener.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuMouseMotionListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, 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,10 +33,12 @@ import javax.swing.MenuSelectionManager; * @author Arnaud Weber */ class MotifMenuMouseMotionListener implements MouseMotionListener { + @Override public void mouseDragged(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseMoved(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuUI.java index bcee2e666fa..5bf2b1e0503 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuUI.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 @@ -61,6 +61,7 @@ public class MotifMenuUI extends BasicMenuUI // menuItem.removeChangeListener(changeListener); // } + @Override protected ChangeListener createChangeListener(JComponent c) { return new MotifChangeHandler((JMenu)c, this); } @@ -76,6 +77,7 @@ public class MotifMenuUI extends BasicMenuUI return false; } + @Override protected MouseInputListener createMouseInputListener(JComponent c) { return new MouseInputHandler(); } @@ -86,6 +88,7 @@ public class MotifMenuUI extends BasicMenuUI } + @Override public void stateChanged(ChangeEvent e) { JMenuItem c = (JMenuItem)e.getSource(); if (c.isArmed() || c.isSelected()) { @@ -100,7 +103,9 @@ public class MotifMenuUI extends BasicMenuUI } protected class MouseInputHandler implements MouseInputListener { + @Override public void mouseClicked(MouseEvent e) {} + @Override public void mousePressed(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); JMenu menu = (JMenu)e.getComponent(); @@ -129,6 +134,7 @@ public class MotifMenuUI extends BasicMenuUI } } + @Override public void mouseReleased(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); @@ -139,11 +145,15 @@ public class MotifMenuUI extends BasicMenuUI manager.processMouseEvent(e); } } + @Override public void mouseEntered(MouseEvent e) {} + @Override public void mouseExited(MouseEvent e) {} + @Override public void mouseDragged(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseMoved(MouseEvent e) { } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifOptionPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifOptionPaneUI.java index 21e8c0f6747..641d5ec1b99 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifOptionPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifOptionPaneUI.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,6 +57,7 @@ public class MotifOptionPaneUI extends BasicOptionPaneUI * Creates and returns a Container containing the buttons. The buttons * are created by calling getButtons. */ + @Override protected Container createButtonArea() { Container b = super.createButtonArea(); @@ -69,17 +70,21 @@ public class MotifOptionPaneUI extends BasicOptionPaneUI /** * Returns null, CDE/Motif does not impose a minimum size. */ + @Override public Dimension getMinimumOptionPaneSize() { return null; } + @Override protected Container createSeparator() { return new JPanel() { + @Override public Dimension getPreferredSize() { return new Dimension(10, 2); } + @Override public void paint(Graphics g) { int width = getWidth(); g.setColor(Color.darkGray); @@ -95,6 +100,7 @@ public class MotifOptionPaneUI extends BasicOptionPaneUI * getIcon to top. This is messaged from * createMessageArea */ + @Override protected void addIcon(Container top) { /* Create the icon. */ Icon sideIcon = getIcon(); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPasswordFieldUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPasswordFieldUI.java index 8e1369d7e96..b906329d353 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPasswordFieldUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPasswordFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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,6 +55,7 @@ public class MotifPasswordFieldUI extends BasicPasswordFieldUI { * * @return the caret object */ + @Override protected Caret createCaret() { return MotifTextUI.createCaret(); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuSeparatorUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuSeparatorUI.java index 164b3227034..5556695429e 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuSeparatorUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuSeparatorUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 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 @@ -47,6 +47,7 @@ public class MotifPopupMenuSeparatorUI extends MotifSeparatorUI return new MotifPopupMenuSeparatorUI(); } + @Override public void paint( Graphics g, JComponent c ) { Dimension s = c.getSize(); @@ -58,6 +59,7 @@ public class MotifPopupMenuSeparatorUI extends MotifSeparatorUI g.drawLine( 0, 1, s.width, 1 ); } + @Override public Dimension getPreferredSize( JComponent c ) { return new Dimension( 0, 2 ); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuUI.java index 0986f81f0ff..6012152905a 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -61,6 +61,7 @@ public class MotifPopupMenuUI extends BasicPopupMenuUI { /* This has to deal with the fact that the title may be wider than the widest child component. */ + @Override public Dimension getPreferredSize(JComponent c) { LayoutManager layout = c.getLayout(); Dimension d = layout.preferredLayoutSize(c); @@ -94,10 +95,12 @@ public class MotifPopupMenuUI extends BasicPopupMenuUI { protected ChangeListener createChangeListener(JPopupMenu m) { return new ChangeListener() { + @Override public void stateChanged(ChangeEvent e) {} }; } + @Override @SuppressWarnings("deprecation") public boolean isPopupTrigger(MouseEvent e) { return ((e.getID()==MouseEvent.MOUSE_PRESSED) diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java index 1a9d7fa1e91..914c305fa08 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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,12 +55,14 @@ public class MotifRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI return new MotifRadioButtonMenuItemUI(); } + @Override protected void installListeners() { super.installListeners(); changeListener = createChangeListener(menuItem); menuItem.addChangeListener(changeListener); } + @Override protected void uninstallListeners() { super.uninstallListeners(); menuItem.removeChangeListener(changeListener); @@ -72,23 +74,28 @@ public class MotifRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI @SuppressWarnings("serial") // Same-version serialization only protected class ChangeHandler implements ChangeListener, Serializable { + @Override public void stateChanged(ChangeEvent e) { JMenuItem c = (JMenuItem)e.getSource(); LookAndFeel.installProperty(c, "borderPainted", c.isArmed()); } } + @Override protected MouseInputListener createMouseInputListener(JComponent c) { return new MouseInputHandler(); } protected class MouseInputHandler implements MouseInputListener { + @Override public void mouseClicked(MouseEvent e) {} + @Override public void mousePressed(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); manager.setSelectedPath(getPath()); } + @Override public void mouseReleased(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); @@ -105,11 +112,15 @@ public class MotifRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI manager.processMouseEvent(e); } } + @Override public void mouseEntered(MouseEvent e) {} + @Override public void mouseExited(MouseEvent e) {} + @Override public void mouseDragged(MouseEvent e) { MenuSelectionManager.defaultManager().processMouseEvent(e); } + @Override public void mouseMoved(MouseEvent e) { } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonUI.java index 58619280493..f9a1dd7bb50 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -68,6 +68,7 @@ public class MotifRadioButtonUI extends BasicRadioButtonUI { // ******************************** // Install Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -76,6 +77,7 @@ public class MotifRadioButtonUI extends BasicRadioButtonUI { } } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; @@ -92,6 +94,7 @@ public class MotifRadioButtonUI extends BasicRadioButtonUI { // ******************************** // Paint Methods // ******************************** + @Override protected void paintFocus(Graphics g, Rectangle t, Dimension d){ g.setColor(getFocusColor()); g.drawRect(0,0,d.width-1,d.height-1); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarButton.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarButton.java index 5b812ca4c8c..2f5eb9708b3 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarButton.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarButton.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -64,6 +64,7 @@ public class MotifScrollBarButton extends BasicArrowButton } + @Override public Dimension getPreferredSize() { switch (direction) { case NORTH: @@ -76,18 +77,22 @@ public class MotifScrollBarButton extends BasicArrowButton } } + @Override public Dimension getMinimumSize() { return getPreferredSize(); } + @Override public Dimension getMaximumSize() { return getPreferredSize(); } + @Override public boolean isFocusTraversable() { return false; } + @Override public void paint(Graphics g) { int w = getWidth(); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarUI.java index 08a283f5e52..48cee86636b 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -52,6 +52,7 @@ public class MotifScrollBarUI extends BasicScrollBarUI return new MotifScrollBarUI(); } + @Override public Dimension getPreferredSize(JComponent c) { Insets insets = c.getInsets(); int dx = insets.left + insets.right; @@ -61,19 +62,23 @@ public class MotifScrollBarUI extends BasicScrollBarUI : new Dimension(dx + 33, dy + 11); } + @Override protected JButton createDecreaseButton(int orientation) { return new MotifScrollBarButton(orientation); } + @Override protected JButton createIncreaseButton(int orientation) { return new MotifScrollBarButton(orientation); } + @Override public void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) { g.setColor(trackColor); g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height); } + @Override public void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { if (thumbBounds.isEmpty() || !scrollbar.isEnabled()) { return; diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSliderUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSliderUI.java index c8820de17c4..c6afd5ce953 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSliderUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSliderUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -64,22 +64,27 @@ public class MotifSliderUI extends BasicSliderUI { return new MotifSliderUI((JSlider)b); } + @Override public Dimension getPreferredHorizontalSize() { return PREFERRED_HORIZONTAL_SIZE; } + @Override public Dimension getPreferredVerticalSize() { return PREFERRED_VERTICAL_SIZE; } + @Override public Dimension getMinimumHorizontalSize() { return MINIMUM_HORIZONTAL_SIZE; } + @Override public Dimension getMinimumVerticalSize() { return MINIMUM_VERTICAL_SIZE; } + @Override protected Dimension getThumbSize() { if ( slider.getOrientation() == JSlider.HORIZONTAL ) { return new Dimension( 30, 15 ); @@ -89,12 +94,15 @@ public class MotifSliderUI extends BasicSliderUI { } } + @Override public void paintFocus(Graphics g) { } + @Override public void paintTrack(Graphics g) { } + @Override public void paintThumb(Graphics g) { Rectangle knobBounds = thumbRect; diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneDivider.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneDivider.java index ba8f0efde4d..21cc1f7c38b 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneDivider.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneDivider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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,6 +84,7 @@ public class MotifSplitPaneDivider extends BasicSplitPaneDivider * overrides to hardcode the size of the divider * PENDING(jeff) - rewrite JSplitPane so that this isn't needed */ + @Override public void setDividerSize(int newSize) { Insets insets = getInsets(); int borderSize = 0; @@ -109,6 +110,7 @@ public class MotifSplitPaneDivider extends BasicSplitPaneDivider */ // PENDING(jeff) - the thumb's location and size is currently hard coded. // It should be dynamic. + @Override public void paint(Graphics g) { Color bgColor = getBackground(); Dimension size = getSize(); @@ -179,6 +181,7 @@ public class MotifSplitPaneDivider extends BasicSplitPaneDivider /** * The minimums size is the same as the preferredSize */ + @Override public Dimension getMinimumSize() { return getPreferredSize(); } @@ -187,6 +190,7 @@ public class MotifSplitPaneDivider extends BasicSplitPaneDivider * Sets the SplitPaneUI that is using the receiver. This is completely * overridden from super to create a different MouseHandler. */ + @Override public void setBasicSplitPaneUI(BasicSplitPaneUI newUI) { if (splitPane != null) { splitPane.removePropertyChangeListener(this); @@ -268,6 +272,7 @@ public class MotifSplitPaneDivider extends BasicSplitPaneDivider * in. */ private class MotifMouseHandler extends MouseHandler { + @Override public void mousePressed(MouseEvent e) { // Constrain the mouse pressed to the thumb. if (e.getSource() == MotifSplitPaneDivider.this && @@ -277,6 +282,7 @@ public class MotifSplitPaneDivider extends BasicSplitPaneDivider } } + @Override public void mouseMoved(MouseEvent e) { if (getDragger() != null) { return; diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneUI.java index c61a1713c9b..90ce672f5c9 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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,6 +51,7 @@ public class MotifSplitPaneUI extends BasicSplitPaneUI /** * Creates the default divider. */ + @Override public BasicSplitPaneDivider createDefaultDivider() { return new MotifSplitPaneDivider(this); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTabbedPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTabbedPaneUI.java index 1c53fc4a179..2b1e9482f9d 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTabbedPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTabbedPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -61,6 +61,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI // UI Installation/De-installation + @Override protected void installDefaults() { super.installDefaults(); @@ -70,6 +71,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI unselectedTabHighlight = UIManager.getColor("TabbedPane.unselectedTabHighlight"); } + @Override protected void uninstallDefaults() { super.uninstallDefaults(); @@ -81,6 +83,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI // UI Rendering + @Override protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { @@ -104,6 +107,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI } } + @Override protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { @@ -126,6 +130,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI } } + @Override protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { @@ -148,6 +153,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI } } + @Override protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, @@ -174,6 +180,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI } + @Override protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, @@ -225,6 +232,7 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI } + @Override protected void paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect, @@ -263,10 +271,12 @@ public class MotifTabbedPaneUI extends BasicTabbedPaneUI } } + @Override protected int getTabRunIndent(int tabPlacement, int run) { return run*3; } + @Override protected int getTabRunOverlay(int tabPlacement) { tabRunOverlay = (tabPlacement == LEFT || tabPlacement == RIGHT)? (int)Math.round((float)maxTabWidth * .10) : diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextAreaUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextAreaUI.java index baddbf3a75d..f71dfe3c30d 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextAreaUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextAreaUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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,6 +57,7 @@ public class MotifTextAreaUI extends BasicTextAreaUI { * * @return the caret object */ + @Override protected Caret createCaret() { return MotifTextUI.createCaret(); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextFieldUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextFieldUI.java index 08437687ebc..8e64aa02a63 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextFieldUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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,6 +55,7 @@ public class MotifTextFieldUI extends BasicTextFieldUI { * * @return the caret object */ + @Override protected Caret createCaret() { return MotifTextUI.createCaret(); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextPaneUI.java index e47ad4c60bf..fabbbb204d0 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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,6 +55,7 @@ public class MotifTextPaneUI extends BasicTextPaneUI { * * @return the caret object */ + @Override protected Caret createCaret() { return MotifTextUI.createCaret(); } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextUI.java index b79ef19df8e..6e2e1059176 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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,6 +75,7 @@ public class MotifTextUI { * @param e the focus event * @see FocusListener#focusGained */ + @Override public void focusGained(FocusEvent e) { super.focusGained(e); getComponent().repaint(); @@ -88,6 +89,7 @@ public class MotifTextUI { * @param e the focus event * @see FocusListener#focusLost */ + @Override public void focusLost(FocusEvent e) { super.focusLost(e); getComponent().repaint(); @@ -101,6 +103,7 @@ public class MotifTextUI { * @param r the current location of the caret, does nothing if null * @see #paint */ + @Override protected void damage(Rectangle r) { if (r != null) { x = r.x - IBeamOverhang - 1; @@ -121,6 +124,7 @@ public class MotifTextUI { * @param g the graphics context * @see #damage */ + @Override @SuppressWarnings("deprecation") public void paint(Graphics g) { if(isVisible()) { diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java index eef60ade9c9..7997145e648 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -71,6 +71,7 @@ public class MotifToggleButtonUI extends BasicToggleButtonUI // ******************************** // Install Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -80,6 +81,7 @@ public class MotifToggleButtonUI extends BasicToggleButtonUI LookAndFeel.installProperty(b, "opaque", Boolean.FALSE); } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; @@ -96,6 +98,7 @@ public class MotifToggleButtonUI extends BasicToggleButtonUI // ******************************** // Paint Methods // ******************************** + @Override protected void paintButtonPressed(Graphics g, AbstractButton b) { if (b.isContentAreaFilled()) { Color oldColor = g.getColor(); diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeCellRenderer.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeCellRenderer.java index 78ec251f05d..b060c62f24b 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeCellRenderer.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeCellRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -70,6 +70,7 @@ public class MotifTreeCellRenderer extends DefaultTreeCellRenderer highlight = UIManager.getColor("Tree.iconHighlight"); } + @Override public void paintIcon(Component c, Graphics g, int x, int y) { g.setColor(bg); @@ -90,10 +91,12 @@ public class MotifTreeCellRenderer extends DefaultTreeCellRenderer g.drawLine(x + 9, y + 8, x + 7, y + 6); } + @Override public int getIconWidth() { return LEAF_SIZE; } + @Override public int getIconHeight() { return LEAF_SIZE; } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeUI.java index 468ef4eb565..11313b9ad12 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, 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 @@ -54,12 +54,14 @@ public class MotifTreeUI extends BasicTreeUI super(); } + @Override public void installUI(JComponent c) { super.installUI(c); } // BasicTreeUI overrides + @Override protected void paintVerticalLine( Graphics g, JComponent c, int x, int top, int bottom ) { if (tree.getComponentOrientation().isLeftToRight()) { @@ -69,6 +71,7 @@ public class MotifTreeUI extends BasicTreeUI } } + @Override protected void paintHorizontalLine( Graphics g, JComponent c, int y, int left, int right ) { g.fillRect( left, y, right - left + 1, 2 ); @@ -96,6 +99,7 @@ public class MotifTreeUI extends BasicTreeUI return new MotifExpandedIcon(); } + @Override public void paintIcon(Component c, Graphics g, int x, int y) { g.setColor(highlight); g.drawLine(x, y, x+SIZE-1, y); @@ -113,7 +117,9 @@ public class MotifTreeUI extends BasicTreeUI g.drawLine(x+3, y+HALF_SIZE, x+SIZE-4, y+HALF_SIZE); } + @Override public int getIconWidth() { return SIZE; } + @Override public int getIconHeight() { return SIZE; } } @@ -126,6 +132,7 @@ public class MotifTreeUI extends BasicTreeUI return new MotifCollapsedIcon(); } + @Override public void paintIcon(Component c, Graphics g, int x, int y) { super.paintIcon(c, g, x, y); g.drawLine(x + HALF_SIZE-1, y + 3, x + HALF_SIZE-1, y + (SIZE - 4)); @@ -141,6 +148,7 @@ public class MotifTreeUI extends BasicTreeUI * Returns the default cell renderer that is used to do the * stamping of each node. */ + @Override public TreeCellRenderer createDefaultCellRenderer() { return new MotifTreeCellRenderer(); } From fb526c8f45de6ca9a57608f728ac223cbca118be Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Wed, 14 Jan 2026 21:37:44 +0000 Subject: [PATCH 102/204] 8373001: LauncherFromOptions.create() not properly handling FileAssociationNoExtensionsException Reviewed-by: almatvee --- .../classes/jdk/jpackage/internal/LauncherFromOptions.java | 6 +++--- .../jpackage/internal/resources/MainResources.properties | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java index ed030a4a726..9e81d144a1e 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.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 @@ -51,6 +51,7 @@ import jdk.jpackage.internal.FileAssociationGroup.FileAssociationNoMimesExceptio import jdk.jpackage.internal.cli.Options; import jdk.jpackage.internal.cli.StandardFaOption; import jdk.jpackage.internal.model.CustomLauncherIcon; +import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.DefaultLauncherIcon; import jdk.jpackage.internal.model.FileAssociation; import jdk.jpackage.internal.model.Launcher; @@ -130,8 +131,7 @@ final class LauncherFromOptions { .advice("error.no-content-types-for-file-association.advice", faID) .create(); } catch (FileAssociationNoExtensionsException ex) { - // TODO: Must do something about this condition! - throw new AssertionError(); + throw new JPackageException(I18N.format("error.no-extensions-for-file-association", faID)); } catch (FileAssociationException ex) { // Should never happen throw new UnsupportedOperationException(ex); 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 07460a18fe8..e1f41e3ff48 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 @@ -93,6 +93,7 @@ error.properties-parameter-not-path=The value "{0}" provided for property "{1}" error.properties-parameter-not-file=The value "{0}" provided for property "{1}" in "{2}" file is not a file error.properties-parameter-not-launcher-shortcut-dir=The value "{0}" provided for property "{1}" in "{2}" file is not a valid shortcut startup directory +error.no-extensions-for-file-association=No extensions were specified for File Association number {0} error.no-content-types-for-file-association=No MIME types were specified for File Association number {0} error.no-content-types-for-file-association.advice=Specify MIME type for File Association number {0} error.too-many-content-types-for-file-association=More than one MIME types was specified for File Association number {0} From d8f45faf5849e66b8f0e35e1d18ed0331a0cb1c2 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Thu, 15 Jan 2026 02:40:36 +0000 Subject: [PATCH 103/204] 8374432: TimeoutResponseBodyTest.java#retriesEnabledForResponseFailure fails run with -Xcomp Reviewed-by: vyazici, dfuchs --- .../java/net/httpclient/TimeoutResponseTestSupport.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/jdk/java/net/httpclient/TimeoutResponseTestSupport.java b/test/jdk/java/net/httpclient/TimeoutResponseTestSupport.java index 67b993eef0b..2d8ff05dbd2 100644 --- a/test/jdk/java/net/httpclient/TimeoutResponseTestSupport.java +++ b/test/jdk/java/net/httpclient/TimeoutResponseTestSupport.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 @@ -75,7 +75,8 @@ public class TimeoutResponseTestSupport { private static final SSLContext SSL_CONTEXT = SimpleSSLContext.findSSLContext(); protected static final Duration REQUEST_TIMEOUT = - Duration.ofMillis(Long.parseLong(System.getProperty("test.requestTimeoutMillis"))); + Duration.ofMillis(jdk.test.lib.Utils.adjustTimeout( + Long.parseLong(System.getProperty("test.requestTimeoutMillis")))); static { assertTrue( @@ -87,7 +88,8 @@ public class TimeoutResponseTestSupport { Integer.parseInt(System.getProperty("jdk.httpclient.redirects.retrylimit", "0")); private static final long RESPONSE_FAILURE_WAIT_DURATION_MILLIS = - Long.parseLong(System.getProperty("test.responseFailureWaitDurationMillis", "0")); + jdk.test.lib.Utils.adjustTimeout( + Long.parseLong(System.getProperty("test.responseFailureWaitDurationMillis", "0"))); static { if (RETRY_LIMIT > 0) { From ce5e0d8a48296b51c9c2eff4867e2a9a70194091 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Thu, 15 Jan 2026 02:44:16 +0000 Subject: [PATCH 104/204] 8373945: Use WB.fullGC() in ClassUnloader.unloadClass to force GC for vmTestbase tests Reviewed-by: cjplummer, lmesnik --- .../LargeObjects/large001/large001.java | 26 ++++------ .../large002/TestDescription.java | 10 ++-- .../large003/TestDescription.java | 10 ++-- .../large004/TestDescription.java | 10 ++-- .../large005/TestDescription.java | 10 ++-- .../reflectype002/TestDescription.java | 9 ++-- .../classname001/TestDescription.java | 10 ++-- .../signature001/TestDescription.java | 10 ++-- .../exclfilter001/TestDescription.java | 9 ++-- .../filter001/TestDescription.java | 9 ++-- .../allfields003/TestDescription.java | 9 ++-- .../allmethods003/TestDescription.java | 9 ++-- .../classobj002/TestDescription.java | 9 ++-- .../equals/equals002/TestDescription.java | 9 ++-- .../failedtoinit002/TestDescription.java | 9 ++-- .../fieldbyname003/TestDescription.java | 9 ++-- .../fields/fields003/TestDescription.java | 9 ++-- .../hashCode/hashcode002/TestDescription.java | 9 ++-- .../isabstract002/TestDescription.java | 9 ++-- .../isinit002/TestDescription.java | 9 ++-- .../isprepared002/TestDescription.java | 9 ++-- .../isverified002/TestDescription.java | 9 ++-- .../methods/methods003/TestDescription.java | 10 ++-- .../methbyname_s003/TestDescription.java | 9 ++-- .../methbyname_ss003/TestDescription.java | 9 ++-- .../name/name002/TestDescription.java | 9 ++-- .../sourcename002/TestDescription.java | 9 ++-- .../visibfield003/TestDescription.java | 9 ++-- .../visibmethod003/TestDescription.java | 9 ++-- .../instancecounts003/instancecounts003.java | 8 ++- .../compmethunload001.java | 16 +++--- .../compmethunload001/TestDescription.java | 6 ++- .../objfree001/TestDescription.java | 5 +- .../events/EM02/em02t003/TestDescription.java | 6 ++- .../events/EM02/em02t005/TestDescription.java | 6 ++- .../events/EM07/em07t002/TestDescription.java | 6 ++- .../EX03/ex03t001/TestDescription.java | 6 ++- .../classload/load001/TestDescription.java | 6 ++- .../classload/load002/TestDescription.java | 6 ++- .../classload/load003/TestDescription.java | 6 ++- .../classload/load004/TestDescription.java | 6 ++- .../classload/load005/TestDescription.java | 6 ++- .../classload/load006/TestDescription.java | 6 ++- .../classload/load007/TestDescription.java | 6 ++- .../classload/load008/TestDescription.java | 6 ++- .../classload/load009/TestDescription.java | 6 ++- .../classload/load010/TestDescription.java | 6 ++- .../classload/load011/TestDescription.java | 6 ++- .../classload/load012/TestDescription.java | 6 ++- .../classload/unload001/TestDescription.java | 6 ++- .../classload/unload002/TestDescription.java | 6 ++- .../classload/unload003/TestDescription.java | 6 ++- .../classload/unload004/TestDescription.java | 6 ++- .../classload/unload005/TestDescription.java | 6 ++- .../classload/unload006/TestDescription.java | 6 ++- .../classload/unload007/TestDescription.java | 6 ++- .../classload/unload008/TestDescription.java | 6 ++- .../classload/unload009/TestDescription.java | 6 ++- .../classload/unload010/TestDescription.java | 6 ++- .../classload/unload011/TestDescription.java | 6 ++- .../classload/unload012/TestDescription.java | 6 ++- .../vmTestbase/nsk/share/ClassUnloader.java | 51 ++++--------------- 62 files changed, 324 insertions(+), 215 deletions(-) diff --git a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large001/large001.java b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large001/large001.java index 8f8cfdcd541..1fd550d3571 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large001/large001.java +++ b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large001/large001.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 @@ -46,9 +46,8 @@ * generated by nsk.share.gc.Generator (see its javadoc for more details). * Each class has a huge number of fields, but this number is less than the JVM * limitation. - * The test loads the classes with nsk.share.gc.GCClassUnloader class that - * extends nsk.share.ClassUnloader and has a bit different algorith of eating - * heap. As soon as a class is loaded, the test creates an instance of + * The test loads the classes with nsk.share.gc.GCClassUnloader class. + * As soon as a class is loaded, the test creates an instance of * it - allocates an object of that type. Then it drops references to the * class and to the instance and tries to unload the class. The test does not * expect any exceptions to be thrown. @@ -57,10 +56,13 @@ * /test/lib * * @comment generate and compile nsk.share.gc.newclass.* classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.gc.GenClassesBuilder * * @run main/othervm/timeout=1200 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -Xlog:gc* * gc.gctests.LargeObjects.large001.large001 * -largeClassesPath classes @@ -79,6 +81,7 @@ import nsk.share.TestFailure; import nsk.share.gc.*; import nsk.share.*; +import jdk.test.whitebox.WhiteBox; public class large001 extends ThreadedGCTest { @@ -146,8 +149,8 @@ public class large001 extends ThreadedGCTest { List refs = new ArrayList(depth); addObjRef(loadedClassInstance, loadedClass, depth, refs); - // Drop all references to the class and try to unload it - Algorithms.eatMemory(getExecutionController()); + // Keep all references to the class and try to unload it + WhiteBox.getWhiteBox().fullGC(); log.debug(id + ": Testing non-null after GC force for: " + name); if (loadedClass == null || loadedClassInstance == null) { throw new Exception("Null class"); @@ -158,21 +161,14 @@ public class large001 extends ThreadedGCTest { throw new Exception("Unexpected null reference"); } } + // Drop all references to the class and try to unload it refs = null; loadedClass = null; loadedClassInstance = null; log.debug(id + ": Unloading class: " + name); - boolean result = unloader.unloadClass(getExecutionController()); - log.debug(id + ": Result of uloading " - + "class " + name + ": " + result); - } - } catch (OutOfMemoryError oome) { - // just skip if we eat memory in several threads... - // rethrow in the case of one thread - if (runParams.getNumberOfThreads() == 1) { - throw oome; + WhiteBox.getWhiteBox().fullGC(); } } catch (Throwable t) { throw new TestFailure("Unexpected exception: ", t); diff --git a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large002/TestDescription.java index c207b80c059..834a5d71822 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large002/TestDescription.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 @@ -47,9 +47,8 @@ * generated by nsk.share.gc.Generator (see its javadoc for more details). * Each class has a huge number of fields, and the number of fields is more than * the JVM limitation. - * The test loads the classes with nsk.share.gc.GCClassUnloader class that - * extends nsk.share.ClassUnloader and has a bit different algorith of eating - * heap. As soon as a class is loaded, the test creates an instance of + * The test loads the classes with nsk.share.gc.GCClassUnloader class. + * As soon as a class is loaded, the test creates an instance of * it - allocates an object of that type. Then it drops references to the * class and to the instance and tries to unload the class. The test does not * expect any exceptions to be thrown. @@ -58,10 +57,13 @@ * /test/lib * * @comment generate and compile nsk.share.gc.newclass.* classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.gc.GenClassesBuilder * * @run main/othervm/timeout=1200 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * gc.gctests.LargeObjects.large001.large001 * -largeClassesPath classes * -isOverLimitFields true diff --git a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large003/TestDescription.java index fd7e1e5e8dd..d094776cb5d 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large003/TestDescription.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 @@ -47,9 +47,8 @@ * generated by nsk.share.gc.Generator (see its javadoc for more details). * Each class has a huge number of fields, but this number is less than the JVM * limitation. - * The test loads the classes with nsk.share.gc.GCClassUnloader class that - * extends nsk.share.ClassUnloader and has a bit different algorith of eating - * heap. As soon as a class is loaded, the test creates an instance of + * The test loads the classes with nsk.share.gc.GCClassUnloader class. + * As soon as a class is loaded, the test creates an instance of * it - allocates an object of that type. Then it drops references to the * class and to the instance and tries to unload the class. The test does not * expect any exceptions to be thrown. @@ -62,10 +61,13 @@ * /test/lib * * @comment generate and compile nsk.share.gc.newclass.* classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.gc.GenClassesBuilder * * @run main/othervm/timeout=1200 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * gc.gctests.LargeObjects.large001.large001 * -largeClassesPath classes * -isOverLimitFields false diff --git a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large004/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large004/TestDescription.java index 2301daba8d3..021170d7d7a 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large004/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large004/TestDescription.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 @@ -47,9 +47,8 @@ * generated by nsk.share.gc.Generator (see its javadoc for more details). * Each class has a huge number of fields, but this number is less than the JVM * limitation. - * The test loads the classes with nsk.share.gc.GCClassUnloader class that - * extends nsk.share.ClassUnloader and has a bit different algorith of eating - * heap. As soon as a class is loaded, the test creates an instance of + * The test loads the classes with nsk.share.gc.GCClassUnloader class. + * As soon as a class is loaded, the test creates an instance of * it - allocates an object of that type. Then it drops references to the * class and to the instance and tries to unload the class. The test does not * expect any exceptions to be thrown. @@ -62,10 +61,13 @@ * /test/lib * * @comment generate and compile nsk.share.gc.newclass.* classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.gc.GenClassesBuilder * * @run main/othervm/timeout=1200 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * gc.gctests.LargeObjects.large001.large001 * -largeClassesPath classes * -isOverLimitFields true diff --git a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large005/TestDescription.java b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large005/TestDescription.java index abf91f48dea..6ad629aa84e 100644 --- a/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large005/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/gc/gctests/LargeObjects/large005/TestDescription.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 @@ -47,9 +47,8 @@ * generated by nsk.share.gc.Generator (see its javadoc for more details). * Each class has a huge number of fields, but this number is less than the JVM * limitation. - * The test loads the classes with nsk.share.gc.GCClassUnloader class that - * extends nsk.share.ClassUnloader and has a bit different algorith of eating - * heap. As soon as a class is loaded, the test creates an instance of + * The test loads the classes with nsk.share.gc.GCClassUnloader class. + * As soon as a class is loaded, the test creates an instance of * it - allocates an object of that type. Then it drops references to the * class and to the instance and tries to unload the class. The test does not * expect any exceptions to be thrown. @@ -62,10 +61,13 @@ * /test/lib * * @comment generate and compile nsk.share.gc.newclass.* classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.gc.GenClassesBuilder * * @run main/othervm/timeout=1200 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * gc.gctests.LargeObjects.large001.large001 * -largeClassesPath classes * -isOverLimitFields true diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002/TestDescription.java index 20584002f6e..efec917716a 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ClassObjectReference.reflectedType.reflectype002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/className/classname001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/className/classname001/TestDescription.java index c967382ccfd..8f906d4d6b2 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/className/classname001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/className/classname001/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -42,7 +42,7 @@ * debugger and debugee communicates with special commands. * The debugger forces debuggee to load checked class, creates and * enables ClassUnloadRequest. Next, debugger forces debuggee to - * unload class, using memory stressing techique, and waits for + * unload class, using whitebox full GC techique, and waits for * ClassUnloadEvent. * If expected ClassUnloadEvent occurs, debugger tests method * ClassUnloadEvent.className() and verifies that this event @@ -81,6 +81,8 @@ * nsk.jdi.ClassUnloadEvent.className.classname001a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -92,6 +94,6 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/classSignature/signature001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/classSignature/signature001/TestDescription.java index c83d6cdf0f3..6974e7077df 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/classSignature/signature001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadEvent/classSignature/signature001/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -44,7 +44,7 @@ * debugger and debugee communicates with special commands. * The debugger forces debugge to load checked classes, creates and * enables ClassUnloadRequest. Next, debugger forces debuggee to - * unload classes, using memory stressing techique, and waits for + * unload classes, using whitebox full GC techique, and waits for * ClassUnloadEvent. * If each expected ClassUnloadEvent occurs, debugger tests method * ClassUnloadEvent.classSignature() and verifies that this event @@ -87,6 +87,8 @@ * nsk.jdi.ClassUnloadEvent.classSignature.signature001a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -98,6 +100,6 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassExclusionFilter/exclfilter001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassExclusionFilter/exclfilter001/TestDescription.java index ad14def18dc..d449ed04f91 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassExclusionFilter/exclfilter001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassExclusionFilter/exclfilter001/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, 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 @@ -67,6 +67,8 @@ * nsk.jdi.ClassUnloadRequest.addClassExclusionFilter.exclfilter001a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -77,6 +79,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassFilter/filter001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassFilter/filter001/TestDescription.java index 6a388fbaea9..1783e44fa43 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassFilter/filter001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassUnloadRequest/addClassFilter/filter001/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, 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,6 +65,8 @@ * nsk.jdi.ClassUnloadRequest.addClassFilter.filter001a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -75,6 +77,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003/TestDescription.java index 25196c3ae9b..053a6ff34c5 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.allFields.allfields003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003/TestDescription.java index 149de042248..a0e8bd2b7f1 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.allMethods.allmethods003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002/TestDescription.java index a639fe76ad8..ab751a1f29c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.classObject.classobj002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals002/TestDescription.java index 1ccd1358c1c..e1c25e9dbdd 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/equals/equals002/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.equals.equals002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedtoinit002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedtoinit002/TestDescription.java index af68db9d869..703bb6c54aa 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedtoinit002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedtoinit002/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.failedToInitialize.failedtoinit002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003/TestDescription.java index bbd8998d3a1..fa5104cad55 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.fieldByName.fieldbyname003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields003/TestDescription.java index d6379dfbdbf..ab6af3a98e1 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fields/fields003/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.fields.fields003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode002/TestDescription.java index f7a960746d9..693171c9dfe 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/hashCode/hashcode002/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.hashCode.hashcode002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isabstract002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isabstract002/TestDescription.java index 6f9c0d5b18e..b61c14d3d4b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isabstract002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isabstract002/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.isAbstract.isabstract002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit002/TestDescription.java index c484851c178..c2b1eac7c1e 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isInitialized/isinit002/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.isInitialized.isinit002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared002/TestDescription.java index 867b7c45e81..9030c57a727 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isPrepared/isprepared002/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.isPrepared.isprepared002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isverified002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isverified002/TestDescription.java index 5f638386da8..a766e7476f6 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isverified002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isverified002/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -57,6 +57,8 @@ * nsk.jdi.ReferenceType.isVerified.isverified002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -67,6 +69,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods003/TestDescription.java index df70528ffd9..5a37d7e66a4 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methods/methods003/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -57,7 +57,8 @@ * @comment compile loadclassXX to bin/loadclassXX * @run driver nsk.share.ExtraClassesBuilder * loadclass - * + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver * nsk.jdi.ReferenceType.methods.methods003 * -verbose @@ -65,6 +66,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003/TestDescription.java index fae4c67e986..6f70f225a14 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.methodsByName_s.methbyname_s003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss003/TestDescription.java index 5d21a5c868f..baf8931a636 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss003/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -57,6 +57,8 @@ * @comment compile loadclassXX to bin/loadclassXX * @run driver nsk.share.ExtraClassesBuilder * loadclass + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * * @run driver * nsk.jdi.ReferenceType.methodsByName_ss.methbyname_ss003 @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name002/TestDescription.java index 91c37d75710..927e07929b4 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/name/name002/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.name.name002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename002/TestDescription.java index 74427e9a8bc..543272a52f0 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/sourceName/sourcename002/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.sourceName.sourcename002a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003/TestDescription.java index db4ba02e457..f349deaad85 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.visibleFields.visibfield003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003/TestDescription.java index 11c4c3566bb..c6de37e2939 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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 @@ -55,6 +55,8 @@ * nsk.jdi.ReferenceType.visibleMethods.visibmethod003a * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * @@ -65,6 +67,7 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="${test.vm.opts} ${test.java.opts}" ./bin + * -debugee.vmkeys="${test.vm.opts} ${test.java.opts} -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI" + * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachine/instanceCounts/instancecounts003/instancecounts003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachine/instanceCounts/instancecounts003/instancecounts003.java index f2150775916..4f4e83188b4 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachine/instanceCounts/instancecounts003/instancecounts003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/VirtualMachine/instanceCounts/instancecounts003/instancecounts003.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * 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,8 @@ * @build nsk.jdi.VirtualMachine.instanceCounts.instancecounts003.instancecounts003 * nsk.share.jdi.TestClass1 * nsk.share.jdi.TestInterfaceImplementer1 + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver * nsk.jdi.VirtualMachine.instanceCounts.instancecounts003.instancecounts003 * -verbose @@ -55,7 +57,9 @@ * -waittime=5 * -debugee.vmkind=java * -transport.address=dynamic - * -debugee.vmkeys="-Xmx256M ${test.vm.opts} ${test.java.opts}" + * -debugee.vmkeys="-Xmx256M ${test.vm.opts} ${test.java.opts} + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI" * -testClassPath ${test.class.path} */ diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001.java index e03c4ff4be2..09fdb1ae3f7 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001.java @@ -47,7 +47,7 @@ public class compmethunload001 { private final static String CLS_TO_BE_UNLOADED = "nsk.jvmti.CompiledMethodUnload.compmethunload001u"; - private final static int MAX_ITERATIONS = 5; + private final static int MAX_ITERATIONS = 10; static { try { @@ -95,7 +95,6 @@ public class compmethunload001 { boolean clsUnloaded = clsUnLoader.unloadClass(); clsUnLoader = null; - System.gc(); } private int runThis(String argv[], PrintStream out) throws Exception { @@ -110,12 +109,13 @@ public class compmethunload001 { int num = unloaded(); int iter = 0; while (num == 0) { - System.gc(); - num = unloaded(); - iter++; - if (iter > MAX_ITERATIONS) { - throw new Failure("PRODUCT BUG: class was not unloaded in " + MAX_ITERATIONS); - } + // The unload is delayed because it happens async + Thread.sleep(1000); + num = unloaded(); + iter++; + if (iter > MAX_ITERATIONS) { + throw new Failure("PRODUCT BUG: class was not unloaded in " + MAX_ITERATIONS); + } } System.out.println("Number of unloaded events " + num + " number of iterations " + iter); return check(); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001/TestDescription.java index 975f0fdee28..b3e43bf7d03 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/CompiledMethodUnload/compmethunload001/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, 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 @@ -46,12 +46,14 @@ * @build nsk.jvmti.CompiledMethodUnload.compmethunload001 * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * * @run main/othervm/native * -agentlib:compmethunload001=-waittime=5 + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.jvmti.CompiledMethodUnload.compmethunload001 * ./bin */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ObjectFree/objfree001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ObjectFree/objfree001/TestDescription.java index 8da18b646c7..c7f5376e53c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ObjectFree/objfree001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ObjectFree/objfree001/TestDescription.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 @@ -51,12 +51,15 @@ * @build nsk.jvmti.ObjectFree.objfree001 * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * * @run main/othervm/native * -agentlib:objfree001=-waittime=5 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.jvmti.ObjectFree.objfree001 * ./bin 5 ./bin */ diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003/TestDescription.java index d3179b3c2c0..c32abdb635b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003/TestDescription.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 @@ -71,13 +71,15 @@ * @build nsk.jvmti.scenarios.events.EM02.em02t003 * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * * @run main/othervm/native * -agentlib:em02t003=-waittime=5 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.jvmti.scenarios.events.EM02.em02t003 * ./bin/loadclass */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t005/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t005/TestDescription.java index 5e8b186fe9d..35fc4ceacdb 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t005/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t005/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, 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 @@ -64,13 +64,15 @@ * @build nsk.jvmti.scenarios.events.EM02.em02t005 * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * * @run main/othervm/native * -agentlib:em02t005=-waittime=5 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.jvmti.scenarios.events.EM02.em02t005 * ./bin/loadclass */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM07/em07t002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM07/em07t002/TestDescription.java index aacc48a1e94..dea19222973 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM07/em07t002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM07/em07t002/TestDescription.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 @@ -57,13 +57,15 @@ * @build nsk.jvmti.scenarios.events.EM07.em07t002 * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * * @run main/othervm/native * -agentlib:em07t002=attempts=2,-waittime=5 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.jvmti.scenarios.events.EM07.em07t002 * ./bin/loadclass */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/extension/EX03/ex03t001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/extension/EX03/ex03t001/TestDescription.java index 0e63411b94b..76798c229f3 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/extension/EX03/ex03t001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/extension/EX03/ex03t001/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, 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 @@ -55,13 +55,15 @@ * @build nsk.jvmti.scenarios.extension.EX03.ex03t001 * * @comment compile loadclassXX to bin/loadclassXX + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.share.ExtraClassesBuilder * loadclass * * @run main/othervm/native * -agentlib:ex03t001=-waittime=5 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.jvmti.scenarios.extension.EX03.ex03t001 * ./bin/loadclass */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load001/TestDescription.java index e93d0052423..979f51007a2 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load001/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -43,12 +43,14 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load002/TestDescription.java index 563eb7500a0..e1dcaaf6946 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load002/TestDescription.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 @@ -46,13 +46,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load003/TestDescription.java index d972a74492c..41ffed813d1 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load003/TestDescription.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 @@ -46,9 +46,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server @@ -56,4 +59,3 @@ * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load004/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load004/TestDescription.java index 7789d6b70ce..b0470cca572 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load004/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load004/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -47,13 +47,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -singleClassloaderClass * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load005/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load005/TestDescription.java index b6eba9ca652..d5be1a61e8a 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load005/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load005/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server @@ -54,4 +57,3 @@ * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load006/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load006/TestDescription.java index af8f67362bf..eff574f05a2 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load006/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load006/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server @@ -55,4 +58,3 @@ * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load007/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load007/TestDescription.java index f8674cdbd21..ecd7e495fb9 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load007/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load007/TestDescription.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 @@ -46,12 +46,14 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=360 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -loadableClassCount=100 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load008/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load008/TestDescription.java index f306fda9e02..9ebc7be7a72 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load008/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load008/TestDescription.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 @@ -44,13 +44,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=420 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server * -loadableClassCount=100 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load009/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load009/TestDescription.java index 50f8116f7c9..5355064e57a 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load009/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load009/TestDescription.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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=420 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server @@ -54,4 +57,3 @@ * -loadableClassCount=100 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load010/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load010/TestDescription.java index 4f477035cd2..fe0ec8c9b62 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load010/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load010/TestDescription.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 @@ -46,13 +46,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=300 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -singleClassloaderClass * -loadableClassCount=100 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load011/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load011/TestDescription.java index 110f270daff..a5a1a5ae2a8 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load011/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load011/TestDescription.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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=360 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server @@ -54,4 +57,3 @@ * -loadableClassCount=100 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load012/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load012/TestDescription.java index 52c9d3e8ae7..ac3f8217a0d 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load012/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/load012/TestDescription.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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=300 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.load001 * classes * -testMode=server @@ -55,4 +58,3 @@ * -loadableClassCount=100 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload001/TestDescription.java index cab8b333848..9690240caa5 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload001/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -46,11 +46,13 @@ * /test/lib * @comment generate and compile LoadableClassXXX classes * @run driver nsk.monitoring.stress.classload.GenClassesBuilder + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload002/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload002/TestDescription.java index cf611e5eb05..52395f92b0c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload002/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload002/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -46,13 +46,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload003/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload003/TestDescription.java index 6c8d9f61e36..7e24f92dafd 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload003/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload003/TestDescription.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 @@ -46,9 +46,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server @@ -56,4 +59,3 @@ * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload004/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload004/TestDescription.java index 8f8e46e40be..8dedf5c1f61 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload004/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload004/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -47,13 +47,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -singleClassloaderClass * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload005/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload005/TestDescription.java index 73aa31b3ff3..e4236d40e58 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload005/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload005/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server @@ -54,4 +57,3 @@ * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload006/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload006/TestDescription.java index 1b3d309a7fd..ef6e3d996d9 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload006/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload006/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -44,9 +44,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server @@ -55,4 +58,3 @@ * -loadableClassCount=1 * -loadersCount=100 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload007/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload007/TestDescription.java index 62eaaa6c40a..cbe155bc77e 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload007/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload007/TestDescription.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 @@ -48,12 +48,14 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -loadableClassCount=100 * -loadersCount=12 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload008/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload008/TestDescription.java index f87f913b769..41894adaed7 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload008/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload008/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -46,13 +46,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server * -loadableClassCount=100 * -loadersCount=12 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload009/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload009/TestDescription.java index 27c19980411..5999a8c7f7c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload009/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload009/TestDescription.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 @@ -46,9 +46,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm/timeout=180 * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server @@ -56,4 +59,3 @@ * -loadableClassCount=100 * -loadersCount=12 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload010/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload010/TestDescription.java index afc3cbb026c..683bca7ab93 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload010/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload010/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -48,13 +48,15 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -singleClassloaderClass * -loadableClassCount=100 * -loadersCount=12 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload011/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload011/TestDescription.java index 037d4c2affe..26227644431 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload011/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload011/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -46,9 +46,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server @@ -56,4 +59,3 @@ * -loadableClassCount=100 * -loadersCount=12 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload012/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload012/TestDescription.java index 256b0f91d1f..446db1d6e28 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload012/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/monitoring/stress/classload/unload012/TestDescription.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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 @@ -46,9 +46,12 @@ * @library /vmTestbase * /test/lib * @comment generate and compile LoadableClassXXX classes + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run driver nsk.monitoring.stress.classload.GenClassesBuilder * @run main/othervm * -XX:-UseGCOverheadLimit + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * nsk.monitoring.stress.classload.unload001 * classes * -testMode=server @@ -57,4 +60,3 @@ * -loadableClassCount=100 * -loadersCount=12 */ - diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java b/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java index e3b6693657c..94b086f5757 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.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 @@ -33,13 +33,14 @@ import java.util.*; import nsk.share.gc.gp.*; import nsk.share.test.ExecutionController; import nsk.share.test.Stresser; +import jdk.test.whitebox.WhiteBox; /** * The ClassUnloader class allows to force VM to unload class(es) - * using memory stressing technique. + * using WhiteBox.fullGC technique. * - *

      The method unloadClass() is provided which eats memory - * to enforce GC to cleanup the heap. So, if all references to a class + *

      The method unloadClass() is provided which calls + * WhiteBox.fullGC to cleanup the heap. So, if all references to a class * and its loader are canceled, this may result in unloading the class. * *

      ClassUnloader mainly intends to unload a class which was loaded @@ -228,24 +229,24 @@ public class ClassUnloader { /** * Forces GC to unload previously loaded classes by cleaning all references - * to class loader with its loaded classes and eating memory. + * to class loader with its loaded classes. * * @return true if classes unloading has been detected or false otherwise * * @throws Failure if exception other than OutOfMemoryError - * is thrown while eating memory + * is thrown while triggering full GC * - * @see #eatMemory() + * @see WhiteBox.getWhiteBox().fullGC() */ - public boolean unloadClass(ExecutionController stresser) { + public boolean unloadClass() { // free references to class and class loader to be able for collecting by GC classObjects.removeAllElements(); customClassLoader = null; - // force class unloading by eating memory pool - eatMemory(stresser); + // force class unloading by triggering full GC + WhiteBox.getWhiteBox().fullGC(); // force GC to unload marked class loader and its classes if (isClassLoaderReclaimed()) { @@ -256,34 +257,4 @@ public class ClassUnloader { // class loader has not been reclaimed return false; } - - public boolean unloadClass() { - Stresser stresser = new Stresser() { - - @Override - public boolean continueExecution() { - return true; - } - - }; - return unloadClass(stresser); - } - - // Stresses memory by allocating arrays of bytes. - public static void eatMemory(ExecutionController stresser) { - GarbageUtils.eatMemory(stresser, 50, 1024, 2); - } - - // Stresses memory by allocating arrays of bytes. - public static void eatMemory() { - Stresser stresser = new Stresser() { - - @Override - public boolean continueExecution() { - return true; - } - - }; - eatMemory(stresser); - } } From 2b1e11c2541f799142bd71e9526cbd04743c6f4e Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Thu, 15 Jan 2026 02:46:20 +0000 Subject: [PATCH 105/204] 8374879: NMethodRelocationTest fails with -Xcomp after 8369150 Reviewed-by: lmesnik, chagedorn --- .../jvmti/NMethodRelocation/NMethodRelocationTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java index 10888dce1b4..5b4a1c7e663 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java @@ -29,7 +29,8 @@ * vm.gc != "Epsilon" & * vm.flavor == "server" & * !vm.emulatedClient & - * (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) + * (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) & + * vm.compMode == "Xmixed" * @library /test/lib /test/hotspot/jtreg * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox From 499b58820225eb96c728816af9ea2ade47d1fc6b Mon Sep 17 00:00:00 2001 From: Alexander Matveev Date: Thu, 15 Jan 2026 03:53:53 +0000 Subject: [PATCH 106/204] 8374215: [macos] Clean and fix "lic_template.plist" to correctly work with multiple languages Reviewed-by: asemenyuk --- .../jdk/jpackage/internal/MacDmgLicense.java | 103 +++++++++ .../jdk/jpackage/internal/MacDmgPackager.java | 31 +-- .../resources/MacResources.properties | 5 + .../resources/MacResources_de.properties | 7 +- .../resources/MacResources_ja.properties | 7 +- .../resources/MacResources_zh_CN.properties | 7 +- .../internal/resources/lic_template.plist | 210 ++++-------------- .../helpers/jdk/jpackage/test/Executor.java | 4 +- .../jdk/tools/jpackage/share/LicenseTest.java | 33 ++- 9 files changed, 210 insertions(+), 197 deletions(-) create mode 100644 src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgLicense.java diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgLicense.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgLicense.java new file mode 100644 index 00000000000..eea09b80929 --- /dev/null +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgLicense.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. 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.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Base64; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +import jdk.jpackage.internal.resources.ResourceLocator; + +final class MacDmgLicense { + + public static void prepareLicensePListFile(Path licenseFile, Path licensePListFile) + throws IOException { + byte[] licenseContentOriginal = + Files.readAllBytes(licenseFile); + String licenseInBase64 = + Base64.getEncoder().encodeToString(licenseContentOriginal); + + Map data = new HashMap<>(); + data.put("APPLICATION_LICENSE_TEXT", licenseInBase64); + data.put("STR_DATA_ENGLISH", + getSTRData("English", Locale.ENGLISH, "MacRoman")); + data.put("STR_DATA_GERMAN", + getSTRData("German", Locale.GERMAN, "MacRoman")); + data.put("STR_DATA_JAPANESE", + getSTRData("Japanese", Locale.JAPANESE, "Shift_JIS")); + data.put("STR_DATA_SIMPLIFIED_CHINESE", + getSTRData("Simplified Chinese", Locale.SIMPLIFIED_CHINESE, "GB2312")); + + new OverridableResource(DEFAULT_LICENSE_PLIST, ResourceLocator.class) + .setCategory(I18N.getString("resource.license-setup")) + .setSubstitutionData(data) + .saveToFile(licensePListFile); + } + + private static void writeSTRDataString(ByteArrayOutputStream bos, + String str, String charset) { + byte [] bytes = str.getBytes(Charset.forName(charset)); + byte [] bytesLength = {(byte)bytes.length}; + bos.writeBytes(bytesLength); + bos.writeBytes(bytes); + } + + // Returns base64 decoded STR section data. + // Strings should be in following order: + // Language, message.dmg.license.button.agree, + // message.dmg.license.button.disagree, message.dmg.license.button.print + // message.dmg.license.button.save, message.dmg.license.message + // STR section data encoded: + // Number of strings in the list (unsigned 16-bit integer, big endian): 6 + // A sequence of strings prefixed with string length (unsigned 8-bit integer) + // Note: Language should not be translated. + private static String getSTRData(String language, Locale locale, String charset) { + ResourceBundle bundle = ResourceBundle.getBundle( + "jdk.jpackage.internal.resources.MacResources", locale); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + byte [] numberOfStrings = {0x00, 0x06}; // Always 6 + bos.writeBytes(numberOfStrings); + + writeSTRDataString(bos, language, charset); + writeSTRDataString(bos, bundle.getString("message.dmg.license.button.agree"), charset); + writeSTRDataString(bos, bundle.getString("message.dmg.license.button.disagree"), charset); + writeSTRDataString(bos, bundle.getString("message.dmg.license.button.print"), charset); + writeSTRDataString(bos, bundle.getString("message.dmg.license.button.save"), charset); + writeSTRDataString(bos, bundle.getString("message.dmg.license.message"), charset); + + return Base64.getEncoder().encodeToString(bos.toByteArray()); + } + + private static final String DEFAULT_LICENSE_PLIST = "lic_template.plist"; +} diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java index 82bb9fc4dad..cf4db226d37 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java @@ -34,7 +34,6 @@ import java.nio.file.LinkOption; import java.nio.file.Path; import java.text.MessageFormat; import java.util.ArrayList; -import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -105,7 +104,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, return env.configDir().resolve(pkg.app().name() + "-volume.icns"); } - Path licenseFile() { + Path licensePListFile() { return env.configDir().resolve(pkg.app().name() + "-license.plist"); } @@ -175,26 +174,6 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, .saveToFile(dmgSetup); } - private void prepareLicense() throws IOException { - final var licFile = pkg.licenseFile(); - if (licFile.isEmpty()) { - return; - } - - byte[] licenseContentOriginal = - Files.readAllBytes(licFile.orElseThrow()); - String licenseInBase64 = - Base64.getEncoder().encodeToString(licenseContentOriginal); - - Map data = new HashMap<>(); - data.put("APPLICATION_LICENSE_TEXT", licenseInBase64); - - env.createResource(DEFAULT_LICENSE_PLIST) - .setCategory(I18N.getString("resource.license-setup")) - .setSubstitutionData(data) - .saveToFile(licenseFile()); - } - private void prepareConfigFiles() throws IOException { env.createResource(DEFAULT_BACKGROUND_IMAGE) @@ -206,7 +185,9 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, .setExternal(pkg.icon().orElse(null)) .saveToFile(volumeIcon()); - prepareLicense(); + if (pkg.licenseFile().isPresent()) { + MacDmgLicense.prepareLicensePListFile(pkg.licenseFile().get(), licensePListFile()); + } prepareDMGSetupScript(); } @@ -359,7 +340,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, "udifrez", normalizedAbsolutePathString(finalDMG), "-xml", - normalizedAbsolutePathString(licenseFile()) + normalizedAbsolutePathString(licensePListFile()) ).retry() .setMaxAttemptsCount(10) .setAttemptTimeout(3, TimeUnit.SECONDS) @@ -441,6 +422,4 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, private static final String DEFAULT_BACKGROUND_IMAGE = "background_dmg.tiff"; private static final String DEFAULT_DMG_SETUP_SCRIPT = "DMGsetup.scpt"; private static final String TEMPLATE_BUNDLE_ICON = "JavaApp.icns"; - - private static final String DEFAULT_LICENSE_PLIST="lic_template.plist"; } 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 ceeab587f66..0237d49f399 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 @@ -64,6 +64,11 @@ message.signing.pkg=Warning: For signing PKG, you might need to set "Always Trus message.setfile.dmg=Setting custom icon on DMG file skipped because 'SetFile' utility was not found. Installing Xcode with Command Line Tools should resolve this issue. message.codesign.failed.reason.app.content="codesign" failed and additional application content was supplied via the "--app-content" parameter. Probably the additional content broke the integrity of the application bundle and caused the failure. Ensure content supplied via the "--app-content" parameter does not break the integrity of the application bundle, or add it in the post-processing step. message.codesign.failed.reason.xcode.tools=Possible reason for "codesign" failure is missing Xcode with command line developer tools. Install Xcode with command line developer tools to see if it resolves the problem. +message.dmg.license.button.agree=Agree +message.dmg.license.button.disagree=Disagree +message.dmg.license.button.print=Print +message.dmg.license.button.save=Save... +message.dmg.license.message=If you agree with the terms of this license, press "Agree" to install the software. If you do not agree, press "Disagree". warning.unsigned.app.image=Warning: Using unsigned app-image to build signed {0}. warning.per.user.app.image.signed=Warning: Support for per-user configuration of the installed application will not be supported due to missing "{0}" in predefined signed application image. warning.non.standard.contents.sub.dir=Warning: The file name of the directory "{0}" specified for the --app-content option is not a standard subdirectory name in the "Contents" directory of the application bundle. The result application bundle may fail code signing and/or notarization. diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties index 3cc56bb6cdf..367f3216f85 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_de.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 @@ -71,6 +71,11 @@ message.signing.pkg=Warnung: Zum Signieren von PKG müssen Sie möglicherweise m message.setfile.dmg=Das Festlegen des benutzerdefinierten Symbols für die DMG-Datei wurde übersprungen, weil das Utility "SetFile" nicht gefunden wurde. Durch Installieren von Xcode mit Befehlszeilentools sollte dieses Problem behoben werden. message.codesign.failed.reason.app.content="codesign" war nicht erfolgreich, und zusätzlicher Anwendungsinhalt wurde über den Parameter "--app-content" angegeben. Wahrscheinlich hat der zusätzliche Inhalt die Integrität des Anwendungs-Bundles beeinträchtigt und den Fehler verursacht. Stellen Sie sicher, das der über den Parameter "--app-content" angegebene Inhalt nicht die Integrität des Anwendungs-Bundles beeinträchtigt, oder fügen Sie ihn im Nachverarbeitungsschritt hinzu. message.codesign.failed.reason.xcode.tools=Möglicher Grund für "codesign"-Fehler ist fehlender Xcode mit Befehlszeilen-Entwicklertools. Installieren Sie Xcode mit Befehlszeilen-Entwicklertools, und prüfen Sie, ob das Problem dadurch beseitigt wird. +message.dmg.license.button.agree=Akzeptieren +message.dmg.license.button.disagree=Ablehnen +message.dmg.license.button.print=Drucken +message.dmg.license.button.save=Sichern... +message.dmg.license.message=Klicken Sie in “Akzeptieren”, wenn Sie mit den Bestimmungen des Software-Lizenzvertrags einverstanden sind. Falls nicht, bitte “Ablehnen” anklicken. Sie können die Software nur installieren, wenn Sie “Akzeptieren” angeklickt haben. warning.unsigned.app.image=Warnung: Nicht signiertes app-image wird zum Erstellen von signiertem {0} verwendet. warning.per.user.app.image.signed=Warnung: Konfiguration der installierten Anwendung pro Benutzer wird nicht unterstützt, da "{0}" im vordefinierten signierten Anwendungsimage fehlt. warning.non.standard.contents.sub.dir=Warnung: Der Dateiname des Verzeichnisses "{0}", das für die Option --app-content angegeben wurde, ist kein Standardunterverzeichnisname im Verzeichnis "Contents" des Anwendungs-Bundles. Möglicherweise verläuft die Codesignierung und/oder Notarisierung im Ergebnisanwendungs-Bundle nicht erfolgreich. diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties index d3150a34a86..ba2497d30dd 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_ja.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 @@ -71,6 +71,11 @@ message.signing.pkg=警告: PKGへの署名の場合、「キーチェーン・ message.setfile.dmg='SetFile'ユーティリティが見つからないため、DMGファイルでのカスタム・アイコンの設定がスキップされました。Xcodeとコマンド・ライン・ツールをインストールすると、この問題は解決されます。 message.codesign.failed.reason.app.content="codesign"が失敗したため、追加のアプリケーション・コンテンツが、"--app-content"パラメータを介して提供されました。追加のコンテンツにより、アプリケーション・バンドルの整合性が損われ、失敗の原因になった可能性があります。"--app-content"パラメータを介して提供されたコンテンツによって、アプリケーション・バンドルの整合性が損われていないことを確認するか、処理後のステップで追加してください。 message.codesign.failed.reason.xcode.tools="codesign"失敗の考えられる理由は、Xcodeとコマンドライン・デベロッパ・ツールの欠落です。Xcodeとコマンドライン・デベロッパ・ツールをインストールして、問題が解決されるかを確認してください。 +message.dmg.license.button.agree=同意します +message.dmg.license.button.disagree=同意しません +message.dmg.license.button.print=印刷する +message.dmg.license.button.save=保存... +message.dmg.license.message=本ソフトウエア使用許諾契約の条件に同意される場合には、ソフトウエアをインストールするために「同意します」を押してください。 同意されない場合には、「同意しません」を押してください。 warning.unsigned.app.image=警告: 署名されていないapp-imageを使用して署名された{0}を作成します。 warning.per.user.app.image.signed=警告: 事前定義済の署名付きアプリケーション・イメージに"{0}"がないため、インストール済アプリケーションのユーザーごとの構成はサポートされません。 warning.non.standard.contents.sub.dir=警告: --app-contentオプションに指定されたディレクトリ"{0}"のファイル名が、アプリケーション・バンドルの"Contents"ディレクトリ内の標準サブディレクトリ名ではありません。結果アプリケーション・バンドルは、コード署名および/または公証に失敗することがあります。 diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties index 8ca2219b72f..f855a31caae 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources_zh_CN.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 @@ -71,6 +71,11 @@ message.signing.pkg=警告:要对 PKG 进行签名,可能需要使用“密 message.setfile.dmg=由于未找到 'SetFile' 实用程序,跳过了针对 DMG 文件设置定制图标的操作。安装带命令行工具的 Xcode 应能解决此问题。 message.codesign.failed.reason.app.content="codesign" 失败,并通过 "--app-content" 参数提供了附加应用程序内容。可能是附加内容破坏了应用程序包的完整性,导致了故障。请确保通过 "--app-content" 参数提供的内容不会破坏应用程序包的完整性,或者在后处理步骤中添加该内容。 message.codesign.failed.reason.xcode.tools="codesign" 失败可能是因为缺少带命令行开发人员工具的 Xcode。请安装带命令行开发人员工具的 Xcode,看看是否可以解决问题。 +message.dmg.license.button.agree=同意 +message.dmg.license.button.disagree=不同意 +message.dmg.license.button.print=打印 +message.dmg.license.button.save=存储... +message.dmg.license.message=如果您同意本许可协议的条款,请按“同意”来安装此软件。如果您不同意,请按“不同意”。 warning.unsigned.app.image=警告:使用未签名的 app-image 生成已签名的 {0}。 warning.per.user.app.image.signed=警告:由于预定义的已签名应用程序映像中缺少 "{0}",不支持对已安装应用程序的每用户配置提供支持。 warning.non.standard.contents.sub.dir=警告:为 --app-content 选项指定的目录 "{0}" 的文件名不是应用程序包的 "Contents" 目录中的标准子目录名称。结果应用程序包可能会使代码签名和/或公证失败。 diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/lic_template.plist b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/lic_template.plist index 072e7168fb7..ac1b32325e8 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/lic_template.plist +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/lic_template.plist @@ -8,7 +8,9 @@ Attributes 0x0000 Data - AAAAAgAAAAAAAAAAAAQAAA== + + AAAABAAAAAAAAAADAAEAAAAOAAIAAQA0AAMAAQ== + ID 5000 Name @@ -21,17 +23,22 @@ Attributes 0x0000 Data - AAYPRW5nbGlzaCBkZWZhdWx0BUFncmVlCERpc2FncmVlBVByaW50B1NhdmUuLi56SWYgeW91IGFncmVlIHdpdGggdGhlIHRlcm1zIG9mIHRoaXMgbGljZW5zZSwgY2xpY2sgIkFncmVlIiB0byBhY2Nlc3MgdGhlIHNvZnR3YXJlLiAgSWYgeW91IGRvIG5vdCBhZ3JlZSwgcHJlc3MgIkRpc2FncmVlLiI= + + STR_DATA_ENGLISH + ID 5000 + Name - English buttons + English (United States) Attributes 0x0000 Data - AAYHRGV1dHNjaAtBa3plcHRpZXJlbghBYmxlaG5lbgdEcnVja2VuClNpY2hlcm4uLi7nS2xpY2tlbiBTaWUgaW4g0kFremVwdGllcmVu0ywgd2VubiBTaWUgbWl0IGRlbiBCZXN0aW1tdW5nZW4gZGVzIFNvZnR3YXJlLUxpemVuenZlcnRyYWdzIGVpbnZlcnN0YW5kZW4gc2luZC4gRmFsbHMgbmljaHQsIGJpdHRlINJBYmxlaG5lbtMgYW5rbGlja2VuLiBTaWUga5pubmVuIGRpZSBTb2Z0d2FyZSBudXIgaW5zdGFsbGllcmVuLCB3ZW5uIFNpZSDSQWt6ZXB0aWVyZW7TIGFuZ2VrbGlja3QgaGFiZW4u + + STR_DATA_GERMAN + ID 5001 Name @@ -41,151 +48,25 @@ Attributes 0x0000 Data - AAYHRW5nbGlzaAVBZ3JlZQhEaXNhZ3JlZQVQcmludAdTYXZlLi4ue0lmIHlvdSBhZ3JlZSB3aXRoIHRoZSB0ZXJtcyBvZiB0aGlzIGxpY2Vuc2UsIHByZXNzICJBZ3JlZSIgdG8gaW5zdGFsbCB0aGUgc29mdHdhcmUuICBJZiB5b3UgZG8gbm90IGFncmVlLCBwcmVzcyAiRGlzYWdyZWUiLg== + + STR_DATA_JAPANESE + ID 5002 Name - English - - - Attributes - 0x0000 - Data - AAYHRXNwYZZvbAdBY2VwdGFyCk5vIGFjZXB0YXIISW1wcmltaXIKR3VhcmRhci4uLsBTaSBlc3SHIGRlIGFjdWVyZG8gY29uIGxvcyB0jnJtaW5vcyBkZSBlc3RhIGxpY2VuY2lhLCBwdWxzZSAiQWNlcHRhciIgcGFyYSBpbnN0YWxhciBlbCBzb2Z0d2FyZS4gRW4gZWwgc3VwdWVzdG8gZGUgcXVlIG5vIGVzdI4gZGUgYWN1ZXJkbyBjb24gbG9zIHSOcm1pbm9zIGRlIGVzdGEgbGljZW5jaWEsIHB1bHNlICJObyBhY2VwdGFyLiI= - ID - 5003 - Name - Spanish - - - Attributes - 0x0000 - Data - AAYIRnJhbo1haXMIQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4= - ID - 5004 - Name - French - - - Attributes - 0x0000 - Data - AAYISXRhbGlhbm8HQWNjZXR0bwdSaWZpdXRvBlN0YW1wYQtSZWdpc3RyYS4uLn9TZSBhY2NldHRpIGxlIGNvbmRpemlvbmkgZGkgcXVlc3RhIGxpY2VuemEsIGZhaSBjbGljIHN1ICJBY2NldHRvIiBwZXIgaW5zdGFsbGFyZSBpbCBzb2Z0d2FyZS4gQWx0cmltZW50aSBmYWkgY2xpYyBzdSAiUmlmaXV0byIu - ID - 5005 - Name - Italian - - - Attributes - 0x0000 - Data - AAYISmFwYW5lc2UKk6+I04K1gtyCtwyTr4jTgrWC3IK5gvEIiPON/IK3gukHlduRti4uLrSWe4Ncg3SDZ4NFg0eDQY5nl3CLlpH4jF+W8YLMj/CMj4LJk6+I04KzguqC6Y/qjYeCyYLNgUGDXIN0g2eDRYNHg0GC8INDg5ODWINngVuDi4K3gumCvYLfgsmBdZOviNOCtYLcgreBdoLwiZ+CtYLEgq2CvoKzgqKBQoFAk6+I04KzguqCyIKij+qNh4LJgs2BQYF1k6+I04K1gtyCuYLxgXaC8ImfgrWCxIKtgr6Cs4KigUI= - ID - 5006 - Name Japanese Attributes 0x0000 Data - AAYKTmVkZXJsYW5kcwJKYQNOZWUFUHJpbnQJQmV3YWFyLi4upEluZGllbiB1IGFra29vcmQgZ2FhdCBtZXQgZGUgdm9vcndhYXJkZW4gdmFuIGRlemUgbGljZW50aWUsIGt1bnQgdSBvcCAnSmEnIGtsaWtrZW4gb20gZGUgcHJvZ3JhbW1hdHV1ciB0ZSBpbnN0YWxsZXJlbi4gSW5kaWVuIHUgbmlldCBha2tvb3JkIGdhYXQsIGtsaWt0IHUgb3AgJ05lZScu + + STR_DATA_SIMPLIFIED_CHINESE + ID - 5007 + 5003 Name - Dutch - - - Attributes - 0x0000 - Data - AAYGU3ZlbnNrCEdvZGuKbm5zBkF2YppqcwhTa3JpdiB1dAhTcGFyYS4uLpNPbSBEdSBnb2Rrim5uZXIgbGljZW5zdmlsbGtvcmVuIGtsaWNrYSBwjCAiR29ka4pubnMiIGaaciBhdHQgaW5zdGFsbGVyYSBwcm9ncmFtcHJvZHVrdGVuLiBPbSBEdSBpbnRlIGdvZGuKbm5lciBsaWNlbnN2aWxsa29yZW4sIGtsaWNrYSBwjCAiQXZimmpzIi4= - ID - 5008 - Name - Swedish - - - Attributes - 0x0000 - Data - AAYRUG9ydHVndZBzLCBCcmFzaWwJQ29uY29yZGFyCURpc2NvcmRhcghJbXByaW1pcglTYWx2YXIuLi6MU2UgZXN0hyBkZSBhY29yZG8gY29tIG9zIHRlcm1vcyBkZXN0YSBsaWNlbo1hLCBwcmVzc2lvbmUgIkNvbmNvcmRhciIgcGFyYSBpbnN0YWxhciBvIHNvZnR3YXJlLiBTZSBui28gZXN0hyBkZSBhY29yZG8sIHByZXNzaW9uZSAiRGlzY29yZGFyIi4= - ID - 5009 - Name - Brazilian Portuguese - - - Attributes - 0x0000 - Data - AAYSU2ltcGxpZmllZCBDaGluZXNlBM2s0uIGsrvNrNLiBLTy06EGtOa0oqGtVMjnufvE+s2s0uKxvtDtv8nQrdLptcTM9b/uo6zH67C0obDNrNLiobHAtLCy17C0y8jtvP6ho8jnufvE+rK7zazS4qOsx+uwtKGwsrvNrNLiobGhow== - ID - 5010 - Name - Simplified Chinese - - - Attributes - 0x0000 - Data - AAYTVHJhZGl0aW9uYWwgQ2hpbmVzZQSmULdOBqSjplC3TgSmQ6ZMBsB4pnOhS1CmcKpHsXqmULdOpbuzXKVpw9K4zKq6sfi02qFBvdCr9qGnplC3TqGopUimd7jLs27F6aFDpnCqR6SjplC3TqFBvdCr9qGnpKOmULdOoaihQw== - ID - 5011 - Name - Traditional Chinese - - - Attributes - 0x0000 - Data - AAYFRGFuc2sERW5pZwVVZW5pZwdVZHNrcml2CkFya2l2ZXIuLi6YSHZpcyBkdSBhY2NlcHRlcmVyIGJldGluZ2Vsc2VybmUgaSBsaWNlbnNhZnRhbGVuLCBza2FsIGR1IGtsaWtrZSBwjCDSRW5pZ9MgZm9yIGF0IGluc3RhbGxlcmUgc29mdHdhcmVuLiBLbGlrIHCMINJVZW5pZ9MgZm9yIGF0IGFubnVsbGVyZSBpbnN0YWxsZXJpbmdlbi4= - ID - 5012 - Name - Danish - - - Attributes - 0x0000 - Data - AAYFU3VvbWkISHl2imtzeW4KRW4gaHl2imtzeQdUdWxvc3RhCVRhbGxlbm5hyW9IeXaKa3N5IGxpc2Vuc3Npc29waW11a3NlbiBlaGRvdCBvc29pdHRhbWFsbGEg1Uh5doprc3nVLiBKb3MgZXQgaHl2imtzeSBzb3BpbXVrc2VuIGVodG9qYSwgb3NvaXRhINVFbiBoeXaKa3N51S4= - ID - 5013 - Name - Finnish - - - Attributes - 0x0000 - Data - AAYRRnJhbo1haXMgY2FuYWRpZW4IQWNjZXB0ZXIHUmVmdXNlcghJbXByaW1lcg5FbnJlZ2lzdHJlci4uLrpTaSB2b3VzIGFjY2VwdGV6IGxlcyB0ZXJtZXMgZGUgbGEgcHKOc2VudGUgbGljZW5jZSwgY2xpcXVleiBzdXIgIkFjY2VwdGVyIiBhZmluIGQnaW5zdGFsbGVyIGxlIGxvZ2ljaWVsLiBTaSB2b3VzIG4nkHRlcyBwYXMgZCdhY2NvcmQgYXZlYyBsZXMgdGVybWVzIGRlIGxhIGxpY2VuY2UsIGNsaXF1ZXogc3VyICJSZWZ1c2VyIi4= - ID - 5014 - Name - French Canadian - - - Attributes - 0x0000 - Data - AAYGS29yZWFuBLW/wMcJtb/AxyC+yMfUBsfBuLDGrgfA+sDlLi4ufrvnv+sgsOi+4LytwMcgs7u/67+hILW/wMfHz7jpLCAitb/AxyIgtNzD37imILStt68gvNLHwcauv/6+7rimILyzxKHHz73KvcO/wC4gtb/Ax8fPwfYgvsq0wrTZuOksICK1v8DHIL7Ix9QiILTcw9+4piC0qbijvcq9w7/ALg== - ID - 5015 - Name - Korean - - - Attributes - 0x0000 - Data - AAYFTm9yc2sERW5pZwlJa2tlIGVuaWcIU2tyaXYgdXQKQXJraXZlci4uLqNIdmlzIERlIGVyIGVuaWcgaSBiZXN0ZW1tZWxzZW5lIGkgZGVubmUgbGlzZW5zYXZ0YWxlbiwga2xpa2tlciBEZSBwjCAiRW5pZyIta25hcHBlbiBmb3IgjCBpbnN0YWxsZXJlIHByb2dyYW12YXJlbi4gSHZpcyBEZSBpa2tlIGVyIGVuaWcsIGtsaWtrZXIgRGUgcIwgIklra2UgZW5pZyIu - ID - 5016 - Name - Norwegian + Chinese (Simplified) TEXT @@ -194,50 +75,49 @@ Attributes 0x0000 Data - APPLICATION_LICENSE_TEXT + + APPLICATION_LICENSE_TEXT + ID 5000 Name - English SLA + English (United States) SLA + + + Attributes + 0x0000 + Data + + APPLICATION_LICENSE_TEXT + + ID + 5001 + Name + German SLA - - TMPL - Attributes 0x0000 Data - E0RlZmF1bHQgTGFuZ3VhZ2UgSUREV1JEBUNvdW50T0NOVAQqKioqTFNUQwtzeXMgbGFuZyBJRERXUkQebG9jYWwgcmVzIElEIChvZmZzZXQgZnJvbSA1MDAwRFdSRBAyLWJ5dGUgbGFuZ3VhZ2U/RFdSRAQqKioqTFNURQ== + + APPLICATION_LICENSE_TEXT + ID - 128 + 5002 Name - LPic + Japanese SLA - - plst - - - Attributes - 0x0050 - Data - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - ID - 0 - Name - - - - styl - Attributes 0x0000 Data - AAMAAAAAAAwACQAUAAAAAAAAAAAAAAAAACcADAAJABQBAAAAAAAAAAAAAAAAKgAMAAkAFAAAAAAAAAAAAAA= + + APPLICATION_LICENSE_TEXT + ID - 5000 + 5003 Name - English SLA + Chinese (Simplified) SLA diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java index b508d4f5ffe..1ae678cd944 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java @@ -183,12 +183,12 @@ public final class Executor extends CommandArguments { return commandOutputControl.charset(); } - Executor storeOutputInFiles(boolean v) { + public Executor storeOutputInFiles(boolean v) { commandOutputControl.storeOutputInFiles(v); return this; } - Executor storeOutputInFiles() { + public Executor storeOutputInFiles() { return storeOutputInFiles(true); } diff --git a/test/jdk/tools/jpackage/share/LicenseTest.java b/test/jdk/tools/jpackage/share/LicenseTest.java index 1c6bfd51b62..9c2d1077584 100644 --- a/test/jdk/tools/jpackage/share/LicenseTest.java +++ b/test/jdk/tools/jpackage/share/LicenseTest.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 @@ -56,6 +56,9 @@ import jdk.jpackage.test.TKit; * * Mac: * + * For DMG license should be displayed on command line when "hdiutil attach" + * is called. + * * Windows * * Installer should display license text matching contents of the license file @@ -96,6 +99,7 @@ public class LicenseTest { LICENSE_FILE)); }); + initMacDmgLicenseVerifier(test.forTypes(PackageType.MAC_DMG)); initLinuxLicenseVerifier(test.forTypes(PackageType.LINUX)); test.run(); @@ -131,6 +135,33 @@ public class LicenseTest { new CustomDebianCopyrightTest().withSubstitution(true).run(); } + private static PackageTest initMacDmgLicenseVerifier(PackageTest test) { + return test + .addBundleVerifier(cmd -> { + verifyLicenseFileInDMGPackage(cmd); + }); + } + + private static void verifyLicenseFileInDMGPackage(JPackageCommand cmd) + throws IOException { + // DMG should have license, so attach with "no", since we only need license. + // With "no" attach will be canceled. + final var attachExec = Executor.of("sh", "-c", String.join(" ", + "no", + "|", + "/usr/bin/hdiutil", + "attach", + JPackageCommand.escapeAndJoin(cmd.outputBundle().toString()) + )).saveOutput().storeOutputInFiles(); + + // Expected exit code is 1, since we canceling license. + final var attachResult = attachExec.executeAndRepeatUntilExitCode(1, 10, 6); + TKit.assertStringListEquals(Files.readAllLines(LICENSE_FILE), + attachResult.stdout(), String.format( + "Check output of \"hdiutil attach\" has the same license as contents of source license file [%s]", + LICENSE_FILE)); + } + private static PackageTest initLinuxLicenseVerifier(PackageTest test) { return test .addBundleVerifier(cmd -> { From b6b337926d5f13ee2bca12ea94530ea59911ff2f Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Thu, 15 Jan 2026 05:58:18 +0000 Subject: [PATCH 107/204] 8371762: Incorrect use of checked_cast in Arguments::process_settings_file Reviewed-by: dholmes, kbarrett --- src/hotspot/share/runtime/arguments.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index cf0a1ab9757..546ae610769 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.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 @@ -64,7 +64,6 @@ #include "runtime/vm_version.hpp" #include "services/management.hpp" #include "utilities/align.hpp" -#include "utilities/checkedCast.hpp" #include "utilities/debug.hpp" #include "utilities/defaultStream.hpp" #include "utilities/macros.hpp" @@ -1207,16 +1206,22 @@ bool Arguments::process_settings_file(const char* file_name, bool should_exist, } char token[1024]; - int pos = 0; + size_t pos = 0; bool in_white_space = true; bool in_comment = false; bool in_quote = false; - int quote_c = 0; + char quote_c = 0; bool result = true; - int c = getc(stream); - while(c != EOF && pos < (int)(sizeof(token)-1)) { + int c_or_eof = getc(stream); + while (c_or_eof != EOF && pos < (sizeof(token) - 1)) { + // We have checked the c_or_eof for EOF. getc should only ever return the + // EOF or an unsigned char converted to an int. We cast down to a char to + // avoid the char to int promotions we would otherwise do in the comparisons + // below (which would be incorrect if we ever compared to a non-ascii char), + // and the int to char conversions we would otherwise do in the assignments. + const char c = static_cast(c_or_eof); if (in_white_space) { if (in_comment) { if (c == '\n') in_comment = false; @@ -1224,7 +1229,7 @@ bool Arguments::process_settings_file(const char* file_name, bool should_exist, if (c == '#') in_comment = true; else if (!isspace((unsigned char) c)) { in_white_space = false; - token[pos++] = checked_cast(c); + token[pos++] = c; } } } else { @@ -1244,10 +1249,10 @@ bool Arguments::process_settings_file(const char* file_name, bool should_exist, } else if (in_quote && (c == quote_c)) { in_quote = false; } else { - token[pos++] = checked_cast(c); + token[pos++] = c; } } - c = getc(stream); + c_or_eof = getc(stream); } if (pos > 0) { token[pos] = '\0'; From d16a9b2ec507251a44f034f1ccf8039f02023d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galder=20Zamarre=C3=B1o?= Date: Thu, 15 Jan 2026 07:22:54 +0000 Subject: [PATCH 108/204] 8373134: C2: Min/Max users of Min/Max uses should be enqueued for GVN Reviewed-by: epeter, bmaillard, dlong --- src/hotspot/share/opto/addnode.cpp | 30 +-- src/hotspot/share/opto/addnode.hpp | 46 ++--- src/hotspot/share/opto/loopnode.cpp | 18 +- src/hotspot/share/opto/macro.cpp | 4 +- src/hotspot/share/opto/movenode.cpp | 4 +- src/hotspot/share/opto/node.hpp | 3 + src/hotspot/share/opto/phaseX.cpp | 9 + src/hotspot/share/opto/vectorization.cpp | 2 +- .../compiler/igvn/TestMinMaxIdentity.java | 186 ++++++++++++++++++ 9 files changed, 251 insertions(+), 51 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 40cd6337c17..e04da430ef0 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1195,7 +1195,7 @@ const Type* XorLNode::Value(PhaseGVN* phase) const { return AddNode::Value(phase); } -Node* MaxNode::build_min_max_int(Node* a, Node* b, bool is_max) { +Node* MinMaxNode::build_min_max_int(Node* a, Node* b, bool is_max) { if (is_max) { return new MaxINode(a, b); } else { @@ -1203,7 +1203,7 @@ Node* MaxNode::build_min_max_int(Node* a, Node* b, bool is_max) { } } -Node* MaxNode::build_min_max_long(PhaseGVN* phase, Node* a, Node* b, bool is_max) { +Node* MinMaxNode::build_min_max_long(PhaseGVN* phase, Node* a, Node* b, bool is_max) { if (is_max) { return new MaxLNode(phase->C, a, b); } else { @@ -1211,7 +1211,7 @@ Node* MaxNode::build_min_max_long(PhaseGVN* phase, Node* a, Node* b, bool is_max } } -Node* MaxNode::build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, const Type* t, PhaseGVN& gvn) { +Node* MinMaxNode::build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, const Type* t, PhaseGVN& gvn) { bool is_int = gvn.type(a)->isa_int(); assert(is_int || gvn.type(a)->isa_long(), "int or long inputs"); assert(is_int == (gvn.type(b)->isa_int() != nullptr), "inconsistent inputs"); @@ -1243,7 +1243,7 @@ Node* MaxNode::build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, co return res; } -Node* MaxNode::build_min_max_diff_with_zero(Node* a, Node* b, bool is_max, const Type* t, PhaseGVN& gvn) { +Node* MinMaxNode::build_min_max_diff_with_zero(Node* a, Node* b, bool is_max, const Type* t, PhaseGVN& gvn) { bool is_int = gvn.type(a)->isa_int(); assert(is_int || gvn.type(a)->isa_long(), "int or long inputs"); assert(is_int == (gvn.type(b)->isa_int() != nullptr), "inconsistent inputs"); @@ -1290,7 +1290,7 @@ static bool can_overflow(const TypeLong* t, jlong c) { // Let = x_operands and = y_operands. // If x == y and neither add(x, x_off) nor add(y, y_off) overflow, return // add(x, op(x_off, y_off)). Otherwise, return nullptr. -Node* MaxNode::extract_add(PhaseGVN* phase, ConstAddOperands x_operands, ConstAddOperands y_operands) { +Node* MinMaxNode::extract_add(PhaseGVN* phase, ConstAddOperands x_operands, ConstAddOperands y_operands) { Node* x = x_operands.first; Node* y = y_operands.first; int opcode = Opcode(); @@ -1327,7 +1327,7 @@ static ConstAddOperands as_add_with_constant(Node* n) { return ConstAddOperands(x, c_type->is_int()->get_con()); } -Node* MaxNode::IdealI(PhaseGVN* phase, bool can_reshape) { +Node* MinMaxNode::IdealI(PhaseGVN* phase, bool can_reshape) { Node* n = AddNode::Ideal(phase, can_reshape); if (n != nullptr) { return n; @@ -1401,7 +1401,7 @@ Node* MaxINode::Identity(PhaseGVN* phase) { return in(2); } - return MaxNode::Identity(phase); + return MinMaxNode::Identity(phase); } //============================================================================= @@ -1434,7 +1434,7 @@ Node* MinINode::Identity(PhaseGVN* phase) { return in(1); } - return MaxNode::Identity(phase); + return MinMaxNode::Identity(phase); } //------------------------------add_ring--------------------------------------- @@ -1564,7 +1564,7 @@ Node* MaxLNode::Identity(PhaseGVN* phase) { return in(2); } - return MaxNode::Identity(phase); + return MinMaxNode::Identity(phase); } Node* MaxLNode::Ideal(PhaseGVN* phase, bool can_reshape) { @@ -1596,7 +1596,7 @@ Node* MinLNode::Identity(PhaseGVN* phase) { return in(1); } - return MaxNode::Identity(phase); + return MinMaxNode::Identity(phase); } Node* MinLNode::Ideal(PhaseGVN* phase, bool can_reshape) { @@ -1610,7 +1610,7 @@ Node* MinLNode::Ideal(PhaseGVN* phase, bool can_reshape) { return nullptr; } -int MaxNode::opposite_opcode() const { +int MinMaxNode::opposite_opcode() const { if (Opcode() == max_opcode()) { return min_opcode(); } else { @@ -1621,7 +1621,7 @@ int MaxNode::opposite_opcode() const { // Given a redundant structure such as Max/Min(A, Max/Min(B, C)) where A == B or A == C, return the useful part of the structure. // 'operation' is the node expected to be the inner 'Max/Min(B, C)', and 'operand' is the node expected to be the 'A' operand of the outer node. -Node* MaxNode::find_identity_operation(Node* operation, Node* operand) { +Node* MinMaxNode::find_identity_operation(Node* operation, Node* operand) { if (operation->Opcode() == Opcode() || operation->Opcode() == opposite_opcode()) { Node* n1 = operation->in(1); Node* n2 = operation->in(2); @@ -1645,17 +1645,17 @@ Node* MaxNode::find_identity_operation(Node* operation, Node* operand) { return nullptr; } -Node* MaxNode::Identity(PhaseGVN* phase) { +Node* MinMaxNode::Identity(PhaseGVN* phase) { if (in(1) == in(2)) { return in(1); } - Node* identity_1 = MaxNode::find_identity_operation(in(2), in(1)); + Node* identity_1 = MinMaxNode::find_identity_operation(in(2), in(1)); if (identity_1 != nullptr) { return identity_1; } - Node* identity_2 = MaxNode::find_identity_operation(in(1), in(2)); + Node* identity_2 = MinMaxNode::find_identity_operation(in(1), in(2)); if (identity_2 != nullptr) { return identity_2; } diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 1bbdae92e48..4151ab5d065 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -324,14 +324,16 @@ public: //------------------------------MaxNode---------------------------------------- // Max (or min) of 2 values. Included with the ADD nodes because it inherits // all the behavior of addition on a ring. -class MaxNode : public AddNode { +class MinMaxNode : public AddNode { private: static Node* build_min_max(Node* a, Node* b, bool is_max, bool is_unsigned, const Type* t, PhaseGVN& gvn); static Node* build_min_max_diff_with_zero(Node* a, Node* b, bool is_max, const Type* t, PhaseGVN& gvn); Node* extract_add(PhaseGVN* phase, ConstAddOperands x_operands, ConstAddOperands y_operands); public: - MaxNode( Node *in1, Node *in2 ) : AddNode(in1,in2) {} + MinMaxNode(Node* in1, Node* in2) : AddNode(in1, in2) { + init_class_id(Class_MinMax); + } virtual int Opcode() const = 0; virtual int max_opcode() const = 0; virtual int min_opcode() const = 0; @@ -373,9 +375,9 @@ public: //------------------------------MaxINode--------------------------------------- // Maximum of 2 integers. Included with the ADD nodes because it inherits // all the behavior of addition on a ring. -class MaxINode : public MaxNode { +class MaxINode : public MinMaxNode { public: - MaxINode( Node *in1, Node *in2 ) : MaxNode(in1,in2) {} + MaxINode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type *add_ring( const Type *, const Type * ) const; virtual const Type *add_id() const { return TypeInt::make(min_jint); } @@ -390,9 +392,9 @@ public: //------------------------------MinINode--------------------------------------- // MINimum of 2 integers. Included with the ADD nodes because it inherits // all the behavior of addition on a ring. -class MinINode : public MaxNode { +class MinINode : public MinMaxNode { public: - MinINode( Node *in1, Node *in2 ) : MaxNode(in1,in2) {} + MinINode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type *add_ring( const Type *, const Type * ) const; virtual const Type *add_id() const { return TypeInt::make(max_jint); } @@ -406,9 +408,9 @@ public: //------------------------------MaxLNode--------------------------------------- // MAXimum of 2 longs. -class MaxLNode : public MaxNode { +class MaxLNode : public MinMaxNode { public: - MaxLNode(Compile* C, Node* in1, Node* in2) : MaxNode(in1, in2) { + MaxLNode(Compile* C, Node* in1, Node* in2) : MinMaxNode(in1, in2) { init_flags(Flag_is_macro); C->add_macro_node(this); } @@ -425,9 +427,9 @@ public: //------------------------------MinLNode--------------------------------------- // MINimum of 2 longs. -class MinLNode : public MaxNode { +class MinLNode : public MinMaxNode { public: - MinLNode(Compile* C, Node* in1, Node* in2) : MaxNode(in1, in2) { + MinLNode(Compile* C, Node* in1, Node* in2) : MinMaxNode(in1, in2) { init_flags(Flag_is_macro); C->add_macro_node(this); } @@ -444,9 +446,9 @@ public: //------------------------------MaxFNode--------------------------------------- // Maximum of 2 floats. -class MaxFNode : public MaxNode { +class MaxFNode : public MinMaxNode { public: - MaxFNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + MaxFNode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type *add_ring(const Type*, const Type*) const; virtual const Type *add_id() const { return TypeF::NEG_INF; } @@ -458,9 +460,9 @@ public: //------------------------------MinFNode--------------------------------------- // Minimum of 2 floats. -class MinFNode : public MaxNode { +class MinFNode : public MinMaxNode { public: - MinFNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + MinFNode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type *add_ring(const Type*, const Type*) const; virtual const Type *add_id() const { return TypeF::POS_INF; } @@ -472,9 +474,9 @@ public: //------------------------------MaxHFNode-------------------------------------- // Maximum of 2 half floats. -class MaxHFNode : public MaxNode { +class MaxHFNode : public MinMaxNode { public: - MaxHFNode(Node* in1, Node* in2) : MaxNode(in1, in2) {} + MaxHFNode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type* add_ring(const Type*, const Type*) const; virtual const Type* add_id() const { return TypeH::NEG_INF; } @@ -486,9 +488,9 @@ public: //------------------------------MinHFNode--------------------------------------- // Minimum of 2 half floats. -class MinHFNode : public MaxNode { +class MinHFNode : public MinMaxNode { public: - MinHFNode(Node* in1, Node* in2) : MaxNode(in1, in2) {} + MinHFNode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type* add_ring(const Type*, const Type*) const; virtual const Type* add_id() const { return TypeH::POS_INF; } @@ -500,9 +502,9 @@ public: //------------------------------MaxDNode--------------------------------------- // Maximum of 2 doubles. -class MaxDNode : public MaxNode { +class MaxDNode : public MinMaxNode { public: - MaxDNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + MaxDNode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type *add_ring(const Type*, const Type*) const; virtual const Type *add_id() const { return TypeD::NEG_INF; } @@ -514,9 +516,9 @@ public: //------------------------------MinDNode--------------------------------------- // Minimum of 2 doubles. -class MinDNode : public MaxNode { +class MinDNode : public MinMaxNode { public: - MinDNode(Node *in1, Node *in2) : MaxNode(in1, in2) {} + MinDNode(Node* in1, Node* in2) : MinMaxNode(in1, in2) {} virtual int Opcode() const; virtual const Type *add_ring(const Type*, const Type*) const; virtual const Type *add_id() const { return TypeD::POS_INF; } diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index dacc1a1a734..8dc34af9c19 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -979,9 +979,9 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { Node* inner_iters_max = nullptr; if (stride_con > 0) { - inner_iters_max = MaxNode::max_diff_with_zero(limit, outer_phi, TypeInteger::bottom(bt), _igvn); + inner_iters_max = MinMaxNode::max_diff_with_zero(limit, outer_phi, TypeInteger::bottom(bt), _igvn); } else { - inner_iters_max = MaxNode::max_diff_with_zero(outer_phi, limit, TypeInteger::bottom(bt), _igvn); + inner_iters_max = MinMaxNode::max_diff_with_zero(outer_phi, limit, TypeInteger::bottom(bt), _igvn); } Node* inner_iters_limit = _igvn.integercon(iters_limit, bt); @@ -989,7 +989,7 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { // Long.MIN_VALUE to Long.MAX_VALUE for instance). Use an unsigned // min. const TypeInteger* inner_iters_actual_range = TypeInteger::make(0, iters_limit, Type::WidenMin, bt); - Node* inner_iters_actual = MaxNode::unsigned_min(inner_iters_max, inner_iters_limit, inner_iters_actual_range, _igvn); + Node* inner_iters_actual = MinMaxNode::unsigned_min(inner_iters_max, inner_iters_limit, inner_iters_actual_range, _igvn); Node* inner_iters_actual_int; if (bt == T_LONG) { @@ -1618,7 +1618,7 @@ void PhaseIdealLoop::transform_long_range_checks(int stride_con, const Node_List Node* max_jint_plus_one_long = longcon((jlong)max_jint + 1); Node* max_range = new AddLNode(max_jint_plus_one_long, L); register_new_node(max_range, entry_control); - R = MaxNode::unsigned_min(R, max_range, TypeLong::POS, _igvn); + R = MinMaxNode::unsigned_min(R, max_range, TypeLong::POS, _igvn); set_subtree_ctrl(R, true); } @@ -1717,9 +1717,9 @@ void PhaseIdealLoop::transform_long_range_checks(int stride_con, const Node_List } Node* PhaseIdealLoop::clamp(Node* R, Node* L, Node* H) { - Node* min = MaxNode::signed_min(R, H, TypeLong::LONG, _igvn); + Node* min = MinMaxNode::signed_min(R, H, TypeLong::LONG, _igvn); set_subtree_ctrl(min, true); - Node* max = MaxNode::signed_max(L, min, TypeLong::LONG, _igvn); + Node* max = MinMaxNode::signed_max(L, min, TypeLong::LONG, _igvn); set_subtree_ctrl(max, true); return max; } @@ -3485,14 +3485,14 @@ void OuterStripMinedLoopNode::adjust_strip_mined_loop(PhaseIterGVN* igvn) { // the loop body to be run for LoopStripMiningIter. Node* max = nullptr; if (stride > 0) { - max = MaxNode::max_diff_with_zero(limit, iv_phi, TypeInt::INT, *igvn); + max = MinMaxNode::max_diff_with_zero(limit, iv_phi, TypeInt::INT, *igvn); } else { - max = MaxNode::max_diff_with_zero(iv_phi, limit, TypeInt::INT, *igvn); + max = MinMaxNode::max_diff_with_zero(iv_phi, limit, TypeInt::INT, *igvn); } // sub is positive and can be larger than the max signed int // value. Use an unsigned min. Node* const_iters = igvn->intcon(scaled_iters); - Node* min = MaxNode::unsigned_min(max, const_iters, TypeInt::make(0, scaled_iters, Type::WidenMin), *igvn); + Node* min = MinMaxNode::unsigned_min(max, const_iters, TypeInt::make(0, scaled_iters, Type::WidenMin), *igvn); // min is the number of iterations for the next inner loop execution: // unsigned_min(max(limit - iv_phi, 0), scaled_iters) if stride > 0 // unsigned_min(max(iv_phi - limit, 0), scaled_iters) if stride < 0 diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 80818a4ddc7..4df03714376 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2577,11 +2577,11 @@ void PhaseMacroExpand::eliminate_opaque_looplimit_macro_nodes() { // a CMoveL construct now. At least until here, the type could be computed // precisely. CMoveL is not so smart, but we can give it at least the best // type we know abouot n now. - Node* repl = MaxNode::signed_max(n->in(1), n->in(2), _igvn.type(n), _igvn); + Node* repl = MinMaxNode::signed_max(n->in(1), n->in(2), _igvn.type(n), _igvn); _igvn.replace_node(n, repl); success = true; } else if (n->Opcode() == Op_MinL) { - Node* repl = MaxNode::signed_min(n->in(1), n->in(2), _igvn.type(n), _igvn); + Node* repl = MinMaxNode::signed_min(n->in(1), n->in(2), _igvn.type(n), _igvn); _igvn.replace_node(n, repl); success = true; } diff --git a/src/hotspot/share/opto/movenode.cpp b/src/hotspot/share/opto/movenode.cpp index 66db1df339b..6b6becb434f 100644 --- a/src/hotspot/share/opto/movenode.cpp +++ b/src/hotspot/share/opto/movenode.cpp @@ -271,9 +271,9 @@ Node* CMoveNode::Ideal_minmax(PhaseGVN* phase, CMoveNode* cmove) { // Create the Min/Max node based on the type and kind if (cmp_op == Op_CmpL) { - return MaxNode::build_min_max_long(phase, cmp_l, cmp_r, is_max); + return MinMaxNode::build_min_max_long(phase, cmp_l, cmp_r, is_max); } else { - return MaxNode::build_min_max_int(cmp_l, cmp_r, is_max); + return MinMaxNode::build_min_max_int(cmp_l, cmp_r, is_max); } } diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 0adb2072100..f1d9785a746 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -130,6 +130,7 @@ class MemBarNode; class MemBarStoreStoreNode; class MemNode; class MergeMemNode; +class MinMaxNode; class MoveNode; class MulNode; class MultiNode; @@ -809,6 +810,7 @@ public: DEFINE_CLASS_ID(AddP, Node, 9) DEFINE_CLASS_ID(BoxLock, Node, 10) DEFINE_CLASS_ID(Add, Node, 11) + DEFINE_CLASS_ID(MinMax, Add, 0) DEFINE_CLASS_ID(Mul, Node, 12) DEFINE_CLASS_ID(ClearArray, Node, 14) DEFINE_CLASS_ID(Halt, Node, 15) @@ -986,6 +988,7 @@ public: DEFINE_CLASS_QUERY(MemBar) DEFINE_CLASS_QUERY(MemBarStoreStore) DEFINE_CLASS_QUERY(MergeMem) + DEFINE_CLASS_QUERY(MinMax) DEFINE_CLASS_QUERY(Move) DEFINE_CLASS_QUERY(Mul) DEFINE_CLASS_QUERY(Multi) diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index c4bdc5e8903..52badca8050 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2633,6 +2633,15 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ } } } + // Check for Max/Min(A, Max/Min(B, C)) where A == B or A == C + if (use->is_MinMax()) { + for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { + Node* u = use->fast_out(i2); + if (u->Opcode() == use->Opcode()) { + worklist.push(u); + } + } + } auto enqueue_init_mem_projs = [&](ProjNode* proj) { add_users_to_worklist0(proj, worklist); }; diff --git a/src/hotspot/share/opto/vectorization.cpp b/src/hotspot/share/opto/vectorization.cpp index 1755b0453eb..8e0ca927a16 100644 --- a/src/hotspot/share/opto/vectorization.cpp +++ b/src/hotspot/share/opto/vectorization.cpp @@ -1122,7 +1122,7 @@ Node* make_last(Node* initL, jint stride, Node* limitL, PhaseIdealLoop* phase) { Node* last = new AddLNode(initL, k_mul_stride); // Make sure that the last does not lie "before" init. - Node* last_clamped = MaxNode::build_min_max_long(&igvn, initL, last, stride > 0); + Node* last_clamped = MinMaxNode::build_min_max_long(&igvn, initL, last, stride > 0); phase->register_new_node_with_ctrl_of(diffL, initL); phase->register_new_node_with_ctrl_of(diffL_m1, initL); diff --git a/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java b/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java new file mode 100644 index 00000000000..d358359ff14 --- /dev/null +++ b/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2025 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8373134 + * @summary Verify that min/max add identity optimizations get applied correctly + * @modules java.base/jdk.internal.misc + * @modules jdk.incubator.vector + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.igvn; + +import compiler.lib.compile_framework.CompileFramework; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import compiler.lib.template_framework.library.CodeGenerationDataNameType; +import compiler.lib.template_framework.library.PrimitiveType; +import compiler.lib.template_framework.library.TestFrameworkClass; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; + +import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Template.scope; + +public class TestMinMaxIdentity { + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("compiler.igvn.templated.MinMaxIdentity", generate(comp)); + + // Compile the source file. + comp.compile("--add-modules=jdk.incubator.vector"); + + comp.invoke("compiler.igvn.templated.MinMaxIdentity", "main", new Object[] {new String[] { + "--add-modules=jdk.incubator.vector", + "--add-opens", "jdk.incubator.vector/jdk.incubator.vector=ALL-UNNAMED" + }}); + } + + private static String generate(CompileFramework comp) { + // Create a list to collect all tests. + List testTemplateTokens = new ArrayList<>(); + + Stream.of(MinMaxOp.values()) + .flatMap(MinMaxOp::generate) + .forEach(testTemplateTokens::add); + + Stream.of(Fp16MinMaxOp.values()) + .flatMap(Fp16MinMaxOp::generate) + .forEach(testTemplateTokens::add); + + // Create the test class, which runs all testTemplateTokens. + return TestFrameworkClass.render( + // package and class name. + "compiler.igvn.templated", "MinMaxIdentity", + // List of imports. + Set.of("jdk.incubator.vector.Float16"), + // classpath, so the Test VM has access to the compiled class files. + comp.getEscapedClassPathOfCompiledClasses(), + // The list of tests. + testTemplateTokens); + } + + enum MinMaxOp { + MIN_D("min", CodeGenerationDataNameType.doubles()), + MAX_D("max", CodeGenerationDataNameType.doubles()), + MIN_F("min", CodeGenerationDataNameType.floats()), + MAX_F("max", CodeGenerationDataNameType.floats()), + MIN_I("min", CodeGenerationDataNameType.ints()), + MAX_I("max", CodeGenerationDataNameType.ints()), + MIN_L("min", CodeGenerationDataNameType.longs()), + MAX_L("max", CodeGenerationDataNameType.longs()); + + final String functionName; + final PrimitiveType type; + + MinMaxOp(String functionName, PrimitiveType type) { + this.functionName = functionName; + this.type = type; + } + + Stream generate() { + return Stream.of(template("a", "b"), template("b", "a")). + map(Template.ZeroArgs::asToken); + } + + private Template.ZeroArgs template(String arg1, String arg2) { + return Template.make(() -> scope( + let("boxedTypeName", type.boxedTypeName()), + let("op", name()), + let("type", type.name()), + let("functionName", functionName), + let("arg1", arg1), + let("arg2", arg2), + """ + @Test + @IR(counts = {IRNode.#op, "= 1"}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION) + @Arguments(values = {Argument.NUMBER_42, Argument.NUMBER_42}) + public #type $test(#type #arg1, #type #arg2) { + int i; + for (i = -10; i < 1; i++) { + } + #type c = a * i; + return #boxedTypeName.#functionName(a, #boxedTypeName.#functionName(b, c)); + } + """ + )); + } + } + + enum Fp16MinMaxOp { + MAX_HF("max"), + MIN_HF("min"); + + final String functionName; + + Fp16MinMaxOp(String functionName) { + this.functionName = functionName; + } + + Stream generate() { + return Stream.of(template("a", "b"), template("b", "a")). + map(Template.ZeroArgs::asToken); + } + + private Template.ZeroArgs template(String arg1, String arg2) { + return Template.make(() -> scope( + let("op", name()), + let("functionName", functionName), + let("arg1", arg1), + let("arg2", arg2), + """ + @Setup + private static Object[] $setup() { + return new Object[] {Float16.valueOf(42), Float16.valueOf(42)}; + } + + @Test + @IR(counts = {IRNode.#op, "= 1"}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION, + applyIfCPUFeatureOr = {"avx512_fp16", "true", "zfh", "true"}) + @IR(counts = {IRNode.#op, "= 1"}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION, + applyIfCPUFeatureAnd = {"fphp", "true", "asimdhp", "true"}) + @Arguments(setup = "$setup") + public Float16 $test(Float16 #arg1, Float16 #arg2) { + int i; + for (i = -10; i < 1; i++) { + } + Float16 c = Float16.multiply(a, Float16.valueOf(i)); + return Float16.#functionName(a, Float16.#functionName(b, c)); + } + """ + )); + } + } +} From f6d26c6b32a3ea394cc9b7f6046cd9d7d635c568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Thu, 15 Jan 2026 07:50:52 +0000 Subject: [PATCH 109/204] 8354853: Clean up x86 registers after 32-bit x86 removal Reviewed-by: aph, shade, mchevalier --- src/hotspot/cpu/x86/register_x86.cpp | 8 +--- src/hotspot/cpu/x86/register_x86.hpp | 56 +++++++----------------- src/hotspot/cpu/x86/vmreg_x86.cpp | 4 +- src/hotspot/cpu/x86/vmreg_x86.hpp | 13 +----- src/hotspot/cpu/x86/vmreg_x86.inline.hpp | 4 +- 5 files changed, 23 insertions(+), 62 deletions(-) diff --git a/src/hotspot/cpu/x86/register_x86.cpp b/src/hotspot/cpu/x86/register_x86.cpp index e6083429344..188af9264a8 100644 --- a/src/hotspot/cpu/x86/register_x86.cpp +++ b/src/hotspot/cpu/x86/register_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 @@ -32,14 +32,10 @@ const KRegister::KRegisterImpl all_KRegisterImpls [KRegister::number_ const char * Register::RegisterImpl::name() const { static const char *const names[number_of_registers] = { -#ifdef _LP64 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31" -#else - "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" -#endif // _LP64 }; return is_valid() ? names[encoding()] : "noreg"; } @@ -54,11 +50,9 @@ const char* FloatRegister::FloatRegisterImpl::name() const { const char* XMMRegister::XMMRegisterImpl::name() const { static const char *const names[number_of_registers] = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7" -#ifdef _LP64 ,"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" ,"xmm16", "xmm17", "xmm18", "xmm19", "xmm20", "xmm21", "xmm22", "xmm23" ,"xmm24", "xmm25", "xmm26", "xmm27", "xmm28", "xmm29", "xmm30", "xmm31" -#endif // _LP64 }; return is_valid() ? names[encoding()] : "xnoreg"; } diff --git a/src/hotspot/cpu/x86/register_x86.hpp b/src/hotspot/cpu/x86/register_x86.hpp index 0a8ecb7be26..5e0326b9aad 100644 --- a/src/hotspot/cpu/x86/register_x86.hpp +++ b/src/hotspot/cpu/x86/register_x86.hpp @@ -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 @@ -34,7 +34,7 @@ class VMRegImpl; typedef VMRegImpl* VMReg; -// The implementation of integer registers for the x86/x64 architectures. +// The implementation of integer registers for the x64 architectures. class Register { private: int _encoding; @@ -44,11 +44,9 @@ private: public: inline friend constexpr Register as_Register(int encoding); - enum { - number_of_registers = LP64_ONLY( 32 ) NOT_LP64( 8 ), - number_of_byte_registers = LP64_ONLY( 32 ) NOT_LP64( 4 ), - max_slots_per_register = LP64_ONLY( 2 ) NOT_LP64( 1 ) - }; + static const int number_of_registers = 32; + static const int number_of_byte_registers = 32; + static const int max_slots_per_register = 2; class RegisterImpl: public AbstractRegisterImpl { friend class Register; @@ -79,11 +77,9 @@ public: // Actually available GP registers for use, depending on actual CPU capabilities and flags. static int available_gp_registers() { -#ifdef _LP64 if (!UseAPX) { return number_of_registers / 2; } -#endif // _LP64 return number_of_registers; } }; @@ -116,9 +112,8 @@ constexpr Register rsp = as_Register(4); constexpr Register rbp = as_Register(5); constexpr Register rsi = as_Register(6); constexpr Register rdi = as_Register(7); -#ifdef _LP64 -constexpr Register r8 = as_Register( 8); -constexpr Register r9 = as_Register( 9); +constexpr Register r8 = as_Register(8); +constexpr Register r9 = as_Register(9); constexpr Register r10 = as_Register(10); constexpr Register r11 = as_Register(11); constexpr Register r12 = as_Register(12); @@ -141,7 +136,6 @@ constexpr Register r28 = as_Register(28); constexpr Register r29 = as_Register(29); constexpr Register r30 = as_Register(30); constexpr Register r31 = as_Register(31); -#endif // _LP64 // The implementation of x87 floating point registers for the ia32 architecture. @@ -154,10 +148,8 @@ private: public: inline friend constexpr FloatRegister as_FloatRegister(int encoding); - enum { - number_of_registers = 8, - max_slots_per_register = 2 - }; + static const int number_of_registers = 8; + static const int max_slots_per_register = 2; class FloatRegisterImpl: public AbstractRegisterImpl { friend class FloatRegister; @@ -217,10 +209,8 @@ private: public: inline friend constexpr XMMRegister as_XMMRegister(int encoding); - enum { - number_of_registers = LP64_ONLY( 32 ) NOT_LP64( 8 ), - max_slots_per_register = LP64_ONLY( 16 ) NOT_LP64( 16 ) // 512-bit - }; + static const int number_of_registers = 32; + static const int max_slots_per_register = 16; // 512-bit class XMMRegisterImpl: public AbstractRegisterImpl { friend class XMMRegister; @@ -250,11 +240,9 @@ public: // Actually available XMM registers for use, depending on actual CPU capabilities and flags. static int available_xmm_registers() { -#ifdef _LP64 if (UseAVX < 3) { return number_of_registers / 2; } -#endif // _LP64 return number_of_registers; } }; @@ -287,7 +275,6 @@ constexpr XMMRegister xmm4 = as_XMMRegister( 4); constexpr XMMRegister xmm5 = as_XMMRegister( 5); constexpr XMMRegister xmm6 = as_XMMRegister( 6); constexpr XMMRegister xmm7 = as_XMMRegister( 7); -#ifdef _LP64 constexpr XMMRegister xmm8 = as_XMMRegister( 8); constexpr XMMRegister xmm9 = as_XMMRegister( 9); constexpr XMMRegister xmm10 = as_XMMRegister(10); @@ -312,7 +299,6 @@ constexpr XMMRegister xmm28 = as_XMMRegister(28); constexpr XMMRegister xmm29 = as_XMMRegister(29); constexpr XMMRegister xmm30 = as_XMMRegister(30); constexpr XMMRegister xmm31 = as_XMMRegister(31); -#endif // _LP64 // The implementation of AVX-512 opmask registers. @@ -394,25 +380,17 @@ constexpr KRegister k7 = as_KRegister(7); // Define a class that exports it. class ConcreteRegisterImpl : public AbstractRegisterImpl { public: - enum { - max_gpr = Register::number_of_registers * Register::max_slots_per_register, - max_fpr = max_gpr + FloatRegister::number_of_registers * FloatRegister::max_slots_per_register, - max_xmm = max_fpr + XMMRegister::number_of_registers * XMMRegister::max_slots_per_register, - max_kpr = max_xmm + KRegister::number_of_registers * KRegister::max_slots_per_register, + static const int max_gpr = Register::number_of_registers * Register::max_slots_per_register; + static const int max_fpr = max_gpr + FloatRegister::number_of_registers * FloatRegister::max_slots_per_register; + static const int max_xmm = max_fpr + XMMRegister::number_of_registers * XMMRegister::max_slots_per_register; + static const int max_kpr = max_xmm + KRegister::number_of_registers * KRegister::max_slots_per_register; // A big enough number for C2: all the registers plus flags // This number must be large enough to cover REG_COUNT (defined by c2) registers. // There is no requirement that any ordering here matches any ordering c2 gives // it's optoregs. - - // x86_32.ad defines additional dummy FILL0-FILL7 registers, in order to tally - // REG_COUNT (computed by ADLC based on the number of reg_defs seen in .ad files) - // with ConcreteRegisterImpl::number_of_registers additional count of 8 is being - // added for 32 bit jvm. - number_of_registers = max_kpr + // gpr/fpr/xmm/kpr - NOT_LP64( 8 + ) // FILL0-FILL7 in x86_32.ad - 1 // eflags - }; + static const int number_of_registers = max_kpr + // gpr/fpr/xmm/kpr + 1; // eflags }; template <> diff --git a/src/hotspot/cpu/x86/vmreg_x86.cpp b/src/hotspot/cpu/x86/vmreg_x86.cpp index 44aee56ef15..45269e69fcb 100644 --- a/src/hotspot/cpu/x86/vmreg_x86.cpp +++ b/src/hotspot/cpu/x86/vmreg_x86.cpp @@ -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 @@ -32,9 +32,7 @@ void VMRegImpl::set_regName() { int i; for (i = 0; i < ConcreteRegisterImpl::max_gpr ; ) { regName[i++] = reg->name(); -#ifdef AMD64 regName[i++] = reg->name(); -#endif // AMD64 reg = reg->successor(); } diff --git a/src/hotspot/cpu/x86/vmreg_x86.hpp b/src/hotspot/cpu/x86/vmreg_x86.hpp index 6f7c7fafb32..032ce654f2f 100644 --- a/src/hotspot/cpu/x86/vmreg_x86.hpp +++ b/src/hotspot/cpu/x86/vmreg_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2024, 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 @@ -52,14 +52,8 @@ inline bool is_KRegister() { } inline Register as_Register() { - - assert( is_Register(), "must be"); - // Yuk -#ifdef AMD64 + assert(is_Register(), "must be"); return ::as_Register(value() >> 1); -#else - return ::as_Register(value()); -#endif // AMD64 } inline FloatRegister as_FloatRegister() { @@ -82,9 +76,6 @@ inline KRegister as_KRegister() { inline bool is_concrete() { assert(is_reg(), "must be"); -#ifndef AMD64 - if (is_Register()) return true; -#endif // AMD64 // Do not use is_XMMRegister() here as it depends on the UseAVX setting. if (value() >= ConcreteRegisterImpl::max_fpr && value() < ConcreteRegisterImpl::max_xmm) { int base = value() - ConcreteRegisterImpl::max_fpr; diff --git a/src/hotspot/cpu/x86/vmreg_x86.inline.hpp b/src/hotspot/cpu/x86/vmreg_x86.inline.hpp index 1aeedc094fd..76d70900fe4 100644 --- a/src/hotspot/cpu/x86/vmreg_x86.inline.hpp +++ b/src/hotspot/cpu/x86/vmreg_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2021, 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 @@ -26,7 +26,7 @@ #define CPU_X86_VMREG_X86_INLINE_HPP inline VMReg Register::RegisterImpl::as_VMReg() const { - return VMRegImpl::as_VMReg(encoding() LP64_ONLY( << 1 )); + return VMRegImpl::as_VMReg(encoding() << 1); } inline VMReg FloatRegister::FloatRegisterImpl::as_VMReg() const { From bf0da3dd5c20410aceab8e6f7a7a31432d17b96d Mon Sep 17 00:00:00 2001 From: Stefan Karlsson Date: Thu, 15 Jan 2026 09:22:42 +0000 Subject: [PATCH 110/204] 8375040: Clearer names for non-metadata oop iterators in ObjArrayKlass Reviewed-by: tschatzl, kbarrett, aboldtch --- .../share/gc/g1/g1FullGCMarker.inline.hpp | 2 +- .../share/gc/g1/g1ParScanThreadState.cpp | 8 ++--- src/hotspot/share/gc/serial/serialFullGC.cpp | 2 +- .../gc/shenandoah/shenandoahMark.inline.hpp | 6 ++-- src/hotspot/share/gc/z/zHeapIterator.cpp | 2 +- src/hotspot/share/gc/z/zIterator.hpp | 2 +- src/hotspot/share/gc/z/zIterator.inline.hpp | 4 +-- src/hotspot/share/oops/objArrayKlass.hpp | 13 ++++--- .../share/oops/objArrayKlass.inline.hpp | 34 ++++++------------- src/hotspot/share/oops/objArrayOop.hpp | 4 +-- src/hotspot/share/oops/objArrayOop.inline.hpp | 10 ++++++ 11 files changed, 42 insertions(+), 45 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp b/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp index 6cbfe2674e8..398ef046bf5 100644 --- a/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp @@ -108,7 +108,7 @@ void G1FullGCMarker::follow_array_chunk(objArrayOop array, int index) { push_objarray(array, end_index); } - array->oop_iterate_range(mark_closure(), beg_index, end_index); + array->oop_iterate_elements_range(mark_closure(), beg_index, end_index); } inline void G1FullGCMarker::follow_object(oop obj) { diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index 80e5fd44fcd..e7b02ed68e7 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -238,9 +238,9 @@ void G1ParScanThreadState::do_partial_array(PartialArrayState* state, bool stole G1HeapRegionAttr dest_attr = _g1h->region_attr(to_array); G1SkipCardMarkSetter x(&_scanner, dest_attr.is_new_survivor()); // Process claimed task. - to_array->oop_iterate_range(&_scanner, - checked_cast(claim._start), - checked_cast(claim._end)); + to_array->oop_iterate_elements_range(&_scanner, + checked_cast(claim._start), + checked_cast(claim._end)); } MAYBE_INLINE_EVACUATION @@ -260,7 +260,7 @@ void G1ParScanThreadState::start_partial_objarray(oop from_obj, // Process the initial chunk. No need to process the type in the // klass, as it will already be handled by processing the built-in // module. - to_array->oop_iterate_range(&_scanner, 0, checked_cast(initial_chunk_size)); + to_array->oop_iterate_elements_range(&_scanner, 0, checked_cast(initial_chunk_size)); } MAYBE_INLINE_EVACUATION diff --git a/src/hotspot/share/gc/serial/serialFullGC.cpp b/src/hotspot/share/gc/serial/serialFullGC.cpp index 546084e38dd..0c8ca51fc99 100644 --- a/src/hotspot/share/gc/serial/serialFullGC.cpp +++ b/src/hotspot/share/gc/serial/serialFullGC.cpp @@ -412,7 +412,7 @@ void SerialFullGC::follow_array_chunk(objArrayOop array, int index) { const int stride = MIN2(len - beg_index, (int) ObjArrayMarkingStride); const int end_index = beg_index + stride; - array->oop_iterate_range(&mark_and_push_closure, beg_index, end_index); + array->oop_iterate_elements_range(&mark_and_push_closure, beg_index, end_index); if (end_index < len) { SerialFullGC::push_objarray(array, end_index); // Push the continuation. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index 0a95ee9f149..849459157b5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -167,7 +167,7 @@ inline void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, if (len <= (int) ObjArrayMarkingStride*2) { // A few slices only, process directly - array->oop_iterate_range(cl, 0, len); + array->oop_iterate_elements_range(cl, 0, len); } else { int bits = log2i_graceful(len); // Compensate for non-power-of-two arrays, cover the array in excess: @@ -216,7 +216,7 @@ inline void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, // Process the irregular tail, if present int from = last_idx; if (from < len) { - array->oop_iterate_range(cl, from, len); + array->oop_iterate_elements_range(cl, from, len); } } } @@ -248,7 +248,7 @@ inline void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, assert (0 < to && to <= len, "to is sane: %d/%d", to, len); #endif - array->oop_iterate_range(cl, from, to); + array->oop_iterate_elements_range(cl, from, to); } template diff --git a/src/hotspot/share/gc/z/zHeapIterator.cpp b/src/hotspot/share/gc/z/zHeapIterator.cpp index d6289178ea8..73ced211058 100644 --- a/src/hotspot/share/gc/z/zHeapIterator.cpp +++ b/src/hotspot/share/gc/z/zHeapIterator.cpp @@ -456,7 +456,7 @@ void ZHeapIterator::follow_array_chunk(const ZHeapIteratorContext& context, cons // Follow array chunk ZHeapIteratorOopClosure cl(this, context, obj); - ZIterator::oop_iterate_range(obj, &cl, start, end); + ZIterator::oop_iterate_elements_range(obj, &cl, start, end); } template diff --git a/src/hotspot/share/gc/z/zIterator.hpp b/src/hotspot/share/gc/z/zIterator.hpp index 3a1de049dd0..e048002e52e 100644 --- a/src/hotspot/share/gc/z/zIterator.hpp +++ b/src/hotspot/share/gc/z/zIterator.hpp @@ -41,7 +41,7 @@ public: static void oop_iterate(oop obj, OopClosureT* cl); template - static void oop_iterate_range(objArrayOop obj, OopClosureT* cl, int start, int end); + static void oop_iterate_elements_range(objArrayOop obj, OopClosureT* cl, int start, int end); // This function skips invisible roots template diff --git a/src/hotspot/share/gc/z/zIterator.inline.hpp b/src/hotspot/share/gc/z/zIterator.inline.hpp index fb20a424288..cbfe1a79aaf 100644 --- a/src/hotspot/share/gc/z/zIterator.inline.hpp +++ b/src/hotspot/share/gc/z/zIterator.inline.hpp @@ -66,9 +66,9 @@ void ZIterator::oop_iterate(oop obj, OopClosureT* cl) { } template -void ZIterator::oop_iterate_range(objArrayOop obj, OopClosureT* cl, int start, int end) { +void ZIterator::oop_iterate_elements_range(objArrayOop obj, OopClosureT* cl, int start, int end) { assert(!is_invisible_object_array(obj), "not safe"); - obj->oop_iterate_range(cl, start, end); + obj->oop_iterate_elements_range(cl, start, end); } template diff --git a/src/hotspot/share/oops/objArrayKlass.hpp b/src/hotspot/share/oops/objArrayKlass.hpp index 12db56351ed..cdf28b883e6 100644 --- a/src/hotspot/share/oops/objArrayKlass.hpp +++ b/src/hotspot/share/oops/objArrayKlass.hpp @@ -135,17 +135,16 @@ class ObjArrayKlass : public ArrayKlass { template inline void oop_oop_iterate_bounded(oop obj, OopClosureType* closure, MemRegion mr); - // Iterate over oop elements within [start, end), and metadata. - template - inline void oop_oop_iterate_range(objArrayOop a, OopClosureType* closure, int start, int end); - - public: - // Iterate over all oop elements. + // Iterate over all oop elements, and no metadata. template inline void oop_oop_iterate_elements(objArrayOop a, OopClosureType* closure); + // Iterate over oop elements within index range [start, end), and no metadata. + template + inline void oop_oop_iterate_elements_range(objArrayOop a, OopClosureType* closure, int start, int end); + private: - // Iterate over all oop elements with indices within mr. + // Iterate over all oop elements bounded by addresses [low, high), and no metadata. template inline void oop_oop_iterate_elements_bounded(objArrayOop a, OopClosureType* closure, void* low, void* high); diff --git a/src/hotspot/share/oops/objArrayKlass.inline.hpp b/src/hotspot/share/oops/objArrayKlass.inline.hpp index a92c42d21a8..ee559548529 100644 --- a/src/hotspot/share/oops/objArrayKlass.inline.hpp +++ b/src/hotspot/share/oops/objArrayKlass.inline.hpp @@ -38,10 +38,18 @@ template void ObjArrayKlass::oop_oop_iterate_elements(objArrayOop a, OopClosureType* closure) { - T* p = (T*)a->base(); - T* const end = p + a->length(); + oop_oop_iterate_elements_range(a, closure, 0, a->length()); +} - for (;p < end; p++) { +// Like oop_oop_iterate but only iterates over a specified range and only used +// for objArrayOops. +template +void ObjArrayKlass::oop_oop_iterate_elements_range(objArrayOop a, OopClosureType* closure, int start, int end) { + T* base = (T*)a->base(); + T* p = base + start; + T* const end_p = base + end; + + for (;p < end_p; ++p) { Devirtualizer::do_oop(closure, p); } } @@ -98,24 +106,4 @@ void ObjArrayKlass::oop_oop_iterate_bounded(oop obj, OopClosureType* closure, Me oop_oop_iterate_elements_bounded(a, closure, mr.start(), mr.end()); } -// Like oop_oop_iterate but only iterates over a specified range and only used -// for objArrayOops. -template -void ObjArrayKlass::oop_oop_iterate_range(objArrayOop a, OopClosureType* closure, int start, int end) { - T* low = (T*)a->base() + start; - T* high = (T*)a->base() + end; - - oop_oop_iterate_elements_bounded(a, closure, low, high); -} - -// Placed here to resolve include cycle between objArrayKlass.inline.hpp and objArrayOop.inline.hpp -template -void objArrayOopDesc::oop_iterate_range(OopClosureType* blk, int start, int end) { - if (UseCompressedOops) { - ((ObjArrayKlass*)klass())->oop_oop_iterate_range(this, blk, start, end); - } else { - ((ObjArrayKlass*)klass())->oop_oop_iterate_range(this, blk, start, end); - } -} - #endif // SHARE_OOPS_OBJARRAYKLASS_INLINE_HPP diff --git a/src/hotspot/share/oops/objArrayOop.hpp b/src/hotspot/share/oops/objArrayOop.hpp index 82bb41b5fd1..0af059efccf 100644 --- a/src/hotspot/share/oops/objArrayOop.hpp +++ b/src/hotspot/share/oops/objArrayOop.hpp @@ -83,9 +83,9 @@ class objArrayOopDesc : public arrayOopDesc { Klass* element_klass(); public: - // special iterators for index ranges, returns size of object + // Special iterators for an element index range. template - void oop_iterate_range(OopClosureType* blk, int start, int end); + void oop_iterate_elements_range(OopClosureType* blk, int start, int end); }; // See similar requirement for oopDesc. diff --git a/src/hotspot/share/oops/objArrayOop.inline.hpp b/src/hotspot/share/oops/objArrayOop.inline.hpp index 21f95b756de..63295a1459d 100644 --- a/src/hotspot/share/oops/objArrayOop.inline.hpp +++ b/src/hotspot/share/oops/objArrayOop.inline.hpp @@ -29,6 +29,7 @@ #include "oops/access.hpp" #include "oops/arrayOop.hpp" +#include "oops/objArrayKlass.inline.hpp" #include "oops/oop.inline.hpp" #include "runtime/globals.hpp" @@ -51,4 +52,13 @@ inline void objArrayOopDesc::obj_at_put(int index, oop value) { HeapAccess::oop_store_at(as_oop(), offset, value); } +template +void objArrayOopDesc::oop_iterate_elements_range(OopClosureType* blk, int start, int end) { + if (UseCompressedOops) { + ((ObjArrayKlass*)klass())->oop_oop_iterate_elements_range(this, blk, start, end); + } else { + ((ObjArrayKlass*)klass())->oop_oop_iterate_elements_range(this, blk, start, end); + } +} + #endif // SHARE_OOPS_OBJARRAYOOP_INLINE_HPP From f6e5c885e7ca90da2f9fd9ec1c00b4a955ccdf29 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Thu, 15 Jan 2026 11:16:00 +0000 Subject: [PATCH 111/204] 8375282: G1: Fix wrong indendation introduced by JDK-8374743 Reviewed-by: kbarrett --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 2ad5a26d5e6..3a0c4a04441 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -687,8 +687,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) { // before the allocation is that we avoid having to keep track of the newly // allocated memory while we do a GC. // Only try that if we can actually perform a GC. - if (is_init_completed() && policy()->need_to_start_conc_mark("concurrent humongous allocation", - word_size)) { + if (is_init_completed() && + policy()->need_to_start_conc_mark("concurrent humongous allocation", word_size)) { try_collect(word_size, GCCause::_g1_humongous_allocation, collection_counters(this)); } From 8ad8920aae5c27de947532ba3cd2b57213208d1e Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Thu, 15 Jan 2026 12:37:50 +0000 Subject: [PATCH 112/204] 8374984: Convert workerUtils to use Atomic Reviewed-by: shade, stefank --- src/hotspot/share/gc/shared/workerUtils.cpp | 25 +++++++++++---------- src/hotspot/share/gc/shared/workerUtils.hpp | 12 +++++----- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/hotspot/share/gc/shared/workerUtils.cpp b/src/hotspot/share/gc/shared/workerUtils.cpp index 422d513a5cd..1826b9d7df8 100644 --- a/src/hotspot/share/gc/shared/workerUtils.cpp +++ b/src/hotspot/share/gc/shared/workerUtils.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 @@ -22,8 +22,9 @@ * */ +#include "cppstdlib/new.hpp" #include "gc/shared/workerUtils.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutexLocker.hpp" // *** WorkerThreadsBarrierSync @@ -80,21 +81,21 @@ void WorkerThreadsBarrierSync::abort() { SubTasksDone::SubTasksDone(uint n) : _tasks(nullptr), _n_tasks(n) { - _tasks = NEW_C_HEAP_ARRAY(bool, n, mtInternal); + _tasks = NEW_C_HEAP_ARRAY(Atomic, n, mtInternal); for (uint i = 0; i < _n_tasks; i++) { - _tasks[i] = false; + ::new (&_tasks[i]) Atomic(false); } } #ifdef ASSERT void SubTasksDone::all_tasks_claimed_impl(uint skipped[], size_t skipped_size) { - if (AtomicAccess::cmpxchg(&_verification_done, false, true)) { + if (!_verification_done.compare_set(false, true)) { // another thread has done the verification return; } // all non-skipped tasks are claimed for (uint i = 0; i < _n_tasks; ++i) { - if (!_tasks[i]) { + if (!_tasks[i].load_relaxed()) { auto is_skipped = false; for (size_t j = 0; j < skipped_size; ++j) { if (i == skipped[j]) { @@ -109,27 +110,27 @@ void SubTasksDone::all_tasks_claimed_impl(uint skipped[], size_t skipped_size) { for (size_t i = 0; i < skipped_size; ++i) { auto task_index = skipped[i]; assert(task_index < _n_tasks, "Array in range."); - assert(!_tasks[task_index], "%d is both claimed and skipped.", task_index); + assert(!_tasks[task_index].load_relaxed(), "%d is both claimed and skipped.", task_index); } } #endif bool SubTasksDone::try_claim_task(uint t) { assert(t < _n_tasks, "bad task id."); - return !_tasks[t] && !AtomicAccess::cmpxchg(&_tasks[t], false, true); + return !_tasks[t].load_relaxed() && _tasks[t].compare_set(false, true); } SubTasksDone::~SubTasksDone() { - assert(_verification_done, "all_tasks_claimed must have been called."); - FREE_C_HEAP_ARRAY(bool, _tasks); + assert(_verification_done.load_relaxed(), "all_tasks_claimed must have been called."); + FREE_C_HEAP_ARRAY(Atomic, _tasks); } // *** SequentialSubTasksDone bool SequentialSubTasksDone::try_claim_task(uint& t) { - t = _num_claimed; + t = _num_claimed.load_relaxed(); if (t < _num_tasks) { - t = AtomicAccess::add(&_num_claimed, 1u) - 1; + t = _num_claimed.fetch_then_add(1u); } return t < _num_tasks; } diff --git a/src/hotspot/share/gc/shared/workerUtils.hpp b/src/hotspot/share/gc/shared/workerUtils.hpp index 5f771a57837..7bf9d7d7656 100644 --- a/src/hotspot/share/gc/shared/workerUtils.hpp +++ b/src/hotspot/share/gc/shared/workerUtils.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. * 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 @@ #include "cppstdlib/type_traits.hpp" #include "memory/allocation.hpp" #include "metaprogramming/enableIf.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutex.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -79,11 +80,11 @@ public: // enumeration type. class SubTasksDone: public CHeapObj { - volatile bool* _tasks; + Atomic* _tasks; uint _n_tasks; // make sure verification logic is run exactly once to avoid duplicate assertion failures - DEBUG_ONLY(volatile bool _verification_done = false;) + DEBUG_ONLY(Atomic _verification_done;) void all_tasks_claimed_impl(uint skipped[], size_t skipped_size) NOT_DEBUG_RETURN; NONCOPYABLE(SubTasksDone); @@ -127,7 +128,7 @@ public: class SequentialSubTasksDone : public CHeapObj { uint _num_tasks; // Total number of tasks available. - volatile uint _num_claimed; // Number of tasks claimed. + Atomic _num_claimed; // Number of tasks claimed. NONCOPYABLE(SequentialSubTasksDone); @@ -135,7 +136,8 @@ public: SequentialSubTasksDone(uint num_tasks) : _num_tasks(num_tasks), _num_claimed(0) { } ~SequentialSubTasksDone() { // Claiming may try to claim more tasks than there are. - assert(_num_claimed >= _num_tasks, "Claimed %u tasks of %u", _num_claimed, _num_tasks); + assert(_num_claimed.load_relaxed() >= _num_tasks, + "Claimed %u tasks of %u", _num_claimed.load_relaxed(), _num_tasks); } // Attempt to claim the next unclaimed task in the sequence, From 78a106ffbba0e056e7421ca9d77af02f9b8379d3 Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Thu, 15 Jan 2026 13:18:20 +0000 Subject: [PATCH 113/204] 8375183: Remove unused SSLConfiguration.maximumProtocolVersion variable Reviewed-by: djelinski, myankelevich, hchao --- .../sun/security/ssl/SSLConfiguration.java | 19 +------------------ .../sun/security/ssl/TransportContext.java | 3 +-- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java index ace60e41af9..3c68c669d05 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.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 @@ -64,9 +64,6 @@ final class SSLConfiguration implements Cloneable { // the configured named groups for the "supported_groups" extensions String[] namedGroups; - // the maximum protocol version of enabled protocols - ProtocolVersion maximumProtocolVersion; - // Configurations per SSLSocket or SSLEngine instance. boolean isClientMode; boolean enableSessionCreation; @@ -249,13 +246,6 @@ final class SSLConfiguration implements Cloneable { CustomizedServerSignatureSchemes.signatureSchemes : SupportedSigSchemes.DEFAULT; this.namedGroups = NamedGroup.SupportedGroups.namedGroups; - this.maximumProtocolVersion = ProtocolVersion.NONE; - for (ProtocolVersion pv : enabledProtocols) { - if (pv.compareTo(maximumProtocolVersion) > 0) { - this.maximumProtocolVersion = pv; - } - } - // Configurations per SSLSocket or SSLEngine instance. this.isClientMode = isClientMode; this.enableSessionCreation = true; @@ -323,13 +313,6 @@ final class SSLConfiguration implements Cloneable { sa = params.getProtocols(); if (sa != null) { this.enabledProtocols = ProtocolVersion.namesOf(sa); - - this.maximumProtocolVersion = ProtocolVersion.NONE; - for (ProtocolVersion pv : enabledProtocols) { - if (pv.compareTo(maximumProtocolVersion) > 0) { - this.maximumProtocolVersion = pv; - } - } } // otherwise, use the default values if (params.getNeedClientAuth()) { diff --git a/src/java.base/share/classes/sun/security/ssl/TransportContext.java b/src/java.base/share/classes/sun/security/ssl/TransportContext.java index 980d9c4a6ce..35bdd2fff36 100644 --- a/src/java.base/share/classes/sun/security/ssl/TransportContext.java +++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.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 @@ -144,7 +144,6 @@ final class TransportContext implements ConnectionContext { // initial security parameters this.conSession = new SSLSessionImpl(); - this.protocolVersion = this.sslConfig.maximumProtocolVersion; this.clientVerifyData = emptyByteArray; this.serverVerifyData = emptyByteArray; From 203eb70110dd546784e03243bf98ff3ddb407030 Mon Sep 17 00:00:00 2001 From: Roger Riggs Date: Thu, 15 Jan 2026 15:54:11 +0000 Subject: [PATCH 114/204] 8291986: ProcessBuilder.redirectErrorStream(true) leaves error stream available Reviewed-by: jpai --- .../unix/native/libjava/ProcessImpl_md.c | 13 +- .../lang/ProcessBuilder/PipelineLeaksFD.java | 263 +++++++++++++++--- .../java/lang/ProcessBuilder/TEST.properties | 1 + 3 files changed, 230 insertions(+), 47 deletions(-) create mode 100644 test/jdk/java/lang/ProcessBuilder/TEST.properties diff --git a/src/java.base/unix/native/libjava/ProcessImpl_md.c b/src/java.base/unix/native/libjava/ProcessImpl_md.c index f7531ad5abe..12597fbb650 100644 --- a/src/java.base/unix/native/libjava/ProcessImpl_md.c +++ b/src/java.base/unix/native/libjava/ProcessImpl_md.c @@ -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 @@ -729,12 +729,13 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, if ((fds[0] == -1 && pipe(in) < 0) || (fds[1] == -1 && pipe(out) < 0) || - (fds[2] == -1 && pipe(err) < 0) || + (fds[2] == -1 && !redirectErrorStream && pipe(err) < 0) || // if not redirecting create the pipe (pipe(childenv) < 0) || (pipe(fail) < 0)) { throwInternalIOException(env, errno, "Bad file descriptor", mode); goto Catch; } + c->fds[0] = fds[0]; c->fds[1] = fds[1]; c->fds[2] = fds[2]; @@ -764,17 +765,19 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, assert(resultPid != 0); if (resultPid < 0) { + char * failMessage = "unknown"; switch (c->mode) { case MODE_VFORK: - throwInternalIOException(env, errno, "vfork failed", c->mode); + failMessage = "vfork failed"; break; case MODE_FORK: - throwInternalIOException(env, errno, "fork failed", c->mode); + failMessage = "fork failed"; break; case MODE_POSIX_SPAWN: - throwInternalIOException(env, errno, "posix_spawn failed", c->mode); + failMessage = "posix_spawn failed"; break; } + throwInternalIOException(env, errno, failMessage, c->mode); goto Catch; } close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec (childproc.c) */ diff --git a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java index d3c44bc9279..2b4cdfa9bdd 100644 --- a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java +++ b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.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 @@ -21,32 +21,42 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +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 java.io.BufferedReader; -import java.io.File; 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.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; /* * @test - * @bug 8289643 8291760 - * @requires (os.family == "linux" & !vm.musl) + * @bug 8289643 8291760 8291986 + * @requires os.family == "mac" | (os.family == "linux" & !vm.musl) * @summary File descriptor leak detection with ProcessBuilder.startPipeline - * @run testng/othervm PipelineLeaksFD + * @run junit/othervm PipelineLeaksFD */ -@Test public class PipelineLeaksFD { - @DataProvider - public Object[][] builders() { + + private static final String OS_NAME = System.getProperty("os.name", "Unknown"); + + private static final long MY_PID = ProcessHandle.current().pid(); + + private static final boolean LSOF_AVAILABLE = checkForLSOF(); + + // Test cases for pipelines with a number of pipeline sequences + public static Object[][] builders() { return new Object[][]{ {List.of(new ProcessBuilder("cat"))}, {List.of(new ProcessBuilder("cat"), @@ -59,20 +69,43 @@ public class PipelineLeaksFD { }; } - @Test(dataProvider = "builders") + // 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 { + lsofForAll(); + return true; + } catch (IOException ioe) { + System.err.println("Skipping: " + ioe); + return false; + } + } + + @EnabledIf("lsofAvailable") + @ParameterizedTest + @MethodSource("builders") void checkForLeaks(List builders) throws IOException { - Set pipesBefore = myPipes(); + List lsofLines = lsofForAll(); + Set pipesBefore = pipesFromLSOF(lsofLines, MY_PID); if (pipesBefore.size() < 3) { - System.out.println(pipesBefore); - Assert.fail("There should be at least 3 pipes before, (0, 1, 2)"); + // 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)"); } + printPipes(pipesBefore, "Before start"); List processes = ProcessBuilder.startPipeline(builders); // Write something through the pipeline final String text = "xyz"; - try (Writer out = processes.get(0).outputWriter()) { + try (Writer out = processes.getFirst().outputWriter()) { out.write(text); } @@ -84,16 +117,17 @@ public class PipelineLeaksFD { try (BufferedReader inputStream = p.inputReader(); BufferedReader errorStream = p.errorReader()) { String outActual = inputStream.readLine(); - Assert.assertEquals(outActual, expectedOut, "stdout, process[ " + i + "]: " + p); + assertEquals(expectedOut, outActual, "stdout, process[ " + i + "]: " + p); String errActual = errorStream.readLine(); - Assert.assertEquals(errActual, expectedErr, "stderr, process[ " + i + "]: " + p); + assertEquals(expectedErr, errActual, "stderr, process[ " + i + "]: " + p); } } - processes.forEach(p -> waitForQuiet(p)); + processes.forEach(PipelineLeaksFD::waitForQuiet); - Set pipesAfter = myPipes(); + lsofLines = lsofForAll(); + Set pipesAfter = pipesFromLSOF(lsofLines, MY_PID); if (!pipesBefore.equals(pipesAfter)) { Set missing = new HashSet<>(pipesBefore); missing.removeAll(pipesAfter); @@ -101,46 +135,191 @@ public class PipelineLeaksFD { Set extra = new HashSet<>(pipesAfter); extra.removeAll(pipesBefore); printPipes(extra, "Extra pipes in pipesAfter"); - Assert.fail("More or fewer pipes than expected"); + // Dump all lsof output to aid debugging + System.out.println("\nOutput from lsof"); + lsofLines.forEach(System.err::println); + fail("More or fewer pipes than expected"); + } + } + + // Test redirectErrorStream, both true and false + public static Object[][] redirectCases() { + return new Object[][] { + {true}, + {false}, + }; + } + + // Test redirectErrorStream (true/false) has the right number of pipes in use + @EnabledIf("lsofAvailable") + @ParameterizedTest() + @MethodSource("redirectCases") + void checkRedirectErrorStream(boolean redirectError) throws IOException { + try (Process p = new ProcessBuilder("cat") + .redirectErrorStream(redirectError) + .start()) { + System.err.printf("Parent PID; %d, Child Pid: %d\n", MY_PID, p.pid()); + List lsofLines = lsofForAll(); + final Set pipes = pipesFromLSOF(lsofLines, p.pid()); + printPipes(pipes, "Parent and waiting child pipes"); + int uniquePipes = redirectError ? 8 : 9; + if (uniquePipes != pipes.size()) { + // Dump all lsof output to aid debugging + System.out.println("\nOutput from lsof"); + lsofLines.forEach(System.err::println); + } + assertEquals(uniquePipes, pipes.size(), + "wrong number of pipes for redirect: " + redirectError); + String expectedTypeName = redirectError + ? "java.lang.ProcessBuilder$NullInputStream" + : "java.lang.ProcessImpl$ProcessPipeInputStream"; + assertEquals(expectedTypeName, p.getErrorStream().getClass().getName(), + "errorStream type is incorrect"); + } catch (IOException ioe) { + fail("Process start", ioe); } } static void printPipes(Set pipes, String label) { - System.out.printf("%s: [%d]%n", label, pipes.size()); - pipes.forEach(r -> System.out.printf("%-20s: %s%n", r.fd(), r.link())); + System.err.printf("%s: [%d]%n", label, pipes.size()); + pipes.forEach(System.err::println); } static void waitForQuiet(Process p) { try { int st = p.waitFor(); if (st != 0) { - System.out.println("non-zero exit status: " + p); + System.err.println("non-zero exit status: " + p); } } catch (InterruptedException ie) { } } /** - * Collect a Set of pairs of /proc fd paths and the symbol links that are pipes. + * Collect a Set of file descriptors and identifying information. + * To identify the pipes in use the `lsof` command is invoked and output scrapped for + * fd's, pids, unique identities of the pipes (to match with parent). * @return A set of PipeRecords, possibly empty */ - static Set myPipes() { - Path path = Path.of("/proc/" + ProcessHandle.current().pid() + "/fd"); - Set pipes = new HashSet<>(); - File[] files = path.toFile().listFiles(f -> Files.isSymbolicLink(f.toPath())); - if (files != null) { - for (File file : files) { - try { - Path link = Files.readSymbolicLink(file.toPath()); - if (link.toString().startsWith("pipe:")) { - pipes.add(new PipeRecord(file.toPath(), link)); - } - } catch (IOException ioe) { - } - } - } - return pipes; + private static Set pipesForPid(long pid) throws IOException { + var lines = lsofForAll(); + return pipesFromLSOF(lines, pid); } - record PipeRecord(Path fd, Path link) { }; + /** + * Extract the PipeRecords from the `lsof` output for this process and a designated child process. + * + * @param lines lines of lsof output + * @param pid pid of child process of interest + * @return a Set of PipeRecords for parent and child + */ + private static LinkedHashSet pipesFromLSOF(List lines, long pid) { + return lines.stream() + .map(PipelineLeaksFD::pipeFromLSOF) + .filter(pr -> pr != null && + (pr.pid() == pid || pr.pid() == MY_PID)) + .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. + * @return a List of lines output from `lsof`. + */ + private static List lsofForAll() throws IOException { + Path tmpDir = Path.of("."); + String tmpPrefix = "lsof-"; + Path lsofEmptyInput = Files.createTempFile(tmpDir, tmpPrefix, ".empty"); + Path lsofOutput = Files.createTempFile(tmpDir, tmpPrefix, ".tmp"); + try (Process p = new ProcessBuilder("lsof") + .redirectOutput(lsofOutput.toFile()) + .redirectInput(lsofEmptyInput.toFile()) // empty input + .redirectError(ProcessBuilder.Redirect.DISCARD) // ignored output + .start()) { + int status = p.waitFor(); + assertEquals(0, 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 rriggs 0 PIPE 0xc76402237956a5cb 16384 ->0xfcb0c07ae447908c + // lsof 55221 rriggs 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 rriggs 14w FIFO 0,12 0t0 117662267 pipe + // java 7612 rriggs 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; + } + + // 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)); + } + } + + // A unique name for a string with a count of uses + // Used to associate pipe between parent and child. + static class KeyedString { + private static final HashMap map = new HashMap<>(); + private static int nextInt = 1; + private final String key; + private final String name; + private int count; + KeyedString(String key, String name) { + this.key = key; + this.name = name; + this.count = 0; + } + + KeyedString(String s) { + String k = "p" + nextInt++; + this(s, k); + } + + static KeyedString getKey(String key, String otherKey) { + var k = map.computeIfAbsent(key, KeyedString::new); + k.count++; + if (otherKey != null) { + map.putIfAbsent(otherKey, k); + } + return k; + } + + public String toString() { + return name + "(" + count + ")"; + } + } } diff --git a/test/jdk/java/lang/ProcessBuilder/TEST.properties b/test/jdk/java/lang/ProcessBuilder/TEST.properties new file mode 100644 index 00000000000..c7e8a6850ca --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/TEST.properties @@ -0,0 +1 @@ +maxOutputSize=6000000 From ee0387be4c562c7f7ad5240f412d4d5363358855 Mon Sep 17 00:00:00 2001 From: Roger Calnan Date: Thu, 15 Jan 2026 17:08:49 +0000 Subject: [PATCH 115/204] 8375342: jdk/javadoc/doccheck/checks/jdkCheckHtml.java failed with duplicate anchors Reviewed-by: alanb, iris --- src/java.base/share/man/java.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/man/java.md b/src/java.base/share/man/java.md index 4343df663e6..956a6aa144b 100644 --- a/src/java.base/share/man/java.md +++ b/src/java.base/share/man/java.md @@ -3283,7 +3283,7 @@ Flags to Xlog]. The following provides quick reference to the `-Xlog` command and syntax for options: -[`-Xlog`]{#-Xlog} +`-Xlog` : Enables JVM logging on an `info` level. `-Xlog:help` From 34705a77f9a90da5ab2a440c11d79aef7bb3ba54 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Thu, 15 Jan 2026 17:38:46 +0000 Subject: [PATCH 116/204] 8375231: Refactor util/ServiceLoader tests to use JUnit 8375232: Refactor util/StringJoiner tests to use JUnit 8375233: Refactor util/Vector tests to use JUnit Reviewed-by: naoto, alanb --- .../util/ServiceLoader/BadProvidersTest.java | 79 +++----- .../java/util/ServiceLoader/CachingTest.java | 18 +- .../java/util/ServiceLoader/ModulesTest.java | 42 ++-- .../ServiceLoader/NoInterferenceTest.java | 14 +- .../java/util/ServiceLoader/ReloadTest.java | 31 +-- .../java/util/ServiceLoader/TwoIterators.java | 19 +- .../basic/ServiceLoaderBasicTest.java | 40 ++-- .../jdk/java/util/StringJoiner/MergeTest.java | 55 +++--- .../StringJoinerOomUtf16Test.java | 19 +- .../util/StringJoiner/StringJoinerTest.java | 182 ++++++++++-------- .../jdk/java/util/Vector/ArrayManagement.java | 81 ++++---- 11 files changed, 304 insertions(+), 276 deletions(-) diff --git a/test/jdk/java/util/ServiceLoader/BadProvidersTest.java b/test/jdk/java/util/ServiceLoader/BadProvidersTest.java index 8560126eb00..0a734a2b214 100644 --- a/test/jdk/java/util/ServiceLoader/BadProvidersTest.java +++ b/test/jdk/java/util/ServiceLoader/BadProvidersTest.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 @@ -26,7 +26,7 @@ * @library /test/lib * @modules jdk.compiler * @build jdk.test.lib.compiler.CompilerUtils - * @run testng/othervm BadProvidersTest + * @run junit/othervm BadProvidersTest * @summary Basic test of ServiceLoader with bad provider and bad provider * factories deployed on the module path */ @@ -50,8 +50,6 @@ import java.util.stream.Collectors; import jdk.test.lib.compiler.CompilerUtils; -import org.testng.annotations.Test; -import org.testng.annotations.DataProvider; import static java.lang.classfile.ClassFile.ACC_PUBLIC; import static java.lang.classfile.ClassFile.ACC_STATIC; @@ -59,13 +57,16 @@ import static java.lang.constant.ConstantDescs.CD_Object; import static java.lang.constant.ConstantDescs.INIT_NAME; import static java.lang.constant.ConstantDescs.MTD_void; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; /** * Basic test of `provides S with PF` and `provides S with P` where the provider * factory or provider */ - public class BadProvidersTest { private static final String TEST_SRC = System.getProperty("test.src"); @@ -137,22 +138,10 @@ public class BadProvidersTest { assertTrue(p != null); } - - @DataProvider(name = "badfactories") - public Object[][] createBadFactories() { - return new Object[][] { - { "classnotpublic", null }, - { "methodnotpublic", null }, - { "badreturntype", null }, - { "returnsnull", null }, - { "throwsexception", null }, - }; - } - - - @Test(dataProvider = "badfactories", - expectedExceptions = ServiceConfigurationError.class) - public void testBadFactory(String testName, String ignore) throws Exception { + @ParameterizedTest + @ValueSource(strings = { "classnotpublic", "methodnotpublic", "badreturntype", + "returnsnull", "throwsexception" }) + public void testBadFactory(String testName) throws Exception { Path mods = compileTest(TEST1_MODULE); // compile the bad factory @@ -164,28 +153,18 @@ public class BadProvidersTest { // copy the compiled class into the module Path classFile = Paths.get("p", "ProviderFactory.class"); Files.copy(output.resolve(classFile), - mods.resolve(TEST1_MODULE).resolve(classFile), - StandardCopyOption.REPLACE_EXISTING); + mods.resolve(TEST1_MODULE).resolve(classFile), + StandardCopyOption.REPLACE_EXISTING); - // load providers and instantiate each one - loadProviders(mods, TEST1_MODULE).forEach(Provider::get); + Assertions.assertThrows(ServiceConfigurationError.class, + // load providers and instantiate each one + () -> loadProviders(mods, TEST1_MODULE).forEach(Provider::get) + ); } - - @DataProvider(name = "badproviders") - public Object[][] createBadProviders() { - return new Object[][] { - { "notpublic", null }, - { "ctornotpublic", null }, - { "notasubtype", null }, - { "throwsexception", null } - }; - } - - - @Test(dataProvider = "badproviders", - expectedExceptions = ServiceConfigurationError.class) - public void testBadProvider(String testName, String ignore) throws Exception { + @ParameterizedTest + @ValueSource(strings = { "notpublic", "ctornotpublic", "notasubtype", "throwsexception" }) + public void testBadProvider(String testName) throws Exception { Path mods = compileTest(TEST2_MODULE); // compile the bad provider @@ -197,11 +176,13 @@ public class BadProvidersTest { // copy the compiled class into the module Path classFile = Paths.get("p", "Provider.class"); Files.copy(output.resolve(classFile), - mods.resolve(TEST2_MODULE).resolve(classFile), - StandardCopyOption.REPLACE_EXISTING); + mods.resolve(TEST2_MODULE).resolve(classFile), + StandardCopyOption.REPLACE_EXISTING); - // load providers and instantiate each one - loadProviders(mods, TEST2_MODULE).forEach(Provider::get); + Assertions.assertThrows(ServiceConfigurationError.class, + // load providers and instantiate each one + () -> loadProviders(mods, TEST2_MODULE).forEach(Provider::get) + ); } @@ -209,7 +190,7 @@ public class BadProvidersTest { * Test a service provider that defines more than one no-args * public static "provider" method. */ - @Test(expectedExceptions = ServiceConfigurationError.class) + @Test public void testWithTwoFactoryMethods() throws Exception { Path mods = compileTest(TEST1_MODULE); @@ -244,8 +225,10 @@ public class BadProvidersTest { .resolve("ProviderFactory.class"); Files.write(classFile, bytes); - // load providers and instantiate each one - loadProviders(mods, TEST1_MODULE).forEach(Provider::get); + Assertions.assertThrows(ServiceConfigurationError.class, + // load providers and instantiate each one + () -> loadProviders(mods, TEST1_MODULE).forEach(Provider::get) + ); } } diff --git a/test/jdk/java/util/ServiceLoader/CachingTest.java b/test/jdk/java/util/ServiceLoader/CachingTest.java index 264c646b5af..138b6b6862f 100644 --- a/test/jdk/java/util/ServiceLoader/CachingTest.java +++ b/test/jdk/java/util/ServiceLoader/CachingTest.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 @@ -24,7 +24,7 @@ /** * @test * @summary Test ServiceLoader caches - * @run testng CachingTest + * @run junit CachingTest */ import java.nio.file.Files; @@ -38,9 +38,9 @@ import java.util.ServiceLoader.Provider; import java.util.Spliterator; import java.util.stream.Collectors; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class CachingTest { @@ -52,17 +52,17 @@ public class CachingTest { public static class S2 implements S { } - private ClassLoader testClassLoader; + private static ClassLoader testClassLoader; // creates the services configuration file and sets the ClassLoader - @BeforeClass - void setup() throws Exception { + @BeforeAll + static void setup() throws Exception { String classes = System.getProperty("test.classes"); Path dir = Paths.get(classes, "META-INF", "services"); Files.createDirectories(dir); Path config = dir.resolve(S.class.getName()); Files.write(config, List.of(S1.class.getName(), S2.class.getName())); - this.testClassLoader = CachingTest.class.getClassLoader(); + testClassLoader = CachingTest.class.getClassLoader(); } private void checkLists(List list1, List list2) { diff --git a/test/jdk/java/util/ServiceLoader/ModulesTest.java b/test/jdk/java/util/ServiceLoader/ModulesTest.java index 002b8458865..d265ce18f60 100644 --- a/test/jdk/java/util/ServiceLoader/ModulesTest.java +++ b/test/jdk/java/util/ServiceLoader/ModulesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, 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 @@ -29,7 +29,7 @@ * @build jdk.test.lib.util.JarUtils * @compile classpath/pearscript/org/pear/PearScriptEngineFactory.java * classpath/pearscript/org/pear/PearScript.java - * @run testng/othervm ModulesTest + * @run junit/othervm ModulesTest * @summary Basic test for ServiceLoader with a provider deployed as a module. */ @@ -55,9 +55,10 @@ import javax.script.ScriptEngineFactory; import jdk.test.lib.util.JarUtils; -import org.testng.annotations.Test; -import org.testng.annotations.BeforeTest; -import static org.testng.Assert.*; +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; /** * Basic test for ServiceLoader. The test make use of two service providers: @@ -67,12 +68,11 @@ import static org.testng.Assert.*; * 2. PearScriptEngine - a ScriptEngineFactory deployed on the class path * with a service configuration file. */ - public class ModulesTest { // Copy the services configuration file for "pearscript" into place. - @BeforeTest - public void setup() throws Exception { + @BeforeAll + public static void setup() throws Exception { Path src = Paths.get(System.getProperty("test.src")); Path classes = Paths.get(System.getProperty("test.classes")); String st = ScriptEngineFactory.class.getName(); @@ -428,30 +428,36 @@ public class ModulesTest { // -- nulls -- - @Test(expectedExceptions = { NullPointerException.class }) + @Test public void testLoadNull1() { - ServiceLoader.load(null); + Assertions.assertThrows(NullPointerException.class, + () -> ServiceLoader.load(null)); } - @Test(expectedExceptions = { NullPointerException.class }) + @Test public void testLoadNull2() { - ServiceLoader.load((Class) null, ClassLoader.getSystemClassLoader()); + Assertions.assertThrows(NullPointerException.class, + () -> ServiceLoader.load((Class) null, ClassLoader.getSystemClassLoader())); } - @Test(expectedExceptions = { NullPointerException.class }) + @Test public void testLoadNull3() { class S { } - ServiceLoader.load((ModuleLayer) null, S.class); + Assertions.assertThrows(NullPointerException.class, () -> { + ServiceLoader.load((ModuleLayer) null, S.class); + }); } - @Test(expectedExceptions = { NullPointerException.class }) + @Test public void testLoadNull4() { - ServiceLoader.load(ModuleLayer.empty(), null); + Assertions.assertThrows(NullPointerException.class, + () -> ServiceLoader.load(ModuleLayer.empty(), null)); } - @Test(expectedExceptions = { NullPointerException.class }) + @Test public void testLoadNull5() { - ServiceLoader.loadInstalled(null); + Assertions.assertThrows(NullPointerException.class, + () -> ServiceLoader.loadInstalled(null)); } /** diff --git a/test/jdk/java/util/ServiceLoader/NoInterferenceTest.java b/test/jdk/java/util/ServiceLoader/NoInterferenceTest.java index ff83b836e20..d91bc9c759f 100644 --- a/test/jdk/java/util/ServiceLoader/NoInterferenceTest.java +++ b/test/jdk/java/util/ServiceLoader/NoInterferenceTest.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 @@ -26,7 +26,7 @@ * @library /test/lib * @modules jdk.compiler * @build jdk.test.lib.compiler.CompilerUtils - * @run testng NoInterferenceTest + * @run junit NoInterferenceTest * @summary Basic test of ServiceLoader that ensures there is no interference * when there are two service interfaces of the same name in a layer * or overridden in a child layer. @@ -46,9 +46,9 @@ import java.util.Set; import jdk.test.lib.compiler.CompilerUtils; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class NoInterferenceTest { @@ -57,8 +57,8 @@ public class NoInterferenceTest { private static final Path MODS_DIR = Paths.get("mods"); private static final List MODULES = Arrays.asList("s1", "p1", "s2", "p2"); - @BeforeTest - void compile() throws Exception { + @BeforeAll + static void compile() throws Exception { Files.createDirectory(MODS_DIR); for (String name : MODULES) { Path src = SRC_DIR.resolve(name); diff --git a/test/jdk/java/util/ServiceLoader/ReloadTest.java b/test/jdk/java/util/ServiceLoader/ReloadTest.java index d48b5c29f4d..be2c0d68773 100644 --- a/test/jdk/java/util/ServiceLoader/ReloadTest.java +++ b/test/jdk/java/util/ServiceLoader/ReloadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, 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,7 +26,7 @@ * @modules java.scripting * @library modules classpath/pearscript * @build ReloadTest org.pear.PearScript org.pear.PearScriptEngineFactory bananascript/* - * @run testng/othervm ReloadTest + * @run junit/othervm ReloadTest * @summary Basic test of ServiceLoader.reload */ @@ -40,12 +40,14 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.ServiceLoader.*; import javax.script.ScriptEngineFactory; -import org.testng.annotations.Test; -import static org.testng.Assert.*; -@Test +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + public class ReloadTest { + @Test public void testReload() { ServiceLoader sl = load(ScriptEngineFactory.class); List names1 = sl.stream() @@ -58,41 +60,42 @@ public class ReloadTest { .map(Provider::get) .map(ScriptEngineFactory::getEngineName) .collect(Collectors.toList()); - assertEquals(names1, names2); + assertEquals(names2, names1); } - @Test(expectedExceptions = { ConcurrentModificationException.class }) + @Test public void testIteratorHasNext() { ServiceLoader sl = load(ScriptEngineFactory.class); Iterator iterator = sl.iterator(); sl.reload(); - iterator.hasNext(); + Assertions.assertThrows(ConcurrentModificationException.class, iterator::hasNext); } - @Test(expectedExceptions = { ConcurrentModificationException.class }) + @Test public void testIteratorNext() { ServiceLoader sl = load(ScriptEngineFactory.class); Iterator iterator = sl.iterator(); assertTrue(iterator.hasNext()); sl.reload(); - iterator.next(); + Assertions.assertThrows(ConcurrentModificationException.class, iterator::next); } - @Test(expectedExceptions = { ConcurrentModificationException.class }) + @Test public void testStreamFindAny() { ServiceLoader sl = load(ScriptEngineFactory.class); Stream> stream = sl.stream(); sl.reload(); - stream.findAny(); + Assertions.assertThrows(ConcurrentModificationException.class, stream::findAny); } - @Test(expectedExceptions = { ConcurrentModificationException.class }) + @Test public void testSpliteratorTryAdvance() { ServiceLoader sl = load(ScriptEngineFactory.class); Stream> stream = sl.stream(); Spliterator> spliterator = stream.spliterator(); sl.reload(); - spliterator.tryAdvance(System.out::println); + Assertions.assertThrows(ConcurrentModificationException.class, + () -> spliterator.tryAdvance(System.out::println)); } } diff --git a/test/jdk/java/util/ServiceLoader/TwoIterators.java b/test/jdk/java/util/ServiceLoader/TwoIterators.java index ddb9cc09508..b86cc45f17f 100644 --- a/test/jdk/java/util/ServiceLoader/TwoIterators.java +++ b/test/jdk/java/util/ServiceLoader/TwoIterators.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 @@ -25,7 +25,7 @@ * @test * @summary Test ServiceLoader with two iterators, interleaving their use * to test that they don't interfere with each other - * @run testng TwoIterators + * @run junit TwoIterators */ import java.nio.file.Files; @@ -35,9 +35,9 @@ import java.util.Arrays; import java.util.Iterator; import java.util.ServiceLoader; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class TwoIterators { @@ -48,18 +48,17 @@ public class TwoIterators { public static class S1 implements S { } public static class S2 implements S { } - private ClassLoader testClassLoader; + private static ClassLoader testClassLoader; // creates the services configuration file and sets the ClassLoader - @BeforeClass - void setup() throws Exception { + @BeforeAll + static void setup() throws Exception { String classes = System.getProperty("test.classes"); Path dir = Paths.get(classes, "META-INF", "services"); Files.createDirectories(dir); Path config = dir.resolve(S.class.getName()); Files.write(config, Arrays.asList(S1.class.getName(), S2.class.getName())); - - this.testClassLoader = TwoIterators.class.getClassLoader(); + testClassLoader = TwoIterators.class.getClassLoader(); } @Test diff --git a/test/jdk/java/util/ServiceLoader/basic/ServiceLoaderBasicTest.java b/test/jdk/java/util/ServiceLoader/basic/ServiceLoaderBasicTest.java index 91aba564cd1..4a9bc808851 100644 --- a/test/jdk/java/util/ServiceLoader/basic/ServiceLoaderBasicTest.java +++ b/test/jdk/java/util/ServiceLoader/basic/ServiceLoaderBasicTest.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 @@ -29,7 +29,7 @@ * @build jdk.test.lib.process.* * jdk.test.lib.util.JarUtils * Basic Load FooService FooProvider1 FooProvider2 FooProvider3 BarProvider - * @run testng ServiceLoaderBasicTest + * @run junit ServiceLoaderBasicTest */ @@ -44,14 +44,16 @@ import jdk.test.lib.Utils; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.util.JarUtils; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static java.util.Arrays.asList; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + public class ServiceLoaderBasicTest { private static final String METAINFO = "META-INF/services/FooService"; @@ -79,8 +81,8 @@ public class ServiceLoaderBasicTest { private static final String XTESTXMETA_CP = XTEST_CP + XMETA; private static final String XTESTXMETAP2_CP = XTESTXMETA_CP + P2; - @BeforeClass - public void initialize() throws Exception { + @BeforeAll + public static void initialize() throws Exception { createProviderConfig(XTEST_CONFIG, "FooProvider1"); createProviderConfig(XMETA_CONFIG, "FooProvider42"); createJar(P2JAR, "FooProvider2", List.of("FooProvider2")); @@ -88,8 +90,7 @@ public class ServiceLoaderBasicTest { Files.copy(P2JAR, P2DUPJAR, REPLACE_EXISTING); } - @DataProvider - public Object[][] testCases() { + private static Object[][] testCases() { return new Object[][]{ // CLI options, Test, Runtime arguments // Success cases @@ -110,23 +111,14 @@ public class ServiceLoaderBasicTest { }; } - @DataProvider - public Object[][] negativeTestCases() { - return new Object[][]{ - {"blah blah"}, - {"9234"}, - {"X!"}, - {"BarProvider"}, - {"FooProvider42"} - }; - } - - @Test(dataProvider = "testCases") + @ParameterizedTest + @MethodSource("testCases") public void testProvider(List args) throws Throwable { runJava(args); } - @Test(dataProvider = "negativeTestCases") + @ParameterizedTest // negative test cases + @ValueSource(strings = { "blah blah", "9234", "X!", "BarProvider", "FooProvider42" }) public void testBadProvider(String providerName) throws Throwable { Files.write(XMETA_CONFIG, providerName.getBytes()); runJava(List.of("-cp", XMETA_CP, "Load", "fail")); @@ -144,12 +136,12 @@ public class ServiceLoaderBasicTest { .shouldHaveExitValue(0); } - private void createProviderConfig(Path config, String providerName) throws Exception { + private static void createProviderConfig(Path config, String providerName) throws Exception { Files.createDirectories(config.getParent()); Files.write(config, providerName.getBytes(), CREATE); } - private void createJar(Path jar, String provider, List files) throws Exception { + private static void createJar(Path jar, String provider, List files) throws Exception { Path xdir = Path.of(provider); createProviderConfig(xdir.resolve(METAINFO), provider); diff --git a/test/jdk/java/util/StringJoiner/MergeTest.java b/test/jdk/java/util/StringJoiner/MergeTest.java index f47ed3ee094..574124b9bdd 100644 --- a/test/jdk/java/util/StringJoiner/MergeTest.java +++ b/test/jdk/java/util/StringJoiner/MergeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, 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,17 +27,18 @@ * @summary test StringJoiner::merge * @modules java.base/jdk.internal.util * @requires vm.bits == "64" & os.maxMemory > 4G - * @run testng/othervm -Xmx4g -XX:+CompactStrings MergeTest + * @run junit/othervm -Xmx4g -XX:+CompactStrings MergeTest */ import java.util.StringJoiner; import java.util.stream.Stream; -import org.testng.annotations.Test; import static jdk.internal.util.ArraysSupport.SOFT_MAX_ARRAY_LENGTH; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; -@Test +import org.junit.jupiter.api.Assertions; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; + public class MergeTest { private static final String[] PREFIXES = {"", "{", "@#$%"}; private static final String[] SUFFIXES = {"", "}", "*&%$"}; @@ -69,12 +70,13 @@ public class MergeTest { return builder.build(); } - @Test(expectedExceptions = {NullPointerException.class}) + @Test public void testNull() { StringJoiner sj = new StringJoiner(",", "{", "}"); - sj.merge(null); + Assertions.assertThrows(NullPointerException.class, () -> sj.merge(null)); } + @Test public void testSimple() { fixesStream().forEach(fixes -> { StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0); @@ -83,10 +85,11 @@ public class MergeTest { Stream.of("d", "e", "f").forEachOrdered(other::add); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + "a,b,c,d,e,f" + fixes.suf0); + assertEquals(fixes.pre0 + "a,b,c,d,e,f" + fixes.suf0, sj.toString()); }); } + @Test public void testEmptyOther() { fixesStream().forEach(fixes -> { StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0); @@ -94,14 +97,15 @@ public class MergeTest { Stream.of("a", "b", "c").forEachOrdered(sj::add); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + "a,b,c" + fixes.suf0); + assertEquals(fixes.pre0 + "a,b,c" + fixes.suf0, sj.toString()); other.setEmptyValue("EMPTY"); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + "a,b,c" + fixes.suf0); + assertEquals(fixes.pre0 + "a,b,c" + fixes.suf0, sj.toString()); }); } + @Test public void testEmptyThis() { fixesStream().forEach(fixes -> { StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0); @@ -109,34 +113,36 @@ public class MergeTest { Stream.of("d", "e", "f").forEachOrdered(other::add); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + "d:e:f" + fixes.suf0); + assertEquals(fixes.pre0 + "d:e:f" + fixes.suf0, sj.toString()); sj = new StringJoiner(",", fixes.pre0, fixes.suf0).setEmptyValue("EMPTY"); - assertEquals(sj.toString(), "EMPTY"); + assertEquals("EMPTY", sj.toString()); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + "d:e:f" + fixes.suf0); + assertEquals(fixes.pre0 + "d:e:f" + fixes.suf0, sj.toString()); }); } + @Test public void testEmptyBoth() { fixesStream().forEach(fixes -> { StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0); StringJoiner other = new StringJoiner(":", fixes.pre1, fixes.suf1); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + fixes.suf0); + assertEquals(fixes.pre0 + fixes.suf0, sj.toString()); other.setEmptyValue("NOTHING"); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + fixes.suf0); + assertEquals(fixes.pre0 + fixes.suf0, sj.toString()); sj = new StringJoiner(",", fixes.pre0, fixes.suf0).setEmptyValue("EMPTY"); - assertEquals(sj.toString(), "EMPTY"); + assertEquals("EMPTY", sj.toString()); sj.merge(other); - assertEquals(sj.toString(), "EMPTY"); + assertEquals("EMPTY", sj.toString()); }); } + @Test public void testCascadeEmpty() { fixesStream().forEach(fixes -> { StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0); @@ -144,13 +150,14 @@ public class MergeTest { StringJoiner o2 = new StringJoiner(",", "<", ">").setEmptyValue("Empty2"); o1.merge(o2); - assertEquals(o1.toString(), "Empty1"); + assertEquals("Empty1", o1.toString()); sj.merge(o1); - assertEquals(sj.toString(), fixes.pre0 + fixes.suf0); + assertEquals(fixes.pre0 + fixes.suf0, sj.toString()); }); } + @Test public void testDelimiter() { fixesStream().forEach(fixes -> { StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0); @@ -159,18 +166,20 @@ public class MergeTest { Stream.of("d", "e", "f").forEachOrdered(other::add); sj.merge(other); - assertEquals(sj.toString(), fixes.pre0 + "a,b,c,d:e:f" + fixes.suf0); + assertEquals(fixes.pre0 + "a,b,c,d:e:f" + fixes.suf0, sj.toString()); }); } + @Test public void testMergeSelf() { fixesStream().forEach(fixes -> { final StringJoiner sj = new StringJoiner(",", fixes.pre0, fixes.suf0).add("a").add("b"); - assertEquals(sj.merge(sj).toString(), fixes.pre0 + "a,b,a,b" + fixes.suf0); - assertEquals(sj.merge(sj).toString(), fixes.pre0 + "a,b,a,b,a,b,a,b" + fixes.suf0); + assertEquals(fixes.pre0 + "a,b,a,b" + fixes.suf0, sj.merge(sj).toString()); + assertEquals(fixes.pre0 + "a,b,a,b,a,b,a,b" + fixes.suf0, sj.merge(sj).toString()); }); } + @Test public void OOM() { String maxString = "*".repeat(SOFT_MAX_ARRAY_LENGTH); diff --git a/test/jdk/java/util/StringJoiner/StringJoinerOomUtf16Test.java b/test/jdk/java/util/StringJoiner/StringJoinerOomUtf16Test.java index b45e66b4d8b..7fde8b36ffa 100644 --- a/test/jdk/java/util/StringJoiner/StringJoinerOomUtf16Test.java +++ b/test/jdk/java/util/StringJoiner/StringJoinerOomUtf16Test.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 @@ -26,18 +26,23 @@ * @summary tests StringJoiner OOME when joining sub-max-length Strings * @modules java.base/jdk.internal.util * @requires vm.bits == "64" & os.maxMemory > 4G - * @run testng/othervm -Xmx4g -XX:+CompactStrings StringJoinerOomUtf16Test + * @run junit/othervm -Xmx4g -XX:+CompactStrings StringJoinerOomUtf16Test */ -import org.testng.annotations.Test; import static jdk.internal.util.ArraysSupport.SOFT_MAX_ARRAY_LENGTH; -import static org.testng.Assert.fail; + +import static org.junit.jupiter.api.Assertions.fail; import java.util.StringJoiner; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Test(groups = {"unit","string","util","libs"}) +@Tag("unit") +@Tag("string") +@Tag("util") +@Tag("libs") public class StringJoinerOomUtf16Test { // the sum of lengths of the following two strings is way less than @@ -48,6 +53,7 @@ public class StringJoinerOomUtf16Test { private static final String OVERFLOW_UTF16_STRING = "\u017D".repeat(((Integer.MAX_VALUE - SOFT_MAX_ARRAY_LENGTH) >> 1) + 1); + @Test public void OOM1() { try { new StringJoiner("") @@ -60,6 +66,7 @@ public class StringJoinerOomUtf16Test { } } + @Test public void OOM2() { try { new StringJoiner(HALF_MAX_LATIN1_STRING) @@ -72,6 +79,7 @@ public class StringJoinerOomUtf16Test { } } + @Test public void OOM3() { try { new StringJoiner(OVERFLOW_UTF16_STRING) @@ -84,6 +92,7 @@ public class StringJoinerOomUtf16Test { } } + @Test public void OOM4() { try { new StringJoiner("", HALF_MAX_LATIN1_STRING, OVERFLOW_UTF16_STRING) diff --git a/test/jdk/java/util/StringJoiner/StringJoinerTest.java b/test/jdk/java/util/StringJoiner/StringJoinerTest.java index 25948fb8e55..aa25623dbc3 100644 --- a/test/jdk/java/util/StringJoiner/StringJoinerTest.java +++ b/test/jdk/java/util/StringJoiner/StringJoinerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, 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,18 +26,25 @@ * @summary tests StringJoinerTest * @modules java.base/jdk.internal.util * @requires vm.bits == "64" & os.maxMemory > 4G - * @run testng/othervm -Xmx4g -XX:+CompactStrings StringJoinerTest + * @run junit/othervm -Xmx4g -XX:+CompactStrings StringJoinerTest * @author Jim Gish */ + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + import java.util.ArrayList; import java.util.StringJoiner; -import org.testng.annotations.Test; + import static jdk.internal.util.ArraysSupport.SOFT_MAX_ARRAY_LENGTH; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; - -@Test(groups = {"unit","string","util","libs"}) +@Tag("unit") +@Tag("string") +@Tag("util") +@Tag("libs") public class StringJoinerTest { private static final String EMPTY = "EMPTY"; @@ -51,6 +58,7 @@ public class StringJoinerTest { private static final String DASH = "-"; private static final String MAX_STRING = "*".repeat(SOFT_MAX_ARRAY_LENGTH); + @Test public void addAddAll() { StringJoiner sj = new StringJoiner(DASH, "{", "}"); sj.add(ONE); @@ -61,7 +69,7 @@ public class StringJoinerTest { nextOne.stream().forEachOrdered(sj::add); String expected = "{"+ONE+DASH+TWO+DASH+THREE+"}"; - assertEquals(sj.toString(), expected); + assertEquals(expected, sj.toString()); } void addAlladd() { @@ -75,10 +83,11 @@ public class StringJoinerTest { sj.add(THREE); String expected = "{"+ONE+DASH+TWO+DASH+THREE+"}"; - assertEquals(sj.toString(), expected); + assertEquals(expected, sj.toString()); } // The following tests do two successive adds of different types + @Test public void addAlladdAll() { StringJoiner sj = new StringJoiner(DASH, "{", "}"); ArrayList firstOne = new ArrayList<>(); @@ -93,9 +102,10 @@ public class StringJoinerTest { nextOne.stream().forEachOrdered(sj::add); String expected = "{"+ONE+DASH+TWO+DASH+THREE+DASH+FOUR+DASH+FIVE+"}"; - assertEquals(sj.toString(), expected); + assertEquals(expected, sj.toString()); } + @Test public void addCharSequence() { StringJoiner sj = new StringJoiner(","); CharSequence cs_one = ONE; @@ -104,13 +114,13 @@ public class StringJoinerTest { sj.add(cs_one); sj.add(cs_two); - assertEquals(sj.toString(), ONE + "," + TWO); + assertEquals(ONE + "," + TWO, sj.toString()); sj = new StringJoiner(DASH, "{", "}"); sj.add(cs_one); sj.add(cs_two); - assertEquals(sj.toString(), "{" + ONE + DASH + TWO + "}"); + assertEquals("{" + ONE + DASH + TWO + "}", sj.toString()); StringBuilder builder = new StringBuilder(ONE); StringBuffer buffer = new StringBuffer(THREE); @@ -118,13 +128,14 @@ public class StringJoinerTest { sj.add(builder).add(buffer); builder.append(TWO); buffer.append(FOUR); - assertEquals(sj.toString(), "{ " + ONE + ", " + THREE + " }", + assertEquals("{ " + ONE + ", " + THREE + " }", sj.toString(), "CharSequence is copied when add"); sj.add(builder); - assertEquals(sj.toString(), "{ " + ONE + ", " + THREE + ", " + ONE + - TWO + " }"); + assertEquals("{ " + ONE + ", " + THREE + ", " + ONE + + TWO + " }", sj.toString()); } + @Test public void addCharSequenceWithEmptyValue() { StringJoiner sj = new StringJoiner(",").setEmptyValue(EMPTY); CharSequence cs_one = ONE; @@ -133,189 +144,200 @@ public class StringJoinerTest { sj.add(cs_one); sj.add(cs_two); - assertEquals(sj.toString(), ONE + "," + TWO); + assertEquals(ONE + "," + TWO, sj.toString()); sj = new StringJoiner(DASH, "{", "}"); sj.add(cs_one); sj.add(cs_two); - assertEquals(sj.toString(), "{" + ONE + DASH + TWO + "}"); + assertEquals("{" + ONE + DASH + TWO + "}", sj.toString()); sj = new StringJoiner(DASH, "{", "}"); - assertEquals(sj.toString(), "{}"); + assertEquals("{}", sj.toString()); sj = new StringJoiner("=", "{", "}").setEmptyValue(""); - assertEquals(sj.toString(), ""); + assertEquals("", sj.toString()); sj = new StringJoiner(DASH, "{", "}").setEmptyValue(EMPTY); - assertEquals(sj.toString(), EMPTY); + assertEquals(EMPTY, sj.toString()); sj.add(cs_one); sj.add(cs_two); - assertEquals(sj.toString(), "{" + ONE + DASH + TWO + "}"); + assertEquals("{" + ONE + DASH + TWO + "}", sj.toString()); } + @Test public void addString() { StringJoiner sj = new StringJoiner(DASH); sj.add(ONE); - assertEquals(sj.toString(), ONE); + assertEquals(ONE, sj.toString()); sj = new StringJoiner(DASH, "{", "}"); sj.add(ONE); - assertEquals(sj.toString(), "{" + ONE + "}"); + assertEquals("{" + ONE + "}", sj.toString()); sj.add(TWO); - assertEquals(sj.toString(), "{" + ONE + DASH + TWO + "}"); + assertEquals("{" + ONE + DASH + TWO + "}", sj.toString()); } + @Test public void lengthWithCustomEmptyValue() { StringJoiner sj = new StringJoiner(DASH, "<", ">").setEmptyValue(EMPTY); - assertEquals(sj.length(), EMPTY.length()); + assertEquals(EMPTY.length(), sj.length()); sj.add(""); - assertEquals(sj.length(), "<>".length()); + assertEquals("<>".length(), sj.length()); sj.add(""); - assertEquals(sj.length(), "<->".length()); + assertEquals("<->".length(), sj.length()); sj.add(ONE); - assertEquals(sj.length(), 4 + ONE_LEN); - assertEquals(sj.toString().length(), sj.length()); + assertEquals(4 + ONE_LEN, sj.length()); + assertEquals(sj.length(), sj.toString().length()); sj.add(TWO); - assertEquals(sj.length(), 5 + ONE_LEN + TWO_LEN); - assertEquals(sj.toString().length(), sj.length()); + assertEquals(5 + ONE_LEN + TWO_LEN, sj.length()); + assertEquals(sj.length(), sj.toString().length()); sj = new StringJoiner("||", "<", "-->"); - assertEquals(sj.length(), 4); - assertEquals(sj.toString().length(), sj.length()); + assertEquals(4, sj.length()); + assertEquals(sj.length(), sj.toString().length()); sj.add("abcdef"); - assertEquals(sj.length(), 10); - assertEquals(sj.toString().length(), sj.length()); + assertEquals(10, sj.length()); + assertEquals(sj.length(), sj.toString().length()); sj.add("xyz"); - assertEquals(sj.length(), 15); - assertEquals(sj.toString().length(), sj.length()); + assertEquals(15, sj.length()); + assertEquals(sj.length(), sj.toString().length()); } + @Test public void noAddAndEmptyValue() { StringJoiner sj = new StringJoiner(DASH, "", "").setEmptyValue(EMPTY); - assertEquals(sj.toString(), EMPTY); + assertEquals(EMPTY, sj.toString()); sj = new StringJoiner(DASH, "<..", ""); - assertEquals(sj.toString(), "<.."); + assertEquals("<..", sj.toString()); sj = new StringJoiner(DASH, "<..", ""); - assertEquals(sj.toString(), "<.."); + assertEquals("<..", sj.toString()); sj = new StringJoiner(DASH, "", "==>"); - assertEquals(sj.toString(), "==>"); + assertEquals("==>", sj.toString()); sj = new StringJoiner(DASH, "{", "}"); - assertEquals(sj.toString(), "{}"); + assertEquals("{}", sj.toString()); } - @Test(expectedExceptions = {NullPointerException.class}) + @Test public void setEmptyValueNull() { - new StringJoiner(DASH, "{", "}").setEmptyValue(null); + Assertions.assertThrows(NullPointerException.class, + () -> new StringJoiner(DASH, "{", "}").setEmptyValue(null)); } - @Test(expectedExceptions = {NullPointerException.class}) + @Test public void setDelimiterNull() { - new StringJoiner(null); + Assertions.assertThrows(NullPointerException.class, + () -> new StringJoiner(null)); } - @Test(expectedExceptions = {NullPointerException.class}) + @Test public void setPrefixNull() { - new StringJoiner(DASH, null, "}"); + Assertions.assertThrows(NullPointerException.class, + () -> new StringJoiner(DASH, null, "}")); } - @Test(expectedExceptions = {NullPointerException.class}) + @Test public void setSuffixNull() { - new StringJoiner(DASH, "{", null); + Assertions.assertThrows(NullPointerException.class, + () -> new StringJoiner(DASH, "{", null)); } + @Test public void stringFromtoString() { StringJoiner sj = new StringJoiner(", "); - assertEquals(sj.toString(), ""); + assertEquals("", sj.toString()); sj = new StringJoiner(",", "{", "}"); - assertEquals(sj.toString(), "{}"); + assertEquals("{}", sj.toString()); sj = new StringJoiner(","); sj.add(ONE); - assertEquals(sj.toString(), ONE); + assertEquals(ONE, sj.toString()); sj.add(TWO); - assertEquals(sj.toString(), ONE + "," + TWO); + assertEquals(ONE + "," + TWO, sj.toString()); sj = new StringJoiner(",", "{--", "--}"); sj.add(ONE); sj.add(TWO); - assertEquals(sj.toString(), "{--" + ONE + "," + TWO + "--}"); + assertEquals("{--" + ONE + "," + TWO + "--}", sj.toString()); } + @Test public void stringFromtoStringWithEmptyValue() { StringJoiner sj = new StringJoiner(" ", "", ""); - assertEquals(sj.toString(), ""); + assertEquals("", sj.toString()); sj = new StringJoiner(", "); - assertEquals(sj.toString(), ""); + assertEquals("", sj.toString()); sj = new StringJoiner(",", "{", "}"); - assertEquals(sj.toString(), "{}"); + assertEquals("{}", sj.toString()); sj = new StringJoiner(",", "{", "}").setEmptyValue(""); - assertEquals(sj.toString(), ""); + assertEquals("", sj.toString()); sj = new StringJoiner(","); sj.add(ONE); - assertEquals(sj.toString(), ONE); + assertEquals(ONE, sj.toString()); sj.add(TWO); - assertEquals(sj.toString(), ONE + "," + TWO); + assertEquals(ONE + "," + TWO, sj.toString()); sj = new StringJoiner(",", "{--", "--}"); sj.add(ONE); - assertEquals(sj.toString(), "{--" + ONE + "--}" ); + assertEquals("{--" + ONE + "--}", sj.toString() ); sj.add(TWO); - assertEquals(sj.toString(), "{--" + ONE + "," + TWO + "--}"); + assertEquals("{--" + ONE + "," + TWO + "--}", sj.toString()); } + @Test public void toStringWithCustomEmptyValue() { StringJoiner sj = new StringJoiner(DASH, "<", ">").setEmptyValue(EMPTY); - assertEquals(sj.toString(), EMPTY); + assertEquals(EMPTY, sj.toString()); sj.add(""); - assertEquals(sj.toString(), "<>"); + assertEquals("<>", sj.toString()); sj.add(""); - assertEquals(sj.toString(), "<->"); + assertEquals("<->", sj.toString()); } private void testCombos(String infix, String prefix, String suffix) { StringJoiner sj = new StringJoiner(infix, prefix, suffix); - assertEquals(sj.toString(), prefix + suffix); - assertEquals(sj.toString().length(), sj.length()); + assertEquals(prefix + suffix, sj.toString()); + assertEquals(sj.length(), sj.toString().length()); // EmptyValue sj = new StringJoiner(infix, prefix, suffix).setEmptyValue(""); - assertEquals(sj.toString(), ""); - assertEquals(sj.toString().length(), sj.length()); + assertEquals("", sj.toString()); + assertEquals(sj.length(), sj.toString().length()); // empty in front sj.add(""); - assertEquals(sj.toString(), prefix + suffix); + assertEquals(prefix + suffix, sj.toString()); // empty in middle sj.add(""); - assertEquals(sj.toString(), prefix + infix + suffix); + assertEquals(prefix + infix + suffix, sj.toString()); sj.add("1"); - assertEquals(sj.toString(), prefix + infix + infix + "1" + suffix); + assertEquals(prefix + infix + infix + "1" + suffix, sj.toString()); // empty at end sj.add(""); - assertEquals(sj.toString(), prefix + infix + infix + "1" + infix + suffix); + assertEquals(prefix + infix + infix + "1" + infix + suffix, sj.toString()); sj = new StringJoiner(infix, prefix, suffix).setEmptyValue(""); sj.add("1"); - assertEquals(sj.toString(), prefix + "1" + suffix); + assertEquals(prefix + "1" + suffix, sj.toString()); sj.add("2"); - assertEquals(sj.toString(), prefix + "1" + infix + "2" + suffix); + assertEquals(prefix + "1" + infix + "2" + suffix, sj.toString()); sj.add(""); - assertEquals(sj.toString(), prefix + "1" + infix + "2" + infix + suffix); + assertEquals(prefix + "1" + infix + "2" + infix + suffix, sj.toString()); sj.add("3"); - assertEquals(sj.toString(), prefix + "1" + infix + "2" + infix + infix + "3" + suffix); + assertEquals(prefix + "1" + infix + "2" + infix + infix + "3" + suffix, sj.toString()); } + @Test public void testDelimiterCombinations() { testCombos("", "", ""); testCombos("", "<", ""); @@ -327,6 +349,7 @@ public class StringJoinerTest { testCombos(",", "<", ">"); } + @Test public void OOM1() { try { new StringJoiner(MAX_STRING, MAX_STRING, MAX_STRING).toString(); @@ -336,6 +359,7 @@ public class StringJoinerTest { } } + @Test public void OOM2() { try { new StringJoiner(MAX_STRING, MAX_STRING, "").toString(); @@ -345,6 +369,7 @@ public class StringJoinerTest { } } + @Test public void OOM3() { try { new StringJoiner(MAX_STRING, "", MAX_STRING).toString(); @@ -354,6 +379,7 @@ public class StringJoinerTest { } } + @Test public void OOM4() { try { new StringJoiner("", MAX_STRING, MAX_STRING).toString(); diff --git a/test/jdk/java/util/Vector/ArrayManagement.java b/test/jdk/java/util/Vector/ArrayManagement.java index ce4e695487c..c1fa6d1c9bc 100644 --- a/test/jdk/java/util/Vector/ArrayManagement.java +++ b/test/jdk/java/util/Vector/ArrayManagement.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * Copyright 2016 Google, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -25,7 +26,7 @@ * @test * @bug 8148174 * @summary brittle white box test of internal array management - * @run testng ArrayManagement + * @run junit ArrayManagement */ import java.lang.reflect.Field; @@ -35,8 +36,8 @@ import java.util.Collections; import java.util.List; import java.util.SplittableRandom; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class ArrayManagement { @@ -61,9 +62,9 @@ public class ArrayManagement { super.ensureCapacity(minCapacity); assertTrue(capacity() >= minCapacity); if (minCapacity <= oldCapacity) - assertEquals(capacity(), oldCapacity); + assertEquals(oldCapacity, capacity()); if (minCapacity > 0) - assertEquals(modCount(), oldModCount + 1); + assertEquals(oldModCount + 1, modCount()); } } @@ -89,117 +90,117 @@ public class ArrayManagement { case 3: assertTrue(list.addAll(size, singletonList())); break; default: throw new AssertionError(); } - assertEquals(list.modCount(), modCount + 1); - assertEquals(list.size(), size + 1); + assertEquals(modCount + 1, list.modCount()); + assertEquals(size + 1, list.size()); } @Test public void defaultCapacity() { PublicVector list = new PublicVector<>(); - assertEquals(new PublicVector().capacity(), DEFAULT_CAPACITY); + assertEquals(DEFAULT_CAPACITY, new PublicVector().capacity()); for (int i = 0; i < DEFAULT_CAPACITY; i++) { addOneElement(list); - assertEquals(list.capacity(), DEFAULT_CAPACITY); + assertEquals(DEFAULT_CAPACITY, list.capacity()); } addOneElement(list); - assertEquals(list.capacity(), newCapacity(DEFAULT_CAPACITY)); + assertEquals(newCapacity(DEFAULT_CAPACITY), list.capacity()); } @Test public void defaultCapacityEnsureCapacity() { PublicVector list = new PublicVector<>(); for (int i = 0; i <= DEFAULT_CAPACITY; i++) { list.ensureCapacity(i); // no-op! - assertEquals(list.capacity(), DEFAULT_CAPACITY); + assertEquals(DEFAULT_CAPACITY, list.capacity()); } for (int i = 0; i < DEFAULT_CAPACITY; i++) { addOneElement(list); - assertEquals(list.capacity(), DEFAULT_CAPACITY); + assertEquals(DEFAULT_CAPACITY, list.capacity()); } addOneElement(list); - assertEquals(list.capacity(), newCapacity(DEFAULT_CAPACITY)); + assertEquals(newCapacity(DEFAULT_CAPACITY), list.capacity()); { int capacity = list.capacity(); list.ensureCapacity(capacity + 1); - assertEquals(list.capacity(), newCapacity(capacity)); + assertEquals(newCapacity(capacity), list.capacity()); } { int capacity = list.capacity(); list.ensureCapacity(3 * capacity); - assertEquals(list.capacity(), 3 * capacity); + assertEquals(3 * capacity, list.capacity()); } } @Test public void ensureCapacityBeyondDefaultCapacity() { PublicVector list = new PublicVector<>(); list.ensureCapacity(DEFAULT_CAPACITY + 1); - assertEquals(list.capacity(), newCapacity(DEFAULT_CAPACITY)); + assertEquals(newCapacity(DEFAULT_CAPACITY), list.capacity()); } @Test public void explicitZeroCapacity() { PublicVector list = new PublicVector<>(0); - assertEquals(list.capacity(), 0); + assertEquals(0, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 1); + assertEquals(1, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 2); + assertEquals(2, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 4); + assertEquals(4, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 4); + assertEquals(4, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 8); + assertEquals(8, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 8); + assertEquals(8, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 8); + assertEquals(8, list.capacity()); list.clear(); - assertEquals(list.capacity(), 8); + assertEquals(8, list.capacity()); } @Test public void explicitZeroCapacityWithCapacityIncrement() { PublicVector list = new PublicVector<>(0, 2); - assertEquals(list.capacity(), 0); + assertEquals(0, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 2); + assertEquals(2, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 2); + assertEquals(2, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 4); + assertEquals(4, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 4); + assertEquals(4, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 6); + assertEquals(6, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 6); + assertEquals(6, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), 8); + assertEquals(8, list.capacity()); list.clear(); - assertEquals(list.capacity(), 8); + assertEquals(8, list.capacity()); } @Test public void explicitLargeCapacity() { int n = DEFAULT_CAPACITY * 3; PublicVector list = new PublicVector<>(n); - assertEquals(list.capacity(), n); + assertEquals(n, list.capacity()); list.ensureCapacity(0); list.ensureCapacity(n); for (int i = 0; i < n; i++) addOneElement(list); - assertEquals(list.capacity(), n); + assertEquals(n, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), newCapacity(n)); + assertEquals(newCapacity(n), list.capacity()); } @Test public void explicitLargeCapacityWithCapacityIncrement() { int n = DEFAULT_CAPACITY * 3; PublicVector list = new PublicVector<>(n, 2); - assertEquals(list.capacity(), n); + assertEquals(n, list.capacity()); list.ensureCapacity(0); list.ensureCapacity(n); for (int i = 0; i < n; i++) addOneElement(list); - assertEquals(list.capacity(), n); + assertEquals(n, list.capacity()); addOneElement(list); - assertEquals(list.capacity(), n + 2); + assertEquals(n + 2, list.capacity()); } @Test public void emptyArraysAreNotShared() { From 3f01e8b9b8f68560545540f9a70391a7ff7726d0 Mon Sep 17 00:00:00 2001 From: Kirill Shirokov Date: Thu, 15 Jan 2026 18:52:44 +0000 Subject: [PATCH 117/204] 8366522: CodeSource.getCodeSigners() throws NPE within empty certs Reviewed-by: mullan --- .../classes/java/security/CodeSource.java | 9 +- .../CodeSource/CodeSourceNoInputs.java | 110 ++++++++++++++++++ 2 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 test/jdk/java/security/CodeSource/CodeSourceNoInputs.java diff --git a/src/java.base/share/classes/java/security/CodeSource.java b/src/java.base/share/classes/java/security/CodeSource.java index 7476b8a1d61..9e69b2f0849 100644 --- a/src/java.base/share/classes/java/security/CodeSource.java +++ b/src/java.base/share/classes/java/security/CodeSource.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 @@ -238,7 +238,12 @@ public class CodeSource implements java.io.Serializable { } else if (certs != null) { // Convert the certs to code signers signers = convertCertArrayToSignerArray(certs); - return signers.clone(); + if (signers != null) { + return signers.clone(); + + } else { + return new CodeSigner[0]; + } } else { return null; diff --git a/test/jdk/java/security/CodeSource/CodeSourceNoInputs.java b/test/jdk/java/security/CodeSource/CodeSourceNoInputs.java new file mode 100644 index 00000000000..e1d2497667b --- /dev/null +++ b/test/jdk/java/security/CodeSource/CodeSourceNoInputs.java @@ -0,0 +1,110 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.CodeSigner; +import java.security.CodeSource; +import java.security.PublicKey; +import java.security.cert.Certificate; + +/** + * @test + * @bug 8366522 + * @summary Verify that getCertificates() and getCodeSigners() return correct + * results when CodeSource is created with empty or null Certificate[] + * or CodeSigner[] arguments, or there are no X509 certificates in + * certs. Make sure that NPE is not thrown from + * CodeSource.getCodeSigners() + */ +public class CodeSourceNoInputs { + private static final Certificate NON_X509_CERT = new Certificate("") { + @Override + public byte[] getEncoded() { + return new byte[0]; + } + + @Override + public void verify(PublicKey key) { + } + + @Override + public void verify(PublicKey key, String sigProvider) { + } + + @Override + public String toString() { + return ""; + } + + @Override + public PublicKey getPublicKey() { + return null; + } + }; + + public static void main(String[] args) throws Exception { + CodeSource cs; + + cs = new CodeSource(null, (Certificate[]) null); + if (cs.getCodeSigners() != null || cs.getCertificates() != null) { + + throw new SecurityException("Both CodeSource.getCodeSigners() " + + "and CodeSource.getCertificates() should return null for " + + "new CodeSource(null, (Certificate[]) null)"); + } + + cs = new CodeSource(null, (CodeSigner[]) null); + if (cs.getCodeSigners() != null || cs.getCertificates() != null) { + + throw new SecurityException("Both CodeSource.getCodeSigners() " + + "and CodeSource.getCertificates() should return null for " + + "new CodeSource(null, (CodeSigners[]) null)"); + } + + cs = new CodeSource(null, new Certificate[0]); + if (cs.getCodeSigners().length != 0 + || cs.getCertificates().length != 0) { + + throw new SecurityException("Both CodeSource.getCodeSigners()" + + "and CodeSource.getCertificates() should return empty arrays " + + "for new CodeSource(null, new Certificate[0])"); + + } + + cs = new CodeSource(null, new CodeSigner[0]); + if (cs.getCodeSigners().length != 0 + || cs.getCertificates().length != 0) { + + throw new SecurityException("Both CodeSource.getCodeSigners() and" + + " CodeSource.getCertificates() should return empty arrays for" + + " new CodeSource(null, new CodeSigners[0])"); + } + + cs = new CodeSource(null, new Certificate[]{NON_X509_CERT}); + if (cs.getCodeSigners().length != 0 || cs.getCertificates() == null) { + + throw new SecurityException("Both CodeSource.getCodeSigners() and" + + " CodeSource.getCertificates() should return arrays for new" + + " CodeSource(null, new CodeSigners[1]{NON-X509-CERTIFICATE})"); + } + } +} From e97fb0e2072a16c59014599719b64e8ea52a4976 Mon Sep 17 00:00:00 2001 From: Koushik Thirupattur Date: Thu, 15 Jan 2026 19:01:24 +0000 Subject: [PATCH 118/204] 8367024: JNI exception pending in Java_sun_security_pkcs11_wrapper_PKCS11_C_1DeriveKey of p11_keymgmt.c:950 Reviewed-by: valeriep, hchao, djelinski --- .../share/native/libj2pkcs11/p11_keymgmt.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c index f2e1a46565d..e540f3b5ed9 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_keymgmt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 2002 Graz University of Technology. All rights reserved. @@ -929,6 +929,11 @@ JNIEXPORT jlong JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1DeriveKey rv = (*ckpFunctions->C_DeriveKey)(ckSessionHandle, ckpMechanism, ckBaseKeyHandle, ckpAttributes, ckAttributesLength, phKey); + /* If derivation failed, do not attempt copy-back */ + if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { + goto cleanup; + } + jKeyHandle = ckLongToJLong(ckKeyHandle); switch (ckpMechanism->mechanism) { @@ -956,8 +961,9 @@ JNIEXPORT jlong JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1DeriveKey // empty break; } - if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { - jKeyHandle =0L; + /* Do not continue if any copy-back operation raised an exception */ + if ((*env)->ExceptionCheck(env)) { + goto cleanup; } cleanup: From 25c834a897ac0cac94942a019c9e377a53851f2c Mon Sep 17 00:00:00 2001 From: Koushik Thirupattur Date: Thu, 15 Jan 2026 19:05:19 +0000 Subject: [PATCH 119/204] 8366807: JNI exception pending in Java_sun_security_pkcs11_wrapper_PKCS11_initializeLibrary of p11_general.c:106 Reviewed-by: valeriep --- .../share/native/libj2pkcs11/p11_general.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_general.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_general.c index e9bcf597a03..f61b8a5fb49 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_general.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_general.c @@ -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. */ /* Copyright (c) 2002 Graz University of Technology. All rights reserved. @@ -100,6 +100,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_initializeLibrary #ifndef NO_CALLBACKS if (notifyListLock == NULL) { notifyListLock = createLockObject(env); + + /* Return immediately if lock creation failed or an exception is pending. */ + if (notifyListLock == NULL || (*env)->ExceptionCheck(env)) { + TRACE0("DEBUG: createLockObject failed, aborting initialization\n"); + return; + } } #endif From a8b845e08ce2f1fbe7d807cd963cb6b5e4df5ce6 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Thu, 15 Jan 2026 19:14:46 +0000 Subject: [PATCH 120/204] 8374445: Fix -Wzero-as-null-pointer-constant warnings in JfrSet Reviewed-by: mgronlun --- src/hotspot/share/jfr/utilities/jfrSet.hpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/jfr/utilities/jfrSet.hpp b/src/hotspot/share/jfr/utilities/jfrSet.hpp index 1432d40e4b2..3d394d10d8b 100644 --- a/src/hotspot/share/jfr/utilities/jfrSet.hpp +++ b/src/hotspot/share/jfr/utilities/jfrSet.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 @@ -25,6 +25,7 @@ #ifndef SHARE_JFR_UTILITIES_JFRSET_HPP #define SHARE_JFR_UTILITIES_JFRSET_HPP +#include "cppstdlib/new.hpp" #include "jfr/utilities/jfrTypes.hpp" #include "memory/allocation.hpp" @@ -67,7 +68,9 @@ class JfrSetStorage : public AnyObj { } else { table = NEW_RESOURCE_ARRAY(K, table_size); } - memset(table, 0, table_size * sizeof(K)); + for (unsigned i = 0; i < table_size; ++i) { + ::new (&table[i]) K{}; + } return table; } @@ -88,7 +91,7 @@ class JfrSetStorage : public AnyObj { assert(is_nonempty(), "invariant"); for (unsigned i = 0; i < _table_size; ++i) { K k = _table[i]; - if (k != 0) { + if (k != K{}) { functor(k); } } @@ -136,11 +139,11 @@ class JfrSet : public JfrSetStorage { _resize_threshold = old_table_size; for (unsigned i = 0; i < old_table_size; ++i) { const K k = old_table[i]; - if (k != 0) { + if (k != K{}) { uint32_t idx = slot_idx(CONFIG::hash(k)); do { K v = this->_table[idx]; - if (v == 0) { + if (v == K{}) { this->_table[idx] = k; break; } @@ -161,7 +164,7 @@ class JfrSet : public JfrSetStorage { K* result = nullptr; while (true) { K v = this->_table[idx]; - if (v == 0) { + if (v == K{}) { result = &this->_table[idx]; break; } @@ -196,7 +199,7 @@ class JfrSet : public JfrSetStorage { // Already exists. return false; } - assert(*slot == 0, "invariant"); + assert(*slot == K{}, "invariant"); *slot = k; if (++this->_elements == _resize_threshold) { resize(); From 30cda00010888b6e9a2bf8cdeaedbb3eb4b6a222 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 15 Jan 2026 19:31:11 +0000 Subject: [PATCH 121/204] 8375294: (fs) Files.copy can fail with EOPNOTSUPP when copy_file_range not supported Reviewed-by: alanb, jpai --- src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c | 5 +++-- src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c b/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c index efbd0ca5684..54d640b03a4 100644 --- a/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c +++ b/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c @@ -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 @@ -63,7 +63,7 @@ Java_sun_nio_ch_FileDispatcherImpl_transferFrom0(JNIEnv *env, jobject this, if (n < 0) { if (errno == EAGAIN) return IOS_UNAVAILABLE; - if (errno == ENOSYS) + if (errno == ENOSYS || errno == EOPNOTSUPP) return IOS_UNSUPPORTED_CASE; if ((errno == EBADF || errno == EINVAL || errno == EXDEV) && ((ssize_t)count >= 0)) @@ -103,6 +103,7 @@ Java_sun_nio_ch_FileDispatcherImpl_transferTo0(JNIEnv *env, jobject this, case EINVAL: case ENOSYS: case EXDEV: + case EOPNOTSUPP: // ignore and try sendfile() break; default: diff --git a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c index c90e99dda07..4677411b0ba 100644 --- a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c +++ b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2024, 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 @@ -193,6 +193,7 @@ Java_sun_nio_fs_LinuxNativeDispatcher_directCopy0 case EINVAL: case ENOSYS: case EXDEV: + case EOPNOTSUPP: // ignore and try sendfile() break; default: From a1b039aa989ca91b6e70962363f720f581c5bfaf Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Thu, 15 Jan 2026 22:33:34 +0000 Subject: [PATCH 122/204] 8286032: keytool -list -alias should not assume it is always a certificate Reviewed-by: weijun --- .../sun/security/tools/keytool/Main.java | 9 +-- .../sun/security/tools/keytool/ListAlias.java | 71 +++++++++++++++++++ .../sun/security/tools/keytool/WeakAlg.java | 14 ++-- 3 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 test/jdk/sun/security/tools/keytool/ListAlias.java diff --git a/src/java.base/share/classes/sun/security/tools/keytool/Main.java b/src/java.base/share/classes/sun/security/tools/keytool/Main.java index 9fb830da338..7f415da5270 100644 --- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java +++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.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 @@ -1294,7 +1294,7 @@ public final class Main { } if (alias != null) { - doPrintEntry(rb.getString("the.certificate"), alias, out); + doPrintEntry(alias, out); } else { doPrintEntries(out); } @@ -2177,9 +2177,10 @@ public final class Main { /** * Prints a single keystore entry. */ - private void doPrintEntry(String label, String alias, PrintStream out) + private void doPrintEntry(String alias, PrintStream out) throws Exception { + String label = "<" + alias + ">"; CertPathConstraintsParameters cpcp; if (!keyStore.containsAlias(alias)) { MessageFormat form = new MessageFormat @@ -2631,7 +2632,7 @@ public final class Main { List aliases = Collections.list(keyStore.aliases()); aliases.sort(String::compareTo); for (String alias : aliases) { - doPrintEntry("<" + alias + ">", alias, out); + doPrintEntry(alias, out); if (verbose || rfc) { out.println(rb.getString("NEWLINE")); out.println(rb.getString diff --git a/test/jdk/sun/security/tools/keytool/ListAlias.java b/test/jdk/sun/security/tools/keytool/ListAlias.java new file mode 100644 index 00000000000..91994c04a9c --- /dev/null +++ b/test/jdk/sun/security/tools/keytool/ListAlias.java @@ -0,0 +1,71 @@ +/* + * 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 8286032 + * @summary Validate the warnings of the keytool -list -alias command + * @library /test/lib + */ + +import jdk.test.lib.SecurityTools; + +public class ListAlias { + + public static void main(String[] args) throws Exception { + SecurityTools.keytool("-keystore ks -storepass changeit " + + "-genseckey -keyalg DES -alias deskey") + .shouldContain("Warning") + .shouldMatch("The generated secret key uses the DES algorithm.*considered a security risk") + .shouldHaveExitValue(0); + + SecurityTools.keytool("-keystore ks -storepass changeit " + + "-list -alias deskey -v") + .shouldContain("Warning") + .shouldMatch(" uses the DES algorithm.*considered a security risk") + .shouldNotContain("The certificate") + .shouldHaveExitValue(0); + + SecurityTools.keytool("-keystore ks -storepass changeit " + + "-genkeypair -keyalg RSA -alias ca -dname CN=CA -ext bc:c " + + "-sigalg SHA1withRSA") + .shouldContain("Warning") + .shouldMatch("The generated certificate uses the SHA1withRSA.*considered a security risk") + .shouldHaveExitValue(0); + + SecurityTools.keytool("-keystore ks -storepass changeit " + + "-list -alias ca -v") + .shouldContain("Warning") + .shouldMatch(" uses the SHA1withRSA.*considered a security risk") + .shouldNotContain("The certificate") + .shouldHaveExitValue(0); + + SecurityTools.keytool("-keystore ks -storepass changeit " + + "-list -v") + .shouldContain("Warning") + .shouldMatch(" uses the DES algorithm.*considered a security risk") + .shouldMatch(" uses the SHA1withRSA.*considered a security risk") + .shouldNotContain("The certificate") + .shouldHaveExitValue(0); + } +} \ No newline at end of file diff --git a/test/jdk/sun/security/tools/keytool/WeakAlg.java b/test/jdk/sun/security/tools/keytool/WeakAlg.java index de2435a4f4b..546a4182e97 100644 --- a/test/jdk/sun/security/tools/keytool/WeakAlg.java +++ b/test/jdk/sun/security/tools/keytool/WeakAlg.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 @@ -750,7 +750,7 @@ public class WeakAlg { oa.shouldNotContain("Warning"); } else { oa.shouldContain("Warning") - .shouldMatch("The certificate.*" + bad + ".*is disabled"); + .shouldMatch("uses.*" + bad + ".*is disabled"); } // With cert content @@ -770,7 +770,7 @@ public class WeakAlg { } else { oa.shouldContain("Warning") .shouldContain(bad + " (disabled)") - .shouldMatch("The certificate.*" + bad + ".*is disabled"); + .shouldMatch("uses.*" + bad + ".*is disabled"); } } @@ -844,11 +844,11 @@ public class WeakAlg { break; case "SHA1withRSA": oa.shouldContain("Warning") - .shouldMatch("The certificate.*" + bad + ".*considered a security risk"); + .shouldMatch("uses.*" + bad + ".*considered a security risk"); break; case "1024-bit RSA key": oa.shouldContain("Warning") - .shouldMatch("The certificate.*" + bad + ".*will be disabled"); + .shouldMatch("uses.*" + bad + ".*will be disabled"); break; } @@ -879,12 +879,12 @@ public class WeakAlg { case "SHA1withRSA": oa.shouldContain("Warning") .shouldContain(bad + " (weak)") - .shouldMatch("The certificate.*" + bad + ".*considered a security risk"); + .shouldMatch("uses.*" + bad + ".*considered a security risk"); break; case "1024-bit RSA key": oa.shouldContain("Warning") .shouldContain(bad + " (weak)") - .shouldMatch("The certificate.*" + bad + ".*will be disabled"); + .shouldMatch("uses.*" + bad + ".*will be disabled"); break; } } From 87cbcadacfa20b24e9ba0bf8374ecbcd331d2b35 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Thu, 15 Jan 2026 22:35:49 +0000 Subject: [PATCH 123/204] 8351892: GenShen: Remove vestigial young generation sizing options Reviewed-by: kdnilsen, ysr --- .../gc/shenandoah/shenandoahGenerationalHeap.cpp | 11 ----------- .../share/gc/shenandoah/shenandoah_globals.hpp | 12 ------------ 2 files changed, 23 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index f887cc9064e..fa78e02e6af 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -104,17 +104,6 @@ void ShenandoahGenerationalHeap::initialize_heuristics() { // Initialize global generation and heuristics even in generational mode. ShenandoahHeap::initialize_heuristics(); - // Max capacity is the maximum _allowed_ capacity. That is, the maximum allowed capacity - // for old would be total heap - minimum capacity of young. This means the sum of the maximum - // allowed for old and young could exceed the total heap size. It remains the case that the - // _actual_ capacity of young + old = total. - size_t region_count = num_regions(); - size_t max_young_regions = MAX2((region_count * ShenandoahMaxYoungPercentage) / 100, (size_t) 1U); - size_t initial_capacity_young = max_young_regions * ShenandoahHeapRegion::region_size_bytes(); - size_t max_capacity_young = initial_capacity_young; - size_t initial_capacity_old = max_capacity() - max_capacity_young; - size_t max_capacity_old = max_capacity() - initial_capacity_young; - _young_generation = new ShenandoahYoungGeneration(max_workers()); _old_generation = new ShenandoahOldGeneration(max_workers()); _young_generation->initialize_heuristics(mode()); diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index 15520c47bbc..254483d1923 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -430,18 +430,6 @@ "by thread type (worker or mutator) and evacuation type (young, " \ "old, or promotion.") \ \ - product(uintx, ShenandoahMinYoungPercentage, 20, EXPERIMENTAL, \ - "The minimum percentage of the heap to use for the young " \ - "generation. Heuristics will not adjust the young generation " \ - "to be less than this.") \ - range(0, 100) \ - \ - product(uintx, ShenandoahMaxYoungPercentage, 100, EXPERIMENTAL, \ - "The maximum percentage of the heap to use for the young " \ - "generation. Heuristics will not adjust the young generation " \ - "to be more than this.") \ - range(0, 100) \ - \ product(uintx, ShenandoahCriticalFreeThreshold, 1, EXPERIMENTAL, \ "How much of the heap needs to be free after recovery cycles, " \ "either Degenerated or Full GC to be claimed successful. If this "\ From 1d889b92bde5dfcb1fbe6cddb389a77f92eb1ce7 Mon Sep 17 00:00:00 2001 From: Volodymyr Paprotski Date: Thu, 15 Jan 2026 23:11:12 +0000 Subject: [PATCH 124/204] 8360271: String.indexOf intrinsics fail with +EnableX86ECoreOpts and -CompactStrings Reviewed-by: thartmann, jbhateja, sviswanathan --- .../x86/c2_stubGenerator_x86_64_string.cpp | 8 +++++--- test/jdk/java/lang/String/IndexOf.java | 19 ++++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp b/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp index 0951bda0d17..77a149addb5 100644 --- a/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp +++ b/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Intel Corporation. All rights reserved. + * Copyright (c) 2024, 2026, Intel Corporation. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1330,10 +1330,12 @@ static void big_case_loop_helper(bool sizeKnown, int size, Label &noMatch, Label // Clarification: The BYTE_K compare above compares haystack[(n-32):(n-1)]. We need to // compare haystack[(k-1):(k-1+31)]. Subtracting either index gives shift value of // (k + 31 - n): x = (k-1+31)-(n-1) = k-1+31-n+1 = k+31-n. + // When isU is set, similarly, shift is from haystack[(n-32):(n-1)] to [(k-2):(k-2+31)] + if (sizeKnown) { - __ movl(temp2, 31 + size); + __ movl(temp2, (isU ? 30 : 31) + size); } else { - __ movl(temp2, 31); + __ movl(temp2, isU ? 30 : 31); __ addl(temp2, needleLen); } __ subl(temp2, hsLength); diff --git a/test/jdk/java/lang/String/IndexOf.java b/test/jdk/java/lang/String/IndexOf.java index b4fd17105a8..48f23de79b0 100644 --- a/test/jdk/java/lang/String/IndexOf.java +++ b/test/jdk/java/lang/String/IndexOf.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Intel Corporation. All rights reserved. + * Copyright (c) 2024, 2026, Intel Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,15 @@ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xcomp -XX:-TieredCompilation -XX:UseAVX=2 -XX:+UnlockDiagnosticVMOptions -XX:+EnableX86ECoreOpts IndexOf */ +/* + * @test + * @bug 8360271 + * @summary test String indexOf() intrinsic + * @requires vm.cpu.features ~= ".*avx2.*" + * @requires vm.compiler2.enabled + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xcomp -XX:-TieredCompilation -XX:UseAVX=2 -XX:+UnlockDiagnosticVMOptions -XX:+EnableX86ECoreOpts -XX:-CompactStrings IndexOf + */ + public class IndexOf { final int scope = 32*2+16+8; final char a, aa, b, c, d; @@ -56,11 +65,11 @@ d = 'd'; break; case UU: - a = '\u0061'; + a = '\u1061'; aa = a; - b = '\u0062'; + b = '\u1062'; c = '\u1063'; - d = '\u0064'; + d = '\u1064'; break; default: //case UL: a = 'a'; @@ -73,7 +82,7 @@ } // needle =~ /ab*d/ - // badNeedle =~ /ab*db*d/ + // badNeedle =~ /ab*cb*d/ interface Append {void append(int pos, char cc);} String newNeedle(int size, int badPosition) { if (size<2) {throw new RuntimeException("Fix testcase "+size);} From fddba3b7ecb11136e9699861b5d86aeb3d481be6 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Fri, 16 Jan 2026 00:47:24 +0000 Subject: [PATCH 125/204] 8375350: Remove usage of AppContext from javax.imageio implementation Reviewed-by: kizune, dnguyen --- .../share/classes/javax/imageio/ImageIO.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/java.desktop/share/classes/javax/imageio/ImageIO.java b/src/java.desktop/share/classes/javax/imageio/ImageIO.java index 8180145a8ea..5d3526df917 100644 --- a/src/java.desktop/share/classes/javax/imageio/ImageIO.java +++ b/src/java.desktop/share/classes/javax/imageio/ImageIO.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 @@ -47,7 +47,6 @@ import javax.imageio.spi.ImageTranscoderSpi; import javax.imageio.spi.ServiceRegistry; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageOutputStream; -import sun.awt.AppContext; /** * A class containing static convenience methods for locating @@ -108,9 +107,7 @@ public final class ImageIO { // ImageInputStreams /** - * A class to hold information about caching. Each - * {@code ThreadGroup} will have its own copy - * via the {@code AppContext} mechanism. + * A class to hold information about caching. */ static class CacheInfo { boolean useCache = true; @@ -144,17 +141,12 @@ public final class ImageIO { } } + private static final CacheInfo info = new CacheInfo(); + /** - * Returns the {@code CacheInfo} object associated with this - * {@code ThreadGroup}. + * Returns the {@code CacheInfo} object. */ private static synchronized CacheInfo getCacheInfo() { - AppContext context = AppContext.getAppContext(); - CacheInfo info = (CacheInfo)context.get(CacheInfo.class); - if (info == null) { - info = new CacheInfo(); - context.put(CacheInfo.class, info); - } return info; } From 9876875e37b5cd4ac5263007ff96611ab0707cd5 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 16 Jan 2026 02:51:40 +0000 Subject: [PATCH 126/204] 8375364: [macos] Some jpackage signing tests fail after JDK-8375240 Reviewed-by: almatvee --- .../jpackage/helpers/jdk/jpackage/test/JPackageCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9b8b05af93b..7874df3fd69 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -971,7 +971,7 @@ public class JPackageCommand extends CommandArguments { executePrerequisiteActions(); nullableOutputBundle().filter(_ -> { - return removeOldOutputBundle; + return !(TKit.isOSX() && MacHelper.signPredefinedAppImage(this)) && removeOldOutputBundle; }).ifPresent(path -> { ThrowingRunnable.toRunnable(() -> { if (Files.isDirectory(path)) { From e4474ad8ae250771e031b8c18809d3e461970365 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Fri, 16 Jan 2026 03:19:28 +0000 Subject: [PATCH 127/204] 8375367: vmTestbase tests reported variable uninitialized by clang23 Reviewed-by: sspitsyn, amenkov, lmesnik --- .../nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002.cpp | 4 ++-- .../nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002a.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002.cpp b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002.cpp index 52459a7994f..8205729e4ef 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002.cpp +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, 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 @@ -113,7 +113,7 @@ static int prepare(JNIEnv* jni) { /** Agent algorithm. */ static void JNICALL agentProc(jvmtiEnv* jvmti, JNIEnv* jni, void* arg) { - jint dummy; + jint dummy = 0; if (!nsk_jvmti_waitForSync(timeout)) return; diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002a.cpp b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002a.cpp index 7f716d009ce..f2cff01948b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002a.cpp +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/multienv/MA04/ma04t002/ma04t002a.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2024, 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 @@ -113,7 +113,7 @@ static int prepare(JNIEnv* jni) { /** Agent algorithm. */ static void JNICALL agentProc(jvmtiEnv* jvmti, JNIEnv* jni, void* arg) { - jint dummy; + jint dummy = 0; if (!nsk_jvmti_waitForSync(timeout)) return; From fda8d0506a511c00e65c3f97aaaf6f018945b213 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Fri, 16 Jan 2026 07:48:26 +0000 Subject: [PATCH 128/204] 8375455: G1: Remove unused G1HeapRegionStats::coarsen_stats() Reviewed-by: kbarrett --- src/hotspot/share/gc/g1/g1CardSet.cpp | 6 +----- src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp | 5 +---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CardSet.cpp b/src/hotspot/share/gc/g1/g1CardSet.cpp index 80cf5fea76a..3441e6bc608 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.cpp +++ b/src/hotspot/share/gc/g1/g1CardSet.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 @@ -1024,10 +1024,6 @@ size_t G1CardSet::num_containers() { return cl._count; } -G1CardSetCoarsenStats G1CardSet::coarsen_stats() { - return _coarsen_stats; -} - void G1CardSet::print_coarsen_stats(outputStream* out) { _last_coarsen_stats.subtract_from(_coarsen_stats); diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp index 0f78e0d6271..878d35397aa 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.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 @@ -123,9 +123,6 @@ public: static void initialize(MemRegion reserved); - // Coarsening statistics since VM start. - static G1CardSetCoarsenStats coarsen_stats() { return G1CardSet::coarsen_stats(); } - inline uintptr_t to_card(OopOrNarrowOopStar from) const; private: From 5664d9148401934cd26308dc4493f4a5656e89bd Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Fri, 16 Jan 2026 08:01:40 +0000 Subject: [PATCH 129/204] 8374769: PPC: MASM::pop_cont_fastpath() should reset _cont_fastpath if SP == _cont_fastpath Reviewed-by: mdoerr --- src/hotspot/cpu/ppc/macroAssembler_ppc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index 5649ead2ea8..809285afddb 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -4535,7 +4535,7 @@ void MacroAssembler::push_cont_fastpath() { Label done; ld_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread); cmpld(CR0, R1_SP, R0); - ble(CR0, done); + ble(CR0, done); // if (SP <= _cont_fastpath) goto done; st_ptr(R1_SP, JavaThread::cont_fastpath_offset(), R16_thread); bind(done); } @@ -4546,7 +4546,7 @@ void MacroAssembler::pop_cont_fastpath() { Label done; ld_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread); cmpld(CR0, R1_SP, R0); - ble(CR0, done); + blt(CR0, done); // if (SP < _cont_fastpath) goto done; li(R0, 0); st_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread); bind(done); From b7346c307fc1aba01c10fc6dc745e5e520b1d7b9 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Fri, 16 Jan 2026 08:03:55 +0000 Subject: [PATCH 130/204] 8375311: Some builds are missing debug helpers Reviewed-by: mdoerr, aph --- src/hotspot/share/utilities/debug.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp index 9e167141259..0e1ca1efb98 100644 --- a/src/hotspot/share/utilities/debug.cpp +++ b/src/hotspot/share/utilities/debug.cpp @@ -70,8 +70,10 @@ #include #include -// These functions needs to be exported on Windows only -#define DEBUGEXPORT WINDOWS_ONLY(JNIEXPORT) +// These functions needs to be exported on Windows +// On Linux it is also beneficial to export them to avoid +// losing them e.g. with linktime gc +#define DEBUGEXPORT JNIEXPORT // Support for showing register content on asserts/guarantees. #ifdef CAN_SHOW_REGISTERS_ON_ASSERT @@ -653,13 +655,12 @@ extern "C" DEBUGEXPORT intptr_t u5p(intptr_t addr, void pp(intptr_t p) { pp((void*)p); } void pp(oop p) { pp((void*)p); } -void help() { +extern "C" DEBUGEXPORT void help() { Command c("help"); tty->print_cr("basic"); tty->print_cr(" pp(void* p) - try to make sense of p"); tty->print_cr(" ps() - print current thread stack"); tty->print_cr(" pss() - print all thread stacks"); - tty->print_cr(" pm(int pc) - print Method* given compiled PC"); tty->print_cr(" findnm(intptr_t pc) - find nmethod*"); tty->print_cr(" findm(intptr_t pc) - find Method*"); tty->print_cr(" find(intptr_t x) - find & print nmethod/stub/bytecode/oop based on pointer into it"); From e7432d574540109e2c4faca11cf49d9272a147e6 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 16 Jan 2026 20:03:00 +0000 Subject: [PATCH 131/204] 8375323: Improve handling of the "--app-content" and "--input" options in jpackage Reviewed-by: almatvee --- .../jdk/jpackage/internal/LinuxPackager.java | 4 +- .../jdk/jpackage/internal/AppImageSigner.java | 2 +- .../internal/MacApplicationBuilder.java | 22 +- .../internal/MacDmgPackageBuilder.java | 16 +- .../jdk/jpackage/internal/MacDmgPackager.java | 6 +- .../jdk/jpackage/internal/MacFromOptions.java | 11 +- .../jdk/jpackage/internal/MacPkgPackager.java | 1 - .../internal/model/MacDmgPackageMixin.java | 13 +- .../jpackage/internal/ApplicationBuilder.java | 24 +- .../internal/ApplicationImageUtils.java | 49 +- .../internal/DefaultBundlingEnvironment.java | 3 +- .../jdk/jpackage/internal/FromOptions.java | 15 +- .../internal/LauncherFromOptions.java | 5 +- .../jpackage/internal/PackagingPipeline.java | 10 +- .../cli/JOptSimpleOptionsBuilder.java | 7 +- .../cli/OptionArrayValueConverter.java | 4 +- .../jdk/jpackage/internal/cli/OptionSpec.java | 13 +- .../internal/cli/OptionSpecBuilder.java | 120 +++-- .../cli/OptionSpecMapperOptionScope.java | 103 +++- .../internal/cli/OptionValueConverter.java | 503 ++++++++++++++---- .../internal/cli/OptionsAnalyzer.java | 1 - .../internal/cli/OptionsProcessor.java | 7 +- .../cli/StandardAppImageFileOption.java | 2 +- .../jpackage/internal/cli/StandardOption.java | 100 +++- .../internal/cli/StandardValueConverter.java | 76 ++- .../jdk/jpackage/internal/cli/Validator.java | 10 +- .../jpackage/internal/cli/ValueConverter.java | 27 +- .../internal/cli/ValueConverterFunction.java | 40 ++ .../jpackage/internal/model/Application.java | 43 +- .../resources/MainResources.properties | 1 + .../jdk/jpackage/internal/util/FileUtils.java | 26 +- .../jpackage/internal/util/RootedPath.java | 185 +++++++ .../jdk/jpackage/internal/WinExePackager.java | 2 +- .../jdk/jpackage/internal/WinMsiPackager.java | 2 +- .../jpackage/internal/AppImageFileTest.java | 2 +- .../internal/PackagingPipelineTest.java | 2 +- .../cli/OptionSpecMutatorOptionScopeTest.java | 10 +- .../jpackage/internal/cli/OptionSpecTest.java | 10 +- .../cli/OptionValueConverterTest.java | 188 ++++++- .../internal/cli/StandardOptionTest.java | 256 ++++++++- .../cli/StandardValueConverterTest.java | 12 +- .../jpackage/internal/util/FileUtilsTest.java | 76 +-- .../tools/jdk/jpackage/test/JUnitUtils.java | 23 +- .../tools/jpackage/share/AppContentTest.java | 80 ++- .../tools/jpackage/share/InOutPathTest.java | 173 +++--- 45 files changed, 1706 insertions(+), 579 deletions(-) create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverterFunction.java create mode 100644 src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RootedPath.java diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackager.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackager.java index af7f5288cc5..551e1ab1af6 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackager.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackager.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 @@ abstract class LinuxPackager implements Consumer { + return rootedPath.branch().getNameCount() == 1; + }).map(RootedPath::fullPath).forEach(contentDir -> { if (!Files.isDirectory(contentDir)) { Log.info(I18N.format("warning.app.content.is.not.dir", contentDir)); - } else if (!CONTENTS_SUB_DIRS.contains(contentDir.getFileName().toString())) { + } else if (!CONTENTS_SUB_DIRS.contains(contentDir.getFileName())) { Log.info(I18N.format("warning.non.standard.contents.sub.dir", contentDir)); } - } + }); } private MacApplicationBuilder createCopyForExternalInfoPlistFile() { @@ -258,6 +263,11 @@ final class MacApplicationBuilder { private static final int MAX_BUNDLE_NAME_LENGTH = 16; // List of standard subdirectories of the "Contents" directory - private static final Set CONTENTS_SUB_DIRS = Set.of("MacOS", - "Resources", "Frameworks", "PlugIns", "SharedSupport"); + private static final Set CONTENTS_SUB_DIRS = Stream.of( + "MacOS", + "Resources", + "Frameworks", + "PlugIns", + "SharedSupport" + ).map(Path::of).collect(Collectors.toUnmodifiableSet()); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java index 10754b1f1b6..b9e407b4a47 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackageBuilder.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,11 +25,13 @@ package jdk.jpackage.internal; import java.nio.file.Path; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; import jdk.jpackage.internal.model.MacDmgPackage; import jdk.jpackage.internal.model.MacDmgPackageMixin; +import jdk.jpackage.internal.util.RootedPath; final class MacDmgPackageBuilder { @@ -37,8 +39,8 @@ final class MacDmgPackageBuilder { this.pkgBuilder = Objects.requireNonNull(pkgBuilder); } - MacDmgPackageBuilder dmgContent(List v) { - dmgContent = v; + MacDmgPackageBuilder dmgRootDirSources(Collection v) { + dmgRootDirSources = v; return this; } @@ -47,8 +49,8 @@ final class MacDmgPackageBuilder { return this; } - List validatedDmgContent() { - return Optional.ofNullable(dmgContent).orElseGet(List::of); + private Collection validatedDmgRootDirSources() { + return Optional.ofNullable(dmgRootDirSources).orElseGet(List::of); } MacDmgPackage create() { @@ -56,10 +58,10 @@ final class MacDmgPackageBuilder { return MacDmgPackage.create(pkg, new MacDmgPackageMixin.Stub( Optional.ofNullable(icon).or((pkg.app())::icon), - validatedDmgContent())); + validatedDmgRootDirSources())); } private Path icon; - private List dmgContent; + private Collection dmgRootDirSources; private final MacPackageBuilder pkgBuilder; } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java index cf4db226d37..24474c88162 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacDmgPackager.java @@ -46,6 +46,7 @@ import jdk.jpackage.internal.PackagingPipeline.TaskID; import jdk.jpackage.internal.model.MacDmgPackage; import jdk.jpackage.internal.util.FileUtils; import jdk.jpackage.internal.util.PathGroup; +import jdk.jpackage.internal.util.RootedPath; record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, MacDmgSystemEnvironment sysEnv) implements Consumer { @@ -60,7 +61,6 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, @Override public void accept(PackagingPipeline.Builder pipelineBuilder) { pipelineBuilder - .excludeDirFromCopying(outputDir) .task(DmgPackageTaskID.COPY_DMG_CONTENT) .action(this::copyDmgContent) .addDependent(PackageTaskID.CREATE_PACKAGE_FILE) @@ -130,9 +130,7 @@ record MacDmgPackager(BuildEnv env, MacDmgPackage pkg, Path outputDir, private void copyDmgContent() throws IOException { final var srcFolder = env.appImageDir(); - for (Path path : pkg.content()) { - FileUtils.copyRecursive(path, srcFolder.resolve(path.getFileName())); - } + RootedPath.copy(pkg.dmgRootDirSources().stream(), srcFolder); } private Executor hdiutil(String... args) { 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 cdf33d6dcba..f3c1765210f 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java @@ -52,6 +52,7 @@ import static jdk.jpackage.internal.model.StandardPackageType.MAC_PKG; import static jdk.jpackage.internal.util.function.ExceptionBox.toUnchecked; import java.nio.file.Path; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -75,6 +76,7 @@ import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.Result; +import jdk.jpackage.internal.util.RootedPath; import jdk.jpackage.internal.util.function.ExceptionBox; @@ -92,7 +94,14 @@ final class MacFromOptions { final var pkgBuilder = new MacDmgPackageBuilder(superPkgBuilder); - MAC_DMG_CONTENT.ifPresentIn(options, pkgBuilder::dmgContent); + MAC_DMG_CONTENT.findIn(options).map((List> v) -> { + // Reverse the order of content sources. + // If there are multiple source files for the same + // destination file, only the first will be used. + // Reversing the order of content sources makes it use the last file + // from the original list of source files for the given destination file. + return v.reversed().stream().flatMap(Collection::stream).toList(); + }).ifPresent(pkgBuilder::dmgRootDirSources); return pkgBuilder.create(); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java index 126248e2330..fc5c85c699c 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackager.java @@ -215,7 +215,6 @@ record MacPkgPackager(BuildEnv env, MacPkgPackage pkg, Optional servic @Override public void accept(PackagingPipeline.Builder pipelineBuilder) { pipelineBuilder - .excludeDirFromCopying(outputDir) .task(PkgPackageTaskID.PREPARE_MAIN_SCRIPTS) .action(this::prepareMainScripts) .addDependent(PackageTaskID.RUN_POST_IMAGE_USER_SCRIPT) diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacDmgPackageMixin.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacDmgPackageMixin.java index 0b502e7ce7e..08797b11509 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacDmgPackageMixin.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/model/MacDmgPackageMixin.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,22 +25,23 @@ package jdk.jpackage.internal.model; import java.nio.file.Path; -import java.util.List; +import java.util.Collection; import java.util.Optional; +import jdk.jpackage.internal.util.RootedPath; public interface MacDmgPackageMixin { Optional icon(); /** - * Returns additional top=level content for DMG package. + * Returns the source paths that should be copied into the top-level directory of a DMG package. *

      * Each item in the list can be a directory or a file. * - * @return the additional top=level content for DMG package + * @return the source paths of additional top-level content for DMG package */ - List content(); + Collection dmgRootDirSources(); - record Stub(Optional icon, List content) implements MacDmgPackageMixin { + record Stub(Optional icon, Collection dmgRootDirSources) implements MacDmgPackageMixin { } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java index 6896668ffdc..9d5407524a8 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.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,6 +27,7 @@ package jdk.jpackage.internal; import static jdk.jpackage.internal.I18N.buildConfigException; import java.nio.file.Path; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; @@ -44,6 +45,7 @@ import jdk.jpackage.internal.model.LauncherIcon; import jdk.jpackage.internal.model.LauncherStartupInfo; import jdk.jpackage.internal.model.ResourceDirLauncherIcon; import jdk.jpackage.internal.model.RuntimeBuilder; +import jdk.jpackage.internal.util.RootedPath; final class ApplicationBuilder { @@ -65,8 +67,8 @@ final class ApplicationBuilder { Optional.ofNullable(version).orElseGet(DEFAULTS::version), Optional.ofNullable(vendor).orElseGet(DEFAULTS::vendor), Optional.ofNullable(copyright).orElseGet(DEFAULTS::copyright), - Optional.ofNullable(srcDir), - Optional.ofNullable(contentDirs).orElseGet(List::of), + Optional.ofNullable(appDirSources).orElseGet(List::of), + Optional.ofNullable(contentDirSources).orElseGet(List::of), appImageLayout, Optional.ofNullable(runtimeBuilder), launchersAsList, @@ -126,13 +128,13 @@ final class ApplicationBuilder { return this; } - ApplicationBuilder srcDir(Path v) { - srcDir = v; + ApplicationBuilder appDirSources(Collection v) { + appDirSources = v; return this; } - ApplicationBuilder contentDirs(List v) { - contentDirs = v; + ApplicationBuilder contentDirSources(Collection v) { + contentDirSources = v; return this; } @@ -246,8 +248,8 @@ final class ApplicationBuilder { app.version(), app.vendor(), app.copyright(), - app.srcDir(), - app.contentDirs(), + app.appDirSources(), + app.contentDirSources(), Objects.requireNonNull(appImageLayout), app.runtimeBuilder(), app.launchers(), @@ -294,9 +296,9 @@ final class ApplicationBuilder { private String version; private String vendor; private String copyright; - private Path srcDir; + private Collection appDirSources; private ExternalApplication externalApp; - private List contentDirs; + private Collection contentDirSources; private AppImageLayout appImageLayout; private RuntimeBuilder runtimeBuilder; private ApplicationLaunchers launchers; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java index 3d2ffbfdc7c..5edc4d69c81 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationImageUtils.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,19 +25,14 @@ package jdk.jpackage.internal; -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; - -import java.io.IOException; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Stream; import jdk.jpackage.internal.PackagingPipeline.ApplicationImageTaskAction; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; @@ -45,7 +40,7 @@ import jdk.jpackage.internal.model.CustomLauncherIcon; import jdk.jpackage.internal.model.DefaultLauncherIcon; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.ResourceDirLauncherIcon; -import jdk.jpackage.internal.util.FileUtils; +import jdk.jpackage.internal.util.RootedPath; final class ApplicationImageUtils { @@ -86,21 +81,14 @@ final class ApplicationImageUtils { }; } - static ApplicationImageTaskAction createCopyContentAction(Supplier> excludeCopyDirs) { + static ApplicationImageTaskAction createCopyContentAction() { return env -> { - var excludeCandidates = Stream.concat( - excludeCopyDirs.get().stream(), - Stream.of(env.env().buildRoot(), env.env().appImageDir()) - ).map(Path::toAbsolutePath).toList(); - - env.app().srcDir().ifPresent(toConsumer(srcDir -> { - copyRecursive(srcDir, env.resolvedLayout().appDirectory(), excludeCandidates); - })); - - for (var srcDir : env.app().contentDirs()) { - copyRecursive(srcDir, - env.resolvedLayout().contentDirectory().resolve(srcDir.getFileName()), - excludeCandidates); + for (var e : List.of( + Map.entry(env.app().appDirSources(), env.resolvedLayout().appDirectory()), + Map.entry(env.app().contentDirSources(), env.resolvedLayout().contentDirectory()) + )) { + RootedPath.copy(e.getKey().stream(), e.getValue(), + StandardCopyOption.REPLACE_EXISTING, LinkOption.NOFOLLOW_LINKS); } }; } @@ -126,21 +114,4 @@ final class ApplicationImageUtils { } }; } - - private static void copyRecursive(Path srcDir, Path dstDir, List excludeDirs) throws IOException { - srcDir = srcDir.toAbsolutePath(); - - List excludes = new ArrayList<>(); - - for (var path : excludeDirs) { - if (Files.isDirectory(path)) { - if (path.startsWith(srcDir) && !Files.isSameFile(path, srcDir)) { - excludes.add(path); - } - } - } - - FileUtils.copyRecursive(srcDir, dstDir.toAbsolutePath(), excludes, - LinkOption.NOFOLLOW_LINKS, StandardCopyOption.REPLACE_EXISTING); - } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java index 3a99dfb04da..331bde29d27 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DefaultBundlingEnvironment.java @@ -146,8 +146,7 @@ class DefaultBundlingEnvironment implements CliBundlingEnvironment { throw new JPackageException(I18N.format("error.root-exists", outputDir)); } - pipelineBuilder.excludeDirFromCopying(outputDir.getParent()) - .create().execute(BuildEnv.withAppImageDir(env, outputDir), app); + pipelineBuilder.create().execute(BuildEnv.withAppImageDir(env, outputDir), app); Log.verbose(I18N.getString("message.app-image-created")); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java index 6b74cab4e65..cccd05792d6 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/FromOptions.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 @@ -46,6 +46,7 @@ import static jdk.jpackage.internal.cli.StandardOption.RESOURCE_DIR; import static jdk.jpackage.internal.cli.StandardOption.VENDOR; import java.nio.file.Path; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; @@ -60,6 +61,7 @@ import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.LauncherModularStartupInfo; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.RuntimeLayout; +import jdk.jpackage.internal.util.RootedPath; final class FromOptions { @@ -168,8 +170,15 @@ final class FromOptions { APP_VERSION.ifPresentIn(options, appBuilder::version); VENDOR.ifPresentIn(options, appBuilder::vendor); COPYRIGHT.ifPresentIn(options, appBuilder::copyright); - INPUT.ifPresentIn(options, appBuilder::srcDir); - APP_CONTENT.ifPresentIn(options, appBuilder::contentDirs); + INPUT.ifPresentIn(options, appBuilder::appDirSources); + APP_CONTENT.findIn(options).map((List> v) -> { + // Reverse the order of content sources. + // If there are multiple source files for the same + // destination file, only the first will be used. + // Reversing the order of content sources makes it use the last file + // from the original list of source files for the given destination file. + return v.reversed().stream().flatMap(Collection::stream).toList(); + }).ifPresent(appBuilder::contentDirSources); if (isRuntimeInstaller) { appBuilder.appImageLayout(runtimeLayout); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java index 9e81d144a1e..1ba384dba2d 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherFromOptions.java @@ -56,6 +56,7 @@ import jdk.jpackage.internal.model.DefaultLauncherIcon; import jdk.jpackage.internal.model.FileAssociation; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.LauncherIcon; +import jdk.jpackage.internal.util.RootedPath; final class LauncherFromOptions { @@ -92,7 +93,9 @@ final class LauncherFromOptions { if (PREDEFINED_APP_IMAGE.findIn(options).isEmpty()) { final var startupInfoBuilder = new LauncherStartupInfoBuilder(); - INPUT.ifPresentIn(options, startupInfoBuilder::inputDir); + INPUT.findIn(options).flatMap(v -> { + return v.stream().findAny().map(RootedPath::root); + }).ifPresent(startupInfoBuilder::inputDir); ARGUMENTS.ifPresentIn(options, startupInfoBuilder::defaultParameters); JAVA_OPTIONS.ifPresentIn(options, startupInfoBuilder::javaOptions); MAIN_JAR.ifPresentIn(options, startupInfoBuilder::mainJar); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java index f15768b2cbf..315e3bce0f1 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/PackagingPipeline.java @@ -32,7 +32,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -428,12 +427,6 @@ final class PackagingPipeline { }); } - Builder excludeDirFromCopying(Path path) { - Objects.requireNonNull(path); - excludeCopyDirs.add(path); - return this; - } - Builder contextMapper(UnaryOperator v) { contextMapper = v; return this; @@ -456,7 +449,6 @@ final class PackagingPipeline { } private final FixedDAG.Builder taskGraphBuilder = FixedDAG.build(); - private final List excludeCopyDirs = new ArrayList<>(); private final Map taskConfig = new HashMap<>(); private UnaryOperator contextMapper; private FixedDAG taskGraphSnapshot; @@ -490,7 +482,7 @@ final class PackagingPipeline { builder.task(BuildApplicationTaskID.CONTENT) .addDependent(BuildApplicationTaskID.APP_IMAGE_FILE) - .applicationAction(ApplicationImageUtils.createCopyContentAction(() -> builder.excludeCopyDirs)).add(); + .applicationAction(ApplicationImageUtils.createCopyContentAction()).add(); return builder; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/JOptSimpleOptionsBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/JOptSimpleOptionsBuilder.java index 57b92471e4a..bff35874645 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/JOptSimpleOptionsBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/JOptSimpleOptionsBuilder.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,6 +27,7 @@ package jdk.jpackage.internal.cli; import static java.util.stream.Collectors.toUnmodifiableMap; import static java.util.stream.Collectors.toUnmodifiableSet; +import static jdk.jpackage.internal.cli.OptionValueConverter.convertString; import java.lang.reflect.Array; import java.util.ArrayList; @@ -565,7 +566,7 @@ final class JOptSimpleOptionsBuilder { final var converter = optionSpec.converter().orElseThrow(); final Result conversionResult = optionValue.map(v -> { - return converter.convert(optionName(), StringToken.of(v)); + return convertString(converter, optionName(), StringToken.of(v)); }).orElseGet(() -> { return Result.ofValue(optionSpec.defaultOptionalValue().orElseThrow()); }); @@ -579,7 +580,7 @@ final class JOptSimpleOptionsBuilder { final String str = getOptionValue(List.of(tokens), optionSpec.mergePolicy()).getFirst(); final String[] token = arrConverter.tokenize(str); if (token.length == 1 && str.equals(token[0])) { - final var singleTokenConversionResult = converter.convert(optionName(), StringToken.of(str)); + final var singleTokenConversionResult = convertString(converter, optionName(), StringToken.of(str)); if (singleTokenConversionResult.hasValue()) { return singleTokenConversionResult; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionArrayValueConverter.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionArrayValueConverter.java index 401d5e15d28..9b757c29d36 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionArrayValueConverter.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionArrayValueConverter.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 @@ -29,7 +29,7 @@ package jdk.jpackage.internal.cli; * * @param option value element type */ -interface OptionArrayValueConverter extends OptionValueConverter { +interface OptionArrayValueConverter extends OptionValueConverter { /** * Splits the given string into tokens and returns the result. diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpec.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpec.java index c6aa46a6940..60674e6bdfe 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpec.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpec.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,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; +import jdk.jpackage.internal.util.Result; /** @@ -56,7 +57,7 @@ import java.util.stream.Stream; */ record OptionSpec( List names, - Optional> converter, + Optional> converter, Set scope, MergePolicy mergePolicy, Optional defaultOptionalValue, @@ -134,7 +135,7 @@ record OptionSpec( }); } - OptionSpec copyWithConverter(OptionValueConverter converter) { + OptionSpec copyWithConverter(OptionValueConverter converter) { if (!defaultOptionalValue.isEmpty()) { throw new UnsupportedOperationException("Can not convert an option spec with optional value"); } @@ -169,12 +170,16 @@ record OptionSpec( return valueType(converter).orElseThrow(); } + Result convert(OptionName optionName, StringToken optionValue) { + return OptionValueConverter.convertString(converter().orElseThrow(), optionName, optionValue); + } + @SuppressWarnings("unchecked") Optional> arrayValueConverter() { return converter.filter(OptionArrayValueConverter.class::isInstance).map(v -> (OptionArrayValueConverter)v); } - private static Optional> valueType(Optional> valueConverter) { + private static Optional> valueType(Optional> valueConverter) { return valueConverter.map(OptionValueConverter::valueType); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java index 6cd1c05b57e..5eecbc9b464 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecBuilder.java @@ -24,6 +24,8 @@ */ package jdk.jpackage.internal.cli; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; + import java.io.File; import java.lang.reflect.Array; import java.util.Arrays; @@ -61,23 +63,49 @@ final class OptionSpecBuilder { this.valueType = Objects.requireNonNull(valueType); } - OptionSpecBuilder(OptionSpecBuilder other) { + private OptionSpecBuilder(OptionSpecBuilder other) { valueType = other.valueType; - name = other.name; - nameAliases.addAll(other.nameAliases); - description = other.description; - mergePolicy = other.mergePolicy; - scope = Set.copyOf(other.scope); + initFrom(other); defaultValue = other.defaultValue; defaultOptionalValue = other.defaultOptionalValue; - valuePattern = other.valuePattern; converterBuilder = other.converterBuilder.copy(); validatorBuilder = other.validatorBuilder.copy(); validator = other.validator; if (other.arrayDefaultValue != null) { arrayDefaultValue = Arrays.copyOf(other.arrayDefaultValue, other.arrayDefaultValue.length); + } else { + arrayDefaultValue = null; } + } + + private OptionSpecBuilder(OptionSpecBuilder other, ValueConverter converter) { + Function converterFunction = toFunction(converter::convert); + + this.valueType = converter.valueType(); + initFrom(other); + converter(other, converter); + + other.defaultValue().map(converterFunction).ifPresent(this::defaultValue); + other.defaultOptionalValue().map(converterFunction).ifPresent(this::defaultOptionalValue); + + if (other.arrayDefaultValue != null) { + arrayDefaultValue = Stream.of(other.arrayDefaultValue).map(converterFunction).toArray(length -> { + @SuppressWarnings("unchecked") + var arr = (T[])Array.newInstance(valueType, length); + return arr; + }); + } + } + + private void initFrom(OptionSpecBuilder other) { + name = other.name; + nameAliases.clear(); + nameAliases.addAll(other.nameAliases); + description = other.description; + mergePolicy = other.mergePolicy; + scope = Set.copyOf(other.scope); + valuePattern = other.valuePattern; arrayValuePatternSeparator = other.arrayValuePatternSeparator; arrayTokenizer = other.arrayTokenizer; } @@ -86,6 +114,14 @@ final class OptionSpecBuilder { return new OptionSpecBuilder<>(this); } + OptionSpecBuilder map(ValueConverter converter) { + return new OptionSpecBuilder<>(this, converter); + } + + OptionSpecBuilder map(Function, OptionSpecBuilder> mapper) { + return mapper.apply(this); + } + Class valueType() { return valueType; } @@ -177,8 +213,8 @@ final class OptionSpecBuilder { return this; } - OptionSpecBuilder validatorExceptionFormatString(UnaryOperator mutator) { - validatorBuilder.formatString(mutator.apply(validatorBuilder.formatString().orElse(null))); + OptionSpecBuilder validatorExceptionFormatString(UnaryOperator mapper) { + validatorBuilder.formatString(mapper.apply(validatorBuilder.formatString().orElse(null))); validator = null; return this; } @@ -188,8 +224,8 @@ final class OptionSpecBuilder { return this; } - OptionSpecBuilder converterExceptionFormatString(UnaryOperator mutator) { - converterBuilder.formatString(mutator.apply(converterBuilder.formatString().orElse(null))); + OptionSpecBuilder converterExceptionFormatString(UnaryOperator mapper) { + converterBuilder.formatString(mapper.apply(converterBuilder.formatString().orElse(null))); return this; } @@ -199,8 +235,8 @@ final class OptionSpecBuilder { return this; } - OptionSpecBuilder validatorExceptionFactory(UnaryOperator> mutator) { - return validatorExceptionFactory(mutator.apply(validatorBuilder.exceptionFactory().orElse(null))); + OptionSpecBuilder validatorExceptionFactory(UnaryOperator> mapper) { + return validatorExceptionFactory(mapper.apply(validatorBuilder.exceptionFactory().orElse(null))); } OptionSpecBuilder converterExceptionFactory(OptionValueExceptionFactory v) { @@ -208,32 +244,42 @@ final class OptionSpecBuilder { return this; } - OptionSpecBuilder converterExceptionFactory(UnaryOperator> mutator) { - return converterExceptionFactory(mutator.apply(converterBuilder.exceptionFactory().orElse(null))); + OptionSpecBuilder converterExceptionFactory(UnaryOperator> mapper) { + return converterExceptionFactory(mapper.apply(converterBuilder.exceptionFactory().orElse(null))); } OptionSpecBuilder exceptionFormatString(String v) { return validatorExceptionFormatString(v).converterExceptionFormatString(v); } - OptionSpecBuilder exceptionFormatString(UnaryOperator mutator) { - return validatorExceptionFormatString(mutator).converterExceptionFormatString(mutator); + OptionSpecBuilder exceptionFormatString(UnaryOperator mapper) { + return validatorExceptionFormatString(mapper).converterExceptionFormatString(mapper); } OptionSpecBuilder exceptionFactory(OptionValueExceptionFactory v) { return validatorExceptionFactory(v).converterExceptionFactory(v); } - OptionSpecBuilder exceptionFactory(UnaryOperator> mutator) { - return validatorExceptionFactory(mutator).converterExceptionFactory(mutator); + OptionSpecBuilder exceptionFactory(UnaryOperator> mapper) { + return validatorExceptionFactory(mapper).converterExceptionFactory(mapper); } - OptionSpecBuilder converter(ValueConverter v) { + OptionSpecBuilder converter(ValueConverter v) { converterBuilder.converter(v); return this; } - OptionSpecBuilder converter(Function v) { + OptionSpecBuilder converter(OptionSpecBuilder other, ValueConverter v) { + converterBuilder = other.finalizeConverterBuilder().map(v); + return this; + } + + OptionSpecBuilder interimConverter(OptionSpecBuilder other) { + converterBuilder = converterBuilder.map(other.finalizeConverterBuilder()); + return this; + } + + OptionSpecBuilder converter(ValueConverterFunction v) { return converter(ValueConverter.create(v, valueType)); } @@ -251,8 +297,8 @@ final class OptionSpecBuilder { } @SuppressWarnings("overloads") - OptionSpecBuilder validator(UnaryOperator> mutator) { - validatorBuilder = mutator.apply(validatorBuilder); + OptionSpecBuilder validator(UnaryOperator> mapper) { + validatorBuilder = mapper.apply(validatorBuilder); validator = null; return this; } @@ -303,8 +349,8 @@ final class OptionSpecBuilder { return this; } - OptionSpecBuilder scope(UnaryOperator> mutator) { - return scope(mutator.apply(scope().orElseGet(Set::of))); + OptionSpecBuilder scope(UnaryOperator> mapper) { + return scope(mapper.apply(scope().orElseGet(Set::of))); } OptionSpecBuilder inScope(OptionScope... v) { @@ -424,10 +470,12 @@ final class OptionSpecBuilder { } private Optional defaultValuePattern() { - return converterBuilder.converter().map(_ -> { + if (converterBuilder.hasConverter()) { final var tokens = name.split("-"); - return tokens[tokens.length - 1]; - }); + return Optional.of(tokens[tokens.length - 1]); + } else { + return Optional.empty(); + } } private List names() { @@ -437,17 +485,21 @@ final class OptionSpecBuilder { ).flatMap(Collection::stream).map(OptionName::new).distinct().toList(); } - private Optional> createConverter() { - if (converterBuilder.converter().isPresent()) { - final var newBuilder = converterBuilder.copy(); - createValidator().ifPresent(newBuilder::validator); - return Optional.of(newBuilder.create()); + private Optional> createConverter() { + if (converterBuilder.hasConverter()) { + return Optional.of(finalizeConverterBuilder().create()); } else { return Optional.empty(); } } - private OptionValueConverter createArrayConverter() { + private OptionValueConverter.Builder finalizeConverterBuilder() { + final var newBuilder = converterBuilder.copy(); + createValidator().ifPresent(newBuilder::validator); + return newBuilder; + } + + private OptionValueConverter createArrayConverter() { final var newBuilder = converterBuilder.copy(); newBuilder.tokenizer(Optional.ofNullable(arrayTokenizer).orElse(str -> { return new String[] { str }; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecMapperOptionScope.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecMapperOptionScope.java index 7b517f768bf..42ed704ec6e 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecMapperOptionScope.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionSpecMapperOptionScope.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,11 +25,17 @@ package jdk.jpackage.internal.cli; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; +import java.util.Optional; +import java.util.SequencedMap; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.IdentityWrapper; import jdk.jpackage.internal.util.SetBuilder; /** @@ -42,9 +48,9 @@ import jdk.jpackage.internal.util.SetBuilder; * varies depending on the current OS. * * @param the type of option value - * @param the type of context + * @param the type of context */ -interface OptionSpecMapperOptionScope extends OptionScope { +sealed interface OptionSpecMapperOptionScope extends OptionScope { OptionSpec createOptionSpec(U context, boolean createArray); @@ -52,7 +58,7 @@ interface OptionSpecMapperOptionScope extends OptionScope { @SuppressWarnings("unchecked") static OptionSpec mapOptionSpec(OptionSpec optionSpec, U context) { - return optionSpec.scope().stream() + var mappedOptionSpec = optionSpec.scope().stream() .filter(OptionSpecMapperOptionScope.class::isInstance) .map(OptionSpecMapperOptionScope.class::cast) .filter(scope -> { @@ -62,6 +68,25 @@ interface OptionSpecMapperOptionScope extends OptionScope { return ((OptionSpecMapperOptionScope)scope).createOptionSpec( context, optionSpec.arrayValueConverter().isPresent()); }).orElse(optionSpec); + + Optional valueType = optionSpec.converter().map(OptionValueConverter::valueType); + Optional mappedValueType = mappedOptionSpec.converter().map(OptionValueConverter::valueType); + + while (!mappedValueType.equals(valueType)) { + // Source and mapped option specs have different option value types. + if (Stream.of(valueType, mappedValueType).anyMatch(Optional::isEmpty) && + Stream.of(valueType, mappedValueType) + .filter(Optional::isPresent).map(Optional::get) + .anyMatch(Predicate.isEqual(Boolean.class))) { + // One option spec doesn't have a converter and another has a converter of type `Boolean`. + // They are compatible, let it pass. + break; + } + + throw new IllegalStateException(String.format("Bad option spec mapping from %s to %s", valueType, mappedValueType)); + } + + return mappedOptionSpec; } static Consumer> createOptionSpecBuilderMutator( @@ -94,23 +119,23 @@ interface OptionSpecMapperOptionScope extends OptionScope { var contextOptionScope = scope.stream() .filter(AccumulatingContextOptionScope.class::isInstance) - .map(AccumulatingContextOptionScope.class::cast) + .map(v -> { + @SuppressWarnings("unchecked") + var tv = (AccumulatingContextOptionScope)v; + return tv; + }) .filter(s -> { return s.contextType().equals(contextType); }) .findFirst(); + contextOptionScope.ifPresent(v -> { - @SuppressWarnings("unchecked") - var mutators = (AccumulatingContextOptionScope)v; - mutators.addMutator(optionSpecBuilderMutator); - if (optionSpecBuilder != mutators.optionSpecBuilder) { - throw new IllegalArgumentException(); - } + v.addMutator(optionSpecBuilder, optionSpecBuilderMutator); }); if (contextOptionScope.isEmpty()) { - var mutators = new AccumulatingContextOptionScope(optionSpecBuilder, contextType); - mutators.addMutator(optionSpecBuilderMutator); + var mutators = new AccumulatingContextOptionScope(contextType); + mutators.addMutator(optionSpecBuilder, optionSpecBuilderMutator); scope = SetBuilder.build().add(scope).add(mutators).create(); } @@ -119,23 +144,24 @@ interface OptionSpecMapperOptionScope extends OptionScope { private static final class AccumulatingContextOptionScope implements OptionSpecMapperOptionScope { - AccumulatingContextOptionScope(OptionSpecBuilder optionSpecBuilder, Class contextType) { - this.optionSpecBuilder = Objects.requireNonNull(optionSpecBuilder); + AccumulatingContextOptionScope(Class contextType) { this.contextType = Objects.requireNonNull(contextType); } @SuppressWarnings("unchecked") @Override public OptionSpec createOptionSpec(U context, boolean createArray) { - var copy = optionSpecBuilder.copy(); - for (var mutator : optionSpecBuilderMutators) { - mutator.accept(copy, context); + var it = builders.values().iterator(); + + var builder = it.next().initBuilder(context, Optional.empty()); + while (it.hasNext()) { + builder = it.next().initBuilder(context, Optional.of(builder)); } if (createArray) { - return (OptionSpec)copy.createArrayOptionSpec(); + return (OptionSpec)builder.createArrayOptionSpec(); } else { - return copy.createOptionSpec(); + return (OptionSpec)builder.createOptionSpec(); } } @@ -144,14 +170,43 @@ interface OptionSpecMapperOptionScope extends OptionScope { return contextType; } - void addMutator(BiConsumer, U> mutator) { - optionSpecBuilderMutators.add(mutator); + void addMutator(OptionSpecBuilder builder, BiConsumer, U> mutator) { + @SuppressWarnings("unchecked") + var builderWithMutators = ((OptionSpecBuilderWithMutators)builders.computeIfAbsent(new IdentityWrapper<>(builder), _ -> { + return new OptionSpecBuilderWithMutators(builder); + })); + + builderWithMutators.addMutator(mutator); } - private final OptionSpecBuilder optionSpecBuilder; private final Class contextType; - private final List, U>> optionSpecBuilderMutators = new ArrayList<>(); + private final SequencedMap>, OptionSpecBuilderWithMutators> builders = new LinkedHashMap<>(); } + private static final class OptionSpecBuilderWithMutators { + + OptionSpecBuilderWithMutators(OptionSpecBuilder builder) { + this.builder = Objects.requireNonNull(builder); + } + + OptionSpecBuilder initBuilder(U context, Optional> other) { + Objects.requireNonNull(context); + Objects.requireNonNull(other); + + var copy = builder.copy(); + other.ifPresent(copy::interimConverter); + for (var mutator : mutators) { + mutator.accept(copy, context); + } + return copy; + } + + void addMutator(BiConsumer, U> mutator) { + mutators.add(Objects.requireNonNull(mutator)); + } + + private final OptionSpecBuilder builder; + private final List, U>> mutators = new ArrayList<>(); + } } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionValueConverter.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionValueConverter.java index 8544853d584..7e249ca1508 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionValueConverter.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionValueConverter.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 @@ -35,28 +35,35 @@ import jdk.jpackage.internal.cli.Validator.ParsedValue; import jdk.jpackage.internal.util.Result; /** - * Defines creating an option value of type {@link T} from a string. + * Defines creating an option value of type {@link U} from value of type {@link T}. * - * @param option value type + * @param input option value type + * @param output option value type */ -interface OptionValueConverter { +interface OptionValueConverter { /** - * Converts the given string value corresponding to the given option name into a - * Java type. + * Converts the given value of type {@link T} corresponding to the given option name + * and option string value to an object of type {@link U}. * * @param optionName the option name - * @param optionValue the string value of the option to convert + * @param optionValue the string value of the option + * @param value the value of the option to convert * @return the conversion result + * @throws ConverterException if internal converter error occurs */ - Result convert(OptionName optionName, StringToken optionValue); + Result convert(OptionName optionName, StringToken optionValue, T value) throws ConverterException; /** * Gives the class of the type of values this converter converts to. * * @return the target class for conversion */ - Class valueType(); + Class valueType(); + + static Result convertString(OptionValueConverter converter, OptionName optionName, StringToken optionValue) { + return converter.convert(optionName, optionValue, optionValue.value()); + } /** * Thrown to indicate an error in the normal execution of the converter. @@ -74,54 +81,78 @@ interface OptionValueConverter { return new Builder<>(); } + static final class Builder { private Builder() { + this(new OneStepBackend<>(new StepBuilder<>(true))); + } + + private Builder(Backend backend) { + this.backend = Objects.requireNonNull(backend); } private Builder(Builder other) { - converter = other.converter; - validator = other.validator; + backend = other.backend.copy(); tokenizer = other.tokenizer; - formatString = other.formatString; - exceptionFactory = other.exceptionFactory; } Builder copy() { return new Builder<>(this); } - OptionValueConverter create() { - return new DefaultOptionValueConverter<>( - converter, - formatString().orElseGet(() -> { - if (exceptionFactory == null) { - return ""; - } else { - return null; - } - }), - exceptionFactory().orElseGet(() -> { - if (formatString == null) { - return OptionValueExceptionFactory.unreachable(); - } else { - return null; - } - }), - validator()); + Builder map(ValueConverter converter) { + Objects.requireNonNull(converter); + return new Builder<>(new TwoStepBackend<>( + backend, + new StepBuilder(false).converter(converter))).tokenizer(tokenizer); + } + + Builder map(Builder other) { + Objects.requireNonNull(other); + switch (backend) { + case OneStepBackend _ -> { + throw new UnsupportedOperationException(); + } + case TwoStepBackend b -> { + var fromInterimValueType = other.backend.valueType().orElseThrow(); + var toInterimValueType = b.interimValueType(); + if (fromInterimValueType.equals(toInterimValueType)) { + @SuppressWarnings("unchecked") + var twoStepBackend = (TwoStepBackend)b; + return new Builder<>(new TwoStepBackend<>( + other.backend, + twoStepBackend.otherConvBuilder())).tokenizer(tokenizer); + } else { + throw new IllegalArgumentException(String.format( + "Expected (%s); actual (%s)", toInterimValueType, fromInterimValueType)); + } + } + } + } + + OptionValueConverter create() { + return backend.create(); } OptionArrayValueConverter createArray() { return new DefaultOptionArrayValueConverter<>(create(), tokenizer); } - Builder converter(ValueConverter v) { - converter = v; + Builder converter(ValueConverter v) { + switch (backend) { + case OneStepBackend b -> { + b.stringConvBuilder().converter(v); + } + case TwoStepBackend _ -> { + throw new UnsupportedOperationException(); + } + } return this; } Builder validator(Validator v) { - validator = v; + backend.validator(v); return this; } @@ -131,12 +162,12 @@ interface OptionValueConverter { } Builder formatString(String v) { - formatString = v; + backend.formatString(v); return this; } Builder exceptionFactory(OptionValueExceptionFactory v) { - exceptionFactory = v; + backend.exceptionFactory(v); return this; } @@ -145,12 +176,30 @@ interface OptionValueConverter { return this; } - Optional> converter() { - return Optional.ofNullable(converter); + boolean hasConverter() { + switch (backend) { + case OneStepBackend b -> { + return b.stringConvBuilder().converter().isPresent(); + } + case TwoStepBackend _ -> { + return true; + } + } } Optional> validator() { - return Optional.ofNullable(validator); + return backend.validator(); + } + + Optional> converter() { + switch (backend) { + case OneStepBackend b -> { + return b.stringConvBuilder().converter(); + } + case TwoStepBackend _ -> { + throw new UnsupportedOperationException(); + } + } } Optional> tokenizer() { @@ -158,68 +207,15 @@ interface OptionValueConverter { } Optional formatString() { - return Optional.ofNullable(formatString); + return backend.formatString(); } Optional> exceptionFactory() { - return Optional.ofNullable(exceptionFactory); + return backend.exceptionFactory(); } - private record DefaultOptionValueConverter(ValueConverter converter, String formatString, - OptionValueExceptionFactory exceptionFactory, - Optional> validator) implements OptionValueConverter { - - DefaultOptionValueConverter { - Objects.requireNonNull(converter); - Objects.requireNonNull(formatString); - Objects.requireNonNull(exceptionFactory); - Objects.requireNonNull(validator); - } - - @Override - public Result convert(OptionName optionName, StringToken optionValue) { - Objects.requireNonNull(optionName); - - final T convertedValue; - try { - convertedValue = converter.convert(optionValue.value()); - } catch (Exception ex) { - return handleException(optionName, optionValue, ex); - } - - final List validationExceptions = validator.map(val -> { - try { - return val.validate(optionName, ParsedValue.create(convertedValue, optionValue)); - } catch (Validator.ValidatorException ex) { - // All unexpected exceptions that the converter yields should be tunneled via ConverterException. - throw new ConverterException(ex.getCause()); - } - }).orElseGet(List::of); - - if (validationExceptions.isEmpty()) { - return Result.ofValue(convertedValue); - } else { - return Result.ofErrors(validationExceptions); - } - } - - @Override - public Class valueType() { - return converter.valueType(); - } - - private Result handleException(OptionName optionName, StringToken optionValue, Exception ex) { - if (ex instanceof IllegalArgumentException) { - return Result.ofError(exceptionFactory.create(optionName, optionValue, formatString, Optional.of(ex))); - } else { - throw new ConverterException(ex); - } - } - } - - - private record DefaultOptionArrayValueConverter(OptionValueConverter elementConverter, + private record DefaultOptionArrayValueConverter(OptionValueConverter elementConverter, Function tokenizer) implements OptionArrayValueConverter { DefaultOptionArrayValueConverter { @@ -229,14 +225,18 @@ interface OptionValueConverter { @SuppressWarnings("unchecked") @Override - public Result convert(OptionName optionName, StringToken optionValue) { + public Result convert(OptionName optionName, StringToken optionValue, String value) { + + if (!value.equals(optionValue.value())) { + throw new IllegalArgumentException(); + } final List exceptions = new ArrayList<>(); final List convertedValues = new ArrayList<>(); final var tokens = tokenize(optionValue.value()); for (var token : tokens) { - final var result = elementConverter.convert(optionName, StringToken.of(optionValue.value(), token)); + final var result = elementConverter.convert(optionName, StringToken.of(optionValue.value(), token), token); exceptions.addAll(result.errors()); if (exceptions.isEmpty()) { result.value().ifPresent(convertedValues::add); @@ -264,10 +264,311 @@ interface OptionValueConverter { } } - private ValueConverter converter; - private Validator validator; + + private record TwoStepOptionValueConverter(OptionValueConverter stringConverter, + OptionValueConverter converter) implements OptionValueConverter { + + TwoStepOptionValueConverter { + Objects.requireNonNull(stringConverter); + Objects.requireNonNull(converter); + } + + @Override + public Result convert(OptionName optionName, StringToken optionValue, String value) { + final var interimResult = stringConverter.convert(optionName, optionValue, value); + return interimResult.flatMap(interimValue -> { + return converter.convert(optionName, optionValue, interimValue); + }); + } + + @Override + public Class valueType() { + return converter.valueType(); + } + } + + + private sealed interface Backend { + + OptionValueConverter create(); + + Backend copy(); + + void validator(Validator v); + + void formatString(String v); + + void exceptionFactory(OptionValueExceptionFactory v); + + Optional> valueType(); + + Optional> validator(); + + Optional formatString(); + + Optional> exceptionFactory(); + } + + + private record OneStepBackend(StepBuilder stringConvBuilder) implements Backend { + + OneStepBackend { + Objects.requireNonNull(stringConvBuilder); + } + + @Override + public Backend copy() { + return new OneStepBackend<>(stringConvBuilder.copy()); + } + + @Override + public OptionValueConverter create() { + return stringConvBuilder.create(); + } + + @Override + public void validator(Validator v) { + stringConvBuilder.validator(v); + } + + @Override + public void formatString(String v) { + stringConvBuilder.formatString(v); + } + + @Override + public void exceptionFactory(OptionValueExceptionFactory v) { + stringConvBuilder.exceptionFactory(v); + } + + @Override + public Optional> valueType() { + return stringConvBuilder.converter().map(ValueConverter::valueType); + } + + @Override + public Optional> validator() { + return stringConvBuilder.validator(); + } + + @Override + public Optional formatString() { + return stringConvBuilder.formatString(); + } + + @Override + public Optional> exceptionFactory() { + return stringConvBuilder.exceptionFactory(); + } + } + + + private record TwoStepBackend(Backend stringConvBuilder, StepBuilder otherConvBuilder) implements Backend { + + TwoStepBackend { + Objects.requireNonNull(stringConvBuilder); + Objects.requireNonNull(otherConvBuilder); + } + + Class interimValueType() { + return stringConvBuilder.valueType().orElseThrow(); + } + + @Override + public Backend copy() { + return new TwoStepBackend<>(stringConvBuilder.copy(), otherConvBuilder.copy()); + } + + @Override + public OptionValueConverter create() { + return new TwoStepOptionValueConverter<>(stringConvBuilder.create(), otherConvBuilder.create()); + } + + @Override + public void validator(Validator v) { + otherConvBuilder.validator(v); + } + + @Override + public void formatString(String v) { + otherConvBuilder.formatString(v); + } + + @Override + public void exceptionFactory(OptionValueExceptionFactory v) { + otherConvBuilder.exceptionFactory(v); + } + + @Override + public Optional> valueType() { + return otherConvBuilder.converter().map(ValueConverter::valueType); + } + + @Override + public Optional> validator() { + return otherConvBuilder.validator(); + } + + @Override + public Optional formatString() { + return otherConvBuilder.formatString(); + } + + @Override + public Optional> exceptionFactory() { + return otherConvBuilder.exceptionFactory(); + } + } + + + private static final class StepBuilder { + + private StepBuilder(boolean starter) { + this.starter = starter; + } + + private StepBuilder(StepBuilder other) { + starter = other.starter; + converter = other.converter; + validator = other.validator; + formatString = other.formatString; + exceptionFactory = other.exceptionFactory; + } + + StepBuilder copy() { + return new StepBuilder<>(this); + } + + OptionValueConverter create() { + return new DefaultOptionValueConverter<>( + converter, + formatString().orElseGet(() -> { + if (exceptionFactory == null) { + return ""; + } else { + return null; + } + }), + exceptionFactory().orElseGet(() -> { + if (formatString == null) { + return OptionValueExceptionFactory.unreachable(); + } else { + return null; + } + }), + validator(), + starter); + } + + StepBuilder converter(ValueConverter v) { + converter = v; + return this; + } + + StepBuilder validator(Validator v) { + validator = v; + return this; + } + + StepBuilder formatString(String v) { + formatString = v; + return this; + } + + StepBuilder exceptionFactory(OptionValueExceptionFactory v) { + exceptionFactory = v; + return this; + } + + Optional> converter() { + return Optional.ofNullable(converter); + } + + Optional> validator() { + return Optional.ofNullable(validator); + } + + Optional formatString() { + return Optional.ofNullable(formatString); + } + + Optional> exceptionFactory() { + return Optional.ofNullable(exceptionFactory); + } + + + private record DefaultOptionValueConverter( + ValueConverter converter, + String formatString, + OptionValueExceptionFactory exceptionFactory, + Optional> validator, + boolean starter) implements OptionValueConverter { + + DefaultOptionValueConverter { + Objects.requireNonNull(converter); + Objects.requireNonNull(formatString); + Objects.requireNonNull(exceptionFactory); + Objects.requireNonNull(validator); + } + + @Override + public Result convert(OptionName optionName, StringToken optionValue, T value) { + Objects.requireNonNull(optionName); + Objects.requireNonNull(optionValue); + Objects.requireNonNull(value); + + if (starter && !value.equals(optionValue.value())) { + throw new IllegalArgumentException(); + } + + final U convertedValue; + try { + convertedValue = converter.convert(value); + } catch (Exception ex) { + return handleException(optionName, optionValue, ex); + } + + final List validationExceptions = validator.map(val -> { + try { + return val.validate(optionName, ParsedValue.create(convertedValue, optionValue)); + } catch (Validator.ValidatorException ex) { + // All unexpected exceptions that the converter yields should be tunneled via ConverterException. + throw new ConverterException(ex.getCause()); + } + }).orElseGet(List::of); + + if (validationExceptions.isEmpty()) { + return Result.ofValue(convertedValue); + } else { + return Result.ofErrors(validationExceptions); + } + } + + @Override + public Class valueType() { + return converter.valueType(); + } + + private Result handleException(OptionName optionName, StringToken optionValue, Exception ex) { + if (ex instanceof IllegalArgumentException) { + return Result.ofError(exceptionFactory.create(optionName, optionValue, formatString, Optional.of(ex))); + } else { + throw new ConverterException(ex); + } + } + } + + + private final boolean starter; + private ValueConverter converter; + private Validator validator; + private String formatString; + private OptionValueExceptionFactory exceptionFactory; + } + + + private final Backend backend; private Function tokenizer; - private String formatString; - private OptionValueExceptionFactory exceptionFactory; } } + diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java index 363aa1b863e..6c32a3d1569 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsAnalyzer.java @@ -272,7 +272,6 @@ final class OptionsAnalyzer { } else { var spec = new StandardOptionContext(os).mapOptionSpec(typeOption.spec()); return spec - .converter().orElseThrow() .convert(spec.name(), StringToken.of(((String[])obj)[0])) .orElseThrow(); } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsProcessor.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsProcessor.java index 54003b714a2..3c0eab77f8a 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsProcessor.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/OptionsProcessor.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 @@ -29,6 +29,7 @@ import static java.util.stream.Collectors.counting; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toMap; import static jdk.jpackage.internal.cli.Option.fromOptionSpecPredicate; +import static jdk.jpackage.internal.cli.OptionValueConverter.convertString; import static jdk.jpackage.internal.cli.StandardOption.ADDITIONAL_LAUNCHERS; import static jdk.jpackage.internal.cli.StandardOption.platformOption; @@ -380,8 +381,8 @@ final class OptionsProcessor { } @Override - public Result convert(OptionName optionName, StringToken optionValue) { - return converter.convert(optionName, optionValue).flatMap(arr -> { + public Result convert(OptionName optionName, StringToken optionValue, String value) { + return convertString(converter, optionName, optionValue).flatMap(arr -> { return Stream.of(arr).map(mapper).reduce(Result.>ofValue(new ArrayList<>()), (result, o) -> { if (Result.allHaveValues(result, o)) { return result.map(v -> { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java index 42f90536753..2538bbf4fb4 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardAppImageFileOption.java @@ -230,7 +230,7 @@ public final class StandardAppImageFileOption { } return strValue.map(v -> { - return spec.converter().orElseThrow().convert(spec.name(), StringToken.of(v)).orElseThrow(); + return spec.convert(spec.name(), StringToken.of(v)).orElseThrow(); }).map(v -> { return Map.entry(option, v); }); 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 fadd9bacb1c..fedb55116a3 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 @@ -37,6 +37,7 @@ import static jdk.jpackage.internal.cli.StandardOptionValueExceptionFactory.ERRO import static jdk.jpackage.internal.cli.StandardOptionValueExceptionFactory.forMessageWithOptionValueAndName; import static jdk.jpackage.internal.cli.StandardValueConverter.addLauncherShortcutConv; import static jdk.jpackage.internal.cli.StandardValueConverter.booleanConv; +import static jdk.jpackage.internal.cli.StandardValueConverter.explodedPathConverter; import static jdk.jpackage.internal.cli.StandardValueConverter.identityConv; import static jdk.jpackage.internal.cli.StandardValueConverter.mainLauncherShortcutConv; import static jdk.jpackage.internal.cli.StandardValueConverter.pathConv; @@ -44,11 +45,13 @@ import static jdk.jpackage.internal.cli.StandardValueConverter.uuidConv; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; import java.util.function.UnaryOperator; import java.util.regex.Matcher; @@ -61,7 +64,7 @@ import jdk.jpackage.internal.model.BundlingOperationDescriptor; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; -import jdk.jpackage.internal.model.PackageType; +import jdk.jpackage.internal.util.RootedPath; import jdk.jpackage.internal.model.SelfContainedException; import jdk.jpackage.internal.util.SetBuilder; @@ -120,9 +123,12 @@ public final class StandardOption { }); })).create(); - public static final OptionValue INPUT = directoryOption("input").addAliases("i") + public static final OptionValue> INPUT = directoryOption("input").addAliases("i") .outOfScope(NOT_BUILDING_APP_IMAGE) - .create(); + .map(explodedPathOptionMapper(explodedPathConverter().create())) + .create(optionValueBuilder -> { + return optionValueBuilder.to(List::of).create(); + }); public static final OptionValue DEST = directoryOption("dest").addAliases("d") .valuePattern("destination path") @@ -193,16 +199,17 @@ public final class StandardOption { .inScope(LauncherProperty.VALUE) .createArray(toList()); - public static final OptionValue> APP_CONTENT = pathOption("app-content") + public static final OptionValue>> APP_CONTENT = existingPathOption("app-content") .tokenizer(",") .valuePattern("additional content") .outOfScope(NOT_BUILDING_APP_IMAGE) + .map(explodedPathOptionMapper(explodedPathConverter().withPathFileName().create())) .mutate(createOptionSpecBuilderMutator((b, context) -> { if (context.os() == OperatingSystem.MACOS) { b.description("help.option.app-content" + resourceKeySuffix(context.os())); } })) - .createArray(toList()); + .createArray(toExplodedPathList()); static final OptionValue FILE_ASSOCIATIONS_INTERNAL = fileOption("file-associations") .tokenizer(pathSeparator()) @@ -324,10 +331,11 @@ public final class StandardOption { // MacOS-specific // - public static final OptionValue> MAC_DMG_CONTENT = pathOption("mac-dmg-content") + public static final OptionValue>> MAC_DMG_CONTENT = existingPathOption("mac-dmg-content") .valuePattern("additional content path") .tokenizer(",") - .createArray(toList()); + .map(explodedPathOptionMapper(explodedPathConverter().withPathFileName().create())) + .createArray(toExplodedPathList()); public static final OptionValue MAC_SIGN = booleanOption("mac-sign").scope(MAC_SIGNING).addAliases("s").create(); @@ -513,12 +521,57 @@ public final class StandardOption { static Consumer> directoryOptionMutator() { return builder -> { builder.mutate(pathOptionMutator()) + .mutate(createOptionSpecBuilderMutator((b, context) -> { + context.asFileSource().ifPresent(propertyFile -> { + b.validatorExceptionFactory(forMessageWithOptionValueAndName(propertyFile)); + b.validatorExceptionFormatString("error.properties-parameter-not-directory"); + }); + })) .validator(StandardValidator.IS_DIRECTORY) .validatorExceptionFactory(ERROR_WITH_VALUE_AND_OPTION_NAME) .validatorExceptionFormatString("error.parameter-not-directory"); }; } + static Consumer> existingPathOptionMutator() { + + return builder -> { + builder.mutate(pathOptionMutator()) + .validator(createExistingPathValidator(Validator.build().exceptionFactory(ERROR_WITH_VALUE_AND_OPTION_NAME), true)) + .mutate(createOptionSpecBuilderMutator((b, context) -> { + context.asFileSource().ifPresent(propertyFile -> { + var validatorBuilder = Validator.build(); + validatorBuilder.exceptionFactory(forMessageWithOptionValueAndName(propertyFile)); + b.validator(createExistingPathValidator(validatorBuilder, false)); + }); + })); + }; + } + + private static Validator createExistingPathValidator(Validator.Builder builder, boolean cmdline) { + + if (cmdline) { + builder.formatString("error.parameter-not-directory"); + } else { + builder.formatString("error.properties-parameter-not-directory"); + } + + var isDirectoryValidator = builder.predicate(StandardValidator.IS_DIRECTORY).create(); + + if (cmdline) { + builder.formatString("error.parameter-not-file"); + } else { + builder.formatString("error.properties-parameter-not-file"); + } + + var isFileValidator = builder.predicate(StandardValidator.IS_FILE_OR_SYMLINK).create(); + + @SuppressWarnings("unchecked") + var validator = (Validator)isDirectoryValidator.or(isFileValidator); + + return validator; + } + static Consumer> booleanOptionMutator() { return builder -> { builder.mutate(createOptionSpecBuilderMutator((b, context) -> { @@ -546,6 +599,30 @@ public final class StandardOption { }; } + static Function, OptionSpecBuilder> explodedPathOptionMapper(ValueConverter conv) { + Objects.requireNonNull(conv); + return builder -> { + return builder.map(conv) + .converterExceptionFactory(ERROR_WITH_VALUE_AND_OPTION_NAME) + .converterExceptionFormatString("error.path-parameter-ioexception") + // Add empty mutator to OptionSpecMapperOptionScope to make + // mapped option spec have `RootedPath[]` type. + // Otherwise, it will have `Path` type. + .mutate(createOptionSpecBuilderMutator((b, context) -> { + })); + }; + } + + private static Function, OptionValue>>> toExplodedPathList() { + return builder -> { + return builder.to((RootedPath[][] v) -> { + return Stream.of(v).map(arr -> { + return (Collection)List.of(arr); + }).toList(); + }).create(); + }; + } + private static OptionSpecBuilder option(String name, Class valueType) { return OptionSpecBuilder.create(valueType) .name(Objects.requireNonNull(name)) @@ -574,6 +651,10 @@ public final class StandardOption { return option(name, Path.class).mutate(pathOptionMutator()); } + private static OptionSpecBuilder existingPathOption(String name) { + return option(name, Path.class).mutate(existingPathOptionMutator()); + } + private static OptionSpecBuilder fileOption(String name) { return option(name, Path.class) .valuePattern("file path") @@ -624,8 +705,7 @@ public final class StandardOption { } private static OptionValue createAddLauncherOption(String name) { - OptionValueConverter propertyFileConverter = fileOption(name) - .create().getSpec().converter().orElseThrow(); + var propertyFileSpec = fileOption(name).create().getSpec(); return option(name, AdditionalLauncher.class) .valuePattern("=") @@ -657,7 +737,7 @@ public final class StandardOption { final Path propertyFile; try { - propertyFile = propertyFileConverter.convert(OptionName.of(name), + propertyFile = propertyFileSpec.convert(OptionName.of(name), StringToken.of(value, components[1])).orElseThrow(); } catch (JPackageException ex) { throw new AddLauncherInvalidPropertyFileException(I18N.format( diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValueConverter.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValueConverter.java index 3a310166fa8..b1e1189654d 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValueConverter.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/StandardValueConverter.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,11 +25,15 @@ package jdk.jpackage.internal.cli; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.UUID; +import java.util.function.Function; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.ParseUtils; +import jdk.jpackage.internal.util.RootedPath; final class StandardValueConverter { @@ -37,33 +41,59 @@ final class StandardValueConverter { private StandardValueConverter() { } - static ValueConverter identityConv() { + static ValueConverter identityConv() { return IDENTITY_CONV; } - static ValueConverter pathConv() { + static ValueConverter pathConv() { return PATH_CONV; } - static ValueConverter uuidConv() { + static ValueConverter uuidConv() { return UUID_CONV; } - static ValueConverter booleanConv() { + static ValueConverter booleanConv() { return BOOLEAN_CONV; } - static ValueConverter mainLauncherShortcutConv() { + static ValueConverter mainLauncherShortcutConv() { return MAIN_LAUNCHER_SHORTCUT_CONV; } - static ValueConverter addLauncherShortcutConv() { + static ValueConverter addLauncherShortcutConv() { return ADD_LAUNCHER_SHORTCUT_CONV; } - private static final ValueConverter IDENTITY_CONV = ValueConverter.create(x -> x, String.class); + static ExplodedPathConverterBuilder explodedPathConverter() { + return new ExplodedPathConverterBuilder(); + } - private static final ValueConverter PATH_CONV = ValueConverter.create(str -> { + static final class ExplodedPathConverterBuilder { + private ExplodedPathConverterBuilder() { + } + + ValueConverter create() { + return ValueConverter.create(path -> { + return explodePath(path, withPathFileName); + }, RootedPath[].class); + } + + ExplodedPathConverterBuilder withPathFileName(boolean v) { + withPathFileName = v; + return this; + } + + ExplodedPathConverterBuilder withPathFileName() { + return withPathFileName(true); + } + + private boolean withPathFileName; + } + + private static final ValueConverter IDENTITY_CONV = ValueConverter.create(x -> x, String.class); + + private static final ValueConverter PATH_CONV = ValueConverter.create(str -> { try { return Path.of(str); } catch (InvalidPathException ex) { @@ -71,13 +101,33 @@ final class StandardValueConverter { } }, Path.class); - private static final ValueConverter UUID_CONV = ValueConverter.create(UUID::fromString, UUID.class); + private static final ValueConverter UUID_CONV = ValueConverter.create(UUID::fromString, UUID.class); - private static final ValueConverter BOOLEAN_CONV = ValueConverter.create(Boolean::valueOf, Boolean.class); + private static final ValueConverter BOOLEAN_CONV = ValueConverter.create(Boolean::valueOf, Boolean.class); - private static final ValueConverter MAIN_LAUNCHER_SHORTCUT_CONV = ValueConverter.create( + private static final ValueConverter MAIN_LAUNCHER_SHORTCUT_CONV = ValueConverter.create( ParseUtils::parseLauncherShortcutForMainLauncher, LauncherShortcut.class); - private static final ValueConverter ADD_LAUNCHER_SHORTCUT_CONV = ValueConverter.create( + private static final ValueConverter ADD_LAUNCHER_SHORTCUT_CONV = ValueConverter.create( ParseUtils::parseLauncherShortcutForAddLauncher, LauncherShortcut.class); + + private static RootedPath[] explodePath(Path path, boolean withPathFileName) throws Exception { + + Function mapper; + if (withPathFileName) { + mapper = RootedPath.toRootedPath(path.getParent()); + } else { + mapper = RootedPath.toRootedPath(path); + } + + RootedPath[] items; + try (var walk = Files.walk(path)) { + items = walk.map(mapper).toArray(RootedPath[]::new); + } catch (IOException ex) { + // IOException is not a converter error, it is a converting error, so map it into IAE. + throw new IllegalArgumentException(ex); + } + + return items; + } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java index 91d9d03bd9f..0701071b00f 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Validator.java @@ -34,7 +34,15 @@ import java.util.stream.Stream; @FunctionalInterface interface Validator { - List validate(OptionName optionName, ParsedValue optionValue); + /** + * Validates the given option value. + * + * @param optionName the name of an option to validate + * @param optionValue the value of an option to validate + * @return the list of validation errors + * @throws ValidatorException if internal validator error occurs + */ + List validate(OptionName optionName, ParsedValue optionValue) throws ValidatorException; default Validator and(Validator after) { Objects.requireNonNull(after); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverter.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverter.java index ba52ca35f50..122b212d295 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverter.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverter.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,41 +25,30 @@ package jdk.jpackage.internal.cli; import java.util.Objects; -import java.util.function.Function; -interface ValueConverter { - - /** - * Converts the given string value into a Java type. - * - * @param value the string to convert - * @return the converted value - * @throws IllegalArgumentException if the given string value can not be - * converted to an object of type {@link T} - */ - T convert(String value) throws IllegalArgumentException; +interface ValueConverter extends ValueConverterFunction { /** * Gives the class of the type of values this converter converts to. * * @return the target class for conversion */ - Class valueType(); + Class valueType(); - static ValueConverter create(Function mapper, Class type) { - Objects.requireNonNull(mapper); + static ValueConverter create(ValueConverterFunction conv, Class type) { + Objects.requireNonNull(conv); Objects.requireNonNull(type); return new ValueConverter<>() { @Override - public T convert(String value) { + public U convert(T value) throws Exception { Objects.requireNonNull(value); - return Objects.requireNonNull(mapper.apply(value)); + return Objects.requireNonNull(conv.convert(value)); } @Override - public Class valueType() { + public Class valueType() { return type; } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverterFunction.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverterFunction.java new file mode 100644 index 00000000000..28702afff75 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/ValueConverterFunction.java @@ -0,0 +1,40 @@ +/* + * 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 + * 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.cli; + +@FunctionalInterface +interface ValueConverterFunction { + + /** + * Converts value of one type into another. + * + * @param value the value to convert + * @return the converted value + * @throws IllegalArgumentException if the given value can not be converted to + * an object of type {@link U} + * @throws Exception if internal converter error occurs + */ + U convert(T value) throws Exception, IllegalArgumentException; +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Application.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Application.java index 5b20a0690d3..7860a04faac 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Application.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/Application.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,11 +25,13 @@ package jdk.jpackage.internal.model; import java.nio.file.Path; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Stream; +import jdk.jpackage.internal.util.RootedPath; /** * A generic application for packaging. @@ -78,27 +80,25 @@ public non-sealed interface Application extends BundleSpec { String copyright(); /** - * Gets the source directory of this application if available or an empty - * {@link Optional} instance. + * Gets the source paths that should be copied into + * {@link ApplicationLayout#appDirectory()} directory of the image of this + * application. *

      - * Source directory is a directory with the applications's classes and other + * Source paths are supposed to contain the applications's classes and other * resources. * - * @return the source directory of this application + * @return the source paths */ - Optional srcDir(); + Collection appDirSources(); /** - * Gets the input content directories of this application. - *

      - * Contents of the content directories will be copied as-is into the dedicated - * location of the application image. + * Gets the source paths that should be copied into + * {@link ApplicationLayout#contentDirectory()} directory of the image of this + * application. * - * @see ApplicationLayout#contentDirectory - * - * @return the input content directories of this application + * @return the source paths */ - List contentDirs(); + Collection contentDirSources(); /** * Gets the unresolved app image layout of this application. @@ -244,8 +244,17 @@ public non-sealed interface Application extends BundleSpec { /** * Default implementation of {@link Application} interface. */ - record Stub(String name, String description, String version, String vendor, String copyright, Optional srcDir, - List contentDirs, AppImageLayout imageLayout, Optional runtimeBuilder, - List launchers, Map extraAppImageFileData) implements Application { + record Stub( + String name, + String description, + String version, + String vendor, + String copyright, + Collection appDirSources, + Collection contentDirSources, + AppImageLayout imageLayout, + Optional runtimeBuilder, + List launchers, + Map extraAppImageFileData) implements Application { } } 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 e1f41e3ff48..6e5de3d9729 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 @@ -91,6 +91,7 @@ error.parameter-add-launcher-malformed=The value "{0}" provided for parameter {1 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 error.properties-parameter-not-path=The value "{0}" provided for property "{1}" in "{2}" file is not a valid path error.properties-parameter-not-file=The value "{0}" provided for property "{1}" in "{2}" file is not a file +error.properties-parameter-not-directory=The value "{0}" provided for property "{1}" in "{2}" file is not a directory error.properties-parameter-not-launcher-shortcut-dir=The value "{0}" provided for property "{1}" in "{2}" file is not a valid shortcut startup directory error.no-extensions-for-file-association=No extensions were specified for File Association number {0} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java index 2f7f2682a71..b143a54cb5c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/FileUtils.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 @@ -57,12 +57,6 @@ public final class FileUtils { public static void copyRecursive(Path src, Path dest, CopyOption... options) throws IOException { - copyRecursive(src, dest, List.of(), options); - } - - public static void copyRecursive(Path src, Path dest, - final List excludes, CopyOption... options) - throws IOException { List copyActions = new ArrayList<>(); @@ -71,24 +65,18 @@ public final class FileUtils { @Override public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) { - if (isPathMatch(dir, excludes)) { - return FileVisitResult.SKIP_SUBTREE; - } else { - copyActions.add(new CopyAction(null, dest.resolve(src.relativize(dir)))); - return FileVisitResult.CONTINUE; - } + copyActions.add(new CopyAction(null, dest.resolve(src.relativize(dir)))); + return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) { - if (!isPathMatch(file, excludes)) { - copyActions.add(new CopyAction(file, dest.resolve(src.relativize(file)))); - } + copyActions.add(new CopyAction(file, dest.resolve(src.relativize(file)))); return FileVisitResult.CONTINUE; } }); - } else if (!isPathMatch(src, excludes)) { + } else { Optional.ofNullable(dest.getParent()).ifPresent(dstDir -> { copyActions.add(new CopyAction(null, dstDir)); }); @@ -113,10 +101,6 @@ public final class FileUtils { } } - private static boolean isPathMatch(Path what, List paths) { - return paths.stream().anyMatch(what::endsWith); - } - private static record CopyAction(Path src, Path dest) { void apply(CopyOption... options) throws IOException { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RootedPath.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RootedPath.java new file mode 100644 index 00000000000..21d3b6b17b0 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/RootedPath.java @@ -0,0 +1,185 @@ +/* + * 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 + * 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.util; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.CopyOption; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + +/** + * A relative path (branch) rooted at the root. + */ +public sealed interface RootedPath { + + Path root(); + Path branch(); + + default Path fullPath() { + return root().resolve(branch()); + } + + public static Function toRootedPath(Path root) { + return path -> { + if (!path.startsWith(root)) { + throw new IllegalArgumentException(String.format("Expected path [%s] to start with [%s] root", path, root)); + } + return new Details.DefaultRootedPath(root, root.relativize(path), Files.isDirectory(path)); + }; + } + + public static void copy(Stream rootedPaths, Path dest, CopyOption...options) throws IOException { + Objects.requireNonNull(rootedPaths); + Objects.requireNonNull(dest); + + var marks = new HashMap(); + + // Preset marks for the preexisting paths. + Files.walkFileTree(dest, new FileVisitor() { + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + marks.put(dir, new Details.PathMark(true, true)); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + marks.put(file, new Details.PathMark(false, true)); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { + return FileVisitResult.TERMINATE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + + }); + + var replacePreexisting = Set.of(options).contains(StandardCopyOption.REPLACE_EXISTING); + + Predicate canReplace = v -> { + return v.isPreexisting() && replacePreexisting; + }; + + try { + rootedPaths.sequential().map(Details.DefaultRootedPath.class::cast).forEach(rootedPath -> { + + final var dstPath = dest.resolve(rootedPath.branch()); + + var dstPathMark = marks.get(dstPath); + + if (!Optional.ofNullable(dstPathMark).map(canReplace::test).orElse(true)) { + // Destination path can not be replaced, bail out. + return; + } + + // Check the ancestors of the destination path. + for (var ancestor : Details.ancestorPaths(rootedPath.branch())) { + var mark = Optional.ofNullable(marks.get(dest.resolve(ancestor))); + + if (!mark.map(Details.PathMark::isDirectory).orElse(true)) { + // `ancestor` is a file, don't overwrite it. + return; + } + } + + dstPathMark = rootedPath.createPathMark(); + marks.put(dstPath, dstPathMark); + + try { + if (replacePreexisting && (rootedPath.isDirectory() != dstPathMark.isDirectory())) { + FileUtils.deleteRecursive(dstPath); + } + + if (rootedPath.isDirectory()) { + Files.createDirectories(dstPath); + } else { + Files.createDirectories(dstPath.getParent()); + Files.copy(rootedPath.fullPath(), dstPath, options); + } + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + } + + static final class Details { + + private Details() { + } + + private record DefaultRootedPath(Path root, Path branch, boolean isDirectory) implements RootedPath { + + DefaultRootedPath { + Objects.requireNonNull(root); + Objects.requireNonNull(branch); + if (branch.isAbsolute()) { + throw new IllegalArgumentException(); + } + } + + PathMark createPathMark() { + return new PathMark(isDirectory); + } + } + + private record PathMark(boolean isDirectory, boolean isPreexisting) { + PathMark(boolean isDirectory) { + this(isDirectory, false); + } + } + + private static List ancestorPaths(Path path) { + var ancestors = new ArrayList(); + while ((path = path.getParent()) != null) { + ancestors.add(path); + } + return ancestors; + } + } +} diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java index fd86331e2f1..21f941eedf5 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExePackager.java @@ -52,7 +52,7 @@ final record WinExePackager(BuildEnv env, WinExePackage pkg, Path outputDir, Pat @Override public void accept(PackagingPipeline.Builder pipelineBuilder) { - pipelineBuilder.excludeDirFromCopying(outputDir) + pipelineBuilder .task(ExePackageTaskID.RUN_POST_MSI_USER_SCRIPT) .action(this::runPostMsiScript) .addDependency(PackageTaskID.CREATE_PACKAGE_FILE) 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 c52be726fd2..c72b14a76d5 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinMsiPackager.java @@ -162,7 +162,7 @@ final class WinMsiPackager implements Consumer { @Override public void accept(PackagingPipeline.Builder pipelineBuilder) { - pipelineBuilder.excludeDirFromCopying(outputDir) + pipelineBuilder .task(PackagingPipeline.PackageTaskID.CREATE_CONFIG_FILES) .action(this::prepareConfigFiles) .add() diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java index 53f4b9b95aa..8fdcc96acff 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/AppImageFileTest.java @@ -222,7 +222,7 @@ public class AppImageFileTest { version, null, null, - Optional.empty(), + List.of(), List.of(), null, Optional.empty(), diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java index 86a6cb075d0..de0ba67fece 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/PackagingPipelineTest.java @@ -616,7 +616,7 @@ public class PackagingPipelineTest { "1.0", "Acme", "copyright", - Optional.empty(), + List.of(), List.of(), appImageLayout, runtimeBuilder, diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecMutatorOptionScopeTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecMutatorOptionScopeTest.java index 1849a3b5333..59c7c85ce85 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecMutatorOptionScopeTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecMutatorOptionScopeTest.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 @@ -75,11 +75,11 @@ public class OptionSpecMutatorOptionScopeTest { assertEquals(OptionName.of("foo"), mappedSpec.name()); } - assertEquals("731", mappedSpec.converter().orElseThrow().convert(spec.name(), StringToken.of("str")).orElseThrow()); + assertEquals("731", mappedSpec.convert(spec.name(), StringToken.of("str")).orElseThrow()); assertEquals(OptionName.of("foo"), spec.name()); - assertEquals("123", spec.converter().orElseThrow().convert(spec.name(), StringToken.of("str")).orElseThrow()); + assertEquals("123", spec.convert(spec.name(), StringToken.of("str")).orElseThrow()); } @Test @@ -99,11 +99,11 @@ public class OptionSpecMutatorOptionScopeTest { var mappedSpec = new DummyContext(731).mapOptionSpec(spec); - assertArrayEquals(new String[] {"731"}, mappedSpec.converter().orElseThrow().convert(spec.name(), StringToken.of("str")).orElseThrow()); + assertArrayEquals(new String[] {"731"}, mappedSpec.convert(spec.name(), StringToken.of("str")).orElseThrow()); assertEquals(OptionName.of("foo"), spec.name()); - assertArrayEquals(new String[] {"123"}, spec.converter().orElseThrow().convert(spec.name(), StringToken.of("str")).orElseThrow()); + assertArrayEquals(new String[] {"123"}, spec.convert(spec.name(), StringToken.of("str")).orElseThrow()); } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecTest.java index a92c677d9cf..66bd44a2402 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionSpecTest.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 @@ -293,11 +293,11 @@ public class OptionSpecTest { return toOptionNames(List.of(names)); } - private static OptionValueConverter converter(ValueConverter conv) { + private static OptionValueConverter converter(ValueConverter conv) { return buildConverter(conv).create(); } - private static OptionValueConverter.Builder buildConverter(ValueConverter conv) { + private static OptionValueConverter.Builder buildConverter(ValueConverter conv) { return OptionValueConverter.build().converter(conv); } @@ -329,7 +329,7 @@ public class OptionSpecTest { return this; } - OptionSpecBuilder converter(OptionValueConverter v) { + OptionSpecBuilder converter(OptionValueConverter v) { converter = v; return this; } @@ -355,7 +355,7 @@ public class OptionSpecTest { } private List names; - private OptionValueConverter converter; + private OptionValueConverter converter; private T defaultOptionalValue; private Set scope = Set.of(new OptionScope() {}); private MergePolicy mergePolicy = MergePolicy.USE_LAST; diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionValueConverterTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionValueConverterTest.java index c026ee0639f..3d8f26523a0 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionValueConverterTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/OptionValueConverterTest.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 @@ -22,10 +22,12 @@ */ package jdk.jpackage.internal.cli; +import static jdk.jpackage.internal.cli.OptionValueConverter.convertString; import static jdk.jpackage.internal.cli.TestUtils.assertExceptionListEquals; import static jdk.jpackage.internal.cli.TestUtils.configureConverter; import static jdk.jpackage.internal.cli.TestUtils.configureValidator; 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.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; @@ -33,6 +35,7 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.nio.file.Path; import java.util.List; import java.util.function.Function; import java.util.stream.Stream; @@ -55,10 +58,10 @@ public class OptionValueConverterTest { if (positive) { final var token = StringToken.of("758"); - assertEquals(758, converter.convert(OptionName.of("number"), token).orElseThrow()); + assertEquals(758, convertString(converter, OptionName.of("number"), token).orElseThrow()); } else { final var token = StringToken.of("foo"); - final var result = converter.convert(OptionName.of("number"), token); + final var result = convertString(converter, OptionName.of("number"), token); assertEquals(1, result.errors().size()); @@ -77,7 +80,7 @@ public class OptionValueConverterTest { .converter(ValueConverter.create(Integer::valueOf, Integer.class)).create(); Function> convertString = (v) -> { - return converter.convert(OptionName.of("foo"), StringToken.of(v)); + return convertString(converter, OptionName.of("foo"), StringToken.of(v)); }; assertEquals(10, convertString.apply("10").orElseThrow()); @@ -89,24 +92,32 @@ public class OptionValueConverterTest { public void test_Builder_invalid() { assertThrowsExactly(NullPointerException.class, OptionValueConverter.build() - .converter(ValueConverter.create(Integer::valueOf, Integer.class)) + .converter(phonyConverter(Integer.class)) .mutate(configureConverter()) .formatString(null)::create); assertThrowsExactly(NullPointerException.class, OptionValueConverter.build() - .converter(ValueConverter.create(Integer::valueOf, Integer.class)) + .converter(phonyConverter(Integer.class)) .mutate(configureConverter()) .exceptionFactory(null)::create); } - @Test - public void test_Builder_copy() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_Builder_copy(boolean twoStep) { Function tokenizer = _ -> { throw new UnsupportedOperationException(); }; - var builder = OptionValueConverter.build() - .converter(ValueConverter.create(Integer::valueOf, Integer.class)) - .validator(Validator.build().predicate(_ -> true).create()) + final OptionValueConverter.Builder builder; + if (twoStep) { + builder = OptionValueConverter.build() + .converter(phonyConverter(Path.class)) + .map(phonyConverter(Integer.class)); + } else { + builder = OptionValueConverter.build().converter(phonyConverter(Integer.class)); + } + + builder.validator(Validator.build().predicate(_ -> true).create()) .mutate(configureConverter()) .tokenizer(tokenizer); @@ -114,7 +125,10 @@ public class OptionValueConverterTest { assertNotSame(copy, builder); - assertSame(builder.converter().orElse(null), copy.converter().orElse(null)); + if (!twoStep) { + assertSame(builder.converter().orElse(null), copy.converter().orElse(null)); + } + assertSame(builder.formatString().orElse(null), copy.formatString().orElse(null)); assertSame(builder.exceptionFactory().orElse(null), copy.exceptionFactory().orElse(null)); assertSame(builder.tokenizer().orElse(null), copy.tokenizer().orElse(null)); @@ -125,6 +139,23 @@ public class OptionValueConverterTest { assertNotEquals(builder.formatString().orElse(null), copy.formatString().orElse(null)); } + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void test_Builder_hasConverter(boolean twoStep) { + + if (twoStep) { + final var builder = OptionValueConverter.build() + .converter(phonyConverter(Path.class)) + .map(phonyConverter(String.class)); + assertTrue(builder.hasConverter()); + } else { + final var builder = OptionValueConverter.build(); + assertFalse(builder.hasConverter()); + builder.converter(phonyConverter(String.class)); + assertTrue(builder.hasConverter()); + } + } + @Test public void test_Builder_createArray() { @@ -134,7 +165,7 @@ public class OptionValueConverterTest { .tokenizer(str -> str.split(":")) .createArray(); - assertNotEquals(List.of(100, 67, 145), List.of(converter.convert(OptionName.of("foo"), StringToken.of("110:67:145")).orElseThrow())); + assertNotEquals(List.of(100, 67, 145), List.of(convertString(converter, OptionName.of("foo"), StringToken.of("110:67:145")).orElseThrow())); assertEquals(Integer[].class, converter.valueType()); } @@ -170,7 +201,7 @@ public class OptionValueConverterTest { var converter = builder.createArray(); - var result = converter.convert(OptionName.of("foo"), StringToken.of("100:-10:-10:67:str:145:-7")); + var result = convertString(converter, OptionName.of("foo"), StringToken.of("100:-10:-10:67:str:145:-7")); assertExceptionListEquals(Stream.of( new IllegalArgumentException("-10"), @@ -192,7 +223,7 @@ public class OptionValueConverterTest { }, Integer.class)).mutate(configureConverter()).create(); final var token = StringToken.of("foo"); - final var ex = assertThrowsExactly(ConverterException.class, () -> converter.convert(OptionName.of("number"), token)); + final var ex = assertThrowsExactly(ConverterException.class, () -> convertString(converter, OptionName.of("number"), token)); assertSame(exception, ex.getCause()); } @@ -209,8 +240,133 @@ public class OptionValueConverterTest { }).mutate(configureValidator()).create()).create(); final var token = StringToken.of("100"); - final var ex = assertThrowsExactly(ConverterException.class, () -> converter.convert(OptionName.of("number"), token)); + final var ex = assertThrowsExactly(ConverterException.class, () -> convertString(converter, OptionName.of("number"), token)); assertSame(exception, ex.getCause()); } + + @Test + public void testTwoStep() { + + final var multiplyConverter = ValueConverter.create(v -> { + return (long)(v * -1); + }, Long.class); + + final var converter = OptionValueConverter.build() + .converter(ValueConverter.create(Integer::parseInt, Integer.class)) + .map(multiplyConverter) + .create(); + + assertEquals(Long.class, converter.valueType()); + + var result = convertString(converter, OptionName.of("foo"), StringToken.of("123")).orElseThrow(); + assertEquals(-123, result); + } + + @Test + public void testMultiStep() { + + final var converter = OptionValueConverter.build() + .converter(ValueConverter.create(Path::of, Path.class)) + .map(ValueConverter.create(v -> { + return v.normalize().toString(); + }, String.class)) + .map(ValueConverter.create(Integer::valueOf, Integer.class)) + .create(); + + assertEquals(Integer.class, converter.valueType()); + + var result = convertString(converter, OptionName.of("foo"), StringToken.of("./123")).orElseThrow(); + assertEquals(123, result); + } + + @ParameterizedTest + @ValueSource(ints = {0, 1, 2}) + public void testTwoStep_map(int type) { + + var tolower = ValueConverter.create(String::toLowerCase, String.class); + var toupper = ValueConverter.create(String::toUpperCase, String.class); + var reverse = ValueConverter.create(str -> { + return new StringBuilder(str).reverse().toString(); + }, String.class); + + final var baseBuilder = OptionValueConverter.build().converter(toupper); + + switch (type) { + case 0 -> { + assertThrowsExactly(UnsupportedOperationException.class, () -> { + baseBuilder.map(OptionValueConverter.build()); + }); + } + case 1 -> { + var builder = baseBuilder.map(reverse); + var ex = assertThrowsExactly(IllegalArgumentException.class, () -> { + builder.map(OptionValueConverter.build().converter(phonyConverter(Object.class))); + }); + assertEquals(String.format("Expected (%s); actual (%s)", String.class, Object.class), ex.getMessage()); + + assertEquals("CBA", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + + var copy = builder.map(baseBuilder); + assertNotSame(copy, builder); + assertEquals("CBA", convertString(copy.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + assertEquals("CBA", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + + copy = builder.map(baseBuilder.copy().converter(tolower)); + assertNotSame(copy, builder); + assertEquals("cba", convertString(copy.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + assertEquals("CBA", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + } + case 2 -> { + var builder = baseBuilder.map(reverse).map(reverse); + var ex = assertThrowsExactly(IllegalArgumentException.class, () -> { + builder.map(OptionValueConverter.build().converter(phonyConverter(Object.class))); + }); + assertEquals(String.format("Expected (%s); actual (%s)", String.class, Object.class), ex.getMessage()); + + assertEquals("ABC", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + + var copy = builder.map(baseBuilder.map(reverse)); + assertNotSame(copy, builder); + assertEquals("ABC", convertString(copy.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + assertEquals("ABC", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + + copy = builder.map(baseBuilder.copy().converter(tolower).map(reverse)); + assertNotSame(copy, builder); + assertEquals("abc", convertString(copy.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + assertEquals("ABC", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + + copy = builder.map(baseBuilder.copy().converter(tolower)); + assertNotSame(copy, builder); + assertEquals("cba", convertString(copy.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + assertEquals("ABC", convertString(builder.create(), OptionName.of("foo"), StringToken.of("aBc")).orElseThrow()); + } + } + } + + @Test + public void testTwoStep_converter() { + + final var builder = OptionValueConverter.build() + // Create the "String -> Path" converter + .converter(ValueConverter.create(Path::of, Path.class)) + // Map the "String -> Path" converter into the "Path -> String" converter + .map(phonyConverter(String.class)) + // Map the "Path -> String" converter into the "String -> String" converter + .map(phonyConverter(String.class)); + + assertThrowsExactly(UnsupportedOperationException.class, () -> { + builder.converter(phonyConverter(String.class)); + }); + + assertThrowsExactly(UnsupportedOperationException.class, () -> { + builder.converter(); + }); + } + + private static ValueConverter phonyConverter(Class valueType) { + return ValueConverter.create(v -> { + throw new AssertionError(); + }, valueType); + } } 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 0b70f4151cc..58a78edb627 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 @@ -24,6 +24,8 @@ package jdk.jpackage.internal.cli; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toMap; +import static jdk.jpackage.internal.cli.TestUtils.assertExceptionListEquals; +import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; @@ -58,9 +60,11 @@ import jdk.jpackage.internal.model.BundleType; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.LauncherShortcut; import jdk.jpackage.internal.model.LauncherShortcutStartupDirectory; +import jdk.jpackage.internal.util.RootedPath; import jdk.jpackage.internal.util.StringBundle; import jdk.jpackage.test.Comm; import jdk.jpackage.test.JUnitAdapter; +import jdk.jpackage.test.JUnitUtils; import jdk.jpackage.test.TKit; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -111,7 +115,7 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { var spec = StandardOption.ICON.getSpec(); - var result = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(name)); + var result = spec.convert(spec.name(), StringToken.of(name)); assertEquals(Path.of(name), result.orElseThrow()); } @@ -121,7 +125,7 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { var spec = StandardOption.ICON.getSpec(); - var result = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(workDir.toString())); + var result = spec.convert(spec.name(), StringToken.of(workDir.toString())); var ex = assertThrows(JPackageException.class, result::orElseThrow); @@ -135,7 +139,7 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { var spec = new StandardOptionContext().forFile(propertyFile).mapOptionSpec(StandardOption.ICON.getSpec()); - var result = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(workDir.toString())); + var result = spec.convert(spec.name(), StringToken.of(workDir.toString())); var ex = assertThrows(JPackageException.class, result::orElseThrow); @@ -150,7 +154,7 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { var tempRoot = workDir.resolve(dir); - var value = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(tempRoot.toString())).orElseThrow(); + var value = spec.convert(spec.name(), StringToken.of(tempRoot.toString())).orElseThrow(); assertEquals(tempRoot, value); } @@ -165,14 +169,14 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { Files.writeString(tempRoot, "foo"); var ex = assertThrowsExactly(JPackageException.class, - spec.converter().orElseThrow().convert(spec.name(), StringToken.of(tempRoot.toString()))::orElseThrow); + spec.convert(spec.name(), StringToken.of(tempRoot.toString()))::orElseThrow); assertEquals(I18N.format("error.parameter-not-empty-directory", tempRoot, "--temp"), ex.getMessage()); assertEquals(NotDirectoryException.class, ex.getCause().getClass()); tempRoot = workDir; ex = assertThrowsExactly(JPackageException.class, - spec.converter().orElseThrow().convert(spec.name(), StringToken.of(tempRoot.toString()))::orElseThrow); + spec.convert(spec.name(), StringToken.of(tempRoot.toString()))::orElseThrow); assertEquals(I18N.format("error.parameter-not-empty-directory", tempRoot, "--temp"), ex.getMessage()); assertEquals(DirectoryNotEmptyException.class, ex.getCause().getClass()); } @@ -200,13 +204,146 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { var spec = StandardOption.TYPE.getSpec(); - var result = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(name)); + var result = spec.convert(spec.name(), StringToken.of(name)); var ex = assertThrows(JPackageException.class, result::orElseThrow); assertEquals(I18N.format("ERR_InvalidInstallerType", name), ex.getMessage()); } + @Test + public void test_APP_CONTENT_valid(@TempDir Path workDir) throws IOException { + + var spec = StandardOption.APP_CONTENT.getSpec(); + + var contentDir = workDir.resolve("a"); + var emptyDir = contentDir.resolve("b/empty-dir"); + var file = contentDir.resolve("file.txt"); + + Files.createDirectories(emptyDir); + Files.createDirectories(file.getParent()); + Files.createFile(file); + + Object convertedValue = spec.convert( + spec.name(), + StringToken.of(Stream.of(contentDir, file).map(Path::toString).collect(joining(","))) + ).orElseThrow(); + + var paths = StandardOption.APP_CONTENT.getFrom(Options.of(Map.of(StandardOption.APP_CONTENT, convertedValue))); + var sortedPathList = paths.stream().flatMap(Collection::stream).map(RootedPath::branch).sorted().toList(); + + var expectedPathList = Stream.of( + "a", + "a/b", + "a/b/empty-dir", + "a/file.txt", + "file.txt" + ).map(Path::of).sorted().toList(); + + assertEquals(expectedPathList, sortedPathList); + } + + @Test + public void test_APP_CONTENT_invalid(@TempDir Path workDir) throws IOException { + var spec = StandardOption.APP_CONTENT.getSpec(); + + var token = StringToken.of(workDir.resolve("nonexistent").toString()); + var result = spec.convert(spec.name(), token); + + assertExceptionListEquals(Stream.of( + "error.parameter-not-directory", + "error.parameter-not-file" + ).map(key -> { + return new JPackageException(I18N.format(key, token.value(), spec.name().formatForCommandLine())); + }).toList(), result.errors()); + } + + @ParameterizedTest + @EnumSource(OptionMutatorTest.TestType.class) + public void test_pathOptionMutator(OptionMutatorTest.TestType type) { + new OptionMutatorTest<>(StandardOption.pathOptionMutator(), StandardValueConverter.pathConv()) + .validValue("file.txt") + .invalidValue("\0") + .cmdlineErrorFormatKeys("error.parameter-not-path") + .propertyFileErrorFormatKeys("error.properties-parameter-not-path") + .test(OptionSpecBuilder.create(Path.class), type); + } + + @ParameterizedTest + @EnumSource(OptionMutatorTest.TestType.class) + public void test_existingPathOptionMutator(OptionMutatorTest.TestType type, @TempDir Path workDir) { + new OptionMutatorTest<>(StandardOption.existingPathOptionMutator(), StandardValueConverter.pathConv()) + .validValue(workDir.toString()) + .invalidValue(workDir.resolve("nonexistent").toString()) + .cmdlineErrorFormatKeys("error.parameter-not-directory", "error.parameter-not-file") + .propertyFileErrorFormatKeys("error.properties-parameter-not-directory", "error.properties-parameter-not-file") + .test(OptionSpecBuilder.create(Path.class), type); + } + + @ParameterizedTest + @EnumSource(OptionMutatorTest.TestType.class) + public void test_directoryOptionMutator(OptionMutatorTest.TestType type, @TempDir Path workDir) { + new OptionMutatorTest<>(StandardOption.directoryOptionMutator(), StandardValueConverter.pathConv()) + .validValue(workDir.toString()) + .invalidValue(workDir.resolve("nonexistent").toString()) + .cmdlineErrorFormatKeys("error.parameter-not-directory") + .propertyFileErrorFormatKeys("error.properties-parameter-not-directory") + .test(OptionSpecBuilder.create(Path.class), type); + } + + @ParameterizedTest + @EnumSource(OptionMutatorTest.TestType.class) + public void test_fileOptionMutator(OptionMutatorTest.TestType type, @TempDir Path workDir) throws IOException { + var test = new OptionMutatorTest<>(StandardOption.fileOptionMutator(), StandardValueConverter.pathConv()) + .invalidValue(workDir.resolve("nonexistent").toString()) + .cmdlineErrorFormatKeys("error.parameter-not-file") + .propertyFileErrorFormatKeys("error.properties-parameter-not-file"); + + switch (type) { + case TEST_CMDLINE_VALID, TEST_PROPERTY_FILE_VALID -> { + var file = workDir.resolve("file.txt"); + Files.createFile(file); + test.validValue(file.toString()); + } + default -> {} + } + + test.test(OptionSpecBuilder.create(Path.class), type); + } + + @ParameterizedTest + @EnumSource(OptionMutatorTest.TestType.class) + public void test_explodedPathOptionMapper(OptionMutatorTest.TestType type, @TempDir Path workDir) throws IOException { + + var explodePath = StandardValueConverter.explodedPathConverter().withPathFileName().create(); + + ValueConverter conv = ValueConverter.create(str -> { + var path = StandardValueConverter.pathConv().convert(str); + return explodePath.convert(path); + }, RootedPath[].class); + + var test = new OptionMutatorTest<>(_ -> {}, conv) + .invalidValue(workDir.resolve("nonexistent").toString()) + .cmdlineErrorFormatKeys("error.parameter-not-directory") + .propertyFileErrorFormatKeys("error.properties-parameter-not-directory"); + + switch (type) { + case TEST_CMDLINE_VALID, TEST_PROPERTY_FILE_VALID -> { + var dir = workDir.resolve("dir"); + Files.createDirectories(dir); + for (var file : List.of("foo.txt", "bar.txt")) { + Files.createFile(workDir.resolve(file)); + } + test.validValue(dir.toString()); + } + default -> {} + } + + test.test(OptionSpecBuilder.create(Path.class) + .mutate(StandardOption.directoryOptionMutator()) + .map(StandardOption.explodedPathOptionMapper(explodePath)), type); + } + @Test public void test_booleanOptionMutator() { @@ -221,6 +358,13 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { assertFalse(option.containsIn(empty)); } + @Test + public void test_booleanOptionMutator_in_property_file() { + new OptionMutatorTest<>(StandardOption.booleanOptionMutator(), StandardValueConverter.booleanConv()) + .validValue(Boolean.TRUE.toString()) + .test(OptionSpecBuilder.create(Boolean.class), OptionMutatorTest.TestType.TEST_PROPERTY_FILE_VALID); + } + @ParameterizedTest @MethodSource public void testLauncherShortcutOptions(LauncherShortcutTestSpec testSpec) { @@ -303,7 +447,7 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { var spec = StandardOption.ARGUMENTS.getOption().spec(); - var result = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(value)); + var result = spec.convert(spec.name(), StringToken.of(value)); assertEquals(expectedTokens, List.of(result.map(String[].class::cast).orElseThrow())); } @@ -350,7 +494,7 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { || (bundlingOperation.os() == appImageOS); }).forEach(bundlingOperation -> { var bundleTypeStr = bundlingOperation.bundleTypeValue(); - var bundleType = spec.converter().orElseThrow().convert(spec.name(), StringToken.of(bundleTypeStr)).orElseThrow(); + var bundleType = spec.convert(spec.name(), StringToken.of(bundleTypeStr)).orElseThrow(); assertSame(bundlingOperation.bundleType(), bundleType); }); } @@ -391,6 +535,100 @@ public class StandardOptionTest extends JUnitAdapter.TestSrcInitializer { } + static final class OptionMutatorTest { + + enum TestType { + TEST_CMDLINE_VALID, + TEST_PROPERTY_FILE_VALID, + TEST_CMDLINE_INVALID, + TEST_PROPERTY_FILE_INVALID; + } + + OptionMutatorTest(Consumer> testee, ValueConverter conv) { + this.testee = Objects.requireNonNull(testee); + this.conv = Objects.requireNonNull(conv); + } + + void test(OptionSpecBuilder specBuilder, TestType type) { + var srcSpec = specBuilder.name("foo").mutate(testee).createOptionSpec(); + OptionSpec spec; + + var propertyFile = Path.of("foo.properties"); + + switch (type) { + case TEST_PROPERTY_FILE_VALID, TEST_PROPERTY_FILE_INVALID -> { + spec = new StandardOptionContext().forFile(propertyFile).mapOptionSpec(srcSpec); + } + default -> { + spec = srcSpec; + } + } + + StringToken token; + switch (type) { + case TEST_CMDLINE_VALID, TEST_PROPERTY_FILE_VALID -> { + token = StringToken.of(Objects.requireNonNull(validValue)); + } + default -> { + token = StringToken.of(Objects.requireNonNull(invalidValue)); + } + } + + var result = spec.convert(spec.name(), token); + + switch (type) { + case TEST_CMDLINE_VALID, TEST_PROPERTY_FILE_VALID -> { + var expected = toFunction(conv::convert).apply(token.value()); + var actual = result.orElseThrow(); + + if (spec.valueType().isArray()) { + JUnitUtils.assertArrayEquals(expected, actual); + } else { + assertEquals(expected, actual); + } + } + case TEST_CMDLINE_INVALID -> { + assertExceptionListEquals(cmdlineErrorFormatKeys.stream().map(key -> { + return new JPackageException(I18N.format(key, token.value(), spec.name().formatForCommandLine())); + }).map(JUnitUtils::removeExceptionCause).toList(), result.errors().stream().map(JUnitUtils::removeExceptionCause).toList()); + } + case TEST_PROPERTY_FILE_INVALID -> { + assertExceptionListEquals(propertyFileErrorFormatKeys.stream().map(key -> { + return new JPackageException(I18N.format(key, token.value(), spec.name().name(), propertyFile)); + }).map(JUnitUtils::removeExceptionCause).toList(), result.errors().stream().map(JUnitUtils::removeExceptionCause).toList()); + } + } + } + + OptionMutatorTest validValue(String v) { + validValue = v; + return this; + } + + OptionMutatorTest invalidValue(String v) { + invalidValue = v; + return this; + } + + OptionMutatorTest cmdlineErrorFormatKeys(String... v) { + cmdlineErrorFormatKeys = List.of(v); + return this; + } + + OptionMutatorTest propertyFileErrorFormatKeys(String... v) { + propertyFileErrorFormatKeys = List.of(v); + return this; + } + + private final Consumer> testee; + private final ValueConverter conv; + private List cmdlineErrorFormatKeys; + private List propertyFileErrorFormatKeys; + private String validValue; + private String invalidValue; + } + + record AddLauncherTestSpec(Optional expected, List expectedErrors, Optional optionValue) { AddLauncherTestSpec { if (expected.isEmpty() == expectedErrors.isEmpty()) { diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardValueConverterTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardValueConverterTest.java index 6b16b1099c7..491dae60d5f 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardValueConverterTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/StandardValueConverterTest.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 @@ -41,7 +41,7 @@ import org.junit.jupiter.params.provider.ValueSource; public class StandardValueConverterTest { @Test - public void test_identityConv() { + public void test_identityConv() throws Exception { final var testee = StandardValueConverter.identityConv(); @@ -58,7 +58,7 @@ public class StandardValueConverterTest { @ParameterizedTest @ValueSource(booleans = {true, false}) - public void test_pathConv(boolean positive) { + public void test_pathConv(boolean positive) throws Exception { final var testee = StandardValueConverter.pathConv(); @@ -72,14 +72,14 @@ public class StandardValueConverterTest { @ParameterizedTest @MethodSource - public void test_booleanConv(String value, Boolean expected) { + public void test_booleanConv(String value, Boolean expected) throws Exception { assertEquals(expected, StandardValueConverter.booleanConv().convert(value)); } @ParameterizedTest @MethodSource - public void test_mainLauncherShortcutConv(String value, LauncherShortcut expected) { + public void test_mainLauncherShortcutConv(String value, LauncherShortcut expected) throws Exception { assertEquals(expected, StandardValueConverter.mainLauncherShortcutConv().convert(value)); } @@ -93,7 +93,7 @@ public class StandardValueConverterTest { @ParameterizedTest @MethodSource - public void test_addLauncherShortcutConv(String value, LauncherShortcut expected) { + public void test_addLauncherShortcutConv(String value, LauncherShortcut expected) throws Exception { assertEquals(expected, StandardValueConverter.addLauncherShortcutConv().convert(value)); } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/FileUtilsTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/FileUtilsTest.java index 2067f0fa628..b7639dbfad4 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/FileUtilsTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/FileUtilsTest.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,92 +24,36 @@ package jdk.jpackage.internal.util; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; -import org.junit.jupiter.params.provider.ValueSource; public class FileUtilsTest { - @ParameterizedTest - @EnumSource(ExcludeType.class) - public void test_copyRecursive_dir(ExcludeType exclude, @TempDir Path workdir) throws IOException { + @Test + public void test_copyRecursive_dir(@TempDir Path workdir) throws IOException { Files.createDirectories(workdir.resolve("from/foo/bar")); Files.createDirectories(workdir.resolve("from/foo/buz")); Files.writeString(workdir.resolve("from/foo/bar/file.txt"), "Hello"); - List excludes = new ArrayList<>(); - switch (exclude) { - case EXCLUDE_FILE -> { - excludes.add(Path.of("file.txt")); - } - case EXCLUDE_DIR -> { - excludes.add(Path.of("bar")); - } - case EXCLUDE_SUBDIR -> { - excludes.add(Path.of("foo")); - } - case EXCLUDE_NONE -> { - } - } - - FileUtils.copyRecursive(workdir.resolve("from"), workdir.resolve("to"), excludes); + FileUtils.copyRecursive(workdir.resolve("from"), workdir.resolve("to")); assertEquals("Hello", Files.readString(workdir.resolve("from/foo/bar/file.txt"))); - - switch (exclude) { - case EXCLUDE_FILE -> { - assertFalse(Files.exists(workdir.resolve("to/foo/bar/file.txt"))); - assertTrue(Files.isDirectory(workdir.resolve("to/foo/bar"))); - } - case EXCLUDE_DIR -> { - assertFalse(Files.exists(workdir.resolve("to/foo/bar"))); - assertTrue(Files.isDirectory(workdir.resolve("to/foo/buz"))); - } - case EXCLUDE_SUBDIR -> { - assertFalse(Files.exists(workdir.resolve("to/foo"))); - assertTrue(Files.isDirectory(workdir.resolve("to"))); - } - case EXCLUDE_NONE -> { - assertEquals("Hello", Files.readString(workdir.resolve("to/foo/bar/file.txt"))); - } - } + assertEquals("Hello", Files.readString(workdir.resolve("to/foo/bar/file.txt"))); } - @ParameterizedTest - @ValueSource(booleans = {true, false}) - public void test_copyRecursive_file(boolean exclude, @TempDir Path workdir) throws IOException { + @Test + public void test_copyRecursive_file(@TempDir Path workdir) throws IOException { Files.createDirectories(workdir.resolve("from/foo/bar")); Files.writeString(workdir.resolve("from/foo/bar/file.txt"), "Hello"); - List excludes = new ArrayList<>(); - if (exclude) { - excludes.add(Path.of("bar/file.txt")); - } - - FileUtils.copyRecursive(workdir.resolve("from/foo/bar/file.txt"), workdir.resolve("to/foo/bar/file.txt"), excludes); + FileUtils.copyRecursive(workdir.resolve("from/foo/bar/file.txt"), workdir.resolve("to/foo/bar/file.txt")); assertEquals("Hello", Files.readString(workdir.resolve("from/foo/bar/file.txt"))); - if (exclude) { - assertFalse(Files.exists(workdir.resolve("to"))); - } else { - assertEquals("Hello", Files.readString(workdir.resolve("to/foo/bar/file.txt"))); - } - } - - enum ExcludeType { - EXCLUDE_NONE, - EXCLUDE_FILE, - EXCLUDE_DIR, - EXCLUDE_SUBDIR, + assertEquals("Hello", Files.readString(workdir.resolve("to/foo/bar/file.txt"))); } } diff --git a/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitUtils.java b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitUtils.java index 0b63688ee20..530a0d5cb1f 100644 --- a/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitUtils.java +++ b/test/jdk/tools/jpackage/junit/tools/jdk/jpackage/test/JUnitUtils.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 @@ -64,6 +64,10 @@ public final class JUnitUtils { return EXCEPTION_OM.toMap(ex); } + public static Exception removeExceptionCause(Exception ex) { + return new ExceptionCauseRemover(ex); + } + public static final class ExceptionPattern { @@ -117,6 +121,23 @@ public final class JUnitUtils { } + private static final class ExceptionCauseRemover extends Exception { + + ExceptionCauseRemover(Exception ex) { + super(ex.getMessage()); + type = ex.getClass(); + } + + public Class getType() { + return type; + } + + private final Class type; + + private static final long serialVersionUID = 1L; + } + + @FunctionalInterface private interface ArrayEqualsAsserter { void accept(T expected, T actual); diff --git a/test/jdk/tools/jpackage/share/AppContentTest.java b/test/jdk/tools/jpackage/share/AppContentTest.java index b6066bb9cc4..e275fae262c 100644 --- a/test/jdk/tools/jpackage/share/AppContentTest.java +++ b/test/jdk/tools/jpackage/share/AppContentTest.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 @@ -41,6 +41,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.TreeMap; +import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; @@ -85,6 +86,7 @@ public class AppContentTest { @Test @ParameterSupplier("test") @ParameterSupplier(value="testSymlink", ifNotOS = WINDOWS) + @ParameterSupplier public void testAppImage(TestSpec testSpec) throws Exception { testSpec.test(new ConfigurationTarget(JPackageCommand.helloAppImage())); } @@ -126,6 +128,14 @@ public class AppContentTest { }).toList(); } + public static Collection testAppImage() { + return Stream.of( + build().add(NonExistentPath.create("*output-app-image*", JPackageCommand::outputBundle)) + ).map(TestSpec.Builder::create).map(v -> { + return new Object[] {v}; + }).toList(); + } + public static Collection testSymlink() { return Stream.of( build().add(TEST_JAVA) @@ -150,7 +160,7 @@ public class AppContentTest { void test(ConfigurationTarget target) { final int expectedJPackageExitCode; - if (contentFactories.stream().flatMap(List::stream).anyMatch(TEST_BAD::equals)) { + if (contentFactories.stream().flatMap(List::stream).anyMatch(NonExistentPath.class::isInstance)) { expectedJPackageExitCode = 1; } else { expectedJPackageExitCode = 0; @@ -159,9 +169,11 @@ public class AppContentTest { final List> allContent = new ArrayList<>(); target.addInitializer(JPackageCommand::setFakeRuntime) - .addRunOnceInitializer(_ -> { + .addInitializer(cmd -> { contentFactories.stream().map(group -> { - return group.stream().map(ContentFactory::create).toList(); + return group.stream().map(contentFactory -> { + return contentFactory.create(cmd); + }).toList(); }).forEach(allContent::add); }).addInitializer(cmd -> { allContent.stream().map(group -> { @@ -192,6 +204,10 @@ public class AppContentTest { }); target.addInstallVerifier(cmd -> { + if (expectedJPackageExitCode != 0) { + return; + } + var appContentRoot = getAppContentRoot(cmd); Set disabledVerifiers = new HashSet<>(); @@ -329,7 +345,7 @@ public class AppContentTest { @FunctionalInterface private interface ContentFactory { - Content create(); + Content create(JPackageCommand cmd); } private interface Content { @@ -337,12 +353,7 @@ public class AppContentTest { Iterable verifiers(Path appContentRoot); } - private sealed interface PathVerifier permits - RegularFileVerifier, - DirectoryVerifier, - SymlinkTargetVerifier, - NoPathVerifier { - + private sealed interface PathVerifier { Path path(); void verify(); } @@ -467,22 +478,45 @@ public class AppContentTest { /** * Non-existing content. */ - private static final class NonExistantPath implements ContentFactory { + private static final class NonExistentPath implements ContentFactory { + + private NonExistentPath(String label, Function makePath) { + this.label = Objects.requireNonNull(label); + this.makePath = Objects.requireNonNull(makePath); + } + @Override - public Content create() { - var nonExistant = TKit.createTempFile("non-existant"); - try { - TKit.deleteIfExists(nonExistant); - } catch (IOException ex) { - throw new UncheckedIOException(ex); + public Content create(JPackageCommand cmd) { + var nonexistent = makePath.apply(cmd); + if (Files.exists(nonexistent)) { + throw new IllegalStateException(); } - return new FileContent(nonExistant, 0); + return new FileContent(nonexistent, 0); } @Override public String toString() { - return "*non-existant*"; + return label; } + + static NonExistentPath create(String label, Function makePath) { + return new NonExistentPath(label, makePath); + } + + static NonExistentPath create(String path) { + return new NonExistentPath(String.format("*%s*", Objects.requireNonNull(path)), _ -> { + var nonexistent = TKit.createTempFile(path); + try { + TKit.deleteIfExists(nonexistent); + return nonexistent; + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); + } + + private final String label; + private final Function makePath; } /** @@ -565,7 +599,7 @@ public class AppContentTest { } @Override - public Content create() { + public Content create(JPackageCommand cmd) { final var appContentRoot = createAppContentRoot(); final var symlinkPath = appContentRoot.resolve(symlinkPath()); @@ -639,7 +673,7 @@ public class AppContentTest { } @Override - public Content create() { + public Content create(JPackageCommand cmd) { Path srcPath = factory.get(); if (!srcPath.endsWith(pathInAppContentRoot)) { throw new IllegalArgumentException(); @@ -672,7 +706,7 @@ public class AppContentTest { private static final ContentFactory TEST_JAVA = createTextFileContent("apps/PrintEnv.java", "Not what someone would expect"); private static final ContentFactory TEST_DUKE = createTextFileContent("duke.txt", "Hi Duke!"); private static final ContentFactory TEST_DIR = createDirTreeContent("apps"); - private static final ContentFactory TEST_BAD = new NonExistantPath(); + private static final ContentFactory TEST_BAD = NonExistentPath.create("non-existent"); // On OSX `--app-content` paths will be copied into the "Contents" folder // of the output app image. diff --git a/test/jdk/tools/jpackage/share/InOutPathTest.java b/test/jdk/tools/jpackage/share/InOutPathTest.java index 6956f66df42..a9fa48dc1c6 100644 --- a/test/jdk/tools/jpackage/share/InOutPathTest.java +++ b/test/jdk/tools/jpackage/share/InOutPathTest.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 @@ -24,26 +24,28 @@ import static jdk.jpackage.test.RunnablePackageTest.Action.CREATE_AND_UNPACK; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; 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.function.ThrowingConsumer; import jdk.jpackage.test.Annotations.Parameters; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.AppImageFile; import jdk.jpackage.test.ApplicationLayout; +import jdk.jpackage.test.ConfigurationTarget; import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.JPackageCommand.StandardAssert; import jdk.jpackage.test.PackageFile; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; import jdk.jpackage.test.TKit; +import jdk.jpackage.internal.util.function.ThrowingConsumer; /* * @test @@ -76,25 +78,6 @@ public final class InOutPathTest { return data; } - @Parameters(ifNotOS = OperatingSystem.MACOS) - public static Collection appContentInputOther() { - return List.of(new Object[][]{ - {PackageTypeAlias.IMAGE, wrap(cmd -> { - additionalContent(cmd, "--app-content", cmd.outputBundle()); - }, "--app-content same as output bundle")}, - }); - } - - @Parameters(ifOS = OperatingSystem.MACOS) - public static Collection appContentInputOSX() { - var contentsFolder = "Contents/MacOS"; - return List.of(new Object[][]{ - {PackageTypeAlias.IMAGE, wrap(cmd -> { - additionalContent(cmd, "--app-content", cmd.outputBundle().resolve(contentsFolder)); - }, String.format("--app-content same as the \"%s\" folder in the output bundle", contentsFolder))}, - }); - } - @Parameters(ifOS = OperatingSystem.MACOS) public static Collection inputOSX() { return List.of(additionalContentInput(PackageType.MAC_DMG, "--mac-dmg-content").toArray(Object[][]::new)); @@ -146,67 +129,54 @@ public final class InOutPathTest { } @Test - public void test() throws Exception { + public void test() { runTest(packageTypes, configure); } private static Envelope wrap(ThrowingConsumer v, String label) { - return new Envelope(v, label); - } - - private static boolean isAppImageValid(JPackageCommand cmd) { - return !cmd.hasArgument("--app-content") && !cmd.hasArgument("--mac-dmg-content"); + return new Envelope(ThrowingConsumer.toConsumer(v), label); } private static void runTest(Set packageTypes, - ThrowingConsumer configure) throws Exception { - ThrowingConsumer configureWrapper = cmd -> { + Consumer configure) { + + ConfigurationTarget cfg; + if (packageTypes.contains(PackageType.IMAGE)) { + cfg = new ConfigurationTarget( + JPackageCommand.helloAppImage(JAR_PATH.toString() + ":")); + } else { + cfg = new ConfigurationTarget(new PackageTest() + .forTypes(packageTypes) + .configureHelloApp(JAR_PATH.toString() + ":")); + } + + var verifier = new AppDirContentVerifier(); + + cfg.addInitializer(cmd -> { // Make sure the input directory is empty in every test run. // This is needed because jpackage output directories in this test // are subdirectories of the input directory. cmd.setInputToEmptyDirectory(); configure.accept(cmd); if (cmd.hasArgument("--temp") && cmd.isImagePackageType()) { - // Request to build app image wit user supplied temp directory, + // Request to build app image with user supplied temp directory, // ignore external runtime if any to make use of the temp directory // for runtime generation. cmd.ignoreDefaultRuntime(true); } else { cmd.setFakeRuntime(); } + }) + .addInitializer(JPackageCommand::executePrerequisiteActions) + .addInitializer(verifier::captureInputDir); - if (!isAppImageValid(cmd)) { - // Standard asserts for .jpackage.xml fail in messed up app image. Disable them. - // Other standard asserts for app image contents should pass. - cmd.excludeStandardAsserts(StandardAssert.APP_IMAGE_FILE); - } - }; + cfg.cmd().ifPresent(JPackageCommand::executeAndAssertHelloAppImageCreated); - if (packageTypes.contains(PackageType.IMAGE)) { - JPackageCommand cmd = JPackageCommand.helloAppImage(JAR_PATH.toString() + ":"); - configureWrapper.accept(cmd); - cmd.executeAndAssertHelloAppImageCreated(); - if (isAppImageValid(cmd)) { - verifyAppImage(cmd); - } + cfg.addInstallVerifier(verifier::verify); - if (cmd.hasArgument("--app-content")) { - // `--app-content` can be set to the app image directory which - // should not exist before jpackage is executed: - // jpackage --name Foo --dest output --app-content output/Foo - // Verify the directory exists after jpackage execution. - // At least this will catch the case when the value of - // `--app-content` option refers to a path unrelated to jpackage I/O. - TKit.assertDirectoryExists(Path.of(cmd.getArgumentValue("--app-content"))); - } - } else { - new PackageTest() - .forTypes(packageTypes) - .configureHelloApp(JAR_PATH.toString() + ":") - .addInitializer(configureWrapper) - .addInstallVerifier(InOutPathTest::verifyAppImage) - .run(CREATE_AND_UNPACK); - } + cfg.test().ifPresent(pkg -> { + pkg.run(CREATE_AND_UNPACK); + }); } private static void outputDirInInputDir(JPackageCommand cmd) throws @@ -217,8 +187,7 @@ public final class InOutPathTest { cmd.setArgumentValue("--dest", outputDir); } - private static void outputDirSameAsInputDir(JPackageCommand cmd) throws - IOException { + private static void outputDirSameAsInputDir(JPackageCommand cmd) { // Set output dir the same as the input dir cmd.setArgumentValue("--dest", cmd.inputDir()); } @@ -238,38 +207,7 @@ public final class InOutPathTest { cmd.addArguments(argName, appContentFile.getParent()); } - private static void verifyAppImage(JPackageCommand cmd) throws IOException { - if (!isAppImageValid(cmd)) { - // Don't verify the contents of app image as it is invalid. - // jpackage exited without getting stuck in infinite spiral. - // No more expectations from the tool for the give arguments. - return; - } - - final Path rootDir = cmd.isImagePackageType() ? cmd.outputBundle() : cmd.pathToUnpackedPackageFile( - cmd.appInstallationDirectory()); - final Path appDir = ApplicationLayout.platformAppImage().resolveAt( - rootDir).appDirectory(); - - final var knownFiles = Set.of( - JAR_PATH.getName(0).toString(), - PackageFile.getPathInAppImage(Path.of("")).getFileName().toString(), - AppImageFile.getPathInAppImage(Path.of("")).getFileName().toString(), - cmd.name() + ".cfg" - ); - - TKit.assertFileExists(appDir.resolve(JAR_PATH)); - - try (Stream actualFilesStream = Files.list(appDir)) { - var unexpectedFiles = actualFilesStream.map(path -> { - return path.getFileName().toString(); - }).filter(Predicate.not(knownFiles::contains)).toList(); - TKit.assertStringListEquals(List.of(), unexpectedFiles, - "Check there are no unexpected files in `app` folder"); - } - } - - private static final record Envelope(ThrowingConsumer value, String label) { + private record Envelope(Consumer value, String label) { @Override public String toString() { // Will produce the same test description for the same label every @@ -291,8 +229,49 @@ public final class InOutPathTest { private final Set packageTypes; } + private static final class AppDirContentVerifier { + + void captureInputDir(JPackageCommand cmd) { + var root = Path.of(cmd.getArgumentValue("--input")); + try (var walk = Files.walk(root)) { + inputDirFiles = walk.map(root::relativize).toList(); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + void verify(JPackageCommand cmd) { + var expectedContent = new HashSet<>(inputDirFiles); + + expectedContent.add(Path.of(cmd.name() + ".cfg")); + if (cmd.isImagePackageType()) { + expectedContent.add(AppImageFile.getPathInAppImage(Path.of("")).getFileName()); + } else { + expectedContent.add(PackageFile.getPathInAppImage(Path.of("")).getFileName()); + } + + final var rootDir = cmd.isImagePackageType() ? cmd.outputBundle() : cmd.pathToUnpackedPackageFile( + cmd.appInstallationDirectory()); + final var appDir = ApplicationLayout.platformAppImage().resolveAt(rootDir).appDirectory(); + + try (var walk = Files.walk(appDir)) { + var unexpectedFiles = walk + .map(appDir::relativize) + .filter(Predicate.not(expectedContent::contains)) + .map(Path::toString) + .toList(); + TKit.assertStringListEquals(List.of(), unexpectedFiles, + "Check there are no unexpected files in `app` folder"); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private Collection inputDirFiles; + } + private final Set packageTypes; - private final ThrowingConsumer configure; + private final Consumer configure; // Placing jar file in the "Resources" subdir of the input directory would allow // to use the input directory with `--app-content` on OSX. From 9b47c23b4b809f7070c6c8279b7ffdf83234dcdb Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Fri, 16 Jan 2026 23:16:43 +0000 Subject: [PATCH 132/204] 8375242: [macos] Improve jpackage signing coverage Reviewed-by: almatvee --- .../jdk/jpackage/test/JPackageCommand.java | 107 +++-- .../helpers/jdk/jpackage/test/MacHelper.java | 419 ++++++++++++++++-- .../helpers/jdk/jpackage/test/MacSign.java | 13 + .../jdk/jpackage/test/MacSignVerify.java | 18 +- .../jpackage/macosx/EntitlementsTest.java | 10 +- .../tools/jpackage/macosx/MacSignTest.java | 122 ++--- .../jpackage/macosx/SigningAppImageTest.java | 102 ++--- .../macosx/SigningAppImageTwoStepsTest.java | 202 ++++++--- .../tools/jpackage/macosx/SigningBase.java | 178 ++++++++ .../jpackage/macosx/SigningPackageTest.java | 335 ++++++++------ .../macosx/SigningPackageTwoStepTest.java | 312 ++++++------- .../SigningRuntimeImagePackageTest.java | 268 ++++++----- .../jpackage/macosx/base/SigningBase.java | 316 ------------- 13 files changed, 1413 insertions(+), 989 deletions(-) create mode 100644 test/jdk/tools/jpackage/macosx/SigningBase.java delete mode 100644 test/jdk/tools/jpackage/macosx/base/SigningBase.java 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 7874df3fd69..f81c35cea0b 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -294,34 +294,8 @@ public class JPackageCommand extends CommandArguments { public JPackageCommand setFakeRuntime() { verifyMutable(); - - ThrowingConsumer createBulkFile = path -> { - Files.createDirectories(path.getParent()); - try (FileOutputStream out = new FileOutputStream(path.toFile())) { - byte[] bytes = new byte[4 * 1024]; - new SecureRandom().nextBytes(bytes); - out.write(bytes); - } - }; - addPrerequisiteAction(cmd -> { - Path fakeRuntimeDir = TKit.createTempDirectory("fake_runtime"); - - TKit.trace(String.format("Init fake runtime in [%s] directory", - fakeRuntimeDir)); - - if (TKit.isOSX()) { - // Make MacAppImageBuilder happy - createBulkFile.accept(fakeRuntimeDir.resolve(Path.of( - "lib/jli/libjli.dylib"))); - } - - // Make sure fake runtime takes some disk space. - // Package bundles with 0KB size are unexpected and considered - // an error by PackageTest. - createBulkFile.accept(fakeRuntimeDir.resolve(Path.of("lib", "bulk"))); - - cmd.setArgumentValue("--runtime-image", fakeRuntimeDir); + cmd.setArgumentValue("--runtime-image", createInputRuntimeImage(RuntimeImageType.RUNTIME_TYPE_FAKE)); }); return this; @@ -390,24 +364,77 @@ public class JPackageCommand extends CommandArguments { return cmd; } + public enum RuntimeImageType { + + /** + * Runtime suitable for running the default "Hello" test app. + */ + RUNTIME_TYPE_HELLO_APP, + + /** + * Fake runtime. + */ + RUNTIME_TYPE_FAKE, + + ; + } + public static Path createInputRuntimeImage() { + return createInputRuntimeImage(RuntimeImageType.RUNTIME_TYPE_HELLO_APP); + } + + public static Path createInputRuntimeImage(RuntimeImageType role) { + Objects.requireNonNull(role); final Path runtimeImageDir; + switch (role) { - if (JPackageCommand.DEFAULT_RUNTIME_IMAGE != null) { - runtimeImageDir = JPackageCommand.DEFAULT_RUNTIME_IMAGE; - } else { - runtimeImageDir = TKit.createTempDirectory("runtime-image").resolve("data"); + case RUNTIME_TYPE_FAKE -> { + Consumer createBulkFile = ThrowingConsumer.toConsumer(path -> { + Files.createDirectories(path.getParent()); + try (FileOutputStream out = new FileOutputStream(path.toFile())) { + byte[] bytes = new byte[4 * 1024]; + new SecureRandom().nextBytes(bytes); + out.write(bytes); + } + }); - new Executor().setToolProvider(JavaTool.JLINK) - .dumpOutput() - .addArguments( - "--output", runtimeImageDir.toString(), - "--add-modules", "java.desktop", - "--strip-debug", - "--no-header-files", - "--no-man-pages") - .execute(); + runtimeImageDir = TKit.createTempDirectory("fake_runtime"); + + TKit.trace(String.format("Init fake runtime in [%s] directory", runtimeImageDir)); + + if (TKit.isOSX()) { + // Make MacAppImageBuilder happy + createBulkFile.accept(runtimeImageDir.resolve(Path.of("lib/jli/libjli.dylib"))); + } + + // Make sure fake runtime takes some disk space. + // Package bundles with 0KB size are unexpected and considered + // an error by PackageTest. + createBulkFile.accept(runtimeImageDir.resolve(Path.of("lib", "bulk"))); + } + + case RUNTIME_TYPE_HELLO_APP -> { + if (JPackageCommand.DEFAULT_RUNTIME_IMAGE != null && !isFakeRuntime(DEFAULT_RUNTIME_IMAGE)) { + runtimeImageDir = JPackageCommand.DEFAULT_RUNTIME_IMAGE; + } else { + runtimeImageDir = TKit.createTempDirectory("runtime-image").resolve("data"); + + new Executor().setToolProvider(JavaTool.JLINK) + .dumpOutput() + .addArguments( + "--output", runtimeImageDir.toString(), + "--add-modules", "java.desktop", + "--strip-debug", + "--no-header-files", + "--no-man-pages") + .execute(); + } + } + + default -> { + throw ExceptionBox.reachedUnreachable(); + } } return runtimeImageDir; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java index 1cb5532d46a..dc1a7b3512b 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java @@ -46,6 +46,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; +import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -66,6 +67,7 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; +import jdk.jpackage.internal.util.Enquoter; import jdk.jpackage.internal.util.FileUtils; import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.internal.util.PListReader; @@ -75,6 +77,8 @@ import jdk.jpackage.internal.util.XmlUtils; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.internal.util.function.ThrowingSupplier; import jdk.jpackage.test.MacSign.CertificateRequest; +import jdk.jpackage.test.MacSign.CertificateType; +import jdk.jpackage.test.MacSign.ResolvedKeychain; import jdk.jpackage.test.PackageTest.PackageHandlers; import jdk.jpackage.test.RunnablePackageTest.Action; import org.xml.sax.SAXException; @@ -430,18 +434,50 @@ public final class MacHelper { } } + public static final class RuntimeBundleBuilder { + + public Path create() { + return createRuntimeBundle(type, Optional.ofNullable(mutator)); + } + + public RuntimeBundleBuilder type(JPackageCommand.RuntimeImageType v) { + type = Objects.requireNonNull(v); + return this; + } + + public RuntimeBundleBuilder mutator(Consumer v) { + mutator = v; + return this; + } + + public RuntimeBundleBuilder mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + + private RuntimeBundleBuilder() { + } + + private JPackageCommand.RuntimeImageType type = JPackageCommand.RuntimeImageType.RUNTIME_TYPE_HELLO_APP; + private Consumer mutator; + }; + + public static RuntimeBundleBuilder buildRuntimeBundle() { + return new RuntimeBundleBuilder(); + } + public static Path createRuntimeBundle(Consumer mutator) { - return createRuntimeBundle(Optional.of(mutator)); + return buildRuntimeBundle().mutator(Objects.requireNonNull(mutator)).create(); } public static Path createRuntimeBundle() { - return createRuntimeBundle(Optional.empty()); + return buildRuntimeBundle().create(); } - public static Path createRuntimeBundle(Optional> mutator) { + private static Path createRuntimeBundle(JPackageCommand.RuntimeImageType type, Optional> mutator) { Objects.requireNonNull(mutator); - final var runtimeImage = JPackageCommand.createInputRuntimeImage(); + final var runtimeImage = JPackageCommand.createInputRuntimeImage(type); final var runtimeBundleWorkDir = TKit.createTempDirectory("runtime-bundle"); @@ -495,25 +531,244 @@ public final class MacHelper { return cmd; } - public record SignKeyOption(Type type, CertificateRequest certRequest) { + public static final class ResolvableCertificateRequest { + + public ResolvableCertificateRequest( + CertificateRequest certRequest, + Function certResolver, + String label) { + + Objects.requireNonNull(certRequest); + Objects.requireNonNull(certResolver); + Objects.requireNonNull(label); + if (label.isBlank()) { + throw new IllegalArgumentException(); + } + + this.certRequest = certRequest; + this.certResolver = certResolver; + this.label = label; + } + + public ResolvableCertificateRequest( + CertificateRequest certRequest, + ResolvedKeychain keychain, + String label) { + this(certRequest, keychain.asCertificateResolver(), label); + } + + @Override + public String toString() { + return label; + } + + public CertificateRequest certRequest() { + return certRequest; + } + + public X509Certificate cert() { + return certResolver.apply(certRequest); + } + + public CertificateType type() { + return certRequest.type(); + } + + public String name() { + return certRequest.name(); + } + + public String shortName() { + return certRequest.shortName(); + } + + public int days() { + return certRequest.days(); + } + + public boolean expired() { + return certRequest.expired(); + } + + public boolean trusted() { + return certRequest.trusted(); + } + + private final CertificateRequest certRequest; + private final Function certResolver; + private final String label; + } + + public interface NamedCertificateRequestSupplier { + + String name(); + + CertificateRequest certRequest(); + + default ResolvableCertificateRequest certRequest(ResolvedKeychain keychain) { + Objects.requireNonNull(keychain); + var certRequest = Objects.requireNonNull(certRequest()); + if (keychain.spec().certificateRequests().contains(certRequest)) { + return new ResolvableCertificateRequest(certRequest, keychain.asCertificateResolver(), name()); + } else { + throw new IllegalArgumentException(String.format( + "Certificate request %s not found in [%s] keychain", + name(), keychain.spec().keychain().name())); + } + } + } + + public record SignKeyOption(Type type, ResolvableCertificateRequest certRequest, Optional customOptionValue) { public SignKeyOption { Objects.requireNonNull(type); Objects.requireNonNull(certRequest); + Objects.requireNonNull(customOptionValue); + if (customOptionValue.isEmpty() == (type == Type.SIGN_KEY_USER_NAME)) { + throw new IllegalArgumentException(); + } + } + + public SignKeyOption(Type type, ResolvableCertificateRequest certRequest) { + this(type, certRequest, Optional.empty()); + } + + public SignKeyOption( + Type type, + NamedCertificateRequestSupplier certRequestSupplier, + ResolvedKeychain keychain) { + + this(type, certRequestSupplier.certRequest(keychain)); + } + + public SignKeyOption(String optionValue, ResolvableCertificateRequest certRequest) { + this(Type.SIGN_KEY_USER_NAME, certRequest, Optional.of(optionValue)); + } + + public SignKeyOption( + String optionValue, + NamedCertificateRequestSupplier certRequestSupplier, + ResolvedKeychain keychain) { + + this(optionValue, certRequestSupplier.certRequest(keychain)); + } + + public enum Name { + KEY_USER_NAME("--mac-signing-key-user-name"), + KEY_IDENTITY_APP_IMAGE("--mac-app-image-sign-identity"), + KEY_IDENTITY_INSTALLER("--mac-installer-sign-identity"), + ; + + Name(String optionName) { + this.optionName = Objects.requireNonNull(optionName); + } + + public String optionName() { + return optionName; + } + + public boolean passThrough() { + return this != KEY_USER_NAME; + } + + private final String optionName; } public enum Type { - SIGN_KEY_USER_NAME, - SIGN_KEY_IDENTITY, + /** + * "--mac-signing-key-user-name" option with custom value + */ + SIGN_KEY_USER_NAME(Name.KEY_USER_NAME), + + /** + * "--mac-signing-key-user-name" option with the short user name, e.g.: + * {@code --mac-signing-key-user-name foo} + */ + SIGN_KEY_USER_SHORT_NAME(Name.KEY_USER_NAME), + + /** + * "--mac-signing-key-user-name" option with the full user name (aka signing + * identity name), e.g.: + * {@code --mac-signing-key-user-name 'Developer ID Application: foo'} + */ + SIGN_KEY_USER_FULL_NAME(Name.KEY_USER_NAME), + + /** + * "--mac-installer-sign-identity" or "--mac-app-image-sign-identity" option + * with the signing identity name, e.g.: + * {@code --mac-app-image-sign-identity 'Developer ID Application: foo'} + */ + SIGN_KEY_IDENTITY(Map.of( + MacSign.CertificateType.CODE_SIGN, Name.KEY_IDENTITY_APP_IMAGE, + MacSign.CertificateType.INSTALLER, Name.KEY_IDENTITY_INSTALLER)), + + /** + * "--mac-app-image-sign-identity" regardless of the type of signing identity + * (for signing app image or .pkg installer). + */ + SIGN_KEY_IDENTITY_APP_IMAGE(Name.KEY_IDENTITY_APP_IMAGE), + + /** + * "--mac-installer-sign-identity" regardless of the type of signing identity + * (for signing app image or .pkg installer). + */ + SIGN_KEY_IDENTITY_INSTALLER(Name.KEY_IDENTITY_INSTALLER), + ; + + Type(Map optionNameMap) { + Objects.requireNonNull(optionNameMap); + this.optionNameMapper = certType -> { + return Optional.of(optionNameMap.get(certType)); + }; + } + + Type(Name optionName) { + Objects.requireNonNull(optionName); + this.optionNameMapper = _ -> Optional.of(optionName); + } + + Type() { + this.optionNameMapper = _ -> Optional.empty(); + } + + public Optional mapOptionName(MacSign.CertificateType certType) { + return optionNameMapper.apply(Objects.requireNonNull(certType)); + } + + public static Type[] defaultValues() { + return new Type[] { + SIGN_KEY_USER_SHORT_NAME, + SIGN_KEY_USER_FULL_NAME, + SIGN_KEY_IDENTITY + }; + } + + private final Function> optionNameMapper; } @Override public String toString() { var sb = new StringBuilder(); + sb.append('{'); applyTo((optionName, _) -> { - sb.append(String.format("{%s: %s}", optionName, certRequest)); + sb.append(optionName); + switch (type) { + case SIGN_KEY_USER_FULL_NAME -> { + sb.append("/full"); + } + case SIGN_KEY_USER_NAME -> { + customOptionValue.ifPresent(optionValue -> { + sb.append("=").append(ENQUOTER.applyTo(optionValue)); + }); + } + default -> { + // NOP + } + } + sb.append(": "); }); + sb.append(certRequest).append('}'); return sb.toString(); } @@ -527,35 +782,127 @@ public final class MacHelper { return sign(cmd); } - private void applyTo(BiConsumer sink) { - switch (certRequest.type()) { - case INSTALLER -> { - switch (type) { - case SIGN_KEY_IDENTITY -> { - sink.accept("--mac-installer-sign-identity", certRequest.name()); - return; - } - case SIGN_KEY_USER_NAME -> { - sink.accept("--mac-signing-key-user-name", certRequest.shortName()); - return; - } - } - } - case CODE_SIGN -> { - switch (type) { - case SIGN_KEY_IDENTITY -> { - sink.accept("--mac-app-image-sign-identity", certRequest.name()); - return; - } - case SIGN_KEY_USER_NAME -> { - sink.accept("--mac-signing-key-user-name", certRequest.shortName()); - return; - } - } - } - } + public List asCmdlineArgs() { + String[] args = new String[2]; + applyTo((optionName, optionValue) -> { + args[0] = optionName; + args[1] = optionValue; + }); + return List.of(args); + } - throw new AssertionError(); + private void applyTo(BiConsumer sink) { + type.mapOptionName(certRequest.type()).ifPresent(optionName -> { + sink.accept(optionName.optionName(), optionValue()); + }); + } + + private String optionValue() { + return customOptionValue.orElseGet(() -> { + switch (type) { + case SIGN_KEY_IDENTITY, + SIGN_KEY_USER_FULL_NAME, + SIGN_KEY_IDENTITY_APP_IMAGE, + SIGN_KEY_IDENTITY_INSTALLER -> { + return certRequest.name(); + } + case SIGN_KEY_USER_SHORT_NAME -> { + return certRequest.shortName(); + } + default -> { + throw new IllegalStateException(); + } + } + }); + } + + private static final Enquoter ENQUOTER = Enquoter.identity() + .setEnquotePredicate(Enquoter.QUOTE_IF_WHITESPACES).setQuoteChar('\''); + } + + public record SignKeyOptionWithKeychain(SignKeyOption signKeyOption, ResolvedKeychain keychain) { + + public SignKeyOptionWithKeychain { + Objects.requireNonNull(signKeyOption); + Objects.requireNonNull(keychain); + } + + public SignKeyOptionWithKeychain( + SignKeyOption.Type type, + ResolvableCertificateRequest certRequest, + ResolvedKeychain keychain) { + + this(new SignKeyOption(type, certRequest), keychain); + } + + public SignKeyOptionWithKeychain( + SignKeyOption.Type type, + NamedCertificateRequestSupplier certRequestSupplier, + ResolvedKeychain keychain) { + + this(type, certRequestSupplier.certRequest(keychain), keychain); + } + + public SignKeyOptionWithKeychain( + String optionValue, + ResolvableCertificateRequest certRequest, + ResolvedKeychain keychain) { + + this(new SignKeyOption(optionValue, certRequest), keychain); + } + + public SignKeyOptionWithKeychain( + String optionValue, + NamedCertificateRequestSupplier certRequestSupplier, + ResolvedKeychain keychain) { + + this(optionValue, certRequestSupplier.certRequest(keychain), keychain); + } + + public SignKeyOptionWithKeychain( + SignKeyOption.Type type, + CertificateRequest certRequest, + ResolvedKeychain keychain) { + + this(new SignKeyOption( + type, + new ResolvableCertificateRequest( + certRequest, + keychain.asCertificateResolver(), + certRequest.toString())), + keychain); + } + + @Override + public String toString() { + return String.format("%s@%s", signKeyOption, keychain.name()); + } + + public SignKeyOption.Type type() { + return signKeyOption.type(); + } + + public ResolvableCertificateRequest certRequest() { + return signKeyOption.certRequest(); + } + + public JPackageCommand addTo(JPackageCommand cmd) { + Optional.ofNullable(cmd.getArgumentValue("--mac-signing-keychain")).ifPresentOrElse(configuredKeychain -> { + if (!configuredKeychain.equals(keychain.name())) { + throw new IllegalStateException(String.format( + "Command line [%s] already has the '--mac-signing-keychain' option, not adding another one with [%s] value", + cmd, keychain.name())); + } + }, () -> { + useKeychain(cmd, keychain); + }); + return signKeyOption.addTo(cmd); + } + + public JPackageCommand setTo(JPackageCommand cmd) { + cmd.removeArgumentWithValue("--mac-signing-keychain"); + useKeychain(cmd, keychain); + return signKeyOption.setTo(cmd); } } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java index 70de6ba92af..3bbbf436300 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSign.java @@ -61,6 +61,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; import javax.naming.ldap.LdapName; @@ -1132,6 +1133,18 @@ public final class MacSign { return certMap; } + public Function asCertificateResolver() { + return certRequest -> { + if (!spec.certificateRequests().contains(certRequest)) { + throw new IllegalArgumentException(String.format( + "Certificate request %s not found in [%s] keychain", + certRequest, name())); + } else { + return Objects.requireNonNull(mapCertificateRequests().get(certRequest)); + } + }; + } + private final KeychainWithCertsSpec spec; private volatile Map certMap; } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java index 9c469c9362e..ddf899d603c 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacSignVerify.java @@ -33,6 +33,7 @@ import java.util.Objects; import java.util.Optional; import java.util.regex.Pattern; import jdk.jpackage.internal.util.PListReader; +import jdk.jpackage.test.MacHelper.ResolvableCertificateRequest; import jdk.jpackage.test.MacSign.CertificateHash; import jdk.jpackage.test.MacSign.CertificateRequest; @@ -41,12 +42,10 @@ import jdk.jpackage.test.MacSign.CertificateRequest; */ public final class MacSignVerify { - public static void verifyAppImageSigned( - JPackageCommand cmd, CertificateRequest certRequest, MacSign.ResolvedKeychain keychain) { + public static void verifyAppImageSigned(JPackageCommand cmd, ResolvableCertificateRequest certRequest) { - cmd.verifyIsOfType(PackageType.MAC); + cmd.verifyIsOfType(PackageType.MAC_DMG, PackageType.MAC_PKG, PackageType.IMAGE); Objects.requireNonNull(certRequest); - Objects.requireNonNull(keychain); final Path bundleRoot; if (cmd.isImagePackageType()) { @@ -70,13 +69,12 @@ public final class MacSignVerify { String.format("Check [%s] has sign origin as expected", bundleRoot)); } - public static void verifyPkgSigned(JPackageCommand cmd, CertificateRequest certRequest, MacSign.ResolvedKeychain keychain) { + public static void verifyPkgSigned(JPackageCommand cmd, ResolvableCertificateRequest certRequest) { cmd.verifyIsOfType(PackageType.MAC_PKG); - assertPkgSigned(cmd.outputBundle(), certRequest, - Objects.requireNonNull(keychain.mapCertificateRequests().get(certRequest))); + assertPkgSigned(cmd.outputBundle(), certRequest); } - public static void assertSigned(Path path, CertificateRequest certRequest) { + public static void assertSigned(Path path, ResolvableCertificateRequest certRequest) { assertSigned(path); TKit.assertEquals(certRequest.name(), findCodesignSignOrigin(path).orElse(null), String.format("Check [%s] signed with certificate", path)); @@ -108,6 +106,10 @@ public final class MacSignVerify { String.format("Check [%s] unsigned", path)); } + public static void assertPkgSigned(Path path, ResolvableCertificateRequest certRequest) { + assertPkgSigned(path, certRequest.certRequest(), certRequest.cert()); + } + public static void assertPkgSigned(Path path, CertificateRequest certRequest, X509Certificate cert) { final var expectedCertChain = List.of(new SignIdentity(certRequest.name(), CertificateHash.of(cert, SHA256))); final var actualCertChain = getPkgCertificateChain(path); diff --git a/test/jdk/tools/jpackage/macosx/EntitlementsTest.java b/test/jdk/tools/jpackage/macosx/EntitlementsTest.java index 6e03c858db3..052e301a9ae 100644 --- a/test/jdk/tools/jpackage/macosx/EntitlementsTest.java +++ b/test/jdk/tools/jpackage/macosx/EntitlementsTest.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 @@ -56,10 +56,9 @@ import jdk.jpackage.test.TKit; * @test * @summary jpackage with --type app-image "--mac-entitlements" parameter * @library /test/jdk/tools/jpackage/helpers - * @library base - * @build SigningBase * @build jdk.jpackage.test.* - * @build EntitlementsTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror EntitlementsTest.java * @requires (jpackage.test.MacSignTests == "run") * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=EntitlementsTest @@ -142,7 +141,8 @@ public class EntitlementsTest { cmd.mutate(MacHelper.useKeychain(keychain)).mutate(new SignKeyOption( SignKeyOption.Type.SIGN_KEY_IDENTITY, - SigningBase.StandardCertificateRequest.CODESIGN.spec() + SigningBase.StandardCertificateRequest.CODESIGN, + keychain )::addTo); cmd.mutate(new AdditionalLauncher("x")::applyTo); diff --git a/test/jdk/tools/jpackage/macosx/MacSignTest.java b/test/jdk/tools/jpackage/macosx/MacSignTest.java index af7cf448bdc..a824fdb0925 100644 --- a/test/jdk/tools/jpackage/macosx/MacSignTest.java +++ b/test/jdk/tools/jpackage/macosx/MacSignTest.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 @@ -21,12 +21,16 @@ * questions. */ +import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_IDENTITY; +import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_USER_FULL_NAME; +import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_USER_SHORT_NAME; +import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_IDENTITY_APP_IMAGE; + import java.io.IOException; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; @@ -37,8 +41,11 @@ import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.MacHelper.NamedCertificateRequestSupplier; +import jdk.jpackage.test.MacHelper.ResolvableCertificateRequest; +import jdk.jpackage.test.MacHelper.SignKeyOption; +import jdk.jpackage.test.MacHelper.SignKeyOptionWithKeychain; import jdk.jpackage.test.MacSign; -import jdk.jpackage.test.MacSign.CertificateRequest; import jdk.jpackage.test.MacSign.CertificateType; import jdk.jpackage.test.MacSignVerify; import jdk.jpackage.test.PackageType; @@ -48,10 +55,9 @@ import jdk.jpackage.test.TKit; * @test * @summary jpackage with --mac-sign * @library /test/jdk/tools/jpackage/helpers - * @library base - * @build SigningBase * @build jdk.jpackage.test.* - * @build MacSignTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror MacSignTest.java * @requires (jpackage.test.MacSignTests == "run") * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=MacSignTest @@ -79,22 +85,28 @@ public class MacSignTest { } MacSign.withKeychain(keychain -> { + + var signingKeyOption = new SignKeyOptionWithKeychain( + SIGN_KEY_IDENTITY, + SigningBase.StandardCertificateRequest.CODESIGN, + keychain); + // --app-content and --type app-image // Expect `message.codesign.failed.reason.app.content` message in the log. // This is not a fatal error, just a warning. // To make jpackage fail, specify bad additional content. - final var cmd = JPackageCommand.helloAppImage() + JPackageCommand.helloAppImage() .ignoreDefaultVerbose(true) .validateOutput(expectedStrings.toArray(CannedFormattedString[]::new)) .addArguments("--app-content", appContent) - .addArguments("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN.spec().name()); + .mutate(signingKeyOption::addTo) + .mutate(cmd -> { + if (MacHelper.isXcodeDevToolsInstalled()) { + // Check there is no warning about missing xcode command line developer tools. + cmd.validateOutput(TKit.assertTextStream(xcodeWarning.getValue()).negate()); + } + }).execute(1); - if (MacHelper.isXcodeDevToolsInstalled()) { - // Check there is no warning about missing xcode command line developer tools. - cmd.validateOutput(TKit.assertTextStream(xcodeWarning.getValue()).negate()); - } - - MacHelper.useKeychain(cmd, keychain).execute(1); }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain()); } @@ -116,13 +128,19 @@ public class MacSignTest { expectedStrings.add(JPackageStringBundle.MAIN.cannedFormattedString("error.tool.failed.with.output", "codesign")); MacSign.withKeychain(keychain -> { - final var cmd = new JPackageCommand().setPackageType(PackageType.IMAGE) + + var signingKeyOption = new SignKeyOptionWithKeychain( + SIGN_KEY_IDENTITY, + SigningBase.StandardCertificateRequest.CODESIGN, + keychain); + + new JPackageCommand().setPackageType(PackageType.IMAGE) .ignoreDefaultVerbose(true) .validateOutput(expectedStrings.toArray(CannedFormattedString[]::new)) .addArguments("--app-image", appImageCmd.outputBundle()) - .addArguments("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN.spec().name()); + .mutate(signingKeyOption::addTo) + .execute(1); - MacHelper.useKeychain(cmd, keychain).execute(1); }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain()); } @@ -189,71 +207,78 @@ public class MacSignTest { @Test @ParameterSupplier @ParameterSupplier("testSelectSigningIdentity_JDK_8371094") - public static void testSelectSigningIdentity(String signingKeyUserName, CertificateRequest certRequest) { + public static void testSelectSigningIdentity(SignKeyOptionWithKeychain signKeyOption) { MacSign.withKeychain(keychain -> { - final var cmd = MacHelper.useKeychain(JPackageCommand.helloAppImage(), keychain) - .setFakeRuntime() - .addArguments("--mac-signing-key-user-name", signingKeyUserName); + final var cmd = JPackageCommand.helloAppImage().setFakeRuntime().mutate(signKeyOption::addTo); - cmd.executeAndAssertHelloAppImageCreated(); + cmd.executeAndAssertImageCreated(); - MacSignVerify.assertSigned(cmd.outputBundle(), certRequest); + MacSignVerify.verifyAppImageSigned(cmd, signKeyOption.certRequest()); }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain()); } public static Collection testSelectSigningIdentity() { + var keychain = SigningBase.StandardKeychain.MAIN.keychain(); return Stream.of( SigningBase.StandardCertificateRequest.CODESIGN, SigningBase.StandardCertificateRequest.CODESIGN_UNICODE - ).map(SigningBase.StandardCertificateRequest::spec).mapMulti((certRequest, acc) -> { - acc.accept(new Object[] {certRequest.shortName(), certRequest}); - acc.accept(new Object[] {certRequest.name(), certRequest}); + ).map(certRequest -> { + return Stream.of( + SIGN_KEY_USER_FULL_NAME, + SIGN_KEY_USER_SHORT_NAME + ).map(type -> { + return new SignKeyOptionWithKeychain(type, certRequest, keychain); + }); + }).flatMap(x -> x).map(v -> { + return new Object[] {v}; }).toList(); } public static Collection testSelectSigningIdentity_JDK_8371094() { return List.of(new Object[] { - "ACME Technologies Limited", SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD.spec() + new SignKeyOptionWithKeychain( + "ACME Technologies Limited", + SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD, + SigningBase.StandardKeychain.MAIN.keychain()) }); } enum SignOption { - EXPIRED_SIGNING_KEY_USER_NAME("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED.spec(), true, false), - EXPIRED_SIGNING_KEY_USER_NAME_PKG("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.PKG_EXPIRED.spec(), true, false), - EXPIRED_SIGN_IDENTITY("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED.spec(), false, false), - EXPIRED_CODESIGN_SIGN_IDENTITY("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED.spec(), false, true), - EXPIRED_PKG_SIGN_IDENTITY("--mac-installer-sign-identity", SigningBase.StandardCertificateRequest.PKG_EXPIRED.spec(), false, true), - GOOD_SIGNING_KEY_USER_NAME("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.CODESIGN.spec(), true, false), - GOOD_SIGNING_KEY_USER_NAME_PKG("--mac-signing-key-user-name", SigningBase.StandardCertificateRequest.PKG.spec(), true, false), - GOOD_CODESIGN_SIGN_IDENTITY("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.CODESIGN.spec(), false, true), - GOOD_PKG_SIGN_IDENTITY("--mac-app-image-sign-identity", SigningBase.StandardCertificateRequest.PKG.spec(), false, true); + EXPIRED_SIGNING_KEY_USER_NAME(SIGN_KEY_USER_SHORT_NAME, SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED), + EXPIRED_SIGNING_KEY_USER_NAME_PKG(SIGN_KEY_USER_SHORT_NAME, SigningBase.StandardCertificateRequest.PKG_EXPIRED), + EXPIRED_SIGN_IDENTITY(SIGN_KEY_USER_FULL_NAME, SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED), + EXPIRED_CODESIGN_SIGN_IDENTITY(SIGN_KEY_IDENTITY, SigningBase.StandardCertificateRequest.CODESIGN_EXPIRED), + EXPIRED_PKG_SIGN_IDENTITY(SIGN_KEY_IDENTITY, SigningBase.StandardCertificateRequest.PKG_EXPIRED), + GOOD_SIGNING_KEY_USER_NAME(SIGN_KEY_USER_SHORT_NAME, SigningBase.StandardCertificateRequest.CODESIGN), + GOOD_SIGNING_KEY_USER_NAME_PKG(SIGN_KEY_USER_SHORT_NAME, SigningBase.StandardCertificateRequest.PKG), + GOOD_CODESIGN_SIGN_IDENTITY(SIGN_KEY_IDENTITY, SigningBase.StandardCertificateRequest.CODESIGN), + GOOD_PKG_SIGN_IDENTITY(SIGN_KEY_IDENTITY_APP_IMAGE, SigningBase.StandardCertificateRequest.PKG); - SignOption(String option, MacSign.CertificateRequest cert, boolean shortName, boolean passThrough) { - this.option = Objects.requireNonNull(option); - this.cert = Objects.requireNonNull(cert); - this.shortName = shortName; - this.passThrough = passThrough; + SignOption(SignKeyOption.Type optionType, NamedCertificateRequestSupplier certRequestSupplier) { + this.option = new SignKeyOption(optionType, new ResolvableCertificateRequest(certRequestSupplier.certRequest(), _ -> { + throw new UnsupportedOperationException(); + }, certRequestSupplier.name())); } boolean passThrough() { - return passThrough; + return option.type().mapOptionName(option.certRequest().type()).orElseThrow().passThrough(); } boolean expired() { - return cert.expired(); + return option.certRequest().expired(); } String identityName() { - return cert.name(); + return option.certRequest().name(); } CertificateType identityType() { - return cert.type(); + return option.certRequest().type(); } List args() { - return List.of(option, shortName ? cert.shortName() : cert.name()); + return option.asCmdlineArgs(); } static JPackageCommand configureOutputValidation(JPackageCommand cmd, List options, @@ -274,9 +299,6 @@ public class MacSignTest { return cmd; } - private final String option; - private final MacSign.CertificateRequest cert; - private final boolean shortName; - private final boolean passThrough; + private final SignKeyOption option; } } diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java index 37ba8a6c299..0f299cb5a24 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.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,90 +21,76 @@ * questions. */ -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; - -import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import jdk.jpackage.test.AdditionalLauncher; -import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.MacHelper.SignKeyOption; +import jdk.jpackage.test.MacHelper.SignKeyOptionWithKeychain; import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSignVerify; /** - * Tests generation of app image with --mac-sign and related arguments. Test will - * generate app image and verify signature of main launcher and app bundle itself. - * This test requires that machine is configured with test certificate for - * "Developer ID Application: jpackage.openjdk.java.net" or alternately - * "Developer ID Application: " + name specified by system property: - * "jpackage.mac.signing.key.user.name" - * in the jpackagerTest keychain (or alternately the keychain specified with - * the system property "jpackage.mac.signing.keychain". - * If this certificate is self-signed, it must have be set to - * always allowed access to this keychain" for user which runs test. - * (If cert is real (not self signed), the do not set trust to allow.) + * Tests signing of an app image. + * + *

      + * Prerequisites: Keychains with self-signed certificates as specified in + * {@link SigningBase.StandardKeychain#MAIN} and + * {@link SigningBase.StandardKeychain#SINGLE}. */ /* * @test * @summary jpackage with --type app-image --mac-sign * @library /test/jdk/tools/jpackage/helpers - * @library base - * @build SigningBase * @build jdk.jpackage.test.* - * @build SigningAppImageTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror SigningAppImageTest.java * @requires (jpackage.test.MacSignTests == "run") - * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main * --jpt-run=SigningAppImageTest * --jpt-before-run=SigningBase.verifySignTestEnvReady */ public class SigningAppImageTest { @Test - // ({"sign or not", "signing-key or sign-identity", "certificate index"}) - // Sign, signing-key and ASCII certificate - @Parameter({"true", "true", "ASCII_INDEX"}) - // Sign, signing-key and UNICODE certificate - @Parameter({"true", "true", "UNICODE_INDEX"}) - // Sign, signing-indentity and UNICODE certificate - @Parameter({"true", "false", "UNICODE_INDEX"}) - // Unsigned - @Parameter({"false", "true", "INVALID_INDEX"}) - public void test(boolean doSign, boolean signingKey, SigningBase.CertIndex certEnum) throws Exception { - MacSign.withKeychain(toConsumer(keychain -> { - test(keychain, doSign, signingKey, certEnum); - }), SigningBase.StandardKeychain.MAIN.keychain()); - } + @ParameterSupplier + public static void test(SignKeyOptionWithKeychain sign) { - private void test(MacSign.ResolvedKeychain keychain, boolean doSign, boolean signingKey, SigningBase.CertIndex certEnum) throws Exception { - final var certIndex = certEnum.value(); + var cmd = JPackageCommand.helloAppImage(); - JPackageCommand cmd = JPackageCommand.helloAppImage(); - if (doSign) { - cmd.addArguments("--mac-sign", - "--mac-signing-keychain", - keychain.name()); - if (signingKey) { - cmd.addArguments("--mac-signing-key-user-name", - SigningBase.getDevName(certIndex)); - } else { - cmd.addArguments("--mac-app-image-sign-identity", - SigningBase.getAppCert(certIndex)); - } - } - AdditionalLauncher testAL = new AdditionalLauncher("testAL"); + var testAL = new AdditionalLauncher("testAL"); testAL.applyTo(cmd); cmd.executeAndAssertHelloAppImageCreated(); - Path launcherPath = cmd.appLauncherPath(); - SigningBase.verifyCodesign(launcherPath, doSign, certIndex); + MacSign.withKeychain(keychain -> { + sign.addTo(cmd); + cmd.executeAndAssertHelloAppImageCreated(); + MacSignVerify.verifyAppImageSigned(cmd, sign.certRequest()); + }, sign.keychain()); + } - Path testALPath = launcherPath.getParent().resolve("testAL"); - SigningBase.verifyCodesign(testALPath, doSign, certIndex); + public static Collection test() { - Path appImage = cmd.outputBundle(); - SigningBase.verifyCodesign(appImage, doSign, certIndex); - if (doSign) { - SigningBase.verifySpctl(appImage, "exec", certIndex); + List data = new ArrayList<>(); + + for (var certRequest : List.of( + SigningBase.StandardCertificateRequest.CODESIGN, + SigningBase.StandardCertificateRequest.CODESIGN_UNICODE + )) { + for (var signIdentityType : SignKeyOption.Type.defaultValues()) { + data.add(new SignKeyOptionWithKeychain( + signIdentityType, + certRequest, + SigningBase.StandardKeychain.MAIN.keychain())); + } } + + return data.stream().map(v -> { + return new Object[] {v}; + }).toList(); } } diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java index 906734e6a9c..2c7ae205e69 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTwoStepsTest.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 @@ -21,38 +21,40 @@ * questions. */ -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; - -import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; import jdk.jpackage.test.AdditionalLauncher; -import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.MacHelper.SignKeyOption; +import jdk.jpackage.test.MacHelper.SignKeyOptionWithKeychain; import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSignVerify; import jdk.jpackage.test.PackageType; import jdk.jpackage.test.TKit; /** - * Tests generation of app image and then signs generated app image with --mac-sign - * and related arguments. Test will generate app image and verify signature of main - * launcher and app bundle itself. This test requires that machine is configured with - * test certificate for "Developer ID Application: jpackage.openjdk.java.net" or - * alternately "Developer ID Application: " + name specified by system property: - * "jpackage.mac.signing.key.user.name" in the jpackagerTest keychain - * (or alternately the keychain specified with the system property - * "jpackage.mac.signing.keychain". If this certificate is self-signed, it must - * have be set to always allowed access to this keychain" for user which runs test. - * (If cert is real (not self signed), the do not set trust to allow.) + * Tests signing of a signed/unsigned predefined app image. + * + *

      + * Prerequisites: Keychains with self-signed certificates as specified in + * {@link SigningBase.StandardKeychain#MAIN} and + * {@link SigningBase.StandardKeychain#SINGLE}. */ /* * @test * @summary jpackage with --type app-image --app-image "appImage" --mac-sign * @library /test/jdk/tools/jpackage/helpers - * @library base - * @build SigningBase * @build jdk.jpackage.test.* - * @build SigningAppImageTwoStepsTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror SigningAppImageTwoStepsTest.java * @requires (jpackage.test.MacSignTests == "run") * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=SigningAppImageTwoStepsTest @@ -61,67 +63,123 @@ import jdk.jpackage.test.TKit; public class SigningAppImageTwoStepsTest { @Test - // ({"sign or not", "signing-key or sign-identity"}) - // Sign and signing-key - @Parameter({"true", "true"}) - // Sign and sign-identity - @Parameter({"true", "false"}) - // Unsigned - @Parameter({"false", "true"}) - public void test(boolean signAppImage, boolean signingKey) throws Exception { - MacSign.withKeychain(toConsumer(keychain -> { - test(keychain, signAppImage, signingKey); - }), SigningBase.StandardKeychain.MAIN.keychain()); + @ParameterSupplier + public static void test(TestSpec spec) { + spec.test(); } - private static void test(MacSign.ResolvedKeychain keychain, boolean signAppImage, boolean signingKey) throws Exception { + public record TestSpec(Optional signAppImage, SignKeyOptionWithKeychain sign) { - Path appimageOutput = TKit.createTempDirectory("appimage"); + public TestSpec { + Objects.requireNonNull(signAppImage); + Objects.requireNonNull(sign); + } - // Generate app image. Signed or unsigned based on test - // parameter. We should able to sign predfined app images - // which are signed or unsigned. - JPackageCommand appImageCmd = JPackageCommand.helloAppImage() - .setArgumentValue("--dest", appimageOutput); - if (signAppImage) { - appImageCmd.addArguments("--mac-sign", - "--mac-signing-keychain", - keychain.name()); - if (signingKey) { - appImageCmd.addArguments("--mac-signing-key-user-name", - SigningBase.getDevName(SigningBase.DEFAULT_INDEX)); - } else { - appImageCmd.addArguments("--mac-app-image-sign-identity", - SigningBase.getAppCert(SigningBase.DEFAULT_INDEX)); + @Override + public String toString() { + return Stream.of( + String.format("app-image=%s", signAppImage.map(Objects::toString).orElse("unsigned")), + sign.toString() + ).collect(Collectors.joining("; ")); + } + + static Builder build() { + return new Builder(); + } + + static class Builder { + + TestSpec create() { + return new TestSpec(Optional.ofNullable(signAppImage), sign); + } + + Builder certRequest(SigningBase.StandardCertificateRequest v) { + certRequest = Objects.requireNonNull(v); + return this; + } + + Builder signIdentityType(SignKeyOption.Type v) { + signIdentityType = Objects.requireNonNull(v); + return this; + } + + Builder sign() { + sign = createSignKeyOption(); + return this; + } + + Builder signAppImage() { + signAppImage = createSignKeyOption(); + return this; + } + + private SignKeyOptionWithKeychain createSignKeyOption() { + return new SignKeyOptionWithKeychain( + signIdentityType, + certRequest, + SigningBase.StandardKeychain.MAIN.keychain()); + } + + private SigningBase.StandardCertificateRequest certRequest = SigningBase.StandardCertificateRequest.CODESIGN; + private SignKeyOption.Type signIdentityType = SignKeyOption.Type.SIGN_KEY_IDENTITY; + + private SignKeyOptionWithKeychain signAppImage; + private SignKeyOptionWithKeychain sign; + } + + void test() { + var appImageCmd = JPackageCommand.helloAppImage() + .setFakeRuntime() + .setArgumentValue("--dest", TKit.createTempDirectory("appimage")); + + // Add an additional launcher + AdditionalLauncher testAL = new AdditionalLauncher("testAL"); + testAL.applyTo(appImageCmd); + + signAppImage.ifPresentOrElse(signOption -> { + MacSign.withKeychain(keychain -> { + signOption.addTo(appImageCmd); + appImageCmd.execute(); + MacSignVerify.verifyAppImageSigned(appImageCmd, signOption.certRequest()); + }, signOption.keychain()); + }, appImageCmd::execute); + + var cmd = new JPackageCommand() + .setPackageType(PackageType.IMAGE) + .addArguments("--app-image", appImageCmd.outputBundle()) + .mutate(sign::addTo); + + cmd.executeAndAssertHelloAppImageCreated(); + MacSignVerify.verifyAppImageSigned(cmd, sign.certRequest()); + } + } + + public static Collection test() { + + List data = new ArrayList<>(); + + for (var appImageSign : withAndWithout(SignKeyOption.Type.SIGN_KEY_IDENTITY)) { + var builder = TestSpec.build(); + appImageSign.ifPresent(signIdentityType -> { + // Sign the input app image bundle with the key not used in the jpackage command line being tested. + // This way we can test if jpackage keeps or replaces the signature of the input app image bundle. + builder.signIdentityType(signIdentityType) + .certRequest(SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD) + .signAppImage(); + }); + for (var signIdentityType : SignKeyOption.Type.defaultValues()) { + builder.signIdentityType(signIdentityType) + .certRequest(SigningBase.StandardCertificateRequest.CODESIGN); + data.add(builder.sign().create()); } } - // Add addtional launcher - AdditionalLauncher testAL = new AdditionalLauncher("testAL"); - testAL.applyTo(appImageCmd); + return data.stream().map(v -> { + return new Object[] {v}; + }).toList(); + } - // Generate app image - appImageCmd.executeAndAssertHelloAppImageCreated(); - - // Double check if it is signed or unsigned based on signAppImage - SigningBase.verifyAppImageSignature(appImageCmd, signAppImage, "testAL"); - - // Sign app image - JPackageCommand cmd = new JPackageCommand(); - cmd.setPackageType(PackageType.IMAGE) - .addArguments("--app-image", appImageCmd.outputBundle().toAbsolutePath()) - .addArguments("--mac-sign") - .addArguments("--mac-signing-keychain", keychain.name()); - if (signingKey) { - cmd.addArguments("--mac-signing-key-user-name", - SigningBase.getDevName(SigningBase.DEFAULT_INDEX)); - } else { - cmd.addArguments("--mac-app-image-sign-identity", - SigningBase.getAppCert(SigningBase.DEFAULT_INDEX)); - } - cmd.executeAndAssertImageCreated(); - - // Should be signed app image - SigningBase.verifyAppImageSignature(appImageCmd, true, "testAL"); + private static List> withAndWithout(T value) { + return List.of(Optional.empty(), Optional.of(value)); } } diff --git a/test/jdk/tools/jpackage/macosx/SigningBase.java b/test/jdk/tools/jpackage/macosx/SigningBase.java new file mode 100644 index 00000000000..5f4367e6096 --- /dev/null +++ b/test/jdk/tools/jpackage/macosx/SigningBase.java @@ -0,0 +1,178 @@ +/* + * 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 + * 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.security.cert.X509Certificate; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; +import jdk.jpackage.test.MacHelper.NamedCertificateRequestSupplier; +import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSign.CertificateRequest; +import jdk.jpackage.test.MacSign.CertificateType; +import jdk.jpackage.test.MacSign.KeychainWithCertsSpec; +import jdk.jpackage.test.MacSign.ResolvedKeychain; +import jdk.jpackage.test.TKit; + + +/* + * @test + * @summary Setup the environment for jpackage macos signing tests. + * Creates required keychains and signing identities. + * Does NOT run any jpackag tests. + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.* + * @compile -Xlint:all -Werror SigningBase.java + * @requires (jpackage.test.MacSignTests == "setup") + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=SigningBase.setUp + */ + +/* + * @test + * @summary Tear down the environment for jpackage macos signing tests. + * Deletes required keychains and signing identities. + * Does NOT run any jpackag tests. + * @library /test/jdk/tools/jpackage/helpers + * @build jdk.jpackage.test.* + * @compile -Xlint:all -Werror SigningBase.java + * @requires (jpackage.test.MacSignTests == "teardown") + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=SigningBase.tearDown + */ + +public class SigningBase { + + public enum StandardCertificateRequest implements NamedCertificateRequestSupplier { + CODESIGN(cert().userName(NAME_ASCII)), + CODESIGN_COPY(cert().days(100).userName(NAME_ASCII)), + CODESIGN_ACME_TECH_LTD(cert().days(100).userName("ACME Technologies Limited (ABC12345)")), + PKG(cert().type(CertificateType.INSTALLER).userName(NAME_ASCII)), + PKG_COPY(cert().type(CertificateType.INSTALLER).days(100).userName(NAME_ASCII)), + CODESIGN_UNICODE(cert().userName(NAME_UNICODE)), + PKG_UNICODE(cert().type(CertificateType.INSTALLER).userName(NAME_UNICODE)), + CODESIGN_EXPIRED(cert().expired().userName("expired jpackage test")), + PKG_EXPIRED(cert().expired().type(CertificateType.INSTALLER).userName("expired jpackage test")); + + StandardCertificateRequest(CertificateRequest.Builder specBuilder) { + this.spec = specBuilder.create(); + } + + @Override + public CertificateRequest certRequest() { + return spec; + } + + private static CertificateRequest.Builder cert() { + return new CertificateRequest.Builder(); + } + + private final CertificateRequest spec; + } + + /** + * Standard keychains used in signing tests. + */ + public enum StandardKeychain { + /** + * The primary keychain with good certificates. + */ + MAIN("jpackagerTest.keychain", + StandardCertificateRequest.CODESIGN, + StandardCertificateRequest.PKG, + StandardCertificateRequest.CODESIGN_UNICODE, + StandardCertificateRequest.PKG_UNICODE, + StandardCertificateRequest.CODESIGN_ACME_TECH_LTD), + /** + * A keychain with some good and some expired certificates. + */ + EXPIRED("jpackagerTest-expired.keychain", + StandardCertificateRequest.CODESIGN, + StandardCertificateRequest.PKG, + StandardCertificateRequest.CODESIGN_EXPIRED, + StandardCertificateRequest.PKG_EXPIRED), + /** + * A keychain with duplicated certificates. + */ + DUPLICATE("jpackagerTest-duplicate.keychain", + StandardCertificateRequest.CODESIGN, + StandardCertificateRequest.PKG, + StandardCertificateRequest.CODESIGN_COPY, + StandardCertificateRequest.PKG_COPY), + ; + + StandardKeychain(String keychainName, StandardCertificateRequest... certs) { + this(keychainName, + certs[0].certRequest(), + Stream.of(certs).skip(1).map(StandardCertificateRequest::certRequest).toArray(CertificateRequest[]::new)); + } + + StandardKeychain(String keychainName, CertificateRequest cert, CertificateRequest... otherCerts) { + final var builder = keychain(keychainName).addCert(cert); + List.of(otherCerts).forEach(builder::addCert); + this.keychain = new ResolvedKeychain(builder.create()); + } + + public ResolvedKeychain keychain() { + return keychain; + } + + public X509Certificate mapCertificateRequest(CertificateRequest certRequest) { + return Objects.requireNonNull(keychain.mapCertificateRequests().get(certRequest)); + } + + public boolean contains(StandardCertificateRequest certRequest) { + return keychain.spec().certificateRequests().contains(certRequest.spec); + } + + private static KeychainWithCertsSpec.Builder keychain(String name) { + return new KeychainWithCertsSpec.Builder().name(name); + } + + private static List signingEnv() { + return Stream.of(values()).map(StandardKeychain::keychain).map(ResolvedKeychain::spec).toList(); + } + + private final ResolvedKeychain keychain; + } + + public static void setUp() { + MacSign.setUp(StandardKeychain.signingEnv()); + } + + public static void tearDown() { + MacSign.tearDown(StandardKeychain.signingEnv()); + } + + public static void verifySignTestEnvReady() { + if (!Inner.SIGN_ENV_READY) { + TKit.throwSkippedException(new IllegalStateException("Misconfigured signing test environment")); + } + } + + private final class Inner { + private static final boolean SIGN_ENV_READY = MacSign.isDeployed(StandardKeychain.signingEnv()); + } + + private static final String NAME_ASCII = "jpackage.openjdk.java.net"; + private static final String NAME_UNICODE = "jpackage.openjdk.java.net (ö)"; +} diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java index b1e9155dacb..8a93ce5f749 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTest.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,180 +21,247 @@ * questions. */ -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; - -import java.nio.file.Path; -import jdk.jpackage.test.Annotations.Parameter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.SequencedSet; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.jpackage.internal.util.function.ExceptionBox; +import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.ApplicationLayout; -import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.MacHelper.ResolvableCertificateRequest; +import jdk.jpackage.test.MacHelper.SignKeyOption; import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSignVerify; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; /** - * Tests generation of dmg and pkg with --mac-sign and related arguments. - * Test will generate pkg and verifies its signature. It verifies that dmg - * is not signed, but app image inside dmg is signed. This test requires that - * the machine is configured with test certificate for - * "Developer ID Installer: jpackage.openjdk.java.net" in - * jpackagerTest keychain with - * always allowed access to this keychain for user which runs test. - * note: - * "jpackage.openjdk.java.net" can be over-ridden by system property - * "jpackage.mac.signing.key.user.name", and - * "jpackagerTest" can be over-ridden by system property - * "jpackage.mac.signing.keychain" + * Tests bundling of .pkg and .dmg packages with various signing options. + * + *

      + * Prerequisites: Keychains with self-signed certificates as specified in + * {@link SigningBase.StandardKeychain#MAIN} and + * {@link SigningBase.StandardKeychain#SINGLE}. */ + /* * @test * @summary jpackage with --type pkg,dmg --mac-sign * @library /test/jdk/tools/jpackage/helpers - * @library base * @key jpackagePlatformPackage - * @build SigningBase * @build jdk.jpackage.test.* - * @build SigningPackageTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror SigningPackageTest.java * @requires (jpackage.test.MacSignTests == "run") * @requires (jpackage.test.SQETest != null) * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=SigningPackageTest - * --jpt-space-subst=* - * --jpt-include=SigningPackageTest.test(true,*true,*true,*ASCII_INDEX) - * --jpt-before-run=SigningBase.verifySignTestEnvReady + * --jpt-run=SigningPackageTest.test + * --jpt-space-subst=* + * --jpt-include=({--mac-signing-key-user-name:*CODESIGN},*{--mac-signing-key-user-name:*PKG},*MAC_DMG+MAC_PKG) + * --jpt-before-run=SigningBase.verifySignTestEnvReady */ /* * @test * @summary jpackage with --type pkg,dmg --mac-sign * @library /test/jdk/tools/jpackage/helpers - * @library base * @key jpackagePlatformPackage - * @build SigningBase * @build jdk.jpackage.test.* - * @build SigningPackageTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror SigningPackageTest.java * @requires (jpackage.test.MacSignTests == "run") * @requires (jpackage.test.SQETest == null) - * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=SigningPackageTest + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=SigningPackageTest.test * --jpt-before-run=SigningBase.verifySignTestEnvReady */ public class SigningPackageTest { - private static boolean isAppImageSigned(JPackageCommand cmd) { - return cmd.hasArgument("--mac-signing-key-user-name") || - cmd.hasArgument("--mac-app-image-sign-identity"); + @Test + @ParameterSupplier + public static void test(TestSpec spec) { + MacSign.withKeychain(_ -> { + spec.test(); + }, spec.keychain()); } - private static boolean isPKGSigned(JPackageCommand cmd) { - return cmd.hasArgument("--mac-signing-key-user-name") || - cmd.hasArgument("--mac-installer-sign-identity"); + public static Collection test() { + return TestSpec.testCases(true).stream().map(v -> { + return new Object[] {v}; + }).toList(); } - private static void verifyPKG(JPackageCommand cmd) { - Path outputBundle = cmd.outputBundle(); - SigningBase.verifyPkgutil(outputBundle, isPKGSigned(cmd), getCertIndex(cmd)); - if (isPKGSigned(cmd)) { - SigningBase.verifySpctl(outputBundle, "install", getCertIndex(cmd)); - } - } + record TestSpec( + Optional appImageSignOption, + Optional packageSignOption, + Set packageTypes) { - private static void verifyDMG(JPackageCommand cmd) { - Path outputBundle = cmd.outputBundle(); - SigningBase.verifyDMG(outputBundle); - } + TestSpec { + Objects.requireNonNull(appImageSignOption); + Objects.requireNonNull(packageSignOption); + Objects.requireNonNull(packageTypes); - private static void verifyAppImageInDMG(JPackageCommand cmd) { - MacHelper.withExplodedDmg(cmd, dmgImage -> { - Path launcherPath = ApplicationLayout.platformAppImage() - .resolveAt(dmgImage).launchersDirectory().resolve(cmd.name()); - // We will be called with all folders in DMG since JDK-8263155, but - // we only need to verify app. - if (dmgImage.endsWith(cmd.name() + ".app")) { - SigningBase.verifyCodesign(launcherPath, isAppImageSigned(cmd), - getCertIndex(cmd)); - SigningBase.verifyCodesign(dmgImage, isAppImageSigned(cmd), - getCertIndex(cmd)); - if (isAppImageSigned(cmd)) { - SigningBase.verifySpctl(dmgImage, "exec", getCertIndex(cmd)); + if (appImageSignOption.isEmpty() && packageSignOption.isEmpty()) { + // No signing. + throw new IllegalArgumentException(); + } + + if (packageTypes.isEmpty() || !PackageType.MAC.containsAll(packageTypes)) { + // Invalid package types. + throw new IllegalArgumentException(); + } + + if (packageSignOption.isPresent()) { + if (!packageTypes.contains(PackageType.MAC_PKG)) { + // .pkg installer should be signed, but .pkg type is missing. + throw new IllegalArgumentException(); + } + + if (appImageSignOption.isEmpty()) { + if (packageSignOption.get().type() != SignKeyOption.Type.SIGN_KEY_IDENTITY) { + // They request to sign the .pkg installer without + // the "--mac-installer-sign-identity" option, + // but didn't specify a signing option for the packaged app image. + // This is wrong because only the "--mac-installer-sign-identity" option + // allows signing a .pkg installer without signing its packaged app image. + throw new IllegalArgumentException(); + } + } else if (appImageSignOption.get().type() != packageSignOption.get().type()) { + // Signing option types should be the same. + throw new IllegalArgumentException(); } } - }); - } - private static int getCertIndex(JPackageCommand cmd) { - if (cmd.hasArgument("--mac-signing-key-user-name")) { - String devName = cmd.getArgumentValue("--mac-signing-key-user-name"); - return SigningBase.getDevNameIndex(devName); - } else { - // Signing-indentity - return SigningBase.CertIndex.UNICODE_INDEX.value(); + if (!(packageTypes instanceof SequencedSet)) { + packageTypes = new TreeSet<>(packageTypes); + } + } + + TestSpec( + Optional appImageSignOption, + Optional packageSignOption, + PackageType... packageTypes) { + this(appImageSignOption, packageSignOption, Set.of(packageTypes)); + } + + @Override + public String toString() { + return Stream.of( + signKeyOptions(), + Stream.of(packageTypes.stream().map(Object::toString).collect(Collectors.joining("+"))) + ).flatMap(x -> x).map(Object::toString).collect(Collectors.joining(", ")); + } + + Stream signKeyOptions() { + return Stream.concat(appImageSignOption.stream(), packageSignOption.stream()); + } + + Optional bundleSignIdentity(PackageType type) { + switch (type) { + case MAC_DMG -> { + return Optional.empty(); + } + case MAC_PKG -> { + return packageSignOption.map(SignKeyOption::certRequest); + } + default -> { + throw new IllegalArgumentException(); + } + } + } + + void test() { + initTest().configureHelloApp().addInstallVerifier(cmd -> { + appImageSignOption.map(SignKeyOption::certRequest).ifPresent(signIdentity -> { + MacSignVerify.verifyAppImageSigned(cmd, signIdentity); + }); + }).run(); + } + + PackageTest initTest() { + return new PackageTest().forTypes(packageTypes).mutate(test -> { + appImageSignOption.ifPresent(signOption -> { + test.addInitializer(signOption::setTo); + }); + packageSignOption.ifPresent(signOption -> { + test.forTypes(PackageType.MAC_PKG, () -> { + test.addInitializer(signOption::setTo); + }); + }); + }).addBundleVerifier(cmd -> { + bundleSignIdentity(cmd.packageType()).ifPresent(signIdentity -> { + MacSignVerify.verifyPkgSigned(cmd, signIdentity); + }); + }).addInitializer(MacHelper.useKeychain(keychain())::accept); + } + + MacSign.ResolvedKeychain keychain() { + return SigningBase.StandardKeychain.MAIN.keychain(); + } + + static List testCases(boolean withUnicode) { + + List data = new ArrayList<>(); + + List> certRequestGroups; + if (withUnicode) { + certRequestGroups = List.of( + List.of(SigningBase.StandardCertificateRequest.CODESIGN, SigningBase.StandardCertificateRequest.PKG), + List.of(SigningBase.StandardCertificateRequest.CODESIGN_UNICODE, SigningBase.StandardCertificateRequest.PKG_UNICODE) + ); + } else { + certRequestGroups = List.of( + List.of(SigningBase.StandardCertificateRequest.CODESIGN, SigningBase.StandardCertificateRequest.PKG) + ); + } + + for (var certRequests : certRequestGroups) { + for (var signIdentityType : SignKeyOption.Type.defaultValues()) { + var keychain = SigningBase.StandardKeychain.MAIN.keychain(); + var appImageSignKeyOption = new SignKeyOption(signIdentityType, certRequests.getFirst(), keychain); + var pkgSignKeyOption = new SignKeyOption(signIdentityType, certRequests.getLast(), keychain); + + switch (signIdentityType) { + case SIGN_KEY_IDENTITY -> { + // Use "--mac-installer-sign-identity" and "--mac-app-image-sign-identity" signing options. + // They allows to sign the packaged app image and the installer (.pkg) separately. + data.add(new TestSpec(Optional.of(appImageSignKeyOption), Optional.empty(), PackageType.MAC)); + data.add(new TestSpec(Optional.empty(), Optional.of(pkgSignKeyOption), PackageType.MAC_PKG)); + } + case SIGN_KEY_USER_SHORT_NAME -> { + // Use "--mac-signing-key-user-name" signing option with short user name or implicit signing option. + // It signs both the packaged app image and the installer (.pkg). + // Thus, if the installer is not signed, it can be used only with .dmg packaging. + data.add(new TestSpec(Optional.of(appImageSignKeyOption), Optional.empty(), PackageType.MAC_DMG)); + } + case SIGN_KEY_USER_FULL_NAME -> { + // Use "--mac-signing-key-user-name" signing option with the full user name. + // Like SIGN_KEY_USER_SHORT_NAME, jpackage will try to use it to sign both + // the packaged app image and the installer (.pkg). + // It will fail to sign the installer, though, because the signing identity is unsuitable. + // That is why, use it with .dmg packaging only and not with .pkg packaging. + data.add(new TestSpec(Optional.of(appImageSignKeyOption), Optional.empty(), PackageType.MAC_DMG)); + continue; + } + default -> { + // SignKeyOption.Type.defaultValues() should return + // such a sequence that makes this code location unreachable. + throw ExceptionBox.reachedUnreachable(); + } + } + data.add(new TestSpec(Optional.of(appImageSignKeyOption), Optional.of(pkgSignKeyOption), PackageType.MAC)); + } + } + + return data; } } - - @Test - // ("signing-key or sign-identity", "sign app-image", "sign pkg", "certificate index"}) - // Signing-key and ASCII certificate - @Parameter({"true", "true", "true", "ASCII_INDEX"}) - // Signing-key and UNICODE certificate - @Parameter({"true", "true", "true", "UNICODE_INDEX"}) - // Signing-indentity and UNICODE certificate - @Parameter({"false", "true", "true", "UNICODE_INDEX"}) - // Signing-indentity, but sign app-image only and UNICODE certificate - @Parameter({"false", "true", "false", "UNICODE_INDEX"}) - // Signing-indentity, but sign pkg only and UNICODE certificate - @Parameter({"false", "false", "true", "UNICODE_INDEX"}) - public static void test(boolean signingKey, boolean signAppImage, boolean signPKG, SigningBase.CertIndex certEnum) throws Exception { - MacSign.withKeychain(toConsumer(keychain -> { - test(keychain, signingKey, signAppImage, signPKG, certEnum); - }), SigningBase.StandardKeychain.MAIN.keychain()); - } - - private static void test(MacSign.ResolvedKeychain keychain, boolean signingKey, boolean signAppImage, boolean signPKG, SigningBase.CertIndex certEnum) throws Exception { - final var certIndex = certEnum.value(); - - new PackageTest() - .configureHelloApp() - .forTypes(PackageType.MAC) - .addInitializer(cmd -> { - cmd.addArguments("--mac-sign", - "--mac-signing-keychain", keychain.name()); - if (signingKey) { - cmd.addArguments("--mac-signing-key-user-name", - SigningBase.getDevName(certIndex)); - } else { - if (signAppImage) { - cmd.addArguments("--mac-app-image-sign-identity", - SigningBase.getAppCert(certIndex)); - } - if (signPKG) { - cmd.addArguments("--mac-installer-sign-identity", - SigningBase.getInstallerCert(certIndex)); - } - } - }) - .forTypes(PackageType.MAC_PKG) - .addBundleVerifier(SigningPackageTest::verifyPKG) - .forTypes(PackageType.MAC_DMG) - .addInitializer(cmd -> { - if (!signingKey) { - // jpackage throws expected error with - // --mac-installer-sign-identity and DMG type - cmd.removeArgumentWithValue("--mac-installer-sign-identity"); - // In case of not signing app image and DMG we need to - // remove signing completely, otherwise we will default - // to --mac-signing-key-user-name once - // --mac-installer-sign-identity is removed. - if (!signAppImage) { - cmd.removeArgumentWithValue("--mac-signing-keychain"); - cmd.removeArgument("--mac-sign"); - } - } - }) - .addBundleVerifier(SigningPackageTest::verifyDMG) - .addBundleVerifier(SigningPackageTest::verifyAppImageInDMG) - .run(); - } } diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java index 586d8d68444..70ec0e600de 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTwoStepTest.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 @@ -21,25 +21,22 @@ * questions. */ -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; - import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.SortedMap; -import java.util.TreeMap; +import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageStringBundle; -import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.MacHelper.ResolvableCertificateRequest; import jdk.jpackage.test.MacHelper.SignKeyOption; +import jdk.jpackage.test.MacHelper.SignKeyOptionWithKeychain; import jdk.jpackage.test.MacSign; import jdk.jpackage.test.MacSignVerify; import jdk.jpackage.test.PackageFile; @@ -52,22 +49,23 @@ import jdk.jpackage.test.TKit; * signed/unsigned .pkg or .dmg package. * *

      - * Prerequisites: A keychain with self-signed certificates as specified in - * {@link SigningBase.StandardKeychain#MAIN}. + * Prerequisites: Keychains with self-signed certificates as specified in + * {@link SigningBase.StandardKeychain#MAIN} and + * {@link SigningBase.StandardKeychain#SINGLE}. */ /* * @test * @summary jpackage with --type pkg,dmg --app-image * @library /test/jdk/tools/jpackage/helpers - * @library base * @key jpackagePlatformPackage - * @build SigningBase * @build jdk.jpackage.test.* - * @build SigningPackageTwoStepTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror SigningPackageTest.java + * @compile -Xlint:all -Werror SigningPackageTwoStepTest.java * @requires (jpackage.test.MacSignTests == "run") * @requires (jpackage.test.SQETest == null) - * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main * --jpt-run=SigningPackageTwoStepTest * --jpt-before-run=SigningBase.verifySignTestEnvReady */ @@ -75,176 +73,148 @@ public class SigningPackageTwoStepTest { @Test @ParameterSupplier - public static void test(TestSpec spec) { - MacSign.withKeychain(toConsumer(keychain -> { - spec.test(keychain); - }), SigningBase.StandardKeychain.MAIN.keychain()); + public static void test(TwoStepsTestSpec spec) { + spec.test(); } - public record TestSpec(Optional signAppImage, Map signPackage) { + @Test + public static void testBundleSignedAppImage() { - public TestSpec { - Objects.requireNonNull(signAppImage); - Objects.requireNonNull(signPackage); + var appImageCmd = JPackageCommand.helloAppImage(); - if ((signAppImage.isEmpty() && signPackage.isEmpty()) || !PackageType.MAC.containsAll(signPackage.keySet())) { - // Unexpected package types. - throw new IllegalArgumentException(); - } + var predefinedAppImageSignOption = predefinedAppImageSignOption(); - // Ensure stable result of toString() call. - if (!SortedMap.class.isInstance(signPackage)) { - signPackage = new TreeMap<>(signPackage); - } - } - - @Override - public String toString() { - var sb = new StringBuilder(); - - signAppImage.ifPresent(signOption -> { - sb.append(String.format("app-image=%s", signOption)); - }); - - if (!sb.isEmpty() && !signPackage.isEmpty()) { - sb.append("; "); - } - - if (!signPackage.isEmpty()) { - sb.append(signPackage); - } - - return sb.toString(); - } - - boolean signNativeBundle() { - return signPackage.isEmpty(); - } - - static Builder build() { - return new Builder(); - } - - static class Builder { - - TestSpec create() { - return new TestSpec(Optional.ofNullable(signAppImage), signPackage); - } - - Builder certRequest(SigningBase.StandardCertificateRequest v) { - return certRequest(v.spec()); - } - - Builder certRequest(MacSign.CertificateRequest v) { - certRequest = Objects.requireNonNull(v); - return this; - } - - Builder signIdentityType(SignKeyOption.Type v) { - signIdentityType = Objects.requireNonNull(v); - return this; - } - - Builder signAppImage() { - signAppImage = createSignKeyOption(); - return this; - } - - Builder signPackage(PackageType type) { - Objects.requireNonNull(type); - signPackage.put(type, createSignKeyOption()); - return this; - } - - Builder signPackage() { - PackageType.MAC.forEach(this::signPackage); - return this; - } - - private SignKeyOption createSignKeyOption() { - return new SignKeyOption(signIdentityType, certRequest); - } - - private MacSign.CertificateRequest certRequest = SigningBase.StandardCertificateRequest.CODESIGN.spec(); - private SignKeyOption.Type signIdentityType = SignKeyOption.Type.SIGN_KEY_IDENTITY; - - private SignKeyOption signAppImage; - private Map signPackage = new HashMap<>(); - } - - void test(MacSign.ResolvedKeychain keychain) { - - var appImageCmd = JPackageCommand.helloAppImage().setFakeRuntime(); - MacHelper.useKeychain(appImageCmd, keychain); - signAppImage.ifPresent(signOption -> { - signOption.setTo(appImageCmd); - }); - - var test = new PackageTest(); - - signAppImage.map(SignKeyOption::certRequest).ifPresent(certRequest -> { - // The predefined app image is signed, verify bundled app image is signed too. - test.addInstallVerifier(cmd -> { - MacSignVerify.verifyAppImageSigned(cmd, certRequest, keychain); - }); - }); - - Optional.ofNullable(signPackage.get(PackageType.MAC_PKG)).map(SignKeyOption::certRequest).ifPresent(certRequest -> { - test.forTypes(PackageType.MAC_PKG, () -> { - test.addBundleVerifier(cmd -> { - MacSignVerify.verifyPkgSigned(cmd, certRequest, keychain); - }); - }); - }); - - test.forTypes(signPackage.keySet()).addRunOnceInitializer(() -> { - appImageCmd.setArgumentValue("--dest", TKit.createTempDirectory("appimage")).execute(0); - }).usePredefinedAppImage(appImageCmd).addInitializer(cmd -> { - MacHelper.useKeychain(cmd, keychain); - Optional.ofNullable(signPackage.get(cmd.packageType())).ifPresent(signOption -> { - signOption.setTo(cmd); - }); - - if (signAppImage.isPresent()) { - // Predefined app image is signed. Expect a warning. - cmd.validateOutput(JPackageStringBundle.MAIN.cannedFormattedString( - "warning.per.user.app.image.signed", - PackageFile.getPathInAppImage(Path.of("")))); - } else if (cmd.packageType() == PackageType.MAC_PKG && signPackage.containsKey(cmd.packageType())) { - // Create signed ".pkg" bundle from the unsigned predefined app image. Expect a warning. - cmd.validateOutput(JPackageStringBundle.MAIN.cannedFormattedString("warning.unsigned.app.image", "pkg")); - } - }) - .run(); - } + new PackageTest().addRunOnceInitializer(() -> { + createPredefinedAppImage(appImageCmd, Optional.of(predefinedAppImageSignOption)); + }).usePredefinedAppImage(appImageCmd).addInitializer(cmd -> { + configureOutputValidator(cmd, true, false); + }).addInstallVerifier(cmd -> { + MacSignVerify.verifyAppImageSigned(cmd, predefinedAppImageSignOption.certRequest()); + }).run(); } public static Collection test() { - List data = new ArrayList<>(); + List data = new ArrayList<>(); - Stream.of(SignKeyOption.Type.values()).flatMap(signIdentityType -> { - return Stream.of( - // Sign both predefined app image and native package. - TestSpec.build().signIdentityType(signIdentityType) - .signAppImage() - .signPackage() - .certRequest(SigningBase.StandardCertificateRequest.PKG) - .signPackage(PackageType.MAC_PKG), + for (var signAppImage : List.of(true, false)) { + Optional appImageSignOption; + if (signAppImage) { + // Sign the predefined app image bundle with the key not used in the jpackage command line being tested. + // This way we can test if jpackage keeps or replaces the signature of + // the predefined app image bundle when backing it in the pkg or dmg installer. + appImageSignOption = Optional.of(predefinedAppImageSignOption()); + } else { + appImageSignOption = Optional.empty(); + } - // Don't sign predefined app image, sign native package. - TestSpec.build().signIdentityType(signIdentityType) - .signPackage() - .certRequest(SigningBase.StandardCertificateRequest.PKG) - .signPackage(PackageType.MAC_PKG), + for (var signPackage : SigningPackageTest.TestSpec.testCases(false)) { + data.add(new TwoStepsTestSpec(appImageSignOption, signPackage)); + } + } - // Sign predefined app image, don't sign native package. - TestSpec.build().signIdentityType(signIdentityType).signAppImage() - ); - }).forEach(data::add); - - return data.stream().map(TestSpec.Builder::create).map(v -> { + return data.stream().map(v -> { return new Object[] {v}; }).toList(); } + + record TwoStepsTestSpec(Optional signAppImage, SigningPackageTest.TestSpec signPackage) { + + TwoStepsTestSpec { + Objects.requireNonNull(signAppImage); + Objects.requireNonNull(signPackage); + } + + @Override + public String toString() { + return Stream.of( + String.format("app-image=%s", signAppImage.map(Objects::toString).orElse("unsigned")), + signPackage.toString() + ).collect(Collectors.joining("; ")); + } + + Optional packagedAppImageSignIdentity() { + return signAppImage.map(SignKeyOptionWithKeychain::certRequest); + } + + void test() { + + var appImageCmd = JPackageCommand.helloAppImage(); + + var test = signPackage.initTest().addRunOnceInitializer(() -> { + createPredefinedAppImage(appImageCmd, signAppImage); + }).usePredefinedAppImage(appImageCmd).addInitializer(cmd -> { + configureOutputValidator(cmd, + signAppImage.isPresent(), + (cmd.packageType() == PackageType.MAC_PKG) && signPackage.packageSignOption().isPresent()); + }).addInstallVerifier(cmd -> { + packagedAppImageSignIdentity().ifPresent(certRequest -> { + MacSignVerify.verifyAppImageSigned(cmd, certRequest); + }); + }); + + MacSign.withKeychain(_ -> { + test.run(); + }, signPackage.keychain()); + } + } + + private static SignKeyOptionWithKeychain predefinedAppImageSignOption() { + // Sign the predefined app image bundle with the key not used in the jpackage command line being tested. + // This way we can test if jpackage keeps or replaces the signature of the input app image bundle. + return new SignKeyOptionWithKeychain( + SignKeyOption.Type.SIGN_KEY_USER_SHORT_NAME, + SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD, + SigningBase.StandardKeychain.MAIN.keychain()); + } + + private static void createPredefinedAppImage(JPackageCommand appImageCmd, Optional signAppImage) { + Objects.requireNonNull(appImageCmd); + Objects.requireNonNull(signAppImage); + + appImageCmd.setFakeRuntime().setArgumentValue("--dest", TKit.createTempDirectory("appimage")); + + signAppImage.ifPresentOrElse(signOption -> { + signOption.setTo(appImageCmd); + + MacSign.withKeychain(_ -> { + appImageCmd.execute(0); + }, signOption.keychain()); + + // Verify that the predefined app image is signed. + MacSignVerify.verifyAppImageSigned(appImageCmd, signOption.certRequest()); + }, () -> { + appImageCmd.execute(0); + }); + } + + private static void configureOutputValidator(JPackageCommand cmd, boolean signAppImage, boolean signPackage) { + var signedPredefinedAppImageWarning = JPackageStringBundle.MAIN.cannedFormattedString( + "warning.per.user.app.image.signed", + PackageFile.getPathInAppImage(Path.of(""))); + + var signedInstallerFromUnsignedPredefinedAppImageWarning = + JPackageStringBundle.MAIN.cannedFormattedString("warning.unsigned.app.image", "pkg"); + + // The warnings are mutually exclusive + final Optional expected; + final List unexpected = new ArrayList<>(); + + if (signAppImage) { + expected = Optional.of(signedPredefinedAppImageWarning); + } else { + unexpected.add(signedPredefinedAppImageWarning); + if (signPackage) { + expected = Optional.of(signedInstallerFromUnsignedPredefinedAppImageWarning); + } else { + expected = Optional.empty(); + unexpected.add(signedInstallerFromUnsignedPredefinedAppImageWarning); + } + } + + expected.ifPresent(cmd::validateOutput); + unexpected.forEach(str -> { + cmd.validateOutput(TKit.assertTextStream(cmd.getValue(str)).negate()); + }); + } } diff --git a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java index ccc39f7a367..604399ebd7a 100644 --- a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.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 @@ -21,17 +21,31 @@ * questions. */ -import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; +import static jdk.jpackage.test.JPackageCommand.RuntimeImageType.RUNTIME_TYPE_FAKE; import java.nio.file.Path; -import java.util.function.Predicate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; -import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.internal.util.MacBundle; +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.MacHelper; +import jdk.jpackage.test.MacHelper.ResolvableCertificateRequest; +import jdk.jpackage.test.MacHelper.SignKeyOption; +import jdk.jpackage.test.MacHelper.SignKeyOptionWithKeychain; import jdk.jpackage.test.MacSign; +import jdk.jpackage.test.MacSignVerify; +import jdk.jpackage.test.MacSignVerify.SpctlType; import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.TKit; /** * Tests generation of dmg and pkg with --mac-sign and related arguments. Test @@ -43,126 +57,182 @@ import jdk.jpackage.test.PackageTest; * app image signing and it will be covered by SigningPackageTest. * *

      - * Following combinations are tested: - *

        - *
      1. "--runtime-image" points to unsigned JDK bundle and --mac-sign is not - * provided. Expected result: runtime image ad-hoc signed. - *
      2. "--runtime-image" points to unsigned JDK bundle and --mac-sign is - * provided. Expected result: Everything is signed with provided certificate. - *
      3. "--runtime-image" points to signed JDK bundle and --mac-sign is not - * provided. Expected result: runtime image is signed with original certificate. - *
      4. "--runtime-image" points to signed JDK bundle and --mac-sign is provided. - * Expected result: runtime image is signed with provided certificate. - *
      5. "--runtime-image" points to JDK image and --mac-sign is not provided. - * Expected result: runtime image ad-hoc signed. - *
      6. "--runtime-image" points to JDK image and --mac-sign is provided. - * Expected result: Everything is signed with provided certificate. - *
      - * - * This test requires that the machine is configured with test certificate for - * "Developer ID Installer: jpackage.openjdk.java.net" in jpackagerTest keychain - * with always allowed access to this keychain for user which runs test. note: - * "jpackage.openjdk.java.net" can be over-ridden by system property - * "jpackage.mac.signing.key.user.name" + * Prerequisites: Keychains with self-signed certificates as specified in + * {@link SigningBase.StandardKeychain#MAIN} and + * {@link SigningBase.StandardKeychain#SINGLE}. */ /* * @test * @summary jpackage with --type pkg,dmg --runtime-image --mac-sign * @library /test/jdk/tools/jpackage/helpers - * @library base * @key jpackagePlatformPackage - * @build SigningBase * @build jdk.jpackage.test.* - * @build SigningRuntimeImagePackageTest + * @compile -Xlint:all -Werror SigningBase.java + * @compile -Xlint:all -Werror SigningPackageTest.java + * @compile -Xlint:all -Werror SigningRuntimeImagePackageTest.java * @requires (jpackage.test.MacSignTests == "run") * @requires (jpackage.test.SQETest == null) - * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main * --jpt-run=SigningRuntimeImagePackageTest * --jpt-before-run=SigningBase.verifySignTestEnvReady */ public class SigningRuntimeImagePackageTest { - private static JPackageCommand addSignOptions(JPackageCommand cmd, MacSign.ResolvedKeychain keychain, int certIndex) { - if (certIndex != SigningBase.CertIndex.INVALID_INDEX.value()) { - cmd.addArguments( - "--mac-sign", - "--mac-signing-keychain", keychain.name(), - "--mac-signing-key-user-name", SigningBase.getDevName(certIndex)); - } - return cmd; - } - - private static Path createInputRuntimeBundle(MacSign.ResolvedKeychain keychain, int certIndex) { - return MacHelper.createRuntimeBundle(cmd -> { - addSignOptions(cmd, keychain, certIndex); - }); + @Test + @ParameterSupplier + public static void test(RuntimeTestSpec spec) { + spec.test(); } @Test - // useJDKBundle - If "true" predefined runtime image will be converted to - // JDK bundle. If "false" JDK image will be used. - // JDKBundleCert - Certificate to sign JDK bundle before calling jpackage. - // signCert - Certificate to sign bundle produced by jpackage. - // 1) unsigned JDK bundle and --mac-sign is not provided - @Parameter({"true", "INVALID_INDEX", "INVALID_INDEX"}) - // 2) unsigned JDK bundle and --mac-sign is provided - @Parameter({"true", "INVALID_INDEX", "ASCII_INDEX"}) - // 3) signed JDK bundle and --mac-sign is not provided - @Parameter({"true", "UNICODE_INDEX", "INVALID_INDEX"}) - // 4) signed JDK bundle and --mac-sign is provided - @Parameter({"true", "UNICODE_INDEX", "ASCII_INDEX"}) - // 5) JDK image and --mac-sign is not provided - @Parameter({"false", "INVALID_INDEX", "INVALID_INDEX"}) - // 6) JDK image and --mac-sign is provided - @Parameter({"false", "INVALID_INDEX", "ASCII_INDEX"}) - public static void test(boolean useJDKBundle, - SigningBase.CertIndex jdkBundleCert, - SigningBase.CertIndex signCert) throws Exception { - MacSign.withKeychain(toConsumer(keychain -> { - test(keychain, useJDKBundle, jdkBundleCert, signCert); - }), SigningBase.StandardKeychain.MAIN.keychain()); + public static void testBundleSignedRuntime() { + + Slot predefinedRuntime = Slot.createEmpty(); + + var signRuntime = runtimeImageSignOption(); + + new PackageTest().addRunOnceInitializer(() -> { + predefinedRuntime.set(createRuntime(Optional.of(signRuntime), RuntimeType.BUNDLE)); + }).addInitializer(cmd -> { + cmd.ignoreDefaultRuntime(true); + cmd.removeArgumentWithValue("--input"); + cmd.setArgumentValue("--runtime-image", predefinedRuntime.get()); + }).addInstallVerifier(cmd -> { + MacSignVerify.verifyAppImageSigned(cmd, signRuntime.certRequest()); + }).run(); } - private static void test(MacSign.ResolvedKeychain keychain, boolean useJDKBundle, - SigningBase.CertIndex jdkBundleCert, - SigningBase.CertIndex signCert) throws Exception { + public static Collection test() { - final Path inputRuntime[] = new Path[1]; + List data = new ArrayList<>(); - new PackageTest() - .addRunOnceInitializer(() -> { - if (useJDKBundle) { - inputRuntime[0] = createInputRuntimeBundle(keychain, jdkBundleCert.value()); - } else { - inputRuntime[0] = JPackageCommand.createInputRuntimeImage(); - } - }) - .addInitializer(cmd -> { - cmd.addArguments("--runtime-image", inputRuntime[0]); - // Remove --input parameter from jpackage command line as we don't - // create input directory in the test and jpackage fails - // if --input references non existent directory. - cmd.removeArgumentWithValue("--input"); - addSignOptions(cmd, keychain, signCert.value()); - }) - .addInstallVerifier(cmd -> { - final var certIndex = Stream.of(signCert, jdkBundleCert) - .filter(Predicate.isEqual(SigningBase.CertIndex.INVALID_INDEX).negate()) - .findFirst().orElse(SigningBase.CertIndex.INVALID_INDEX).value(); + for (var runtimeSpec : List.of( + Map.entry(RuntimeType.IMAGE, false /* unsigned */), + Map.entry(RuntimeType.BUNDLE, false /* unsigned */), + Map.entry(RuntimeType.BUNDLE, true /* signed */) + )) { + var runtimeType = runtimeSpec.getKey(); + var signRuntime = runtimeSpec.getValue(); - final var signed = certIndex != SigningBase.CertIndex.INVALID_INDEX.value(); + Optional runtimeSignOption; + if (signRuntime) { + // Sign the runtime bundle with the key not used in the jpackage command line being tested. + // This way we can test if jpackage keeps or replaces the signature of + // the predefined runtime bundle when backing it in the pkg or dmg installer. + runtimeSignOption = Optional.of(runtimeImageSignOption()); + } else { + runtimeSignOption = Optional.empty(); + } - final var unfoldedBundleDir = cmd.appRuntimeDirectory(); + for (var signPackage : SigningPackageTest.TestSpec.testCases(false)) { + data.add(new RuntimeTestSpec(runtimeSignOption, runtimeType, signPackage)); + } + } - final var libjli = unfoldedBundleDir.resolve("Contents/MacOS/libjli.dylib"); + return data.stream().map(v -> { + return new Object[] {v}; + }).toList(); + } - SigningBase.verifyCodesign(libjli, signed, certIndex); - SigningBase.verifyCodesign(unfoldedBundleDir, signed, certIndex); - if (signed) { - SigningBase.verifySpctl(unfoldedBundleDir, "exec", certIndex); - } - }) - .run(); + enum RuntimeType { + IMAGE, + BUNDLE, + ; + } + + record RuntimeTestSpec( + Optional signRuntime, + RuntimeType runtimeType, + SigningPackageTest.TestSpec signPackage) { + + RuntimeTestSpec { + Objects.requireNonNull(signRuntime); + Objects.requireNonNull(runtimeType); + Objects.requireNonNull(signPackage); + } + + @Override + public String toString() { + var runtimeToken = new StringBuilder(); + runtimeToken.append("runtime={").append(runtimeType); + signRuntime.ifPresent(v -> { + runtimeToken.append(", ").append(v); + }); + runtimeToken.append('}'); + return Stream.of(runtimeToken, signPackage).map(Objects::toString).collect(Collectors.joining("; ")); + } + + Optional packagedAppImageSignIdentity() { + if (runtimeType == RuntimeType.IMAGE) { + return signPackage.appImageSignOption().map(SignKeyOption::certRequest); + } else { + return signPackage.appImageSignOption().or(() -> { + return signRuntime.map(SignKeyOptionWithKeychain::signKeyOption); + }).map(SignKeyOption::certRequest); + } + } + + void test() { + + Slot predefinedRuntime = Slot.createEmpty(); + + var test = signPackage.initTest().addRunOnceInitializer(() -> { + predefinedRuntime.set(createRuntime(signRuntime, runtimeType)); + }).addInitializer(cmd -> { + cmd.ignoreDefaultRuntime(true); + cmd.removeArgumentWithValue("--input"); + cmd.setArgumentValue("--runtime-image", predefinedRuntime.get()); + }).addInstallVerifier(cmd -> { + packagedAppImageSignIdentity().ifPresent(certRequest -> { + MacSignVerify.verifyAppImageSigned(cmd, certRequest); + }); + }); + + MacSign.withKeychain(_ -> { + test.run(); + }, signPackage.keychain()); + } + } + + private static SignKeyOptionWithKeychain runtimeImageSignOption() { + // Sign the runtime bundle with the key not used in the jpackage command line being tested. + // This way we can test if jpackage keeps or replaces the signature of + // the predefined runtime bundle when backing it in the pkg or dmg installer. + return new SignKeyOptionWithKeychain( + SignKeyOption.Type.SIGN_KEY_USER_SHORT_NAME, + SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD, + SigningBase.StandardKeychain.MAIN.keychain()); + } + + private static Path createRuntime(Optional signRuntime, RuntimeType runtimeType) { + if (runtimeType == RuntimeType.IMAGE && signRuntime.isEmpty()) { + return JPackageCommand.createInputRuntimeImage(RUNTIME_TYPE_FAKE); + } else { + Slot runtimeBundle = Slot.createEmpty(); + + MacSign.withKeychain(keychain -> { + var runtimeBundleBuilder = MacHelper.buildRuntimeBundle(); + signRuntime.ifPresent(signingOption -> { + runtimeBundleBuilder.mutator(signingOption::setTo); + }); + runtimeBundle.set(runtimeBundleBuilder.type(RUNTIME_TYPE_FAKE).create()); + }, SigningBase.StandardKeychain.MAIN.keychain()); + + if (runtimeType == RuntimeType.IMAGE) { + return MacBundle.fromPath(runtimeBundle.get()).orElseThrow().homeDir(); + } else { + // Verify the runtime bundle is properly signed/unsigned. + signRuntime.map(SignKeyOptionWithKeychain::certRequest).ifPresentOrElse(certRequest -> { + MacSignVerify.assertSigned(runtimeBundle.get(), certRequest); + var signOrigin = MacSignVerify.findSpctlSignOrigin(SpctlType.EXEC, runtimeBundle.get()).orElse(null); + TKit.assertEquals(certRequest.name(), signOrigin, + String.format("Check [%s] has sign origin as expected", runtimeBundle.get())); + }, () -> { + MacSignVerify.assertAdhocSigned(runtimeBundle.get()); + }); + return runtimeBundle.get(); + } + } } } diff --git a/test/jdk/tools/jpackage/macosx/base/SigningBase.java b/test/jdk/tools/jpackage/macosx/base/SigningBase.java deleted file mode 100644 index b1a709c9cf0..00000000000 --- a/test/jdk/tools/jpackage/macosx/base/SigningBase.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.nio.file.Path; -import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.stream.Stream; -import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.MacSign; -import jdk.jpackage.test.MacSign.CertificateRequest; -import jdk.jpackage.test.MacSign.CertificateType; -import jdk.jpackage.test.MacSign.KeychainWithCertsSpec; -import jdk.jpackage.test.MacSign.ResolvedKeychain; -import jdk.jpackage.test.MacSignVerify; -import jdk.jpackage.test.TKit; - - -/* - * @test - * @summary Setup the environment for jpackage macos signing tests. - * Creates required keychains and signing identities. - * Does NOT run any jpackag tests. - * @library /test/jdk/tools/jpackage/helpers - * @build jdk.jpackage.test.* - * @compile -Xlint:all -Werror SigningBase.java - * @requires (jpackage.test.MacSignTests == "setup") - * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=SigningBase.setUp - */ - -/* - * @test - * @summary Tear down the environment for jpackage macos signing tests. - * Deletes required keychains and signing identities. - * Does NOT run any jpackag tests. - * @library /test/jdk/tools/jpackage/helpers - * @build jdk.jpackage.test.* - * @compile -Xlint:all -Werror SigningBase.java - * @requires (jpackage.test.MacSignTests == "teardown") - * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=SigningBase.tearDown - */ - -public class SigningBase { - - public enum StandardCertificateRequest { - CODESIGN(cert().userName(DEV_NAMES[CertIndex.ASCII_INDEX.value()])), - CODESIGN_COPY(cert().days(100).userName(DEV_NAMES[CertIndex.ASCII_INDEX.value()])), - CODESIGN_ACME_TECH_LTD(cert().days(100).userName("ACME Technologies Limited (ABC12345)")), - PKG(cert().type(CertificateType.INSTALLER).userName(DEV_NAMES[CertIndex.ASCII_INDEX.value()])), - PKG_COPY(cert().type(CertificateType.INSTALLER).days(100).userName(DEV_NAMES[CertIndex.ASCII_INDEX.value()])), - CODESIGN_UNICODE(cert().userName(DEV_NAMES[CertIndex.UNICODE_INDEX.value()])), - PKG_UNICODE(cert().type(CertificateType.INSTALLER).userName(DEV_NAMES[CertIndex.UNICODE_INDEX.value()])), - CODESIGN_EXPIRED(cert().expired().userName("expired jpackage test")), - PKG_EXPIRED(cert().expired().type(CertificateType.INSTALLER).userName("expired jpackage test")); - - StandardCertificateRequest(CertificateRequest.Builder specBuilder) { - this.spec = specBuilder.create(); - } - - public CertificateRequest spec() { - return spec; - } - - private static CertificateRequest.Builder cert() { - return new CertificateRequest.Builder(); - } - - private final CertificateRequest spec; - } - - /** - * Standard keychains used in signing tests. - */ - public enum StandardKeychain { - /** - * The primary keychain with good certificates. - */ - MAIN("jpackagerTest.keychain", - StandardCertificateRequest.CODESIGN, - StandardCertificateRequest.PKG, - StandardCertificateRequest.CODESIGN_UNICODE, - StandardCertificateRequest.PKG_UNICODE, - StandardCertificateRequest.CODESIGN_ACME_TECH_LTD), - /** - * A keychain with some good and some expired certificates. - */ - EXPIRED("jpackagerTest-expired.keychain", - StandardCertificateRequest.CODESIGN, - StandardCertificateRequest.PKG, - StandardCertificateRequest.CODESIGN_EXPIRED, - StandardCertificateRequest.PKG_EXPIRED), - /** - * A keychain with duplicated certificates. - */ - DUPLICATE("jpackagerTest-duplicate.keychain", - StandardCertificateRequest.CODESIGN, - StandardCertificateRequest.PKG, - StandardCertificateRequest.CODESIGN_COPY, - StandardCertificateRequest.PKG_COPY); - - StandardKeychain(String keychainName, StandardCertificateRequest... certs) { - this(keychainName, certs[0].spec(), Stream.of(certs).skip(1).map(StandardCertificateRequest::spec).toArray(CertificateRequest[]::new)); - } - - StandardKeychain(String keychainName, CertificateRequest cert, CertificateRequest... otherCerts) { - final var builder = keychain(keychainName).addCert(cert); - List.of(otherCerts).forEach(builder::addCert); - this.keychain = new ResolvedKeychain(builder.create()); - } - - public ResolvedKeychain keychain() { - return keychain; - } - - public X509Certificate mapCertificateRequest(CertificateRequest certRequest) { - return Objects.requireNonNull(keychain.mapCertificateRequests().get(certRequest)); - } - - private static KeychainWithCertsSpec.Builder keychain(String name) { - return new KeychainWithCertsSpec.Builder().name(name); - } - - private static List signingEnv() { - return Stream.of(values()).map(StandardKeychain::keychain).map(ResolvedKeychain::spec).toList(); - } - - private final ResolvedKeychain keychain; - } - - public static void setUp() { - MacSign.setUp(StandardKeychain.signingEnv()); - } - - public static void tearDown() { - MacSign.tearDown(StandardKeychain.signingEnv()); - } - - public static void verifySignTestEnvReady() { - if (!Inner.SIGN_ENV_READY) { - TKit.throwSkippedException(new IllegalStateException("Misconfigured signing test environment")); - } - } - - private final class Inner { - private static final boolean SIGN_ENV_READY = MacSign.isDeployed(StandardKeychain.signingEnv()); - } - - enum CertIndex { - ASCII_INDEX(0), - UNICODE_INDEX(1), - INVALID_INDEX(-1); - - CertIndex(int value) { - this.value = value; - } - - int value() { - return value; - } - - private final int value; - } - - public static int DEFAULT_INDEX = 0; - private static String [] DEV_NAMES = { - "jpackage.openjdk.java.net", - "jpackage.openjdk.java.net (ö)", - }; - - public static String getDevName(int certIndex) { - // Always use values from system properties if set - String value = System.getProperty("jpackage.mac.signing.key.user.name"); - if (value != null) { - return value; - } - - return DEV_NAMES[certIndex]; - } - - public static int getDevNameIndex(String devName) { - return Arrays.binarySearch(DEV_NAMES, devName); - } - - public static String getAppCert(int certIndex) { - return "Developer ID Application: " + getDevName(certIndex); - } - - public static String getInstallerCert(int certIndex) { - return "Developer ID Installer: " + getDevName(certIndex); - } - - public static void verifyCodesign(Path target, boolean signed, int certIndex) { - if (signed) { - final var certRequest = getCertRequest(certIndex); - MacSignVerify.assertSigned(target, certRequest); - } else { - MacSignVerify.assertAdhocSigned(target); - } - } - - // Since we no longer have unsigned app image, but we need to check - // DMG which is not adhoc or certificate signed and we cannot use verifyCodesign - // for this. verifyDMG() is introduced to check that DMG is unsigned. - // Should not be used to validated anything else. - public static void verifyDMG(Path target) { - if (!target.toString().toLowerCase().endsWith(".dmg")) { - throw new IllegalArgumentException("Unexpected target: " + target); - } - - MacSignVerify.assertUnsigned(target); - } - - public static void verifySpctl(Path target, String type, int certIndex) { - final var standardCertIndex = Stream.of(CertIndex.values()).filter(v -> { - return v.value() == certIndex; - }).findFirst().orElseThrow(); - - final var standardType = Stream.of(MacSignVerify.SpctlType.values()).filter(v -> { - return v.value().equals(type); - }).findFirst().orElseThrow(); - - final String expectedSignOrigin; - if (standardCertIndex == CertIndex.INVALID_INDEX) { - expectedSignOrigin = null; - } else if (standardType == MacSignVerify.SpctlType.EXEC) { - expectedSignOrigin = getCertRequest(certIndex).name(); - } else if (standardType == MacSignVerify.SpctlType.INSTALL) { - expectedSignOrigin = getPkgCertRequest(certIndex).name(); - } else { - throw new IllegalArgumentException(); - } - - final var signOrigin = MacSignVerify.findSpctlSignOrigin(standardType, target).orElse(null); - - TKit.assertEquals(signOrigin, expectedSignOrigin, - String.format("Check [%s] has sign origin as expected", target)); - } - - public static void verifyPkgutil(Path target, boolean signed, int certIndex) { - if (signed) { - final var certRequest = getPkgCertRequest(certIndex); - MacSignVerify.assertPkgSigned(target, certRequest, StandardKeychain.MAIN.mapCertificateRequest(certRequest)); - } else { - MacSignVerify.assertUnsigned(target); - } - } - - public static void verifyAppImageSignature(JPackageCommand appImageCmd, - boolean isSigned, String... launchers) throws Exception { - Path launcherPath = appImageCmd.appLauncherPath(); - SigningBase.verifyCodesign(launcherPath, isSigned, SigningBase.DEFAULT_INDEX); - - final List launchersList = List.of(launchers); - launchersList.forEach(launcher -> { - Path testALPath = launcherPath.getParent().resolve(launcher); - SigningBase.verifyCodesign(testALPath, isSigned, SigningBase.DEFAULT_INDEX); - }); - - Path appImage = appImageCmd.outputBundle(); - SigningBase.verifyCodesign(appImage, isSigned, SigningBase.DEFAULT_INDEX); - if (isSigned) { - SigningBase.verifySpctl(appImage, "exec", SigningBase.DEFAULT_INDEX); - } - } - - private static CertificateRequest getCertRequest(int certIndex) { - switch (CertIndex.values()[certIndex]) { - case ASCII_INDEX -> { - return StandardCertificateRequest.CODESIGN.spec(); - } - case UNICODE_INDEX -> { - return StandardCertificateRequest.CODESIGN_UNICODE.spec(); - } - default -> { - throw new IllegalArgumentException(); - } - } - } - - private static CertificateRequest getPkgCertRequest(int certIndex) { - switch (CertIndex.values()[certIndex]) { - case ASCII_INDEX -> { - return StandardCertificateRequest.PKG.spec(); - } - case UNICODE_INDEX -> { - return StandardCertificateRequest.PKG_UNICODE.spec(); - } - default -> { - throw new IllegalArgumentException(); - } - } - } -} From 0dd5b59194f32f54c2ec6572833f45e1402515ba Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Sat, 17 Jan 2026 04:30:02 +0000 Subject: [PATCH 133/204] 8375370: XRBackendNative.c reported variable uninitialized by clang23 Reviewed-by: prr --- .../unix/native/libawt_xawt/java2d/x11/XRBackendNative.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c b/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c index 0ea86e3e9b3..ebb3e32890e 100644 --- a/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c +++ b/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2020, 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 @@ -339,7 +339,7 @@ Java_sun_java2d_xr_XRBackendNative_createPixmap(JNIEnv *env, jobject this, JNIEXPORT jint JNICALL Java_sun_java2d_xr_XRBackendNative_createPictureNative (JNIEnv *env, jclass cls, jint drawable, jlong formatPtr) { - XRenderPictureAttributes pict_attr; + XRenderPictureAttributes pict_attr = {0}; return XRenderCreatePicture(awt_display, (Drawable) drawable, (XRenderPictFormat *) jlong_to_ptr(formatPtr), 0, &pict_attr); From 436c62afd285a3ce2be9aef59876df4b9f0955ff Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Sat, 17 Jan 2026 06:24:31 +0000 Subject: [PATCH 134/204] 8373867: Improve robustness of Attach API for finding tmp directory Reviewed-by: sspitsyn, amenkov --- .../sun/tools/attach/VirtualMachineImpl.java | 55 +++++++--- .../attach/AttachNotSupportedException.java | 13 ++- .../attach/TestWithoutDumpableProcess.java | 102 ++++++++++++++++++ 3 files changed, 154 insertions(+), 16 deletions(-) create mode 100644 test/jdk/com/sun/tools/attach/TestWithoutDumpableProcess.java diff --git a/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java b/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java index af8870ecf64..998e8d037b4 100644 --- a/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java +++ b/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java @@ -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,6 +41,8 @@ import java.util.regex.Pattern; import static java.nio.charset.StandardCharsets.UTF_8; +import sun.jvmstat.monitor.MonitoredHost; + /* * Linux implementation of HotSpotVirtualMachine */ @@ -228,7 +230,7 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { // Return the socket file for the given process. private File findSocketFile(long pid, long ns_pid) throws AttachNotSupportedException, IOException { - return new File(findTargetProcessTmpDirectory(pid, ns_pid), ".java_pid" + ns_pid); + return new File(findTargetProcessTmpDirectory(pid), ".java_pid" + ns_pid); } // On Linux a simple handshake is used to start the attach mechanism @@ -243,14 +245,14 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { // Do not canonicalize the file path, or we will fail to attach to a VM in a container. f.createNewFile(); } catch (IOException _) { - f = new File(findTargetProcessTmpDirectory(pid, ns_pid), fn.toString()); + f = new File(findTargetProcessTmpDirectory(pid), fn.toString()); f.createNewFile(); } return f; } - private String findTargetProcessTmpDirectory(long pid, long ns_pid) throws AttachNotSupportedException, IOException { - final var procPidRoot = PROC.resolve(Long.toString(pid)).resolve(ROOT_TMP); + private String findTargetProcessTmpDirectory(long pid) throws AttachNotSupportedException { + final var tmpOnProcPidRoot = PROC.resolve(Long.toString(pid)).resolve(ROOT_TMP); /* We need to handle at least 4 different cases: * 1. Caller and target processes share PID namespace and root filesystem (host to host or container to @@ -261,21 +263,44 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { * 4. Caller and target processes share neither PID namespace nor root filesystem (host to container) * * if target is elevated, we cant use /proc//... so we have to fallback to /tmp, but that may not be shared - * with the target/attachee process, we can try, except in the case where the ns_pid also exists in this pid ns - * which is ambiguous, if we share /tmp with the intended target, the attach will succeed, if we do not, - * then we will potentially attempt to attach to some arbitrary process with the same pid (in this pid ns) - * as that of the intended target (in its * pid ns). + * with the target/attachee process, so we should check whether /tmp on both is same. This method would throw + * AttachNotSupportedException if they are different because we cannot make a connection with target VM. * - * so in that case we should prehaps throw - or risk sending SIGQUIT to some arbitrary process... which could kill it - * - * however we can also check the target pid's signal masks to see if it catches SIGQUIT and only do so if in + * In addition, we can also check the target pid's signal masks to see if it catches SIGQUIT and only do so if in * fact it does ... this reduces the risk of killing an innocent process in the current ns as opposed to * attaching to the actual target JVM ... c.f: checkCatchesAndSendQuitTo() below. - * - * note that if pid == ns_pid we are in a shared pid ns with the target and may (potentially) share /tmp */ - return (Files.isWritable(procPidRoot) ? procPidRoot : TMPDIR).toString(); + try { + if (Files.isWritable(tmpOnProcPidRoot)) { + return tmpOnProcPidRoot.toString(); + } else if (Files.isSameFile(tmpOnProcPidRoot, TMPDIR)) { + return TMPDIR.toString(); + } else { + throw new AttachNotSupportedException("Unable to access the filesystem of the target process"); + } + } catch (IOException ioe) { + try { + boolean found = MonitoredHost.getMonitoredHost("//localhost") + .activeVms() + .stream() + .anyMatch(i -> pid == i.intValue()); + if (found) { + // We can use /tmp because target process is on same host + // even if we cannot access /proc//root. + // The process with capsh/setcap would fall this pattern. + return TMPDIR.toString(); + } else { + throw new AttachNotSupportedException("Unable to access the filesystem of the target process", ioe); + } + } catch (AttachNotSupportedException e) { + // AttachNotSupportedException happened in above should go through + throw e; + } catch (Exception e) { + // Other exceptions would be wrapped with AttachNotSupportedException + throw new AttachNotSupportedException("Unable to access the filesystem of the target process", e); + } + } } // Return the inner most namespaced PID if there is one, diff --git a/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java b/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java index 382c8a57b26..9d62badb94c 100644 --- a/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java +++ b/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, 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 @@ -62,4 +62,15 @@ public class AttachNotSupportedException extends Exception { super(s); } + /** + * Constructs an AttachNotSupportedException with + * the specified cause. + * + * @param message the detail message. + * @param cause the cause of this exception. + */ + public AttachNotSupportedException(String message, Throwable cause) { + super(message, cause); + } + } diff --git a/test/jdk/com/sun/tools/attach/TestWithoutDumpableProcess.java b/test/jdk/com/sun/tools/attach/TestWithoutDumpableProcess.java new file mode 100644 index 00000000000..be9d0c8790c --- /dev/null +++ b/test/jdk/com/sun/tools/attach/TestWithoutDumpableProcess.java @@ -0,0 +1,102 @@ +/* + * 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. + */ + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; + +import com.sun.tools.attach.VirtualMachine; + +import jdk.test.lib.Asserts; +import jdk.test.lib.thread.ProcessThread; +import jdk.test.lib.process.ProcessTools; + +/* + * @test + * @bug 8226919 8373867 + * @summary Test to make sure attach target process which is not dumpable. + * @library /test/lib + * @modules jdk.attach + * @requires os.family == "linux" + * + * @run main/timeout=200 TestWithoutDumpableProcess + */ +public class TestWithoutDumpableProcess { + + private static final String EXPECTED_PROP_KEY = "attach.test"; + private static final String EXPECTED_PROP_VALUE = "true"; + + public static class Debuggee { + + // Disable dumpable attribute via prctl(2) + private static void disableDumpable() throws Throwable { + var linker = Linker.nativeLinker(); + var prctl = linker.downcallHandle(linker.defaultLookup().findOrThrow("prctl"), + FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG), + Linker.Option.firstVariadicArg(1), Linker.Option.captureCallState("errno")); + var errnoSeg = Arena.global().allocate(Linker.Option.captureStateLayout()); + final int PR_SET_DUMPABLE = 4; // from linux/prctl.h + + int ret = (int)prctl.invoke(errnoSeg, PR_SET_DUMPABLE, 0L); + if (ret == -1){ + var hndErrno = Linker.Option + .captureStateLayout() + .varHandle(MemoryLayout.PathElement.groupElement("errno")); + int errno = (int)hndErrno.get(errnoSeg, 0L); + throw new RuntimeException("prctl: errno=" + errno); + } + } + + public static void main(String[] args) throws Throwable { + disableDumpable(); + IO.println(Application.READY_MSG); + + while (IO.readln().equals(Application.SHUTDOWN_MSG)); + } + + public static ProcessThread start() { + var args = new String[]{ + "--enable-native-access=ALL-UNNAMED", + String.format("-D%s=%s", EXPECTED_PROP_KEY, EXPECTED_PROP_VALUE), Debuggee.class.getName() + }; + var pb = ProcessTools.createLimitedTestJavaProcessBuilder(args); + var pt = new ProcessThread("runApplication", Application.READY_MSG::equals, pb); + pt.start(); + return pt; + } + + } + + public static void main(String[] args) throws Exception { + var pt = Debuggee.start(); + var vm = VirtualMachine.attach(Long.toString(pt.getPid())); + var val = vm.getSystemProperties().getProperty(EXPECTED_PROP_KEY); + + Asserts.assertNotNull(val, "Expected sysprop not found"); + Asserts.assertEquals(val, "true", "Unexpected sysprop value"); + } + +} From a0e6f028a8952f61d9115f7bdf04b8a87f8ebba4 Mon Sep 17 00:00:00 2001 From: Shawn M Emery Date: Sat, 17 Jan 2026 11:08:30 +0000 Subject: [PATCH 135/204] 8360934: Add AVX-512 intrinsics for ML-KEM - enhancement on AVX512_VBMI Co-authored-by: Sandhya Viswanathan Reviewed-by: jbhateja, vpaprotski --- .../cpu/x86/stubGenerator_x86_64_kyber.cpp | 92 ++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp index 3e5593322d5..7d5dee6a5df 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.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 @@ -64,6 +64,39 @@ static address kyberAvx512ConstsAddr(int offset) { const Register scratch = r10; +ATTRIBUTE_ALIGNED(64) static const uint8_t kyberAvx512_12To16Dup[] = { +// 0 - 63 + 0, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11, 12, 13, 13, 14, 15, 16, + 16, 17, 18, 19, 19, 20, 21, 22, 22, 23, 24, 25, 25, 26, 27, 28, 28, 29, 30, + 31, 31, 32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40, 40, 41, 42, 43, 43, 44, + 45, 46, 46, 47 + }; + +static address kyberAvx512_12To16DupAddr() { + return (address) kyberAvx512_12To16Dup; +} + +ATTRIBUTE_ALIGNED(64) static const uint16_t kyberAvx512_12To16Shift[] = { +// 0 - 31 + 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, + 4, 0, 4, 0, 4, 0, 4 + }; + +static address kyberAvx512_12To16ShiftAddr() { + return (address) kyberAvx512_12To16Shift; +} + +ATTRIBUTE_ALIGNED(64) static const uint64_t kyberAvx512_12To16And[] = { +// 0 - 7 + 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF, + 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF, + 0x0FFF0FFF0FFF0FFF, 0x0FFF0FFF0FFF0FFF + }; + +static address kyberAvx512_12To16AndAddr() { + return (address) kyberAvx512_12To16And; +} + ATTRIBUTE_ALIGNED(64) static const uint16_t kyberAvx512NttPerms[] = { // 0 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, @@ -822,10 +855,65 @@ address generate_kyber12To16_avx512(StubGenerator *stubgen, const Register perms = r11; - Label Loop; + Label Loop, VBMILoop; __ addptr(condensed, condensedOffs); + if (VM_Version::supports_avx512_vbmi()) { + // mask load for the first 48 bytes of each vector + __ mov64(rax, 0x0000FFFFFFFFFFFF); + __ kmovql(k1, rax); + + __ lea(perms, ExternalAddress(kyberAvx512_12To16DupAddr())); + __ evmovdqub(xmm20, Address(perms), Assembler::AVX_512bit); + + __ lea(perms, ExternalAddress(kyberAvx512_12To16ShiftAddr())); + __ evmovdquw(xmm21, Address(perms), Assembler::AVX_512bit); + + __ lea(perms, ExternalAddress(kyberAvx512_12To16AndAddr())); + __ evmovdquq(xmm22, Address(perms), Assembler::AVX_512bit); + + __ align(OptoLoopAlignment); + __ BIND(VBMILoop); + + __ evmovdqub(xmm0, k1, Address(condensed, 0), false, + Assembler::AVX_512bit); + __ evmovdqub(xmm1, k1, Address(condensed, 48), false, + Assembler::AVX_512bit); + __ evmovdqub(xmm2, k1, Address(condensed, 96), false, + Assembler::AVX_512bit); + __ evmovdqub(xmm3, k1, Address(condensed, 144), false, + Assembler::AVX_512bit); + + __ evpermb(xmm4, k0, xmm20, xmm0, false, Assembler::AVX_512bit); + __ evpermb(xmm5, k0, xmm20, xmm1, false, Assembler::AVX_512bit); + __ evpermb(xmm6, k0, xmm20, xmm2, false, Assembler::AVX_512bit); + __ evpermb(xmm7, k0, xmm20, xmm3, false, Assembler::AVX_512bit); + + __ evpsrlvw(xmm4, xmm4, xmm21, Assembler::AVX_512bit); + __ evpsrlvw(xmm5, xmm5, xmm21, Assembler::AVX_512bit); + __ evpsrlvw(xmm6, xmm6, xmm21, Assembler::AVX_512bit); + __ evpsrlvw(xmm7, xmm7, xmm21, Assembler::AVX_512bit); + + __ evpandq(xmm0, xmm22, xmm4, Assembler::AVX_512bit); + __ evpandq(xmm1, xmm22, xmm5, Assembler::AVX_512bit); + __ evpandq(xmm2, xmm22, xmm6, Assembler::AVX_512bit); + __ evpandq(xmm3, xmm22, xmm7, Assembler::AVX_512bit); + + store4regs(parsed, 0, xmm0_3, _masm); + + __ addptr(condensed, 192); + __ addptr(parsed, 256); + __ subl(parsedLength, 128); + __ jcc(Assembler::greater, VBMILoop); + + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ mov64(rax, 0); // return 0 + __ ret(0); + + return start; + } + __ lea(perms, ExternalAddress(kyberAvx512_12To16PermsAddr())); load4regs(xmm24_27, perms, 0, _masm); From 1cdb8174220e52c055406e0e927bc982c91ac595 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Sun, 18 Jan 2026 07:35:12 +0000 Subject: [PATCH 136/204] 8375575: AttachNotSupportedException constructor missing @since 27 Reviewed-by: liach --- .../com/sun/tools/attach/AttachNotSupportedException.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java b/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java index 9d62badb94c..725db3e7732 100644 --- a/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java +++ b/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java @@ -68,6 +68,8 @@ public class AttachNotSupportedException extends Exception { * * @param message the detail message. * @param cause the cause of this exception. + * + * @since 27 */ public AttachNotSupportedException(String message, Throwable cause) { super(message, cause); From a67979c4e6dcea70e63cc79a105be12a9306c660 Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Mon, 19 Jan 2026 02:33:18 +0000 Subject: [PATCH 137/204] 8375125: assert(false) failed: "Attempting to acquire lock NativeHeapTrimmer_lock/nosafepoint out of order with lock ConcurrentHashTableResize_lock/nosafepoint-2 -- possible deadlock" when using native heap trimmer Reviewed-by: dholmes, stuefe --- src/hotspot/share/classfile/stringTable.cpp | 7 +- src/hotspot/share/classfile/symbolTable.cpp | 7 +- ...stTrimNativeHeapIntervalTablesCleanup.java | 107 ++++++++++++++++++ 3 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/os/TestTrimNativeHeapIntervalTablesCleanup.java diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index c775014cfac..20dfad0d980 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.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 @@ -614,6 +614,10 @@ struct StringTableDeleteCheck : StackObj { }; void StringTable::clean_dead_entries(JavaThread* jt) { + // BulkDeleteTask::prepare() may take ConcurrentHashTableResize_lock (nosafepoint-2). + // When NativeHeapTrimmer is enabled, SuspendMark may take NativeHeapTrimmer::_lock (nosafepoint). + // Take SuspendMark first to keep lock order and avoid deadlock. + NativeHeapTrimmer::SuspendMark sm("stringtable"); StringTableHash::BulkDeleteTask bdt(_local_table); if (!bdt.prepare(jt)) { return; @@ -621,7 +625,6 @@ void StringTable::clean_dead_entries(JavaThread* jt) { StringTableDeleteCheck stdc; StringTableDoDelete stdd; - NativeHeapTrimmer::SuspendMark sm("stringtable"); { TraceTime timer("Clean", TRACETIME_LOG(Debug, stringtable, perf)); while(bdt.do_task(jt, stdc, stdd)) { diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp index ec639a2b4d3..c49aa10fa0d 100644 --- a/src/hotspot/share/classfile/symbolTable.cpp +++ b/src/hotspot/share/classfile/symbolTable.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 @@ -763,6 +763,10 @@ struct SymbolTableDeleteCheck : StackObj { }; void SymbolTable::clean_dead_entries(JavaThread* jt) { + // BulkDeleteTask::prepare() may take ConcurrentHashTableResize_lock (nosafepoint-2). + // When NativeHeapTrimmer is enabled, SuspendMark may take NativeHeapTrimmer::_lock (nosafepoint). + // Take SuspendMark first to keep lock order and avoid deadlock. + NativeHeapTrimmer::SuspendMark sm("symboltable"); SymbolTableHash::BulkDeleteTask bdt(_local_table); if (!bdt.prepare(jt)) { return; @@ -770,7 +774,6 @@ void SymbolTable::clean_dead_entries(JavaThread* jt) { SymbolTableDeleteCheck stdc; SymbolTableDoDelete stdd; - NativeHeapTrimmer::SuspendMark sm("symboltable"); { TraceTime timer("Clean", TRACETIME_LOG(Debug, symboltable, perf)); while (bdt.do_task(jt, stdc, stdd)) { diff --git a/test/hotspot/jtreg/runtime/os/TestTrimNativeHeapIntervalTablesCleanup.java b/test/hotspot/jtreg/runtime/os/TestTrimNativeHeapIntervalTablesCleanup.java new file mode 100644 index 00000000000..3ced98b616d --- /dev/null +++ b/test/hotspot/jtreg/runtime/os/TestTrimNativeHeapIntervalTablesCleanup.java @@ -0,0 +1,107 @@ +/* + * 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 8375125 + * @summary Trigger StringTable::clean_dead_entries or SymbolTable::clean_dead_entries + * with -XX:TrimNativeHeapInterval enabled,should not violate lock ordering. + * @requires vm.debug + * @requires vm.gc != "Epsilon" + * @library /test/lib + * @modules java.compiler + * @run main/othervm -Xms128m -Xmx128m + * -XX:TrimNativeHeapInterval=300000 + * TestTrimNativeHeapIntervalTablesCleanup string + * @run main/othervm -Xms128m -Xmx128m + * -XX:TrimNativeHeapInterval=300000 + * TestTrimNativeHeapIntervalTablesCleanup symbol + */ + +import java.util.LinkedList; +import jdk.test.lib.compiler.InMemoryJavaCompiler; + +public class TestTrimNativeHeapIntervalTablesCleanup { + + public static void main(String[] args) throws Exception{ + if (args.length != 1) { + throw new IllegalArgumentException("Expected 1 argument: string|symbol"); + } + switch (args[0]) { + case "string": + testStringTableCleanup(); + break; + case "symbol": + testSymbolTableCleanup(); + break; + default: + throw new IllegalArgumentException("Unknown mode: " + args[0]); + } + System.out.println("passed: " + args[0]); + } + + static void testStringTableCleanup() throws Exception{ + final int rounds = 30; + final int maxSize = 200_000; + final int pruneEvery = 50_000; + final int pruneCount = 25_000; + long stringNum = 0; + + for (int round = 0; round < rounds; round++) { + LinkedList list = new LinkedList<>(); + for (int i = 0; i < maxSize; i++, stringNum++) { + if (i != 0 && (i % pruneEvery) == 0) { + int toRemove = Math.min(pruneCount, list.size()); + list.subList(0, toRemove).clear(); + } + list.push(Long.toString(stringNum).intern()); + } + System.gc(); + Thread.sleep(1000); + } + } + + static void testSymbolTableCleanup() throws Exception { + final int rounds = 10; + final int classesPerRound = 100; + + for (int r = 0; r < rounds; r++) { + for (int i = 0; i < classesPerRound; i++) { + String cn = "C" + r + "_" + i; + byte[] bytes = InMemoryJavaCompiler.compile( + cn, + "public class " + cn + " { int m" + i + "() { return " + i + "; } }" + ); + new ClassLoader(null) { + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if (!name.equals(cn)) throw new ClassNotFoundException(name); + return defineClass(name, bytes, 0, bytes.length); + } + }.loadClass(cn).getDeclaredConstructor().newInstance(); + } + System.gc(); + Thread.sleep(1000); + } + } +} \ No newline at end of file From f8fb78042639d4c436fdad7f501ca4ca28dfe9e3 Mon Sep 17 00:00:00 2001 From: Valerie Peng Date: Fri, 18 Jul 2025 23:49:30 +0000 Subject: [PATCH 138/204] 8265429: Improve GCM encryption Co-authored-by: Daniel Jelinski Reviewed-by: rhalade, pkumaraswamy, ahgross, jnimeh, djelinski --- .../share/native/libj2pkcs11/p11_crypt.c | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c index d969fabffd0..052c7011860 100644 --- a/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_crypt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 2002 Graz University of Technology. All rights reserved. @@ -184,9 +184,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Encrypt if (directIn != 0) { inBufP = (CK_BYTE_PTR) jlong_to_ptr(directIn); - } else { + } else if (jIn != NULL) { inBufP = (*env)->GetPrimitiveArrayCritical(env, jIn, NULL); + // may happen if out of memory if (inBufP == NULL) { return 0; } + } else { + inBufP = NULL; } if (directOut != 0) { @@ -194,7 +197,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Encrypt } else { outBufP = (*env)->GetPrimitiveArrayCritical(env, jOut, NULL); if (outBufP == NULL) { - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } return 0; @@ -208,7 +211,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Encrypt (CK_BYTE_PTR)(outBufP + jOutOfs), &ckEncryptedLen); - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } if (directOut == 0) { @@ -251,9 +254,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1EncryptUpdate if (directIn != 0) { inBufP = (CK_BYTE_PTR) jlong_to_ptr(directIn); - } else { + } else if (jIn != NULL) { inBufP = (*env)->GetPrimitiveArrayCritical(env, jIn, NULL); + // may happen if out of memory if (inBufP == NULL) { return 0; } + } else { + inBufP = NULL; } if (directOut != 0) { @@ -261,7 +267,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1EncryptUpdate } else { outBufP = (*env)->GetPrimitiveArrayCritical(env, jOut, NULL); if (outBufP == NULL) { - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } return 0; @@ -275,7 +281,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1EncryptUpdate (CK_BYTE_PTR)(outBufP + jOutOfs), &ckEncryptedPartLen); - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } if (directOut == 0) { @@ -462,9 +468,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Decrypt if (directIn != 0) { inBufP = (CK_BYTE_PTR) jlong_to_ptr(directIn); - } else { + } else if (jIn != NULL) { inBufP = (*env)->GetPrimitiveArrayCritical(env, jIn, NULL); + // may happen if out of memory if (inBufP == NULL) { return 0; } + } else { + inBufP = NULL; } if (directOut != 0) { @@ -472,7 +481,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Decrypt } else { outBufP = (*env)->GetPrimitiveArrayCritical(env, jOut, NULL); if (outBufP == NULL) { - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } return 0; @@ -485,7 +494,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1Decrypt (CK_BYTE_PTR)(outBufP + jOutOfs), &ckOutLen); - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } if (directOut == 0) { @@ -528,9 +537,12 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1DecryptUpdate if (directIn != 0) { inBufP = (CK_BYTE_PTR) jlong_to_ptr(directIn); - } else { + } else if (jIn != NULL) { inBufP = (*env)->GetPrimitiveArrayCritical(env, jIn, NULL); + // may happen if out of memory if (inBufP == NULL) { return 0; } + } else { + inBufP = NULL; } if (directOut != 0) { @@ -538,7 +550,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1DecryptUpdate } else { outBufP = (*env)->GetPrimitiveArrayCritical(env, jOut, NULL); if (outBufP == NULL) { - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } return 0; @@ -551,7 +563,7 @@ Java_sun_security_pkcs11_wrapper_PKCS11_C_1DecryptUpdate (CK_BYTE_PTR)(outBufP + jOutOfs), &ckDecryptedPartLen); - if (directIn == 0) { + if (directIn == 0 && inBufP != NULL) { (*env)->ReleasePrimitiveArrayCritical(env, jIn, inBufP, JNI_ABORT); } if (directOut == 0) { From 9f3f960b364bad96bfcd469d7993d2aedbc020a4 Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Mon, 18 Aug 2025 10:25:12 +0000 Subject: [PATCH 139/204] 8364214: Enhance polygon data support Reviewed-by: rhalade, psadhukhan, mschoene, prr --- .../share/classes/sun/java2d/SunGraphics2D.java | 6 +++--- .../share/classes/sun/java2d/pipe/SpanClipRenderer.java | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java index 1bebf379997..e92c485a363 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1901,9 +1901,9 @@ public final class SunGraphics2D if (usrClip == null) { clipState = CLIP_DEVICE; clipRegion = devClip; - } else if (usrClip instanceof Rectangle2D) { + } else if (usrClip instanceof Rectangle2D clip) { clipState = CLIP_RECTANGULAR; - clipRegion = devClip.getIntersection((Rectangle2D) usrClip); + clipRegion = devClip.getIntersection(clip); } else { PathIterator cpi = usrClip.getPathIterator(null); int[] box = new int[4]; diff --git a/src/java.desktop/share/classes/sun/java2d/pipe/SpanClipRenderer.java b/src/java.desktop/share/classes/sun/java2d/pipe/SpanClipRenderer.java index 62d1f119f33..69c4954cac4 100644 --- a/src/java.desktop/share/classes/sun/java2d/pipe/SpanClipRenderer.java +++ b/src/java.desktop/share/classes/sun/java2d/pipe/SpanClipRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,8 @@ package sun.java2d.pipe; import java.awt.Rectangle; import java.awt.Shape; + +import sun.java2d.InvalidPipeException; import sun.java2d.SunGraphics2D; /** @@ -67,7 +69,9 @@ public class SpanClipRenderer implements CompositePipe public Object startSequence(SunGraphics2D sg, Shape s, Rectangle devR, int[] abox) { RegionIterator ri = sg.clipRegion.getIterator(); - + if (ri.region.isRectangular()) { + throw new InvalidPipeException("Invalid clip data"); + } return new SCRcontext(ri, outpipe.startSequence(sg, s, devR, abox)); } From 3b6ac2af9c8637891092955474b27e5400650dfc Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Wed, 20 Aug 2025 03:17:34 +0000 Subject: [PATCH 140/204] 8362308: Enhance Bitmap operations Reviewed-by: mschoene, rhalade, psadhukhan, prr --- .../libmlib_image/mlib_ImageConvMxN_Fp.c | 9 +++++++- .../libmlib_image/mlib_ImageConvMxN_ext.c | 6 ++++- .../libmlib_image/mlib_ImageConv_16ext.c | 23 ++++++++++++++++++- .../libmlib_image/mlib_ImageConv_16nw.c | 7 +++++- .../libmlib_image/mlib_ImageConv_32nw.c | 7 +++++- .../libmlib_image/mlib_ImageConv_8ext.c | 23 ++++++++++++++++++- .../native/libmlib_image/mlib_ImageConv_8nw.c | 7 +++++- .../libmlib_image/mlib_ImageConv_u16ext.c | 23 ++++++++++++++++++- .../libmlib_image/mlib_ImageConv_u16nw.c | 7 +++++- .../libmlib_image/mlib_ImageLookUp_Bit.c | 12 +++++++++- .../native/libmlib_image/mlib_ImageScanPoly.c | 11 ++++++++- 11 files changed, 124 insertions(+), 11 deletions(-) diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c index fa9a186d6d7..bc2df9b0b1a 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_Fp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -76,6 +76,7 @@ #include "mlib_ImageCheck.h" #include "mlib_SysMath.h" #include "mlib_ImageConv.h" +#include "safe_math.h" /***************************************************************/ static void mlib_ImageConvMxNMulAdd_F32(mlib_f32 *dst, @@ -272,6 +273,9 @@ mlib_status mlib_convMxNext_f32(mlib_image *dst, mlib_s32 nch = mlib_ImageGetChannels(dst); mlib_s32 i, j, j1, k; + if (!SAFE_TO_MULT(3, wid_e) || !SAFE_TO_ADD(3 * wid_e, m)) { + return MLIB_FAILURE; + } if (3 * wid_e + m > 1024) { dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64)); @@ -629,6 +633,9 @@ mlib_status mlib_convMxNext_d64(mlib_image *dst, mlib_s32 nch = mlib_ImageGetChannels(dst); mlib_s32 i, j, j1, k; + if (!SAFE_TO_MULT(3, wid_e) || !SAFE_TO_ADD(3 * wid_e, m)) { + return MLIB_FAILURE; + } if (3 * wid_e + m > 1024) { dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64)); diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c index ee15935dcfe..5869b0a54af 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConvMxN_ext.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -82,6 +82,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" +#include "safe_math.h" /***************************************************************/ static void mlib_ImageConvMxNMulAdd_S32(mlib_d64 *dst, @@ -229,6 +230,9 @@ mlib_status mlib_convMxNext_s32(mlib_image *dst, /* internal buffer */ + if (!SAFE_TO_MULT(3, wid_e) || !SAFE_TO_ADD(3 * wid_e, m)) { + return MLIB_FAILURE; + } if (3 * wid_e + m > 1024) { dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64)); diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c index 57486b1cae5..00469d25719 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16ext.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* * This define switches between functions of different data types @@ -260,8 +261,14 @@ static mlib_status mlib_ImageConv1xN_ext(mlib_image *dst, if (max_hsize > hgt) max_hsize = hgt; shgt = hgt + (n - 1); + if (!SAFE_TO_ADD(max_hsize, (n - 1))) { + return MLIB_FAILURE; + } smax_hsize = max_hsize + (n - 1); + if (!SAFE_TO_ADD(smax_hsize, 1) || !SAFE_TO_MULT(2, (smax_hsize + 1))) { + return MLIB_FAILURE; + } bsize = 2 * (smax_hsize + 1); if (bsize > BUFF_SIZE) { @@ -509,8 +516,16 @@ mlib_status CONV_FUNC_MxN FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_ADD(wid, (m - 1))) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 3), swid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { @@ -919,8 +934,14 @@ mlib_status CONV_FUNC_MxN_I chan1 = nchannel; chan2 = chan1 + chan1; + if (!SAFE_TO_ADD(wid, (m - 1))) { + return MLIB_FAILURE; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 2), swid)) { + return MLIB_FAILURE; + } bsize = (n + 2)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16nw.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16nw.c index 3b6985b7876..2e035d12453 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16nw.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_16nw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ #include "mlib_image.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* This define switches between functions of different data types @@ -466,6 +467,10 @@ mlib_status CONV_FUNC(MxN)(mlib_image *dst, FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_MULT((n + 3), wid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*wid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_32nw.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_32nw.c index 380ed044878..bb264d9dcd2 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_32nw.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_32nw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" +#include "safe_math.h" /***************************************************************/ #define CACHE_SIZE (64*1024) @@ -335,6 +336,10 @@ mlib_status CONV_FUNC(MxN)(mlib_image *dst, FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_MULT((n + 2), wid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 2)*wid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c index c8b58e6f138..136d5a2b814 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8ext.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* * This define switches between functions of different data types @@ -245,8 +246,14 @@ static mlib_status mlib_ImageConv1xN_ext(mlib_image *dst, if (max_hsize > hgt) max_hsize = hgt; shgt = hgt + (n - 1); + if (!SAFE_TO_ADD(max_hsize, (n - 1))) { + return MLIB_FAILURE; + } smax_hsize = max_hsize + (n - 1); + if (!SAFE_TO_ADD(smax_hsize, 1) || !SAFE_TO_MULT(2, (smax_hsize + 1))) { + return MLIB_FAILURE; + } bsize = 2 * (smax_hsize + 1); if (bsize > BUFF_SIZE) { @@ -494,8 +501,16 @@ mlib_status CONV_FUNC_MxN FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_ADD(wid, (m - 1))) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 3), swid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { @@ -904,8 +919,14 @@ mlib_status CONV_FUNC_MxN_I chan1 = nchannel; chan2 = chan1 + chan1; + if (!SAFE_TO_ADD(wid, (m - 1))) { + return MLIB_FAILURE; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 2), swid)) { + return MLIB_FAILURE; + } bsize = (n + 2)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8nw.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8nw.c index f65fda45c58..c144404b0f4 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8nw.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_8nw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* This define switches between functions of different data types @@ -467,6 +468,10 @@ mlib_status CONV_FUNC(MxN)(mlib_image *dst, FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_MULT((n + 3), wid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*wid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c index b2757979a84..81a06f2fc28 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16ext.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ #include "mlib_image.h" #include "mlib_ImageConv.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* * This define switches between functions of different data types @@ -270,8 +271,14 @@ static mlib_status mlib_ImageConv1xN_ext(mlib_image *dst, if (max_hsize > hgt) max_hsize = hgt; shgt = hgt + (n - 1); + if (!SAFE_TO_ADD(max_hsize, (n - 1))) { + return MLIB_FAILURE; + } smax_hsize = max_hsize + (n - 1); + if (!SAFE_TO_ADD(smax_hsize, 1) || !SAFE_TO_MULT(2, (smax_hsize + 1))) { + return MLIB_FAILURE; + } bsize = 2 * (smax_hsize + 1); if (bsize > BUFF_SIZE) { @@ -519,8 +526,16 @@ mlib_status CONV_FUNC_MxN FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_ADD(wid, (m - 1))) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 3), swid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { @@ -927,8 +942,14 @@ mlib_status CONV_FUNC_MxN_I chan1 = nchannel; chan2 = chan1 + chan1; + if (!SAFE_TO_ADD(wid, (m - 1))) { + return MLIB_FAILURE; + } swid = wid + (m - 1); + if (!SAFE_TO_MULT((n + 2), swid)) { + return MLIB_FAILURE; + } bsize = (n + 2)*swid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16nw.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16nw.c index a3234cf8959..49412c7d7ef 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16nw.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageConv_u16nw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ #include "mlib_image.h" #include "mlib_c_ImageConv.h" +#include "safe_math.h" /* This define switches between functions of different data types @@ -466,6 +467,10 @@ mlib_status CONV_FUNC(MxN)(mlib_image *dst, FREE_AND_RETURN_STATUS; } + if (!SAFE_TO_MULT((n + 3), wid)) { + status = MLIB_FAILURE; + FREE_AND_RETURN_STATUS; + } bsize = (n + 3)*wid; if ((bsize > BUFF_SIZE) || (n > MAX_N)) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageLookUp_Bit.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageLookUp_Bit.c index 2e77c20aa57..cfd5e3e671e 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageLookUp_Bit.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageLookUp_Bit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,6 +50,7 @@ #include "mlib_image.h" #include "mlib_ImageLookUp.h" +#include "safe_math.h" /***************************************************************/ #define MAX_WIDTH 512 @@ -302,6 +303,9 @@ mlib_status mlib_ImageLookUp_Bit_U8_2(const mlib_u8 *src, mlib_u8 *buff = (mlib_u8*)buff_lcl, *buffs; mlib_u32 val0, val1; + if (!SAFE_TO_MULT(xsize, 2)) { + return MLIB_FAILURE; + } size = xsize * 2; if (size > MAX_WIDTH) { @@ -440,6 +444,9 @@ mlib_status mlib_ImageLookUp_Bit_U8_3(const mlib_u8 *src, mlib_u8 *buff = (mlib_u8*)buff_lcl, *buffs; mlib_u32 l0, h0, v0, l1, h1, v1, l2, h2, v2; + if (!SAFE_TO_MULT(3, xsize)) { + return MLIB_FAILURE; + } size = 3 * xsize; if (size > MAX_WIDTH) { @@ -583,6 +590,9 @@ mlib_status mlib_ImageLookUp_Bit_U8_4(const mlib_u8 *src, mlib_u8 *buff = (mlib_u8*)buff_lcl, *buffs; mlib_u32 l, h; + if (!SAFE_TO_MULT(xsize, 4)) { + return MLIB_FAILURE; + } size = xsize * 4; if (size > MAX_WIDTH) { diff --git a/src/java.desktop/share/native/libmlib_image/mlib_ImageScanPoly.c b/src/java.desktop/share/native/libmlib_image/mlib_ImageScanPoly.c index a6f4cfdd36e..72adc212af6 100644 --- a/src/java.desktop/share/native/libmlib_image/mlib_ImageScanPoly.c +++ b/src/java.desktop/share/native/libmlib_image/mlib_ImageScanPoly.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -101,6 +101,11 @@ mlib_status mlib_AffineEdges(mlib_affine_param *param, return MLIB_FAILURE; } + int intSize = sizeof(mlib_s32); + if (!SAFE_TO_MULT(dstHeight, intSize) || + !SAFE_TO_ADD(dstHeight * intSize, 7)) { + return MLIB_FAILURE; + } bsize0 = (dstHeight * sizeof(mlib_s32) + 7) & ~7; if (lineAddr == NULL) { @@ -109,6 +114,10 @@ mlib_status mlib_AffineEdges(mlib_affine_param *param, param->buff_malloc = NULL; + if (!SAFE_TO_MULT(4, bsize0) || !SAFE_TO_ADD(4 * bsize0, bsize1)) { + return MLIB_FAILURE; + } + if ((4 * bsize0 + bsize1) > buff_size) { buff = param->buff_malloc = mlib_malloc(4 * bsize0 + bsize1); From 97bd4458416dffd901ad07be028a08b3d6dc4881 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Tue, 26 Aug 2025 03:07:27 +0000 Subject: [PATCH 141/204] 8365271: Improve Swing supports Reviewed-by: tr, prr, rhalade, aivanov --- .../classes/javax/swing/plaf/basic/BasicOptionPaneUI.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java index a6d2a448186..8ab31b1a2ad 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -466,12 +466,12 @@ public class BasicOptionPaneUI extends OptionPaneUI { str = s.substring(index2 + "".length()); s = s.substring(index1, index2 + + "".length()); } - JLabel label; - label = new JLabel(s, JLabel.LEADING); + JLabel label = new JLabel(); if (Boolean.TRUE.equals( this.optionPane.getClientProperty("html.disable"))) { label.putClientProperty("html.disable", true); } + label.setText(s); label.setName("OptionPane.label"); configureMessageLabel(label); addMessageComponents(container, cons, label, maxll, true); From dc46a17f1e569e2ae6857eaed4b1365b6cab02e1 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Wed, 3 Sep 2025 17:23:16 +0000 Subject: [PATCH 142/204] 8365058: Enhance CopyOnWriteArraySet Reviewed-by: rhalade, skoivu, vklang, rriggs --- .../util/concurrent/CopyOnWriteArraySet.java | 41 +++++++++ .../SerializationTest.java | 84 +++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 test/jdk/java/util/concurrent/CopyOnWriteArraySet/SerializationTest.java diff --git a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java index cef1682b0b1..abc9fdb348c 100644 --- a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java +++ b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java @@ -35,6 +35,13 @@ package java.util.concurrent; +import jdk.internal.misc.Unsafe; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectStreamException; +import java.io.Serial; +import java.io.StreamCorruptedException; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; @@ -445,4 +452,38 @@ public class CopyOnWriteArraySet extends AbstractSet return Spliterators.spliterator (al.getArray(), Spliterator.IMMUTABLE | Spliterator.DISTINCT); } + + /** + * De-serialization without data not supported for this class. + */ + @Serial + private void readObjectNoData() throws ObjectStreamException { + throw new StreamCorruptedException("Deserialized CopyOnWriteArraySet requires data"); + } + + /** + * Reconstitutes the {@code CopyOnWriteArraySet} instance from a stream + * (that is, deserializes it). + * @throws StreamCorruptedException if the object read from the stream is invalid. + */ + @Serial + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + CopyOnWriteArrayList newAl; // Set during the duplicate check + + @SuppressWarnings("unchecked") + CopyOnWriteArrayList inAl = (CopyOnWriteArrayList) in.readFields().get("al", null); + + if (inAl == null + || inAl.getClass() != CopyOnWriteArrayList.class + || (newAl = new CopyOnWriteArrayList<>()).addAllAbsent(inAl) != inAl.size()) { + throw new StreamCorruptedException("Content is invalid"); + } + + final Unsafe U = Unsafe.getUnsafe(); + U.putReference( + this, + U.objectFieldOffset(CopyOnWriteArraySet.class, "al"), + newAl + ); + } } diff --git a/test/jdk/java/util/concurrent/CopyOnWriteArraySet/SerializationTest.java b/test/jdk/java/util/concurrent/CopyOnWriteArraySet/SerializationTest.java new file mode 100644 index 00000000000..7700eda6cd6 --- /dev/null +++ b/test/jdk/java/util/concurrent/CopyOnWriteArraySet/SerializationTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8365058 + * @summary Check basic correctness of de-serialization + * @run junit SerializationTest + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SerializationTest { + + // Ensure basic serialization round trip correctness + @ParameterizedTest + @MethodSource + void roundTripTest(CopyOnWriteArraySet expected) { + var bytes = ser(expected); + var actual = deSer(bytes); + assertEquals(CopyOnWriteArraySet.class, actual.getClass()); + assertEquals(expected, actual); + } + + private static Stream> roundTripTest() { + return Stream.of( + new CopyOnWriteArraySet<>(), + new CopyOnWriteArraySet<>(List.of(1, 2, 3)), + new CopyOnWriteArraySet<>(Set.of("Foo", "Bar", "Baz")) + ); + } + + private static byte[] ser(Object obj) { + return assertDoesNotThrow(() -> { + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream)) { + oos.writeObject(obj); + return byteArrayOutputStream.toByteArray(); + } + }, "Unexpected error during serialization"); + } + + private static Object deSer(byte[] bytes) { + return assertDoesNotThrow(() -> { + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream)) { + return ois.readObject(); + } + }, "Unexpected error during de-serialization"); + } +} From 3afb831ae45182e4219decacc355fae100a41b05 Mon Sep 17 00:00:00 2001 From: Stuart Marks Date: Thu, 4 Sep 2025 18:11:37 +0000 Subject: [PATCH 143/204] 8341496: Improve JMX connections Co-authored-by: Daniel Fuchs Reviewed-by: skoivu, rhalade, coffeys, dfuchs, kevinw, jnimeh --- .../rmi/ssl/SslRMIClientSocketFactory.java | 13 +++++++++- .../management/security/SecurityTest.java | 2 ++ .../rmi/ssl/SSLSocketParametersTest.java | 1 + .../bootstrap/JMXInterfaceBindingTest.java | 24 +++++++++++++++++++ .../jmxremote/bootstrap/RmiBootstrapTest.java | 3 ++- .../bootstrap/RmiRegistrySslTest.java | 1 + 6 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/java.rmi/share/classes/javax/rmi/ssl/SslRMIClientSocketFactory.java b/src/java.rmi/share/classes/javax/rmi/ssl/SslRMIClientSocketFactory.java index ab6664b9d8f..a4475c8bd1e 100644 --- a/src/java.rmi/share/classes/javax/rmi/ssl/SslRMIClientSocketFactory.java +++ b/src/java.rmi/share/classes/javax/rmi/ssl/SslRMIClientSocketFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ import java.net.Socket; import java.rmi.server.RMIClientSocketFactory; import java.util.StringTokenizer; import javax.net.SocketFactory; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; @@ -119,6 +120,16 @@ public class SslRMIClientSocketFactory // final SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(host, port); + + if (Boolean.parseBoolean( + System.getProperty("jdk.rmi.ssl.client.enableEndpointIdentification", "true"))) { + SSLParameters params = sslSocket.getSSLParameters(); + if (params == null) { + params = new SSLParameters(); + } + params.setEndpointIdentificationAlgorithm("HTTPS"); + sslSocket.setSSLParameters(params); + } // Set the SSLSocket Enabled Cipher Suites // final String enabledCipherSuites = diff --git a/test/jdk/javax/management/security/SecurityTest.java b/test/jdk/javax/management/security/SecurityTest.java index 7212aea883f..b46bbfa759f 100644 --- a/test/jdk/javax/management/security/SecurityTest.java +++ b/test/jdk/javax/management/security/SecurityTest.java @@ -402,6 +402,8 @@ public class SecurityTest { opts.add(JDKToolFinder.getJDKTool("java")); opts.addAll(Arrays.asList(jdk.test.lib.Utils.getTestJavaOpts())); + opts.add("-Djdk.rmi.ssl.client.enableEndpointIdentification=false"); + // We need to forward some properties to the client side opts.add("-Dtest.src=" + System.getProperty("test.src")); diff --git a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java index 3aa7a98c394..7616b112e39 100644 --- a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java +++ b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java @@ -137,6 +137,7 @@ public class SSLSocketParametersTest extends SSLContextTemplate { } public static void main(String[] args) throws Exception { + System.setProperty("jdk.rmi.ssl.client.enableEndpointIdentification", "false"); SSLSocketParametersTest test = new SSLSocketParametersTest(); test.runTest(Integer.parseInt(args[0])); } diff --git a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java index 61359084297..c331cb9050c 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java @@ -21,6 +21,29 @@ * questions. */ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + import java.io.File; import java.io.PrintWriter; import java.net.InetAddress; @@ -205,6 +228,7 @@ public class JMXInterfaceBindingTest { // This is needed for testing on loopback args.add("-Djava.rmi.server.hostname=" + address); if (useSSL) { + args.add("-Djdk.rmi.ssl.client.enableEndpointIdentification=false"); args.add("-Dcom.sun.management.jmxremote.registry.ssl=true"); args.add("-Djavax.net.ssl.keyStore=" + KEYSTORE_LOC); args.add("-Djavax.net.ssl.trustStore=" + TRUSTSTORE_LOC); diff --git a/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java b/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java index 2a99c5e7d52..b62d3f1bbd7 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/RmiBootstrapTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -169,6 +169,7 @@ public class RmiBootstrapTest extends RmiTestBase { final List credentialFiles = prepareTestFiles(args[0]); Security.setProperty("jdk.tls.disabledAlgorithms", ""); + System.setProperty("jdk.rmi.ssl.client.enableEndpointIdentification", "false"); try { MAX_GET_FREE_PORT_TRIES = Integer.parseInt(System.getProperty("test.getfreeport.max.tries", "10")); diff --git a/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java b/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java index 1aa20937962..b4ef5e224f8 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/RmiRegistrySslTest.java @@ -179,6 +179,7 @@ public class RmiRegistrySslTest { initTestEnvironment(); List command = new ArrayList<>(); + command.add("-Djdk.rmi.ssl.client.enableEndpointIdentification=false"); command.add("-Dtest.src=" + TEST_SRC); command.add("-Dtest.rmi.port=" + port); command.addAll(Arrays.asList(args)); From 84ee4f976b1580944bd77bdbd8ccd23569bce3ac Mon Sep 17 00:00:00 2001 From: Renjith Kannath Pariyangad Date: Wed, 10 Sep 2025 11:56:45 +0000 Subject: [PATCH 144/204] 8366446: Test java/awt/geom/ConcurrentDrawPolygonTest.java fails intermittently Reviewed-by: jdv, aivanov, prr, rhalade --- .../share/classes/sun/java2d/SunGraphics2D.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java index e92c485a363..9815d657eee 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphics2D.java @@ -1898,14 +1898,16 @@ public final class SunGraphics2D protected void validateCompClip() { int origClipState = clipState; - if (usrClip == null) { + final Shape clip = usrClip; + + if (clip == null) { clipState = CLIP_DEVICE; clipRegion = devClip; - } else if (usrClip instanceof Rectangle2D clip) { + } else if (clip instanceof Rectangle2D rect2d) { clipState = CLIP_RECTANGULAR; - clipRegion = devClip.getIntersection(clip); + clipRegion = devClip.getIntersection(rect2d); } else { - PathIterator cpi = usrClip.getPathIterator(null); + PathIterator cpi = clip.getPathIterator(null); int[] box = new int[4]; ShapeSpanIterator sr = LoopPipe.getFillSSI(this); try { From 7e3e35abef13ddf38d4268e1269c1d18566149ab Mon Sep 17 00:00:00 2001 From: Stuart Marks Date: Wed, 10 Sep 2025 16:40:58 +0000 Subject: [PATCH 145/204] 8367277: Fix copyright header in JMXInterfaceBindingTest.java Reviewed-by: dfuchs, rhalade, iris, coffeys --- .../bootstrap/JMXInterfaceBindingTest.java | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java index c331cb9050c..1f4707fab3b 100644 --- a/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java +++ b/test/jdk/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java @@ -1,27 +1,5 @@ /* * Copyright (c) 2015, Red Hat Inc - * 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. - */ - -/* * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * From f24fadc6240e2dcb5bcd732c91ccc03d1aa19e8a Mon Sep 17 00:00:00 2001 From: Michael McMahon Date: Mon, 15 Sep 2025 13:31:30 +0000 Subject: [PATCH 146/204] 8362632: Improve HttpServer Request handling Reviewed-by: djelinski, dfuchs --- .../com/sun/net/httpserver/Headers.java | 37 +++++-------------- .../sun/net/httpserver/ExchangeImpl.java | 19 +++++++--- .../classes/sun/net/httpserver/Utils.java | 33 +++++++++++++++++ 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java index 2decd48c806..9389aae8691 100644 --- a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java +++ b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/Headers.java @@ -36,6 +36,7 @@ import java.util.Set; import java.util.function.BiFunction; import java.util.stream.Collectors; import sun.net.httpserver.UnmodifiableHeaders; +import sun.net.httpserver.Utils; /** * HTTP request and response headers are represented by this class which @@ -216,8 +217,13 @@ public class Headers implements Map> { @Override public List put(String key, List value) { + // checkHeader is called in this class to fail fast + // It also must be called in sendResponseHeaders because + // Headers instances internal state can be modified + // external to these methods. + Utils.checkHeader(key, false); for (String v : value) - checkValue(v); + Utils.checkHeader(v, true); return map.put(normalize(key), value); } @@ -229,7 +235,8 @@ public class Headers implements Map> { * @param value the value to add to the header */ public void add(String key, String value) { - checkValue(value); + Utils.checkHeader(key, false); + Utils.checkHeader(value, true); String k = normalize(key); List l = map.get(k); if (l == null) { @@ -239,30 +246,6 @@ public class Headers implements Map> { l.add(value); } - private static void checkValue(String value) { - int len = value.length(); - for (int i=0; i= len - 2) { - throw new IllegalArgumentException("Illegal CR found in header"); - } - char c1 = value.charAt(i+1); - char c2 = value.charAt(i+2); - if (c1 != '\n') { - throw new IllegalArgumentException("Illegal char found after CR in header"); - } - if (c2 != ' ' && c2 != '\t') { - throw new IllegalArgumentException("No whitespace found after CRLF in header"); - } - i+=2; - } else if (c == '\n') { - throw new IllegalArgumentException("Illegal LF found in header"); - } - } - } - /** * Sets the given {@code value} as the sole header value for the given * {@code key}. If the mapping does not already exist, then it is created. @@ -304,7 +287,7 @@ public class Headers implements Map> { public void replaceAll(BiFunction, ? extends List> function) { var f = function.andThen(values -> { Objects.requireNonNull(values); - values.forEach(Headers::checkValue); + values.forEach(value -> Utils.checkHeader(value, true)); return values; }); Map.super.replaceAll(f); diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java index 8da98cdcfa5..ad6805938a2 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ExchangeImpl.java @@ -207,6 +207,8 @@ class ExchangeImpl { return uos_orig; } + private static final byte[] CRLF = new byte[] {0x0D, 0x0A}; + public void sendResponseHeaders(int rCode, long contentLen) throws IOException { @@ -215,10 +217,11 @@ class ExchangeImpl { throw new IOException("headers already sent"); } this.rcode = rCode; - String statusLine = "HTTP/1.1 " + rCode + Code.msg(rCode) + "\r\n"; + String statusLine = "HTTP/1.1 " + rCode + Code.msg(rCode); ByteArrayOutputStream tmpout = new ByteArrayOutputStream(); PlaceholderOutputStream o = getPlaceholderResponseBody(); - tmpout.write(bytes(statusLine, 0), 0, statusLine.length()); + tmpout.write(bytes(statusLine, false, 0), 0, statusLine.length()); + tmpout.write(CRLF); boolean noContentToSend = false; // assume there is content boolean noContentLengthHeader = false; // must not send Content-length is set rspHdrs.set("Date", FORMATTER.format(Instant.now())); @@ -305,11 +308,11 @@ class ExchangeImpl { List values = entry.getValue(); for (String val : values) { int i = key.length(); - buf = bytes(key, 2); + buf = bytes(key, true, 2); buf[i++] = ':'; buf[i++] = ' '; os.write(buf, 0, i); - buf = bytes(val, 2); + buf = bytes(val, false, 2); i = val.length(); buf[i++] = '\r'; buf[i++] = '\n'; @@ -327,8 +330,14 @@ class ExchangeImpl { * Make sure that at least "extra" bytes are free at end * of rspbuf. Reallocate rspbuf if not big enough. * caller must check return value to see if rspbuf moved + * + * Header values are supposed to be limited to 7-bit ASCII + * but 8-bit has to be allowed (for ISO_8859_1). For efficiency + * we just down cast 16 bit Java chars to byte. We don't allow + * any character that can't be encoded in 8 bits. */ - private byte[] bytes(String s, int extra) { + private byte[] bytes(String s, boolean isKey, int extra) throws IOException { + Utils.checkHeader(s, !isKey); int slen = s.length(); if (slen+extra > rspbuf.length) { int diff = slen + extra - rspbuf.length; diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/Utils.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/Utils.java index 43dadb84a90..41834172f27 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/Utils.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/Utils.java @@ -88,4 +88,37 @@ public class Utils { } return true; } + + /* Throw IAE if illegal character found. isValue is true if String is + * a value. Otherwise it is header name + */ + public static void checkHeader(String str, boolean isValue) { + int len = str.length(); + for (int i=0; i= len - 2) { + throw new IllegalArgumentException("Illegal CR found in header"); + } + char c1 = str.charAt(i+1); + char c2 = str.charAt(i+2); + if (c1 != '\n') { + throw new IllegalArgumentException("Illegal char found after CR in header"); + } + if (c2 != ' ' && c2 != '\t') { + throw new IllegalArgumentException("No whitespace found after CRLF in header"); + } + i+=2; + } else if (c == '\n') { + throw new IllegalArgumentException("Illegal LF found in header"); + } else if (c > 255) { + throw new IllegalArgumentException("Illegal character found in header"); + } + } + } + } From eddbd359654cf6e2a437367461231ba37ee76918 Mon Sep 17 00:00:00 2001 From: Harshitha Onkar Date: Wed, 24 Sep 2025 18:05:45 +0000 Subject: [PATCH 147/204] 8359501: Enhance Handling of URIs Reviewed-by: rhalade, ahgross, azvegint, prr --- .../sun/lwawt/macosx/CDesktopPeer.java | 57 ++++++-- .../native/libawt_lwawt/awt/CDesktopPeer.m | 124 ++++++++++++++---- .../classes/sun/awt/windows/WDesktopPeer.java | 53 +++++++- .../native/libawt/windows/awt_Desktop.cpp | 49 ++++++- test/jdk/java/awt/Desktop/BrowseTest.java | 26 +++- .../EditAndPrintTest/EditAndPrintTest.java | 2 +- 6 files changed, 258 insertions(+), 53 deletions(-) diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java index a4ec0767298..cc0e253f23b 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDesktopPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,10 @@ import java.awt.peer.DesktopPeer; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.lang.annotation.Native; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; /** @@ -44,6 +47,12 @@ import java.net.URI; */ public final class CDesktopPeer implements DesktopPeer { + @Native private static final int OPEN = 0; + @Native private static final int BROWSE = 1; + @Native private static final int EDIT = 2; + @Native private static final int PRINT = 3; + @Native private static final int MAIL = 4; + @Override public boolean isSupported(Action action) { return true; @@ -51,27 +60,27 @@ public final class CDesktopPeer implements DesktopPeer { @Override public void open(File file) throws IOException { - this.lsOpenFile(file, false); + this.lsOpenFile(file, OPEN); } @Override public void edit(File file) throws IOException { - this.lsOpenFile(file, false); + this.lsOpenFile(file, EDIT); } @Override public void print(File file) throws IOException { - this.lsOpenFile(file, true); + this.lsOpenFile(file, PRINT); } @Override public void mail(URI uri) throws IOException { - this.lsOpen(uri); + this.lsOpen(uri, MAIL); } @Override public void browse(URI uri) throws IOException { - this.lsOpen(uri); + this.lsOpen(uri, BROWSE); } @Override @@ -162,24 +171,44 @@ public final class CDesktopPeer implements DesktopPeer { } } - private void lsOpen(URI uri) throws IOException { - int status = _lsOpenURI(uri.toString()); + private void lsOpen(URI uri, int action) throws IOException { + int status = _lsOpenURI(uri.toString(), action); if (status != 0 /* noErr */) { - throw new IOException("Failed to mail or browse " + uri + ". Error code: " + status); + String actionString = (action == MAIL) ? "mail" : "browse"; + throw new IOException("Failed to " + actionString + " " + uri + + ". Error code: " + status); } } - private void lsOpenFile(File file, boolean print) throws IOException { - int status = _lsOpenFile(file.getCanonicalPath(), print); + private void lsOpenFile(File file, int action) throws IOException { + int status = -1; + Path tmpFile = null; + String tmpTxtPath = null; + try { + if (action == EDIT) { + tmpFile = Files.createTempFile("TmpFile", ".txt"); + tmpTxtPath = tmpFile.toAbsolutePath().toString(); + } + status = _lsOpenFile(file.getCanonicalPath(), action, tmpTxtPath); + } catch (Exception e) { + throw new IOException("Failed to create tmp file: ", e); + } finally { + if (tmpFile != null) { + Files.deleteIfExists(tmpFile); + } + } if (status != 0 /* noErr */) { - throw new IOException("Failed to open, edit or print " + file + ". Error code: " + status); + String actionString = (action == OPEN) ? "open" + : (action == EDIT) ? "edit" : "print"; + throw new IOException("Failed to " + actionString + " " + file + + ". Error code: " + status); } } - private static native int _lsOpenURI(String uri); + private static native int _lsOpenURI(String uri, int action); - private static native int _lsOpenFile(String path, boolean print); + private static native int _lsOpenFile(String path, int action, String tmpTxtPath); } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m index 7555c7990c4..e1841c9398c 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDesktopPeer.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,27 +27,60 @@ #import "JNIUtilities.h" #import #import +#import "sun_lwawt_macosx_CDesktopPeer.h" /* * Class: sun_lwawt_macosx_CDesktopPeer * Method: _lsOpenURI - * Signature: (Ljava/lang/String;)I; + * Signature: (Ljava/lang/String;I)I */ JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_CDesktopPeer__1lsOpenURI -(JNIEnv *env, jclass clz, jstring uri) +(JNIEnv *env, jclass clz, jstring uri, jint action) { - OSStatus status = noErr; + __block OSStatus status = noErr; JNI_COCOA_ENTER(env); - // I would love to use NSWorkspace here, but it's not thread safe. Why? I don't know. - // So we use LaunchServices directly. + NSURL *urlToOpen = [NSURL URLWithString:JavaStringToNSString(env, uri)]; + NSURL *appURI = nil; - NSURL *url = [NSURL URLWithString:JavaStringToNSString(env, uri)]; + if (action == sun_lwawt_macosx_CDesktopPeer_BROWSE) { + // To get the defaultBrowser + NSURL *httpsURL = [NSURL URLWithString:@"https://"]; + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + appURI = [workspace URLForApplicationToOpenURL:httpsURL]; + } else if (action == sun_lwawt_macosx_CDesktopPeer_MAIL) { + // To get the default mailer + NSURL *mailtoURL = [NSURL URLWithString:@"mailto://"]; + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + appURI = [workspace URLForApplicationToOpenURL:mailtoURL]; + } - LSLaunchFlags flags = kLSLaunchDefaults; + if (appURI == nil) { + return -1; + } - LSApplicationParameters params = {0, flags, NULL, NULL, NULL, NULL, NULL}; - status = LSOpenURLsWithRole((CFArrayRef)[NSArray arrayWithObject:url], kLSRolesAll, NULL, ¶ms, NULL, 0); + // Prepare NSOpenConfig object + NSArray *urls = @[urlToOpen]; + NSWorkspaceOpenConfiguration *configuration = [NSWorkspaceOpenConfiguration configuration]; + configuration.activates = YES; // To bring app to foreground + configuration.promptsUserIfNeeded = YES; // To allow macOS desktop prompts + + // dispatch semaphores used to wait for the completion handler to update and return status + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout + + // Asynchronous call to openURL + [[NSWorkspace sharedWorkspace] openURLs:urls + withApplicationAtURL:appURI + configuration:configuration + completionHandler:^(NSRunningApplication *app, NSError *error) { + if (error) { + status = (OSStatus) error.code; + } + dispatch_semaphore_signal(semaphore); + }]; + + dispatch_semaphore_wait(semaphore, timeout); JNI_COCOA_EXIT(env); return status; @@ -56,32 +89,73 @@ JNI_COCOA_EXIT(env); /* * Class: sun_lwawt_macosx_CDesktopPeer * Method: _lsOpenFile - * Signature: (Ljava/lang/String;Z)I; + * Signature: (Ljava/lang/String;I;Ljava/lang/String;)I; */ JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_CDesktopPeer__1lsOpenFile -(JNIEnv *env, jclass clz, jstring jpath, jboolean print) +(JNIEnv *env, jclass clz, jstring jpath, jint action, jstring jtmpTxtPath) { - OSStatus status = noErr; + __block OSStatus status = noErr; JNI_COCOA_ENTER(env); - // I would love to use NSWorkspace here, but it's not thread safe. Why? I don't know. - // So we use LaunchServices directly. - NSString *path = NormalizedPathNSStringFromJavaString(env, jpath); - - NSURL *url = [NSURL fileURLWithPath:(NSString *)path]; + NSURL *urlToOpen = [NSURL fileURLWithPath:(NSString *)path]; // This byzantine workaround is necessary, or else directories won't open in Finder - url = (NSURL *)CFURLCreateWithFileSystemPath(NULL, (CFStringRef)[url path], kCFURLPOSIXPathStyle, false); + urlToOpen = (NSURL *)CFURLCreateWithFileSystemPath(NULL, (CFStringRef)[urlToOpen path], + kCFURLPOSIXPathStyle, false); - LSLaunchFlags flags = kLSLaunchDefaults; - if (print) flags |= kLSLaunchAndPrint; + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + NSURL *appURI = [workspace URLForApplicationToOpenURL:urlToOpen]; + NSURL *defaultTerminalApp = [workspace URLForApplicationToOpenURL:[NSURL URLWithString:@"file:///bin/sh"]]; - LSApplicationParameters params = {0, flags, NULL, NULL, NULL, NULL, NULL}; - status = LSOpenURLsWithRole((CFArrayRef)[NSArray arrayWithObject:url], kLSRolesAll, NULL, ¶ms, NULL, 0); - [url release]; + // Prepare NSOpenConfig object + NSArray *urls = @[urlToOpen]; + NSWorkspaceOpenConfiguration *configuration = [NSWorkspaceOpenConfiguration configuration]; + configuration.activates = YES; // To bring app to foreground + configuration.promptsUserIfNeeded = YES; // To allow macOS desktop prompts + + // pre-checks for open/print/edit before calling openURLs API + if (action == sun_lwawt_macosx_CDesktopPeer_OPEN + || action == sun_lwawt_macosx_CDesktopPeer_PRINT) { + if (appURI == nil + || [[urlToOpen absoluteString] containsString:[appURI absoluteString]] + || [[defaultTerminalApp absoluteString] containsString:[appURI absoluteString]]) { + return -1; + } + // Additionally set forPrinting=TRUE for print + if (action == sun_lwawt_macosx_CDesktopPeer_PRINT) { + configuration.forPrinting = YES; + } + } else if (action == sun_lwawt_macosx_CDesktopPeer_EDIT) { + if (appURI == nil + || [[urlToOpen absoluteString] containsString:[appURI absoluteString]]) { + return -1; + } + // for EDIT: if (defaultApp = TerminalApp) then set appURI = DefaultTextEditor + if ([[defaultTerminalApp absoluteString] containsString:[appURI absoluteString]]) { + NSString *path = NormalizedPathNSStringFromJavaString(env, jtmpTxtPath); + NSURL *tempFilePath = [NSURL fileURLWithPath:(NSString *)path]; + appURI = [workspace URLForApplicationToOpenURL:tempFilePath]; + } + } + + // dispatch semaphores used to wait for the completion handler to update and return status + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout + + // Asynchronous call - openURLs:withApplicationAtURL + [[NSWorkspace sharedWorkspace] openURLs:urls + withApplicationAtURL:appURI + configuration:configuration + completionHandler:^(NSRunningApplication *app, NSError *error) { + if (error) { + status = (OSStatus) error.code; + } + dispatch_semaphore_signal(semaphore); + }]; + + dispatch_semaphore_wait(semaphore, timeout); JNI_COCOA_EXIT(env); return status; } - diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java index 788c1477265..e5b628dd74b 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WDesktopPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,9 @@ import java.io.File; import java.io.IOException; import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import javax.swing.event.EventListenerList; import sun.awt.shell.ShellFolder; @@ -50,9 +53,11 @@ import sun.awt.shell.ShellFolder; */ final class WDesktopPeer implements DesktopPeer { /* Constants for the operation verbs */ - private static String ACTION_OPEN_VERB = "open"; - private static String ACTION_EDIT_VERB = "edit"; - private static String ACTION_PRINT_VERB = "print"; + private static final String ACTION_OPEN_VERB = "open"; + private static final String ACTION_EDIT_VERB = "edit"; + private static final String ACTION_PRINT_VERB = "print"; + private static final String ACTION_BROWSE_VERB = "browse"; + private static final String ACTION_MAIL_VERB = "mail"; private static native void init(); @@ -95,12 +100,12 @@ final class WDesktopPeer implements DesktopPeer { @Override public void mail(URI uri) throws IOException { - this.ShellExecute(uri, ACTION_OPEN_VERB); + this.ShellExecute(uri, ACTION_MAIL_VERB); } @Override public void browse(URI uri) throws IOException { - this.ShellExecute(uri, ACTION_OPEN_VERB); + this.launchUriInBrowser(uri); } private void ShellExecute(File file, String verb) throws IOException { @@ -121,6 +126,42 @@ final class WDesktopPeer implements DesktopPeer { } } + private void launchUriInBrowser(URI uri) throws IOException { + String defaultBrowser = getDefaultBrowser(); + if (defaultBrowser == null) { + throw new IOException("Failed to get default browser"); + } + + List cmdLineTokens = getCmdLineTokens(uri, defaultBrowser); + try { + ProcessBuilder pb = new ProcessBuilder(cmdLineTokens); + pb.start(); + } catch (Exception e) { + throw new IOException("Error launching Browser: ", e); + } + } + + private static List getCmdLineTokens(URI uri, String defaultBrowser) { + if (defaultBrowser.contains("%1")) { + defaultBrowser = defaultBrowser.replace("%1", uri.toString()); + } else { + defaultBrowser = defaultBrowser + " " + uri.toString(); + } + + List cmdLineTokens = new ArrayList<>(); + int firstIndex = defaultBrowser.indexOf("\""); + int secondIndex = defaultBrowser.indexOf("\"", firstIndex + 1); + + if (firstIndex == 0 && secondIndex != firstIndex) { + cmdLineTokens.add(defaultBrowser.substring(firstIndex, secondIndex + 1)); + defaultBrowser = defaultBrowser.substring(secondIndex + 1).trim(); + } + cmdLineTokens.addAll(Arrays.asList(defaultBrowser.split(" "))); + return cmdLineTokens; + } + + private static native String getDefaultBrowser(); + private static native String ShellExecute(String fileOrUri, String verb); private static final EventListenerList listenerList = new EventListenerList(); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp index ba79523249c..ebb43b2f078 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Desktop.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,12 @@ #include #include #include "awt_Toolkit.h" +#include +#include // for AssocQueryStringW +#include +#include +#include +#include // for SaferiIsExecutableFileType #define BUFFER_LIMIT MAX_PATH+1 @@ -78,14 +84,23 @@ JNIEXPORT jstring JNICALL Java_sun_awt_windows_WDesktopPeer_ShellExecute LPCWSTR fileOrUri_c = JNU_GetStringPlatformChars(env, fileOrUri_j, NULL); CHECK_NULL_RETURN(fileOrUri_c, NULL); LPCWSTR verb_c = JNU_GetStringPlatformChars(env, verb_j, NULL); + if (verb_c == NULL) { JNU_ReleaseStringPlatformChars(env, fileOrUri_j, fileOrUri_c); return NULL; } + if (wcscmp(verb_c, L"open") == 0) { + BOOL isExecutable = SaferiIsExecutableFileType(fileOrUri_c, FALSE); + if (isExecutable) { + return env->NewStringUTF("Unsupported URI content"); + } + } + // set action verb for mail() to open before calling ShellExecute + LPCWSTR actionVerb = wcscmp(verb_c, L"mail") == 0 ? L"open" : verb_c; // 6457572: ShellExecute possibly changes FPU control word - saving it here unsigned oldcontrol87 = _control87(0, 0); - HINSTANCE retval = ::ShellExecute(NULL, verb_c, fileOrUri_c, NULL, NULL, + HINSTANCE retval = ::ShellExecute(NULL, actionVerb, fileOrUri_c, NULL, NULL, SW_SHOWNORMAL); DWORD error = ::GetLastError(); _control87(oldcontrol87, 0xffffffff); @@ -113,10 +128,38 @@ JNIEXPORT jstring JNICALL Java_sun_awt_windows_WDesktopPeer_ShellExecute return errmsg; } } - return NULL; } +/* + * Class: sun_awt_windows_WDesktopPeer + * Method: getDefaultBrowser + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_sun_awt_windows_WDesktopPeer_getDefaultBrowser +(JNIEnv *env, jclass cls) +{ + LPCWSTR fileExtension = L"https"; + WCHAR defaultBrowser_c [MAX_PATH]; + DWORD cchBuffer = MAX_PATH; + + // Use AssocQueryString to get the default browser + HRESULT hr = AssocQueryStringW( + ASSOCF_NONE, // No special flags + ASSOCSTR_COMMAND, // Request the command string + fileExtension, // File extension + NULL, // pszExtra (optional) + defaultBrowser_c, // Output buffer - result + &cchBuffer // Size of the output buffer + ); + + if (FAILED(hr)) { + return NULL; + } + + return JNU_NewStringPlatform(env, defaultBrowser_c); +} + /* * Class: sun_awt_windows_WDesktopPeer * Method: moveToTrash diff --git a/test/jdk/java/awt/Desktop/BrowseTest.java b/test/jdk/java/awt/Desktop/BrowseTest.java index 33de1ecdca7..28e08fe16c7 100644 --- a/test/jdk/java/awt/Desktop/BrowseTest.java +++ b/test/jdk/java/awt/Desktop/BrowseTest.java @@ -40,10 +40,27 @@ import jtreg.SkippedException; public class BrowseTest extends JPanel { static final String INSTRUCTIONS = """ - This test could launch default file manager to open user's home - directory, and default web browser to show the URL of java vendor. - After test execution close the native file manager and web browser + Set your default browser as per the test platform. + macOS - Safari + windows - MS Edge + linux - Firefox + + This test checks 2 cases: + + 1) Directory URI: + On macOS and windows, verify that a browser window opens and + EITHER the browser OR native file manager shows the user's + home directory. + + On Linux verify that the user's home directory is shown by the + default file manager. + + 2) Web URI: + Verify that the Web URI (URL of java vendor) opens in the browser. + + After test execution close the native file manager and any web browser windows if they were launched by test. + Also check output for any unexpected EXCEPTIONS, if you see any failure messages press Fail otherwise press Pass. """; @@ -53,7 +70,7 @@ public class BrowseTest extends JPanel { URI dirURI = new File(System.getProperty("user.home")).toURI(); URI webURI = URI.create(System.getProperty("java.vendor.url", "http://www.java.com")); - boolean failed = false; + PassFailJFrame.log("Testing 1st case: Directory URI ..."); try { PassFailJFrame.log("Try to browse " + dirURI + " ..."); desktop.browse(dirURI); @@ -62,6 +79,7 @@ public class BrowseTest extends JPanel { PassFailJFrame.log("EXCEPTION: " + e.getMessage()); } + PassFailJFrame.log("Testing 2nd case: Web URI ..."); try { PassFailJFrame.log("Try to browse " + webURI + " ..."); desktop.browse(webURI); diff --git a/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java b/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java index b2d7ef28df1..77d86ceb42a 100644 --- a/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java +++ b/test/jdk/java/awt/Desktop/EditAndPrintTest/EditAndPrintTest.java @@ -48,7 +48,7 @@ public class EditAndPrintTest extends JPanel { This test tries to edit and print a directory, which will expectedly raise IOException. Then this test would edit and print a .txt file, which should be successful. After test execution close the editor if it was launched by test. - If you see any EXCEPTION messages in the output press FAIL. + If you see any EXCEPTION messages in case of .txt file in the output press FAIL. """; static Desktop desktop; From 82e5771b0be205c2ef9500ffa750bf97da21823c Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Thu, 9 Oct 2025 04:40:38 +0000 Subject: [PATCH 148/204] 8365280: Enhance JOptionPane Reviewed-by: rhalade, prr, tr, aivanov --- .../swing/plaf/basic/BasicOptionPaneUI.java | 102 +++++++----------- .../swing/JOptionPane/TestJOptionHTMLTag.java | 68 ------------ 2 files changed, 39 insertions(+), 131 deletions(-) delete mode 100644 test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java index 8ab31b1a2ad..8c7ac94b04e 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -452,78 +452,54 @@ public class BasicOptionPaneUI extends OptionPaneUI { } else if ((nl = s.indexOf('\n')) >= 0) { nll = 1; } - if (s.contains("")) { - /* line break in html text is done by
      tag - * and not by /n so it's incorrect to address newline - * same as non-html text. - * Text between tags are extracted - * and rendered as JLabel text - */ - int index1 = s.indexOf(""); - int index2 = s.indexOf(""); - String str = ""; - if (index2 >= 0) { - str = s.substring(index2 + "".length()); - s = s.substring(index1, index2 + + "".length()); + if (nl >= 0) { + // break up newlines + if (nl == 0) { + JPanel breakPanel = new JPanel() { + public Dimension getPreferredSize() { + Font f = getFont(); + + if (f != null) { + return new Dimension(1, f.getSize() + 2); + } + return new Dimension(0, 0); + } + }; + breakPanel.setName("OptionPane.break"); + addMessageComponents(container, cons, breakPanel, maxll, + true); + } else { + addMessageComponents(container, cons, s.substring(0, nl), + maxll, false); } + // Prevent recursion of more than + // 200 successive newlines in a message + // and indicate message is truncated via ellipsis + if (recursionCount++ > 200) { + recursionCount = 0; + addMessageComponents(container, cons, new String("..."), + maxll, false); + return; + } + addMessageComponents(container, cons, s.substring(nl + nll), maxll, + false); + + } else if (len > maxll) { + Container c = Box.createVerticalBox(); + c.setName("OptionPane.verticalBox"); + burstStringInto(c, s, maxll); + addMessageComponents(container, cons, c, maxll, true); + + } else { JLabel label = new JLabel(); if (Boolean.TRUE.equals( - this.optionPane.getClientProperty("html.disable"))) { + optionPane.getClientProperty("html.disable"))) { label.putClientProperty("html.disable", true); } label.setText(s); label.setName("OptionPane.label"); configureMessageLabel(label); addMessageComponents(container, cons, label, maxll, true); - if (!str.isEmpty()) { - addMessageComponents(container, cons, str, maxll, false); - } - } else { - if (nl >= 0) { - // break up newlines - if (nl == 0) { - JPanel breakPanel = new JPanel() { - public Dimension getPreferredSize() { - Font f = getFont(); - - if (f != null) { - return new Dimension(1, f.getSize() + 2); - } - return new Dimension(0, 0); - } - }; - breakPanel.setName("OptionPane.break"); - addMessageComponents(container, cons, breakPanel, maxll, - true); - } else { - addMessageComponents(container, cons, s.substring(0, nl), - maxll, false); - } - // Prevent recursion of more than - // 200 successive newlines in a message - // and indicate message is truncated via ellipsis - if (recursionCount++ > 200) { - recursionCount = 0; - addMessageComponents(container, cons, new String("..."), - maxll, false); - return; - } - addMessageComponents(container, cons, s.substring(nl + nll), maxll, - false); - - } else if (len > maxll) { - Container c = Box.createVerticalBox(); - c.setName("OptionPane.verticalBox"); - burstStringInto(c, s, maxll); - addMessageComponents(container, cons, c, maxll, true); - - } else { - JLabel label; - label = new JLabel(s, JLabel.LEADING); - label.setName("OptionPane.label"); - configureMessageLabel(label); - addMessageComponents(container, cons, label, maxll, true); - } } } } diff --git a/test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java b/test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java deleted file mode 100644 index 94318492bd9..00000000000 --- a/test/jdk/javax/swing/JOptionPane/TestJOptionHTMLTag.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. - * 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 5074006 - * @key headful - * @library /java/awt/regtesthelpers - * @build PassFailJFrame - * @summary Swing JOptionPane shows tag as a string after newline - * @run main/manual TestJOptionHTMLTag -*/ - -import javax.swing.JDialog; -import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; - -public class TestJOptionHTMLTag { - static String instructions - = """ - INSTRUCTIONS: - A dialog will be shown. - If it does not contain string, press Pass else press Fail. - """; - static PassFailJFrame passFailJFrame; - - public static void main(String[] args) throws Exception { - - SwingUtilities.invokeAndWait(() -> { - try { - String message = "" + "This is a test\n" + ""; - JOptionPane optionPane = new JOptionPane(); - optionPane.setMessage(message); - optionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE); - JDialog dialog = new JDialog(); - dialog.setContentPane(optionPane); - dialog.pack(); - dialog.setVisible(true); - - passFailJFrame = new PassFailJFrame(instructions); - PassFailJFrame.addTestWindow(dialog); - PassFailJFrame.positionTestWindow(dialog, PassFailJFrame.Position.HORIZONTAL); - } catch (Exception e) { - e.printStackTrace(); - } - }); - passFailJFrame.awaitAndCheck(); - } -} - From 07f981f6b0bb8a7e444fd744791f73853e9fa325 Mon Sep 17 00:00:00 2001 From: Jamil Nimeh Date: Mon, 3 Nov 2025 14:53:21 +0000 Subject: [PATCH 149/204] 8368032: Enhance Certificate Checking Reviewed-by: ahgross, coffeys, rhalade, mullan, abarashev --- .../provider/certpath/URICertStore.java | 358 +++++++++++++++++- .../share/conf/security/java.security | 45 +++ .../x509/URICertStore/AIACertTimeout.java | 2 + .../x509/URICertStore/ExtensionsWithLDAP.java | 94 ++--- 4 files changed, 452 insertions(+), 47 deletions(-) diff --git a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java index 28729a56dbd..3e1fc8db164 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.io.InputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; +import java.net.URISyntaxException; import java.net.URLConnection; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; @@ -48,8 +49,11 @@ import java.security.cert.X509CRL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; +import java.util.Optional; +import java.util.Set; import sun.security.x509.AccessDescription; import sun.security.x509.GeneralNameInterface; @@ -58,6 +62,8 @@ import sun.security.util.Cache; import sun.security.util.Debug; import sun.security.util.SecurityProperties; +import javax.security.auth.x500.X500Principal; + /** * A CertStore that retrieves Certificates or * CRLs from a URI, for example, as specified in an X.509 @@ -182,6 +188,166 @@ class URICertStore extends CertStoreSpi { return timeoutVal; } + /** + * Enumeration for the allowed schemes we support when following a + * URI from an authorityInfoAccess extension on a certificate. + */ + private enum AllowedScheme { + HTTP(HttpFtpRuleMatcher.HTTP), + HTTPS(HttpFtpRuleMatcher.HTTPS), + LDAP(LdapRuleMatcher.LDAP), + LDAPS(LdapRuleMatcher.LDAPS), + FTP(HttpFtpRuleMatcher.FTP); + + final URIRuleMatcher ruleMatcher; + + AllowedScheme(URIRuleMatcher matcher) { + ruleMatcher = matcher; + } + + /** + * Return an {@code AllowedScheme} based on a case-insensitive match + * @param name the scheme name to be matched + * @return the {@code AllowedScheme} that corresponds to the + * {@code name} provided, or null if there is no match. + */ + static AllowedScheme nameOf(String name) { + if (name == null) { + return null; + } + + try { + return AllowedScheme.valueOf(name.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException _) { + return null; + } + } + } + + private static Set CA_ISS_URI_FILTERS = null; + private static final boolean CA_ISS_ALLOW_ANY; + + static { + boolean allowAny = false; + try { + if (Builder.USE_AIA) { + CA_ISS_URI_FILTERS = new LinkedHashSet<>(); + String aiaPropVal = Optional.ofNullable( + SecurityProperties.getOverridableProperty( + "com.sun.security.allowedAIALocations")). + map(String::trim).orElse(""); + if (aiaPropVal.equalsIgnoreCase("any")) { + allowAny = true; + if (debug != null) { + debug.println("allowedAIALocations: Warning: " + + "Allow-All URI filtering enabled!"); + } + } else { + // Load all the valid rules from the Security property + if (!aiaPropVal.isEmpty()) { + String[] aiaUriStrs = aiaPropVal.trim().split("\\s+"); + addCaIssUriFilters(aiaUriStrs); + } + + if (CA_ISS_URI_FILTERS.isEmpty()) { + if (debug != null) { + debug.println("allowedAIALocations: Warning: " + + "No valid filters found. Deny-all URI " + + "filtering is active."); + } + } + } + } + } finally { + CA_ISS_ALLOW_ANY = allowAny; + } + } + + /** + * Populate the filter collection from the list of AIA CA issuer URIs + * found in the {@code com.sun.security.allowedAIALocations} security + * or system property. + * + * @param aiaUriStrs array containing String URI filters + */ + private static void addCaIssUriFilters(String[] aiaUriStrs) { + for (String aiaStr : aiaUriStrs) { + if (aiaStr != null && !aiaStr.isEmpty()) { + try { + AllowedScheme scheme; + URI aiaUri = new URI(aiaStr).normalize(); + // It must be absolute and non-opaque + if (!aiaUri.isAbsolute() || aiaUri.isOpaque()) { + if (debug != null) { + debug.println("allowedAIALocations: Skipping " + + "non-absolute or opaque URI " + aiaUri); + } + } else if (aiaUri.getHost() == null) { + // We do not allow rules with URIs that omit a hostname + // or address. + if (debug != null) { + debug.println("allowedAIALocations: Skipping " + + "URI rule with no hostname or address: " + + aiaUri); + } + } else if ((scheme = AllowedScheme.nameOf( + aiaUri.getScheme())) != null) { + // When it is an LDAP type, we can check the path + // portion (the DN) for proper structure and reject + // the rule early if it isn't correct. + if (scheme == AllowedScheme.LDAP || + scheme == AllowedScheme.LDAPS) { + try { + new X500Principal(aiaUri.getPath(). + replaceFirst("^/+", "")); + } catch (IllegalArgumentException iae) { + if (debug != null) { + debug.println("allowedAIALocations: " + + "Skipping LDAP rule: " + iae); + } + continue; + } + } + + // When a URI has a non-null query or fragment + // warn the user upon adding the rule that those + // components will be ignored + if (aiaUri.getQuery() != null) { + if (debug != null) { + debug.println("allowedAIALocations: " + + "Rule will ignore non-null query"); + } + } + if (aiaUri.getFragment() != null) { + if (debug != null) { + debug.println("allowedAIALocations: " + + "Rule will ignore non-null fragment"); + } + } + + CA_ISS_URI_FILTERS.add(aiaUri); + if (debug != null) { + debug.println("allowedAIALocations: Added " + + aiaUri + " to URI filters"); + } + } else { + if (debug != null) { + debug.println("allowedAIALocations: Disallowed " + + "filter URI scheme: " + + aiaUri.getScheme()); + } + } + } catch (URISyntaxException urise) { + if (debug != null) { + debug.println("allowedAIALocations: Skipping " + + "filter URI entry " + aiaStr + + ": parse failure at index " + urise.getIndex()); + } + } + } + } + } + /** * Creates a URICertStore. * @@ -244,6 +410,39 @@ class URICertStore extends CertStoreSpi { return null; } URI uri = ((URIName) gn).getURI(); + + // Before performing any instantiation make sure that + // the URI passes any filtering rules. This processing should + // only occur if the com.sun.security.enableAIAcaIssuers is true + // and the "any" rule has not been specified. + if (Builder.USE_AIA && !CA_ISS_ALLOW_ANY) { + URI normAIAUri = uri.normalize(); + AllowedScheme scheme = AllowedScheme.nameOf(normAIAUri.getScheme()); + + if (scheme == null) { + if (debug != null) { + debug.println("allowedAIALocations: No matching ruleset " + + "for scheme " + normAIAUri.getScheme()); + } + return null; + } + + // Go through each of the filter rules and see if any will + // make a positive match against the caIssuer URI. If nothing + // matches then we won't instantiate a URICertStore. + if (CA_ISS_URI_FILTERS.stream().noneMatch(rule -> + scheme.ruleMatcher.matchRule(rule, normAIAUri))) { + if (debug != null) { + debug.println("allowedAIALocations: Warning - " + + "The caIssuer URI " + normAIAUri + + " in the AuthorityInfoAccess extension is denied " + + "access. Use the com.sun.security.allowedAIALocations" + + " security/system property to allow access."); + } + return null; + } + } + try { return URICertStore.getInstance(new URICertStoreParameters(uri)); } catch (Exception ex) { @@ -270,7 +469,7 @@ class URICertStore extends CertStoreSpi { @Override @SuppressWarnings("unchecked") public synchronized Collection engineGetCertificates - (CertSelector selector) throws CertStoreException { + (CertSelector selector) throws CertStoreException { if (ldap) { // caching mechanism, see the class description for more info. @@ -462,4 +661,159 @@ class URICertStore extends CertStoreSpi { super(spi, p, type, params); } } + + /** + * URIRuleMatcher - abstract base class for the rule sets used for + * various URI schemes. + */ + static abstract class URIRuleMatcher { + protected final int wellKnownPort; + + protected URIRuleMatcher(int port) { + wellKnownPort = port; + } + + /** + * Attempt to match the scheme, host and port between a filter + * rule URI and a URI coming from an AIA extension. + * + * @param filterRule the filter rule to match against + * @param caIssuer the AIA URI being compared + * @return true if the scheme, host and port numbers match, false if + * any of the components do not match. If a port number is omitted in + * either the filter rule or AIA URI, the well-known port for that + * scheme is used in the comparison. + */ + boolean schemeHostPortCheck(URI filterRule, URI caIssuer) { + if (!filterRule.getScheme().equalsIgnoreCase( + caIssuer.getScheme())) { + return false; + } else if (!filterRule.getHost().equalsIgnoreCase( + caIssuer.getHost())) { + return false; + } else { + try { + // Check for port matching, taking into consideration + // default ports + int fPort = (filterRule.getPort() == -1) ? wellKnownPort : + filterRule.getPort(); + int caiPort = (caIssuer.getPort() == -1) ? wellKnownPort : + caIssuer.getPort(); + if (fPort != caiPort) { + return false; + } + } catch (IllegalArgumentException iae) { + return false; + } + } + return true; + } + + /** + * Attempt to match an AIA URI against a specific filter rule. The + * specific rules to apply are implementation dependent. + * + * @param filterRule the filter rule to match against + * @param caIssuer the AIA URI being compared + * @return true if all matching rules pass, false if any fail. + */ + abstract boolean matchRule(URI filterRule, URI caIssuer); + } + + static class HttpFtpRuleMatcher extends URIRuleMatcher { + static final HttpFtpRuleMatcher HTTP = new HttpFtpRuleMatcher(80); + static final HttpFtpRuleMatcher HTTPS = new HttpFtpRuleMatcher(443); + static final HttpFtpRuleMatcher FTP = new HttpFtpRuleMatcher(21); + + private HttpFtpRuleMatcher(int port) { + super(port); + } + + @Override + boolean matchRule(URI filterRule, URI caIssuer) { + // Check for scheme/host/port matching + if (!schemeHostPortCheck(filterRule, caIssuer)) { + return false; + } + + // Check the path component to make sure the filter is at + // least a root of the AIA caIssuer URI's path. It must be + // a case-sensitive match for all platforms. + if (!isRootOf(filterRule, caIssuer)) { + if (debug != null) { + debug.println("allowedAIALocations: Match failed: " + + "AIA URI is not within the rule's path hierarchy."); + } + return false; + } + return true; + } + + /** + * Performs a hierarchical containment check, ensuring that the + * base URI's path is a root component of the candidate path. The + * path comparison is case-sensitive. If the base path ends in a + * slash (/) then all candidate paths that begin with the base + * path are allowed. If it does not end in a slash, then it is + * assumed that the leaf node in the base path is a file component + * and both paths must match exactly. + * + * @param base the URI that contains the root path + * @param candidate the URI that contains the path being evaluated + * @return true if {@code candidate} is a child path of {@code base}, + * false otherwise. + */ + private static boolean isRootOf(URI base, URI candidate) { + // Note: The URIs have already been normalized at this point and + // HTTP URIs cannot have null paths. If it's an empty path + // then consider the path to be "/". + String basePath = Optional.of(base.getPath()). + filter(p -> !p.isEmpty()).orElse("/"); + String candPath = Optional.of(candidate.getPath()). + filter(p -> !p.isEmpty()).orElse("/"); + return (basePath.endsWith("/")) ? candPath.startsWith(basePath) : + candPath.equals(basePath); + } + } + + static class LdapRuleMatcher extends URIRuleMatcher { + static final LdapRuleMatcher LDAP = new LdapRuleMatcher(389); + static final LdapRuleMatcher LDAPS = new LdapRuleMatcher(636); + + private LdapRuleMatcher(int port) { + super(port); + } + + @Override + boolean matchRule(URI filterRule, URI caIssuer) { + // Check for scheme/host/port matching + if (!schemeHostPortCheck(filterRule, caIssuer)) { + return false; + } + + // Obtain the base DN component and compare + try { + X500Principal filterBaseDn = new X500Principal( + filterRule.getPath().replaceFirst("^/+", "")); + X500Principal caIssBaseDn = new X500Principal( + caIssuer.getPath().replaceFirst("^/+", "")); + if (!filterBaseDn.equals(caIssBaseDn)) { + if (debug != null) { + debug.println("allowedAIALocations: Match failed: " + + "Base DN mismatch (" + filterBaseDn + " vs " + + caIssBaseDn + ")"); + } + return false; + } + } catch (IllegalArgumentException iae) { + if (debug != null) { + debug.println("allowedAIALocations: Match failed on DN: " + + iae); + } + return false; + } + + return true; + } + } } diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index b5cbce413b2..9a81ba86268 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -1655,3 +1655,48 @@ jdk.tls.alpnCharset=ISO_8859_1 # withEncryption method. # jdk.epkcs8.defaultAlgorithm=PBEWithHmacSHA256AndAES_128 + +# +# X.509 AuthorityInfoAccess caIssuer URI Filtering +# +# This property defines a whitespace-separated list of filters that +# are applied to URIs found in the authorityInfoAccess extension in +# X.509 certificates. Any caIssuers URIs in X.509 certificates are only +# followed when the com.sun.security.enableAIAcaIssuers System property is +# enabled and the filter allows the URI. By default this property imposes a +# deny-all ruleset. This property may be overridden by a System property +# of the same name. +# +# The filters must take the form of absolute, hierarchical URIs as defined by +# the java.net.URI class. Additionally, only the following protocols are +# allowed as filters: http, https, ldap and ftp. +# See RFC 5280, section 4.2.2.1 for details about the types of URIs allowed for +# the extension and their specific requirements. +# The filter matching rules are applied to each CA issuer URI as follows: +# 1. The scheme must match (case-insensitive). +# 2. A hostname or address must be specified in the filter URI. It must match +# the host or address in the caIssuers URI (case-insensitive). No name +# resolution is performed on hostnames to match IP addresses. +# 3. The port number must match. For filter and caIssuer URIs, when a port +# number is omitted, the well-known port for that scheme will be used in the +# comparison. +# 4. For hierarchical filesystem schemes (e.g. http[s], ftp): +# a. The normalized path portion of the filter URI is matched in a +# case-sensitive manner. If the final component of the path does not end +# in a slash (/), it is considered to be a file path component and must +# be an exact match of the caIssuer's URI file path component. If the +# final filter component ends in a slash, then it must either match or be +# a prefix of the caIssuer's URI path component (e.g. a filter path of +# /ab/cd/ will match a caIssuer path of /ab/cd/, /ab/cd/ef and +# /ab/cd/ef/ghi). +# b. Query strings will be ignored in filter rules and caIssuer URIs. +# c. Fragments will be ignored in filter rules and caIssuer URIs. +# 5. For ldap URIs: +# a. The base DN must be an exact match (case-insensitive). +# b. Any query string in the rule, if specified, is ignored. +# 6. A single value "any" (case-insensitive) will create an allow-all rule. +# +# As an example, here is a valid filter policy consisting of two rules: +# com.sun.security.allowedAIALocations=http://some.company.com/cacert \ +# ldap://ldap.company.com/dc=company,dc=com?caCertificate;binary +com.sun.security.allowedAIALocations= diff --git a/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java b/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java index cabb225bf1c..5491d7b0d7a 100644 --- a/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java +++ b/test/jdk/sun/security/x509/URICertStore/AIACertTimeout.java @@ -47,6 +47,7 @@ import com.sun.net.httpserver.*; import java.io.*; import java.math.BigInteger; import java.net.InetSocketAddress; +import java.security.Security; import java.security.cert.*; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -69,6 +70,7 @@ public class AIACertTimeout { private static X509Certificate eeCert; public static void main(String[] args) throws Exception { + Security.setProperty("com.sun.security.allowedAIALocations", "any"); int servTimeoutMsec = (args != null && args.length >= 1) ? Integer.parseInt(args[0]) : -1; boolean expectedPass = args != null && args.length >= 2 && diff --git a/test/jdk/sun/security/x509/URICertStore/ExtensionsWithLDAP.java b/test/jdk/sun/security/x509/URICertStore/ExtensionsWithLDAP.java index 3b598d78d9f..2214e9256c3 100644 --- a/test/jdk/sun/security/x509/URICertStore/ExtensionsWithLDAP.java +++ b/test/jdk/sun/security/x509/URICertStore/ExtensionsWithLDAP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; +import java.security.Security; import java.security.cert.CertPath; import java.security.cert.CertPathValidator; import java.security.cert.CertPathValidatorException; @@ -47,7 +48,6 @@ import java.security.cert.PKIXParameters; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -67,25 +67,27 @@ public class ExtensionsWithLDAP { * Not After : Jan 17 18:03:59 2043 GMT * Subject: CN=Root */ - private static final String CA_CERT = "" - + "-----BEGIN CERTIFICATE-----\n" - + "MIIC8TCCAdmgAwIBAgIJAJsSNtj5wdqqMA0GCSqGSIb3DQEBDQUAMA8xDTALBgNV\n" - + "BAMMBFJvb3QwHhcNMTUwOTAxMTgwMzU5WhcNNDMwMTE3MTgwMzU5WjAPMQ0wCwYD\n" - + "VQQDDARSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvj892vPm\n" - + "bB++x9QqqyBveP+ZqQ2B1stV7vh5JmDnOTevkZUOcemp3SXu/esNLSbpL+fARYXH\n" - + "V5ubnrfip6RbvcxPfVIIDJrRTLIIsU6W7M6/LJLbLkEVGy4ZV4IHkOw9W2O92rcv\n" - + "BkoqhzZnOTGR6uT3rRcKx4RevEKBKhZO+OPPf//lnckOybmYL7t7yQrajzHro76b\n" - + "QTXYjAUq/DKhglXfC7vF/JzlAvG2IunGmIfjGcnuDo/9X3Bxef/q5TxCS35fvb7t\n" - + "svC+g2QhTcBkQh4uNW2jSjlTIVp1uErCfP5aCjLaez5mqmb1hxPIlcvsNR23HwU6\n" - + "bQO7z7NBo9Do6QIDAQABo1AwTjAdBgNVHQ4EFgQUmLZNOBBkqdYoElyxklPYHmAb\n" - + "QXIwHwYDVR0jBBgwFoAUmLZNOBBkqdYoElyxklPYHmAbQXIwDAYDVR0TBAUwAwEB\n" - + "/zANBgkqhkiG9w0BAQ0FAAOCAQEAYV4fOhDi5q7+XNXCxO8Eil2frR9jqdP4LaQp\n" - + "3L0evW0gvPX68s2WmkPWzIu4TJcpdGFQqxyQFSXuKBXjthyiln77QItGTHWeafES\n" - + "q5ESrKdSaJZq1bTIrrReCIP74f+fY/F4Tnb3dCqzaljXfzpdbeRsIW6gF71xcOUQ\n" - + "nnPEjGVPLUegN+Wn/jQpeLxxIB7FmNXncdRUfMfZ43xVSKuMCy1UUYqJqTa/pXZj\n" - + "jCMeRPThRjRqHlJ69jStfWUQATbLyj9KN09rUaJxzmUSt61UqJi7sjcGySaCjAJc\n" - + "IcCdVmX/DmRLsdv8W36O3MgrvpT1zR3kaAlv2d8HppnBqcL3xg==\n" - + "-----END CERTIFICATE-----"; + private static final String CA_CERT = + """ + -----BEGIN CERTIFICATE----- + MIIC8TCCAdmgAwIBAgIJAJsSNtj5wdqqMA0GCSqGSIb3DQEBDQUAMA8xDTALBgNV + BAMMBFJvb3QwHhcNMTUwOTAxMTgwMzU5WhcNNDMwMTE3MTgwMzU5WjAPMQ0wCwYD + VQQDDARSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvj892vPm + bB++x9QqqyBveP+ZqQ2B1stV7vh5JmDnOTevkZUOcemp3SXu/esNLSbpL+fARYXH + V5ubnrfip6RbvcxPfVIIDJrRTLIIsU6W7M6/LJLbLkEVGy4ZV4IHkOw9W2O92rcv + BkoqhzZnOTGR6uT3rRcKx4RevEKBKhZO+OPPf//lnckOybmYL7t7yQrajzHro76b + QTXYjAUq/DKhglXfC7vF/JzlAvG2IunGmIfjGcnuDo/9X3Bxef/q5TxCS35fvb7t + svC+g2QhTcBkQh4uNW2jSjlTIVp1uErCfP5aCjLaez5mqmb1hxPIlcvsNR23HwU6 + bQO7z7NBo9Do6QIDAQABo1AwTjAdBgNVHQ4EFgQUmLZNOBBkqdYoElyxklPYHmAb + QXIwHwYDVR0jBBgwFoAUmLZNOBBkqdYoElyxklPYHmAbQXIwDAYDVR0TBAUwAwEB + /zANBgkqhkiG9w0BAQ0FAAOCAQEAYV4fOhDi5q7+XNXCxO8Eil2frR9jqdP4LaQp + 3L0evW0gvPX68s2WmkPWzIu4TJcpdGFQqxyQFSXuKBXjthyiln77QItGTHWeafES + q5ESrKdSaJZq1bTIrrReCIP74f+fY/F4Tnb3dCqzaljXfzpdbeRsIW6gF71xcOUQ + nnPEjGVPLUegN+Wn/jQpeLxxIB7FmNXncdRUfMfZ43xVSKuMCy1UUYqJqTa/pXZj + jCMeRPThRjRqHlJ69jStfWUQATbLyj9KN09rUaJxzmUSt61UqJi7sjcGySaCjAJc + IcCdVmX/DmRLsdv8W36O3MgrvpT1zR3kaAlv2d8HppnBqcL3xg== + -----END CERTIFICATE----- + """; /* * Certificate: @@ -106,39 +108,41 @@ public class ExtensionsWithLDAP { * Authority Information Access: * CA Issuers - URI:ldap://ldap.host.for.aia/dc=Root?cACertificate */ - private static final String EE_CERT = "" - + "-----BEGIN CERTIFICATE-----\n" - + "MIIDHTCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQ0FADAPMQ0wCwYDVQQDDARSb290\n" - + "MB4XDTE1MDkwMTE4MDM1OVoXDTQzMDExNzE4MDM1OVowDTELMAkGA1UEAwwCRUUw\n" - + "ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpyz97liuWPDYcLH9TX8Bi\n" - + "T78olCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgK\n" - + "mLhuczF3M9VIcWr+JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz\n" - + "7leikne7KmclHvTfvFd0WDI7Gb9vo4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXR\n" - + "v5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfFe1DDsMg/KpKGiILYZ+g2qtVM\n" - + "ZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e+sO6H24w2F19\n" - + "AgMBAAGjgYUwgYIwNAYDVR0fBC0wKzApoCegJYYjbGRhcDovL2xkYXAuaG9zdC5m\n" - + "b3IuY3JsZHAvbWFpbi5jcmwwSgYIKwYBBQUHAQEEPjA8MDoGCCsGAQUFBzAChi5s\n" - + "ZGFwOi8vbGRhcC5ob3N0LmZvci5haWEvZGM9Um9vdD9jQUNlcnRpZmljYXRlMA0G\n" - + "CSqGSIb3DQEBDQUAA4IBAQBWDfZHpuUx0yn5d3+BuztFqoks1MkGdk+USlH0TB1/\n" - + "gWWBd+4S4PCKlpSur0gj2rMW4fP5HQfNlHci8JV8/bG4KuKRAXW56dg1818Hl3pc\n" - + "iIrUSRn8uUjH3p9qb+Rb/u3mmVQRyJjN2t/zceNsO8/+Dd808OB9aEwGs8lMT0nn\n" - + "ZYaaAqYz1GIY/Ecyx1vfEZEQ1ljo6i/r70C3igbypBUShxSiGsleiVTLOGNA+MN1\n" - + "/a/Qh0bkaQyTGqK3bwvzzMeQVqWu2EWTBD/PmND5ExkpRICdv8LBVXfLnpoBr4lL\n" - + "hnxn9+e0Ah+t8dS5EKfn44w5bI5PCu2bqxs6RCTxNjcY\n" - + "-----END CERTIFICATE-----"; + private static final String EE_CERT = + """ + -----BEGIN CERTIFICATE----- + MIIDHTCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQ0FADAPMQ0wCwYDVQQDDARSb290 + MB4XDTE1MDkwMTE4MDM1OVoXDTQzMDExNzE4MDM1OVowDTELMAkGA1UEAwwCRUUw + ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpyz97liuWPDYcLH9TX8Bi + T78olCmAfmevvch6ncXUVuCzbdaKuKXwn4EVbDszsVJLoK5zdtP+X3iDhutj+IgK + mLhuczF3M9VIcWr+JJUyTH4+3h/RT8cjCDZOmk9iXkb5ifruVsLqzb9g+Vp140Oz + 7leikne7KmclHvTfvFd0WDI7Gb9vo4f5rT717BXJ/n+M6pNk8DLpLiEu6eziYvXR + v5x+t5Go3x0eCXdaxEQUf2j876Wfr2qHRJK7lDfFe1DDsMg/KpKGiILYZ+g2qtVM + ZSxtp5BZEtfB5qV/IE5kWO+mCIAGpXSZIdbERR6pZUq8GLEe1T9e+sO6H24w2F19 + AgMBAAGjgYUwgYIwNAYDVR0fBC0wKzApoCegJYYjbGRhcDovL2xkYXAuaG9zdC5m + b3IuY3JsZHAvbWFpbi5jcmwwSgYIKwYBBQUHAQEEPjA8MDoGCCsGAQUFBzAChi5s + ZGFwOi8vbGRhcC5ob3N0LmZvci5haWEvZGM9Um9vdD9jQUNlcnRpZmljYXRlMA0G + CSqGSIb3DQEBDQUAA4IBAQBWDfZHpuUx0yn5d3+BuztFqoks1MkGdk+USlH0TB1/ + gWWBd+4S4PCKlpSur0gj2rMW4fP5HQfNlHci8JV8/bG4KuKRAXW56dg1818Hl3pc + iIrUSRn8uUjH3p9qb+Rb/u3mmVQRyJjN2t/zceNsO8/+Dd808OB9aEwGs8lMT0nn + ZYaaAqYz1GIY/Ecyx1vfEZEQ1ljo6i/r70C3igbypBUShxSiGsleiVTLOGNA+MN1 + /a/Qh0bkaQyTGqK3bwvzzMeQVqWu2EWTBD/PmND5ExkpRICdv8LBVXfLnpoBr4lL + hnxn9+e0Ah+t8dS5EKfn44w5bI5PCu2bqxs6RCTxNjcY + -----END CERTIFICATE-----"""; public static void main(String[] args) throws Exception { String extension = args[0]; String targetHost = args[1]; - + Security.setProperty("com.sun.security.allowedAIALocations", + "ldap://" + targetHost + "/dc=Root"); X509Certificate trustedCert = loadCertificate(CA_CERT); X509Certificate eeCert = loadCertificate(EE_CERT); Set trustedCertsSet = new HashSet<>(); trustedCertsSet.add(new TrustAnchor(trustedCert, null)); - CertPath cp = (CertPath) CertificateFactory.getInstance("X509") - .generateCertPath(Arrays.asList(eeCert)); + CertPath cp = CertificateFactory.getInstance("X509") + .generateCertPath(List.of(eeCert)); // CertPath validator should try to parse CRLDP and AIA extensions, // and load CRLs/certs which they point to. @@ -151,7 +155,7 @@ public class ExtensionsWithLDAP { = (InetSocketAddress) socket.getRemoteSocketAddress(); hosts.add(remoteAddress.getHostName()); }; - try (SocksProxy proxy = SocksProxy.startProxy(socketConsumer)) { + try (SocksProxy _ = SocksProxy.startProxy(socketConsumer)) { CertPathValidator.getInstance("PKIX").validate(cp, new PKIXParameters(trustedCertsSet)); throw new RuntimeException("CertPathValidatorException not thrown"); From 75172e06585060e5efca080a11d8a8a51b40afed Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Mon, 19 Jan 2026 07:45:21 +0000 Subject: [PATCH 150/204] 8374717: Unclear wording in docs for recursion for List, Map and LazyConstant Reviewed-by: rriggs --- src/java.base/share/classes/java/lang/LazyConstant.java | 7 +++---- src/java.base/share/classes/java/util/List.java | 4 ++-- src/java.base/share/classes/java/util/Map.java | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/java.base/share/classes/java/lang/LazyConstant.java b/src/java.base/share/classes/java/lang/LazyConstant.java index 703d67b8abf..85f9d0e82fd 100644 --- a/src/java.base/share/classes/java/lang/LazyConstant.java +++ b/src/java.base/share/classes/java/lang/LazyConstant.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 @@ -87,9 +87,8 @@ import java.util.function.Supplier; * is thrown. Hence, a lazy constant can never hold a {@code null} value. Clients who * want to use a nullable constant can wrap the value into an {@linkplain Optional} holder. *

      - * If the computing function recursively invokes itself (directly or indirectly via - * the lazy constant), an {@linkplain IllegalStateException} is thrown, and the lazy - * constant is not initialized. + * If the computing function recursively invokes itself via the lazy constant, an + * {@linkplain IllegalStateException} is thrown, and the lazy constant is not initialized. * *

      Composing lazy constants

      * A lazy constant can depend on other lazy constants, forming a dependency graph diff --git a/src/java.base/share/classes/java/util/List.java b/src/java.base/share/classes/java/util/List.java index 43408de292a..5f9a90e1748 100644 --- a/src/java.base/share/classes/java/util/List.java +++ b/src/java.base/share/classes/java/util/List.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 @@ -1224,7 +1224,7 @@ public interface List extends SequencedCollection { * The returned list and its {@link List#subList(int, int) subList()} or * {@link List#reversed()} views implement the {@link RandomAccess} interface. *

      - * If the provided computing function recursively calls itself or the returned + * If the provided computing function recursively calls itself via the returned * lazy list for the same index, an {@linkplain IllegalStateException} * will be thrown. *

      diff --git a/src/java.base/share/classes/java/util/Map.java b/src/java.base/share/classes/java/util/Map.java index 177f0522b1b..fa16fb89050 100644 --- a/src/java.base/share/classes/java/util/Map.java +++ b/src/java.base/share/classes/java/util/Map.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 @@ -1777,7 +1777,7 @@ public interface Map { * The values of any {@link Map#values()} or {@link Map#entrySet()} views of * the returned map are also lazily computed. *

      - * If the provided computing function recursively calls itself or + * If the provided computing function recursively calls itself via * the returned lazy map for the same key, an {@linkplain IllegalStateException} * will be thrown. *

      From 9d7ecd51d72a1a9f34a19c07813e8b5530e6a944 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 19 Jan 2026 08:32:03 +0000 Subject: [PATCH 151/204] 8375437: G1: Convert G1EvacFailureRegions to use Atomic Reviewed-by: stefank, iwalulya --- src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp | 6 +++--- src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp | 6 ++++-- src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp b/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp index ffcb5a0022f..37553e2aa56 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp +++ b/src/hotspot/share/gc/g1/g1EvacFailureRegions.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, 2022, Huawei Technologies Co., Ltd. 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 @@ -28,7 +29,6 @@ #include "gc/g1/g1EvacFailureRegions.inline.hpp" #include "gc/g1/g1HeapRegion.hpp" #include "memory/allocation.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/bitMap.inline.hpp" G1EvacFailureRegions::G1EvacFailureRegions() : @@ -43,7 +43,7 @@ G1EvacFailureRegions::~G1EvacFailureRegions() { } void G1EvacFailureRegions::pre_collection(uint max_regions) { - AtomicAccess::store(&_num_regions_evac_failed, 0u); + _num_regions_evac_failed.store_relaxed(0u); _regions_evac_failed.resize(max_regions); _regions_pinned.resize(max_regions); _regions_alloc_failed.resize(max_regions); @@ -69,6 +69,6 @@ void G1EvacFailureRegions::par_iterate(G1HeapRegionClosure* closure, G1CollectedHeap::heap()->par_iterate_regions_array(closure, hrclaimer, _evac_failed_regions, - AtomicAccess::load(&_num_regions_evac_failed), + num_regions_evac_failed(), worker_id); } diff --git a/src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp b/src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp index 9d29957b782..f752a3f8ab7 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp +++ b/src/hotspot/share/gc/g1/g1EvacFailureRegions.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, 2022, Huawei Technologies Co., Ltd. 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,6 +26,7 @@ #ifndef SHARE_GC_G1_G1EVACFAILUREREGIONS_HPP #define SHARE_GC_G1_G1EVACFAILUREREGIONS_HPP +#include "runtime/atomic.hpp" #include "utilities/bitMap.hpp" class G1AbstractSubTask; @@ -53,14 +55,14 @@ class G1EvacFailureRegions { // Evacuation failed regions (indexes) in the current collection. uint* _evac_failed_regions; // Number of regions evacuation failed in the current collection. - volatile uint _num_regions_evac_failed; + Atomic _num_regions_evac_failed; public: G1EvacFailureRegions(); ~G1EvacFailureRegions(); uint get_region_idx(uint idx) const { - assert(idx < _num_regions_evac_failed, "precondition"); + assert(idx < _num_regions_evac_failed.load_relaxed(), "precondition"); return _evac_failed_regions[idx]; } diff --git a/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp b/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp index 6eec9b63e6b..fb456475b56 100644 --- a/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp +++ b/src/hotspot/share/gc/g1/g1EvacFailureRegions.inline.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, 2022, Huawei Technologies Co., Ltd. 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 @@ -29,10 +30,9 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" -#include "runtime/atomicAccess.hpp" uint G1EvacFailureRegions::num_regions_evac_failed() const { - return AtomicAccess::load(&_num_regions_evac_failed); + return _num_regions_evac_failed.load_relaxed(); } bool G1EvacFailureRegions::has_regions_evac_failed() const { @@ -57,7 +57,7 @@ bool G1EvacFailureRegions::record(uint worker_id, uint region_idx, bool cause_pi bool success = _regions_evac_failed.par_set_bit(region_idx, memory_order_relaxed); if (success) { - size_t offset = AtomicAccess::fetch_then_add(&_num_regions_evac_failed, 1u); + size_t offset = _num_regions_evac_failed.fetch_then_add(1u); _evac_failed_regions[offset] = region_idx; G1CollectedHeap* g1h = G1CollectedHeap::heap(); From 30f39d88e5af36bb6db458c03215e9fa6a31d6f3 Mon Sep 17 00:00:00 2001 From: David Briemann Date: Mon, 19 Jan 2026 08:54:18 +0000 Subject: [PATCH 152/204] 8375530: PPC64: incorrect quick verify_method_data_pointer check causes poor performance in debug build Reviewed-by: mdoerr, shade --- src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index fc865be015e..f7bf457f72c 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -1109,11 +1109,11 @@ void InterpreterMacroAssembler::verify_method_data_pointer() { lhz(R11_scratch1, in_bytes(DataLayout::bci_offset()), R28_mdx); ld(R12_scratch2, in_bytes(Method::const_offset()), R19_method); addi(R11_scratch1, R11_scratch1, in_bytes(ConstMethod::codes_offset())); - add(R11_scratch1, R12_scratch2, R12_scratch2); + add(R11_scratch1, R11_scratch1, R12_scratch2); cmpd(CR0, R11_scratch1, R14_bcp); beq(CR0, verify_continue); - call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::verify_mdp ), R19_method, R14_bcp, R28_mdx); + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::verify_mdp), R19_method, R14_bcp, R28_mdx); bind(verify_continue); #endif From 3e181485709d108ef3d1e6b595fbd95ecc8ef74a Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 19 Jan 2026 09:02:33 +0000 Subject: [PATCH 153/204] 8375439: G1: Convert G1MonotonicArena class to use Atomic Reviewed-by: stefank, iwalulya --- src/hotspot/share/gc/g1/g1MonotonicArena.cpp | 55 +++++++++---------- src/hotspot/share/gc/g1/g1MonotonicArena.hpp | 47 ++++++++-------- .../share/gc/g1/g1MonotonicArena.inline.hpp | 11 ++-- 3 files changed, 56 insertions(+), 57 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1MonotonicArena.cpp b/src/hotspot/share/gc/g1/g1MonotonicArena.cpp index a9c6462680f..3f97870a67f 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArena.cpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArena.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 @@ -24,7 +24,6 @@ #include "gc/g1/g1MonotonicArena.inline.hpp" #include "memory/allocation.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/vmOperations.hpp" #include "utilities/globalCounter.inline.hpp" @@ -61,13 +60,13 @@ void G1MonotonicArena::SegmentFreeList::bulk_add(Segment& first, size_t num, size_t mem_size) { _list.prepend(first, last); - AtomicAccess::add(&_num_segments, num, memory_order_relaxed); - AtomicAccess::add(&_mem_size, mem_size, memory_order_relaxed); + _num_segments.add_then_fetch(num, memory_order_relaxed); + _mem_size.add_then_fetch(mem_size, memory_order_relaxed); } void G1MonotonicArena::SegmentFreeList::print_on(outputStream* out, const char* prefix) { out->print_cr("%s: segments %zu size %zu", - prefix, AtomicAccess::load(&_num_segments), AtomicAccess::load(&_mem_size)); + prefix, _num_segments.load_relaxed(), _mem_size.load_relaxed()); } G1MonotonicArena::Segment* G1MonotonicArena::SegmentFreeList::get_all(size_t& num_segments, @@ -75,12 +74,12 @@ G1MonotonicArena::Segment* G1MonotonicArena::SegmentFreeList::get_all(size_t& nu GlobalCounter::CriticalSection cs(Thread::current()); Segment* result = _list.pop_all(); - num_segments = AtomicAccess::load(&_num_segments); - mem_size = AtomicAccess::load(&_mem_size); + num_segments = _num_segments.load_relaxed(); + mem_size = _mem_size.load_relaxed(); if (result != nullptr) { - AtomicAccess::sub(&_num_segments, num_segments, memory_order_relaxed); - AtomicAccess::sub(&_mem_size, mem_size, memory_order_relaxed); + _num_segments.sub_then_fetch(num_segments, memory_order_relaxed); + _mem_size.sub_then_fetch(mem_size, memory_order_relaxed); } return result; } @@ -96,8 +95,8 @@ void G1MonotonicArena::SegmentFreeList::free_all() { Segment::delete_segment(cur); } - AtomicAccess::sub(&_num_segments, num_freed, memory_order_relaxed); - AtomicAccess::sub(&_mem_size, mem_size_freed, memory_order_relaxed); + _num_segments.sub_then_fetch(num_freed, memory_order_relaxed); + _mem_size.sub_then_fetch(mem_size_freed, memory_order_relaxed); } G1MonotonicArena::Segment* G1MonotonicArena::new_segment(Segment* const prev) { @@ -115,7 +114,7 @@ G1MonotonicArena::Segment* G1MonotonicArena::new_segment(Segment* const prev) { } // Install it as current allocation segment. - Segment* old = AtomicAccess::cmpxchg(&_first, prev, next); + Segment* old = _first.compare_exchange(prev, next); if (old != prev) { // Somebody else installed the segment, use that one. Segment::delete_segment(next); @@ -126,9 +125,9 @@ G1MonotonicArena::Segment* G1MonotonicArena::new_segment(Segment* const prev) { _last = next; } // Successfully installed the segment into the list. - AtomicAccess::inc(&_num_segments, memory_order_relaxed); - AtomicAccess::add(&_mem_size, next->mem_size(), memory_order_relaxed); - AtomicAccess::add(&_num_total_slots, next->num_slots(), memory_order_relaxed); + _num_segments.add_then_fetch(1u, memory_order_relaxed); + _mem_size.add_then_fetch(next->mem_size(), memory_order_relaxed); + _num_total_slots.add_then_fetch(next->num_slots(), memory_order_relaxed); return next; } } @@ -155,7 +154,7 @@ uint G1MonotonicArena::slot_size() const { } void G1MonotonicArena::drop_all() { - Segment* cur = AtomicAccess::load_acquire(&_first); + Segment* cur = _first.load_acquire(); if (cur != nullptr) { assert(_last != nullptr, "If there is at least one segment, there must be a last one."); @@ -175,25 +174,25 @@ void G1MonotonicArena::drop_all() { cur = next; } #endif - assert(num_segments == _num_segments, "Segment count inconsistent %u %u", num_segments, _num_segments); - assert(mem_size == _mem_size, "Memory size inconsistent"); + assert(num_segments == _num_segments.load_relaxed(), "Segment count inconsistent %u %u", num_segments, _num_segments.load_relaxed()); + assert(mem_size == _mem_size.load_relaxed(), "Memory size inconsistent"); assert(last == _last, "Inconsistent last segment"); - _segment_free_list->bulk_add(*first, *_last, _num_segments, _mem_size); + _segment_free_list->bulk_add(*first, *_last, _num_segments.load_relaxed(), _mem_size.load_relaxed()); } - _first = nullptr; + _first.store_relaxed(nullptr); _last = nullptr; - _num_segments = 0; - _mem_size = 0; - _num_total_slots = 0; - _num_allocated_slots = 0; + _num_segments.store_relaxed(0); + _mem_size.store_relaxed(0); + _num_total_slots.store_relaxed(0); + _num_allocated_slots.store_relaxed(0); } void* G1MonotonicArena::allocate() { assert(slot_size() > 0, "instance size not set."); - Segment* cur = AtomicAccess::load_acquire(&_first); + Segment* cur = _first.load_acquire(); if (cur == nullptr) { cur = new_segment(cur); } @@ -201,7 +200,7 @@ void* G1MonotonicArena::allocate() { while (true) { void* slot = cur->allocate_slot(); if (slot != nullptr) { - AtomicAccess::inc(&_num_allocated_slots, memory_order_relaxed); + _num_allocated_slots.add_then_fetch(1u, memory_order_relaxed); guarantee(is_aligned(slot, _alloc_options->slot_alignment()), "result " PTR_FORMAT " not aligned at %u", p2i(slot), _alloc_options->slot_alignment()); return slot; @@ -213,7 +212,7 @@ void* G1MonotonicArena::allocate() { } uint G1MonotonicArena::num_segments() const { - return AtomicAccess::load(&_num_segments); + return _num_segments.load_relaxed(); } #ifdef ASSERT @@ -238,7 +237,7 @@ uint G1MonotonicArena::calculate_length() const { template void G1MonotonicArena::iterate_segments(SegmentClosure& closure) const { - Segment* cur = AtomicAccess::load_acquire(&_first); + Segment* cur = _first.load_acquire(); assert((cur != nullptr) == (_last != nullptr), "If there is at least one segment, there must be a last one"); diff --git a/src/hotspot/share/gc/g1/g1MonotonicArena.hpp b/src/hotspot/share/gc/g1/g1MonotonicArena.hpp index 211820c5254..d8e658b5a64 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArena.hpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArena.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) 2021, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,6 +28,7 @@ #include "gc/shared/freeListAllocator.hpp" #include "nmt/memTag.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/lockFreeStack.hpp" @@ -65,27 +66,27 @@ private: // AllocOptions provides parameters for Segment sizing and expansion. const AllocOptions* _alloc_options; - Segment* volatile _first; // The (start of the) list of all segments. - Segment* _last; // The last segment of the list of all segments. - volatile uint _num_segments; // Number of assigned segments to this allocator. - volatile size_t _mem_size; // Memory used by all segments. + Atomic _first; // The (start of the) list of all segments. + Segment* _last; // The last segment of the list of all segments. + Atomic _num_segments; // Number of assigned segments to this allocator. + Atomic _mem_size; // Memory used by all segments. SegmentFreeList* _segment_free_list; // The global free segment list to preferentially // get new segments from. - volatile uint _num_total_slots; // Number of slots available in all segments (allocated + not yet used). - volatile uint _num_allocated_slots; // Number of total slots allocated ever (including free and pending). + Atomic _num_total_slots; // Number of slots available in all segments (allocated + not yet used). + Atomic _num_allocated_slots; // Number of total slots allocated ever (including free and pending). inline Segment* new_segment(Segment* const prev); DEBUG_ONLY(uint calculate_length() const;) public: - const Segment* first_segment() const { return AtomicAccess::load(&_first); } + const Segment* first_segment() const { return _first.load_relaxed(); } - uint num_total_slots() const { return AtomicAccess::load(&_num_total_slots); } + uint num_total_slots() const { return _num_total_slots.load_relaxed(); } uint num_allocated_slots() const { - uint allocated = AtomicAccess::load(&_num_allocated_slots); + uint allocated = _num_allocated_slots.load_relaxed(); assert(calculate_length() == allocated, "Must be"); return allocated; } @@ -116,11 +117,11 @@ static constexpr uint SegmentPayloadMaxAlignment = 8; class alignas(SegmentPayloadMaxAlignment) G1MonotonicArena::Segment { const uint _slot_size; const uint _num_slots; - Segment* volatile _next; + Atomic _next; // Index into the next free slot to allocate into. Full if equal (or larger) // to _num_slots (can be larger because we atomically increment this value and // check only afterwards if the allocation has been successful). - uint volatile _next_allocate; + Atomic _next_allocate; const MemTag _mem_tag; static size_t header_size() { return align_up(sizeof(Segment), SegmentPayloadMaxAlignment); } @@ -139,21 +140,21 @@ class alignas(SegmentPayloadMaxAlignment) G1MonotonicArena::Segment { Segment(uint slot_size, uint num_slots, Segment* next, MemTag mem_tag); ~Segment() = default; public: - Segment* volatile* next_addr() { return &_next; } + Atomic* next_addr() { return &_next; } void* allocate_slot(); uint num_slots() const { return _num_slots; } - Segment* next() const { return _next; } + Segment* next() const { return _next.load_relaxed(); } void set_next(Segment* next) { assert(next != this, " loop condition"); - _next = next; + _next.store_relaxed(next); } void reset(Segment* next) { - _next_allocate = 0; + _next_allocate.store_relaxed(0); assert(next != this, " loop condition"); set_next(next); memset(payload(0), 0, payload_size()); @@ -166,7 +167,7 @@ public: uint length() const { // _next_allocate might grow larger than _num_slots in multi-thread environments // due to races. - return MIN2(_next_allocate, _num_slots); + return MIN2(_next_allocate.load_relaxed(), _num_slots); } static size_t size_in_bytes(uint slot_size, uint num_slots) { @@ -176,7 +177,7 @@ public: static Segment* create_segment(uint slot_size, uint num_slots, Segment* next, MemTag mem_tag); static void delete_segment(Segment* segment); - bool is_full() const { return _next_allocate >= _num_slots; } + bool is_full() const { return _next_allocate.load_relaxed() >= _num_slots; } }; static_assert(alignof(G1MonotonicArena::Segment) >= SegmentPayloadMaxAlignment, "assert alignment of Segment (and indirectly its payload)"); @@ -186,15 +187,15 @@ static_assert(alignof(G1MonotonicArena::Segment) >= SegmentPayloadMaxAlignment, // performed by multiple threads concurrently. // Counts and memory usage are current on a best-effort basis if accessed concurrently. class G1MonotonicArena::SegmentFreeList { - static Segment* volatile* next_ptr(Segment& segment) { + static Atomic* next_ptr(Segment& segment) { return segment.next_addr(); } using SegmentStack = LockFreeStack; SegmentStack _list; - volatile size_t _num_segments; - volatile size_t _mem_size; + Atomic _num_segments; + Atomic _mem_size; public: SegmentFreeList() : _list(), _num_segments(0), _mem_size(0) { } @@ -210,8 +211,8 @@ public: void print_on(outputStream* out, const char* prefix = ""); - size_t num_segments() const { return AtomicAccess::load(&_num_segments); } - size_t mem_size() const { return AtomicAccess::load(&_mem_size); } + size_t num_segments() const { return _num_segments.load_relaxed(); } + size_t mem_size() const { return _mem_size.load_relaxed(); } }; // Configuration for G1MonotonicArena, e.g slot size, slot number of next Segment. diff --git a/src/hotspot/share/gc/g1/g1MonotonicArena.inline.hpp b/src/hotspot/share/gc/g1/g1MonotonicArena.inline.hpp index dd9ccae1849..cf1b35ccead 100644 --- a/src/hotspot/share/gc/g1/g1MonotonicArena.inline.hpp +++ b/src/hotspot/share/gc/g1/g1MonotonicArena.inline.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) 2021, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,14 +28,13 @@ #include "gc/g1/g1MonotonicArena.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/globalCounter.inline.hpp" inline void* G1MonotonicArena::Segment::allocate_slot() { - if (_next_allocate >= _num_slots) { + if (_next_allocate.load_relaxed() >= _num_slots) { return nullptr; } - uint result = AtomicAccess::fetch_then_add(&_next_allocate, 1u, memory_order_relaxed); + uint result = _next_allocate.fetch_then_add(1u, memory_order_relaxed); if (result >= _num_slots) { return nullptr; } @@ -48,8 +47,8 @@ inline G1MonotonicArena::Segment* G1MonotonicArena::SegmentFreeList::get() { Segment* result = _list.pop(); if (result != nullptr) { - AtomicAccess::dec(&_num_segments, memory_order_relaxed); - AtomicAccess::sub(&_mem_size, result->mem_size(), memory_order_relaxed); + _num_segments.sub_then_fetch(1u, memory_order_relaxed); + _mem_size.sub_then_fetch(result->mem_size(), memory_order_relaxed); } return result; } From e0edc656240d18b4468212c38f136084a50be301 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 19 Jan 2026 12:57:44 +0000 Subject: [PATCH 154/204] 8375463: G1: Remove AtomicAccess include from files that do not use it Reviewed-by: stefank, iwalulya --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 1 - src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp | 3 +-- src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp | 3 +-- src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp | 1 - src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp | 3 +-- src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp | 3 +-- src/hotspot/share/gc/g1/g1ParScanThreadState.cpp | 3 +-- 7 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 3a0c4a04441..b6c3c0b0907 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -103,7 +103,6 @@ #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/cpuTimeCounters.hpp" #include "runtime/handles.inline.hpp" #include "runtime/init.hpp" diff --git a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp index 83c846e84d4..c02b028112b 100644 --- a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.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 @@ -37,7 +37,6 @@ #include "gc/shared/weakProcessor.inline.hpp" #include "logging/log.hpp" #include "memory/iterator.inline.hpp" -#include "runtime/atomicAccess.hpp" class G1AdjustLiveClosure : public StackObj { G1AdjustClosure* _adjust_closure; diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp index fae73a2c6bf..13c7a6a8d3e 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.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 @@ -31,7 +31,6 @@ #include "memory/allocation.hpp" #include "memory/padded.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/globals_extension.hpp" #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp index 878d35397aa..950098c706e 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp @@ -30,7 +30,6 @@ #include "gc/g1/g1CodeRootSet.hpp" #include "gc/g1/g1CollectionSetCandidates.hpp" #include "gc/g1/g1FromCardCache.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" #include "utilities/bitMap.hpp" diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp index fbd529cb1d3..f621b1318c1 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.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 @@ -30,7 +30,6 @@ #include "gc/g1/g1CardSet.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/bitMap.inline.hpp" void G1HeapRegionRemSet::set_state_untracked() { diff --git a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp index d7e0c6e394f..529ef62b44d 100644 --- a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp +++ b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.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 @@ -28,7 +28,6 @@ #include "nmt/memTracker.hpp" #include "oops/markWord.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/os.hpp" #include "utilities/align.hpp" #include "utilities/bitMap.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index e7b02ed68e7..75a8ef1a336 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.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 @@ -42,7 +42,6 @@ #include "memory/allocation.inline.hpp" #include "oops/access.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/prefetch.inline.hpp" #include "utilities/globalDefinitions.hpp" From 6942bb2b313c2d81e95f692dd947733b1149e8b8 Mon Sep 17 00:00:00 2001 From: Andreas Steiner Date: Mon, 19 Jan 2026 13:54:06 +0000 Subject: [PATCH 155/204] 8374802: java/net/DatagramSocket/SendReceiveMaxSize.java fails on AIX due to small default RCVBUF size Reviewed-by: alanb --- test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java b/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java index 3cea9073199..ded087d35e8 100644 --- a/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java +++ b/test/jdk/java/net/DatagramSocket/SendReceiveMaxSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, 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 @@ -55,6 +55,8 @@ import java.net.MulticastSocket; import java.nio.channels.DatagramChannel; import java.util.Random; +import static java.net.StandardSocketOptions.SO_RCVBUF; +import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertEquals; import static org.testng.Assert.expectThrows; @@ -102,6 +104,10 @@ public class SendReceiveMaxSize { DatagramSocketSupplier supplier, Class exception) throws IOException { try (var receiver = new DatagramSocket(new InetSocketAddress(HOST_ADDR, 0))) { + assertTrue(receiver.getOption(SO_RCVBUF) >= capacity, + receiver.getOption(SO_RCVBUF) + + " for UDP receive buffer too small to hold capacity " + + capacity); var port = receiver.getLocalPort(); var addr = new InetSocketAddress(HOST_ADDR, port); try (var sender = supplier.open()) { From e7f1f16a88ce239f22f86e479a5e806f531fbe31 Mon Sep 17 00:00:00 2001 From: Christian Hagedorn Date: Mon, 19 Jan 2026 14:02:02 +0000 Subject: [PATCH 156/204] 8375271: [IR Framework] Rename IREncoding to ApplicableIRRules and driver/flag/test VM to Driver/Flag/Test VM Reviewed-by: dfenacci, thartmann, mhaessig --- .../lib/ir_framework/AbstractInfo.java | 4 +- .../compiler/lib/ir_framework/CompLevel.java | 4 +- .../jtreg/compiler/lib/ir_framework/IR.java | 4 +- .../jtreg/compiler/lib/ir_framework/README.md | 24 +++---- .../compiler/lib/ir_framework/Scenario.java | 8 +-- .../lib/ir_framework/TestFramework.java | 46 ++++++------- .../ir_framework/driver/FlagVMProcess.java | 26 ++++---- .../ir_framework/driver/TestVMException.java | 8 +-- .../ir_framework/driver/TestVMProcess.java | 35 +++++----- ...rser.java => ApplicableIRRulesParser.java} | 65 ++++++++++--------- .../irmatching/parser/IRMethodBuilder.java | 5 +- .../irmatching/parser/TestClassParser.java | 14 ++-- .../driver/irmatching/parser/TestMethod.java | 6 +- .../driver/irmatching/parser/TestMethods.java | 8 +-- .../driver/irmatching/parser/VMInfo.java | 4 +- .../irmatching/parser/VMInfoParser.java | 18 ++--- .../parser/hotspot/CompileQueueMessages.java | 8 +-- .../parser/hotspot/HotSpotPidFileParser.java | 12 ++-- .../flag/CompilePhaseCollector.java | 4 +- .../lib/ir_framework/flag/FlagVM.java | 14 ++-- .../shared/NoTestsRunException.java | 10 +-- .../shared/TestFrameworkSocket.java | 34 +++++----- ...ter.java => ApplicableIRRulesPrinter.java} | 25 +++---- .../lib/ir_framework/test/TestVM.java | 20 +++--- .../lib/ir_framework/test/VMInfoPrinter.java | 4 +- .../ir_framework/tests/TestIRMatching.java | 9 +-- .../tests/TestPhaseIRMatching.java | 4 +- 27 files changed, 213 insertions(+), 210 deletions(-) rename test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/{IREncodingParser.java => ApplicableIRRulesParser.java} (60%) rename test/hotspot/jtreg/compiler/lib/ir_framework/test/{IREncodingPrinter.java => ApplicableIRRulesPrinter.java} (96%) diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/AbstractInfo.java b/test/hotspot/jtreg/compiler/lib/ir_framework/AbstractInfo.java index 20a865b1337..b57b209112c 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/AbstractInfo.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/AbstractInfo.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 @@ -103,7 +103,7 @@ abstract public class AbstractInfo { } /** - * Returns a boolean indicating if the test VM runs with flags that allow C2 compilations. + * Returns a boolean indicating if the Test VM runs with flags that allow C2 compilations. * * @return {@code true} if C2 compilations are allowed; * {@code false} otherwise (run with {@code -XX:TieredStopAtLevel={1,2,3}, -XX:-UseCompiler}). diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/CompLevel.java b/test/hotspot/jtreg/compiler/lib/ir_framework/CompLevel.java index b6e55c0f617..f8760b4dfad 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/CompLevel.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/CompLevel.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 @@ -51,7 +51,7 @@ public enum CompLevel { * Can only be used at {@link Test#compLevel()}. After the warm-up, the framework keeps invoking the test over a span * of 10s (configurable by setting the property flag {@code -DWaitForCompilationTimeout}) until HotSpot compiles the * {@link Test} method. If the method was not compiled after 10s, an exception is thrown. The framework does not wait - * for the compilation if the test VM is run with {@code -Xcomp}, {@code -XX:-UseCompiler}, or + * for the compilation if the Test VM is run with {@code -Xcomp}, {@code -XX:-UseCompiler}, or * {@code -DExcludeRandom=true}. */ WAIT_FOR_COMPILATION(-4), diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IR.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IR.java index fd2cd69056a..96df71dacd7 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IR.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IR.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 @@ -55,7 +55,7 @@ import java.lang.annotation.RetentionPolicy; * For any other flag specified either by user code (e.g. {@link Scenario#Scenario(int, String...)}, * {@link TestFramework#runWithFlags(String...) etc.} or as part of the JTreg whitelist, IR verification is applied. * To restrict the application of IR rules when certain flags are present that could change the IR, each {@code @IR} - * annotation can specify additional preconditions on the allowed test VM flags that must hold when an IR rule is applied. + * annotation can specify additional preconditions on the allowed Test VM flags that must hold when an IR rule is applied. * If the specified preconditions fail, then the framework does not apply the IR rule. These preconditions can be * set with {@link #applyIf()}, {@link #applyIfNot()}, {@link #applyIfAnd()}, or {@link #applyIfOr()}. *

      diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/README.md b/test/hotspot/jtreg/compiler/lib/ir_framework/README.md index c0df8b54658..da59bd61b13 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/README.md +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/README.md @@ -115,7 +115,7 @@ The [@IR](./IR.java) annotation provides two kinds of checks: - `counts`: A list of one or more "IR node/user-defined regex - counter" pairs which specify how often each IR node/user-defined regex should be matched on the compilation output of each compile phase. #### Disable/Enable IR Rules based on VM Flags -One might also want to restrict the application of certain `@IR` rules depending on the used flags in the test VM. These could be flags defined by the user or by JTreg. In the latter case, the flags must be whitelisted in `JTREG_WHITELIST_FLAGS` in [TestFramework](./TestFramework.java) (i.e. have no unexpected impact on the IR except if the flag simulates a specific machine setup like `UseAVX={1,2,3}` etc.) to enable an IR verification by the framework. The `@IR` rules thus have an option to restrict their application: +One might also want to restrict the application of certain `@IR` rules depending on the used flags in the Test VM. These could be flags defined by the user or by JTreg. In the latter case, the flags must be whitelisted in `JTREG_WHITELIST_FLAGS` in [TestFramework](./TestFramework.java) (i.e. have no unexpected impact on the IR except if the flag simulates a specific machine setup like `UseAVX={1,2,3}` etc.) to enable an IR verification by the framework. The `@IR` rules thus have an option to restrict their application: - `applyIf`: Only apply a rule if a flag has the specified value/range of values. - `applyIfNot`: Only apply a rule if a flag has **not** a specified value/range of values @@ -144,7 +144,7 @@ An IR verification cannot always be performed. Certain VM flags explicitly disab More information about IR matching can be found in the Javadocs of [IR](./IR.java). Concrete examples on how to specify IR constraint/rules can be found in [IRExample](../../../testlibrary_tests/ir_framework/examples/IRExample.java), [TestIRMatching](../../../testlibrary_tests/ir_framework/tests/TestIRMatching.java) (internal framework test), and [TestPhaseIRMatching](../../../testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java) (internal framework test). ### 2.3 Test VM Flags and Scenarios -The recommended way to use the framework is by defining a single `@run driver` statement in the JTreg header which, however, does not allow the specification of additional test VM flags. Instead, the user has the possibility to provide VM flags by calling `TestFramework.runWithFlags()` or by creating a `TestFramework` builder object on which `addFlags()` can be called. +The recommended way to use the framework is by defining a single `@run driver` statement in the JTreg header which, however, does not allow the specification of additional Test VM flags. Instead, the user has the possibility to provide VM flags by calling `TestFramework.runWithFlags()` or by creating a `TestFramework` builder object on which `addFlags()` can be called. If a user wants to provide multiple flag combinations for a single test, he or she has the option to provide different scenarios. A scenario based flag will always have precedence over other user defined flags. More information about scenarios can be found in the Javadocs of [Scenario](./Scenario.java). If a user wants to test all combinations of multiple sets of flags, they can use `TestFramework.addCrossProductScenarios()`. @@ -174,16 +174,16 @@ The framework provides various stress and debug flags. They should mainly be use - `-DExclude=test3`: Provide a list of `@Test` method names which should be excluded from execution. - `-DScenarios=1,2`: Provide a list of scenario indexes to specify which scenarios should be executed. - `-DWarmup=200`: Provide a new default value of the number of warm-up iterations (framework default is 2000). This might have an influence on the resulting IR and could lead to matching failures (the user can also set a fixed default warm-up value in a test with `testFrameworkObject.setDefaultWarmup(200)`). -- `-DReportStdout=true`: Print the standard output of the test VM. +- `-DReportStdout=true`: Print the standard output of the Test VM. - `-DVerbose=true`: Enable more fine-grained logging (slows the execution down). -- `-DReproduce=true`: Flag to use when directly running a test VM to bypass dependencies to the driver VM state (for example, when reproducing an issue). +- `-DReproduce=true`: Flag to use when directly running a Test VM to bypass dependencies to the Driver VM state (for example, when reproducing an issue). - `-DPrintTimes=true`: Print the execution time measurements of each executed test. - `-DPrintRuleMatchingTime=true`: Print the time of matching IR rules per method. Slows down the execution as the rules are warmed up before measurement. -- `-DVerifyVM=true`: The framework runs the test VM with additional verification flags (slows the execution down). +- `-DVerifyVM=true`: The framework runs the Test VM with additional verification flags (slows the execution down). - `-DExcludeRandom=true`: The framework randomly excludes some methods from compilation. IR verification is disabled completely with this flag. - `-DFlipC1C2=true`: The framework compiles all `@Test` annotated method with C1 if a C2 compilation would have been applied and vice versa. IR verification is disabled completely with this flag. - `-DShuffleTests=false`: Disables the random execution order of all tests (such a shuffling is always done by default). -- `-DDumpReplay=true`: Add the `DumpReplay` directive to the test VM. +- `-DDumpReplay=true`: Add the `DumpReplay` directive to the Test VM. - `-DGCAfter=true`: Perform `System.gc()` after each test (slows the execution down). - `-DTestCompilationTimeout=20`: Change the default waiting time (default: 10s) for a compilation of a normal `@Test` annotated method. - `-DWaitForCompilationTimeout=20`: Change the default waiting time (default: 10s) for a compilation of a `@Test` annotated method with compilation level [WAIT\_FOR\_COMPILATION](./CompLevel.java). @@ -193,12 +193,12 @@ The framework provides various stress and debug flags. They should mainly be use ## 3. Test Framework Execution This section gives an overview of how the framework is executing a JTreg test that calls the framework from within its `main()` method. -The framework will spawn a new "test VM" to execute the user defined tests. The test VM collects all tests of the test class specified by the user code in `main()` and ensures that there is no violation of the required format by the framework. In a next step, the framework does the following for each test in general: +The framework will spawn a new "Test VM" to execute the user defined tests. The Test VM collects all tests of the test class specified by the user code in `main()` and ensures that there is no violation of the required format by the framework. In a next step, the framework does the following for each test in general: 1. Warm the test up for a predefined number of times (default 2000). This can also be adapted for all tests by using `testFrameworkobject.setDefaultWarmup(100)` or for individual tests with an additional [@Warmup](./Warmup.java) annotation. 2. After the warm-up is finished, the framework compiles the associated `@Test` annotated method at the specified compilation level (default: C2). 3. After the compilation, the test is invoked one more time. -Once the test VM terminates, IR verification (if possible) is performed on the output of the test VM. If any test throws an exception during its execution or if IR matching fails, the failures are collected and reported in a pretty format. Check the standard error and output for more information and how to reproduce these failures. +Once the Test VM terminates, IR verification (if possible) is performed on the output of the Test VM. If any test throws an exception during its execution or if IR matching fails, the failures are collected and reported in a pretty format. Check the standard error and output for more information and how to reproduce these failures. Some of the steps above can be different due to the kind of the test or due to using non-default annotation properties. These details and differences are described in the Javadocs for the three tests (see section 2.1 Different Tests). @@ -212,10 +212,10 @@ Additional testing was performed by converting all compiler Inline Types tests t ## 5. Framework Package Structure A user only needs to import classes from the package `compiler.lib.ir_framework` (e.g. `import compiler.lib.ir_framework.*;`) which represents the interface classes to the framework. The remaining framework internal classes are kept in separate subpackages and should not directly be imported: -- `compiler.lib.ir_framework.driver`: These classes are used while running the driver VM (same VM as the one running the user code's `main()` method of a JTreg test). -- `compiler.lib.ir_framework.flag`: These classes are used while running the flag VM to determine additional flags for the test VM which are required for IR verification. -- `compiler.lib.ir_framework.test`: These classes are used while running the test VM (i.e. the actual execution of the user tests as described in section 3). -- `compiler.lib.ir_framework.shared`: These classes can be called from either the driver, flag, or test VM. +- `compiler.lib.ir_framework.driver`: These classes are used while running the Driver VM (same VM as the one running the user code's `main()` method of a JTreg test). +- `compiler.lib.ir_framework.flag`: These classes are used while running the Flag VM to determine additional flags for the Test VM which are required for IR verification. +- `compiler.lib.ir_framework.test`: These classes are used while running the Test VM (i.e. the actual execution of the user tests as described in section 3). +- `compiler.lib.ir_framework.shared`: These classes can be called from either the driver, flag, or Test VM. ## 6. Summary The initial design and feature set was kept simple and straight forward and serves well for small to medium sized tests. There are a lot of possibilities to further enhance the framework and make it more powerful. This can be tackled in additional RFEs. A few ideas can be found as subtasks of the [initial RFE](https://bugs.openjdk.org/browse/JDK-8254129) for this framework. diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/Scenario.java b/test/hotspot/jtreg/compiler/lib/ir_framework/Scenario.java index 17776f7285c..65f61173e2a 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/Scenario.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/Scenario.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 @@ -121,16 +121,16 @@ public class Scenario { } /** - * Get the test VM output (stdout + stderr) of this scenario from the last execution of the framework. + * Get the Test VM output (stdout + stderr) of this scenario from the last execution of the framework. * - * @return the test VM output. + * @return the Test VM output. */ public String getTestVMOutput() { return testVMOutput; } /** - * Set the test VM output, called by the framework. + * Set the Test VM output, called by the framework. */ void setTestVMOutput(String testVMOutput) { this.testVMOutput = testVMOutput; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index 09e291ce5a4..1fea5da52ef 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.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 @@ -85,19 +85,19 @@ import java.util.stream.Stream; * {@code runXX()} methods of {@link TestFramework}. The second way, which gives more control, is to create a new * {@code TestFramework} builder object on which {@link #start()} needs to be eventually called to start the testing. *

      - * The framework is called from the driver VM in which the JTreg test is initially run by specifying {@code + * The framework is called from the Driver VM in which the JTreg test is initially run by specifying {@code * @run driver} in the JTreg header. This strips all additionally specified JTreg VM and Javaoptions. - * The framework creates a new flag VM with all these flags added again in order to figure out which flags are + * The framework creates a new Flag VM with all these flags added again in order to figure out which flags are * required to run the tests specified in the test class (e.g. {@code -XX:+PrintIdeal} and {@code -XX:+PrintOptoAssembly} * for IR matching). *

      - * After the flag VM terminates, it starts a new test VM which performs the execution of the specified + * After the Flag VM terminates, it starts a new Test VM which performs the execution of the specified * tests in the test class as described in {@link Test}, {@link Check}, and {@link Run}. *

      - * In a last step, once the test VM has terminated without exceptions, IR matching is performed if there are any IR + * In a last step, once the Test VM has terminated without exceptions, IR matching is performed if there are any IR * rules and if no VM flags disable it (e.g. not running with {@code -Xint}, see {@link IR} for more details). * The IR regex matching is done on the output of {@code -XX:+PrintIdeal} and {@code -XX:+PrintOptoAssembly} by parsing - * the hotspot_pid file of the test VM. Failing IR rules are reported by throwing a {@link IRViolationException}. + * the hotspot_pid file of the Test VM. Failing IR rules are reported by throwing a {@link IRViolationException}. * * @see Test * @see Check @@ -166,7 +166,7 @@ public class TestFramework { ############################################################# - To only run the failed tests use -DTest, -DExclude, and/or -DScenarios. - - To also get the standard output of the test VM run with + - To also get the standard output of the Test VM run with -DReportStdout=true or for even more fine-grained logging use -DVerbose=true. ############################################################# @@ -236,10 +236,10 @@ public class TestFramework { } /** - * Tests the class from which this method was invoked from. The test VM is called with the specified {@code flags}. + * Tests the class from which this method was invoked from. The Test VM is called with the specified {@code flags}. *

        *
      • The {@code flags} override any set VM or Javaoptions flags by JTreg by default.

        - * Use {@code -DPreferCommandLineFlags=true} if you want to prefer the JTreg VM and Javaoptions flags over + * Use {@code -DPreferCommandLineFlags=true} if you want to prefer the JTreg VM and Javaoptions flags over * the specified {@code flags} of this method.

      • *
      • If you want to run your entire JTreg test with additional flags, use this method.

      • *
      • If you want to run your entire JTreg test with additional flags but for another test class then the one @@ -248,7 +248,7 @@ public class TestFramework { * {@link #addScenarios(Scenario...)}

      • *
      * - * @param flags VM flags to be used for the test VM. + * @param flags VM flags to be used for the Test VM. */ public static void runWithFlags(String... flags) { StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); @@ -258,13 +258,13 @@ public class TestFramework { } /** - * Add VM flags to be used for the test VM. These flags override any VM or Javaoptions set by JTreg by default.

      + * Add VM flags to be used for the Test VM. These flags override any VM or Javaoptions set by JTreg by default.

      * Use {@code -DPreferCommandLineFlags=true} if you want to prefer the VM or Javaoptions over the scenario flags. * *

      * The testing can be started by invoking {@link #start()} * - * @param flags VM options to be applied to the test VM. + * @param flags VM options to be applied to the Test VM. * @return the same framework instance. */ public TestFramework addFlags(String... flags) { @@ -306,7 +306,7 @@ public class TestFramework { } /** - * Add scenarios to be used for the test VM. A test VM is called for each scenario in {@code scenarios} by using the + * Add scenarios to be used for the Test VM. A Test VM is called for each scenario in {@code scenarios} by using the * specified VM flags in the scenario. The scenario flags override any flags set by {@link #addFlags(String...)} * and thus also override any VM or Javaoptions set by JTreg by default.

      * Use {@code -DPreferCommandLineFlags=true} if you want to prefer the VM and Javaoptions over the scenario flags. @@ -314,7 +314,7 @@ public class TestFramework { *

      * The testing can be started by invoking {@link #start()} * - * @param scenarios scenarios which specify specific flags for the test VM. + * @param scenarios scenarios which specify specific flags for the Test VM. * @return the same framework instance. */ public TestFramework addScenarios(Scenario... scenarios) { @@ -503,10 +503,10 @@ public class TestFramework { } /** - * Get the VM output of the test VM. Use {@code -DVerbose=true} to enable more debug information. If scenarios + * Get the VM output of the Test VM. Use {@code -DVerbose=true} to enable more debug information. If scenarios * were run, use {@link Scenario#getTestVMOutput()}. * - * @return the last test VM output. + * @return the last Test VM output. */ public static String getLastTestVMOutput() { return TestVMProcess.getLastTestVMOutput(); @@ -796,9 +796,9 @@ public class TestFramework { } /** - * Execute a separate "flag" VM with White Box access to determine all test VM flags. The flag VM sends an encoding of - * all required flags for the test VM to the driver VM over a socket. Once the flag VM exits, this driver VM parses the - * test VM flags, which also determine if IR matching should be done, and then starts the test VM to execute all tests. + * Execute a separate Flag VM with White Box access to determine all Test VM flags. The Flag VM sends an encoding of + * all required flags for the Test VM to the Driver VM over a socket. Once the Flag VM exits, this Driver VM parses the + * Test VM flags, which also determine if IR matching should be done, and then starts the Test VM to execute all tests. */ private void start(Scenario scenario) { if (scenario != null && !scenario.isEnabled()) { @@ -823,12 +823,12 @@ public class TestFramework { "" : " - [" + String.join(", ", additionalFlags) + "]"; if (shouldVerifyIR) { - // Only need to use flag VM if an IR verification is possibly done. + // Only need to use Flag VM if an IR verification is possibly done. System.out.println("Run Flag VM:"); FlagVMProcess flagVMProcess = new FlagVMProcess(testClass, additionalFlags); shouldVerifyIR = flagVMProcess.shouldVerifyIR(); if (shouldVerifyIR) { - // Add more flags for the test VM which are required to do IR verification. + // Add more flags for the Test VM which are required to do IR verification. additionalFlags.addAll(flagVMProcess.getTestVMFlags()); } // else: Flag VM found a reason to not do IR verification. } else { @@ -882,7 +882,7 @@ public class TestFramework { try { TestClassParser testClassParser = new TestClassParser(testClass, isAllowNotCompilable); Matchable testClassMatchable = testClassParser.parse(testVMProcess.getHotspotPidFileName(), - testVMProcess.getIrEncoding()); + testVMProcess.getApplicableIRRules()); IRMatcher matcher = new IRMatcher(testClassMatchable); matcher.match(); } catch (IRViolationException e) { @@ -892,7 +892,7 @@ public class TestFramework { } else { System.out.println("IR verification disabled either due to no @IR annotations, through explicitly setting " + "-DVerify=false, due to not running a debug build, using a non-whitelisted JTreg VM or " + - "Javaopts flag like -Xint, or running the test VM with other VM flags added by user code " + + "Javaopts flag like -Xint, or running the Test VM with other VM flags added by user code " + "that make the IR verification impossible (e.g. -XX:-UseCompile, " + "-XX:TieredStopAtLevel=[1,2,3], etc.)."); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/FlagVMProcess.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/FlagVMProcess.java index 0b7d1db125c..86eb1935207 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/FlagVMProcess.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/FlagVMProcess.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 @@ -41,9 +41,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * This class prepares, creates, and runs the "flag" VM with verification of proper termination. The flag VM determines - * the flags required for the "test" VM. The flag VM writes these flags to a dedicated file which is then parsed by this - * class after the termination of the flag VM. + * This class prepares, creates, and runs the Flag VM with verification of proper termination. The Flag VM determines + * the flags required for the Test VM. The Flag VM writes these flags to a dedicated file which is then parsed by this + * class after the termination of the Flag VM. * * @see FlagVM */ @@ -73,7 +73,7 @@ public class FlagVMProcess { String patternString = "(.*DShouldDoIRVerification=(true|false).*)"; Pattern pattern = Pattern.compile(patternString); Matcher matcher = pattern.matcher(flags); - TestFramework.check(matcher.find(), "Invalid flag encoding emitted by flag VM"); + TestFramework.check(matcher.find(), "Invalid flag encoding emitted by Flag VM"); // Maybe we run with flags that make IR verification impossible shouldVerifyIR = Boolean.parseBoolean(matcher.group(2)); testVMFlags.addAll(Arrays.asList(matcher.group(1).split(FlagVM.TEST_VM_FLAGS_DELIMITER))); @@ -91,8 +91,8 @@ public class FlagVMProcess { } /** - * The flag VM needs White Box access to prepare all test VM flags. The flag VM will write the test VM flags to - * a dedicated file which is afterwards parsed by the driver VM and added as flags to the test VM. + * The Flag VM needs White Box access to prepare all Test VM flags. The Flag VM will write the Test VM flags to + * a dedicated file which is afterwards parsed by the Driver VM and added as flags to the Test VM. */ private void prepareVMFlags(Class testClass, List additionalFlags) { cmds.add("-Dtest.jdk=" + Utils.TEST_JDK); @@ -103,7 +103,7 @@ public class FlagVMProcess { cmds.add("-Xbootclasspath/a:."); cmds.add("-XX:+UnlockDiagnosticVMOptions"); cmds.add("-XX:+WhiteBoxAPI"); - // TestFramework and scenario flags might have an influence on the later used test VM flags. Add them as well. + // TestFramework and scenario flags might have an influence on the later used Test VM flags. Add them as well. cmds.addAll(additionalFlags); cmds.add(FlagVM.class.getCanonicalName()); cmds.add(testClass.getCanonicalName()); @@ -111,10 +111,10 @@ public class FlagVMProcess { private void start() { try { - // Run "flag" VM with White Box access to determine the test VM flags and if IR verification should be done. + // Run Flag VM with White Box access to determine the Test VM flags and if IR verification should be done. oa = ProcessTools.executeTestJava(cmds); } catch (Exception e) { - throw new TestRunException("Failed to execute TestFramework flag VM", e); + throw new TestRunException("Failed to execute TestFramework Flag VM", e); } testVMFlagsFile = FlagVM.TEST_VM_FLAGS_FILE_PREFIX + oa.pid() + FlagVM.FILE_POSTFIX; @@ -125,14 +125,14 @@ public class FlagVMProcess { String flagVMOutput = oa.getOutput(); int exitCode = oa.getExitValue(); if (VERBOSE && exitCode == 0) { - System.out.println("--- OUTPUT TestFramework flag VM ---"); + System.out.println("--- OUTPUT TestFramework Flag VM ---"); System.out.println(flagVMOutput); } if (exitCode != 0) { - System.err.println("--- OUTPUT TestFramework flag VM ---"); + System.err.println("--- OUTPUT TestFramework Flag VM ---"); System.err.println(flagVMOutput); - throw new RuntimeException("TestFramework flag VM exited with " + exitCode); + throw new RuntimeException("TestFramework Flag VM exited with " + exitCode); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMException.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMException.java index 63b3d522b86..8de9ac8e348 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMException.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMException.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 @@ -26,7 +26,7 @@ package compiler.lib.ir_framework.driver; import compiler.lib.ir_framework.shared.TestFormatException; /** - * Exception that is thrown if the test VM has thrown any kind of exception (except for {@link TestFormatException}). + * Exception that is thrown if the Test VM has thrown any kind of exception (except for {@link TestFormatException}). */ public class TestVMException extends RuntimeException { private final String exceptionInfo; @@ -37,9 +37,9 @@ public class TestVMException extends RuntimeException { } /** - * Get some more detailed information about the exception thrown in the test VM and how to reproduce it. + * Get some more detailed information about the exception thrown in the Test VM and how to reproduce it. * - * @return a formatted string containing information about the exception of the test VM and how to reproduce it. + * @return a formatted string containing information about the exception of the Test VM and how to reproduce it. */ public String getExceptionInfo() { return exceptionInfo; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java index 2b7fd2a1eea..a172dce1991 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.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 @@ -41,9 +41,9 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; /** - * This class prepares, creates, and runs the "test" VM with verification of proper termination. The class also stores - * information about the test VM which is later queried for IR matching. The communication between this driver VM - * and the test VM is done over a dedicated socket. + * This class prepares, creates, and runs the Test VM with verification of proper termination. The class also stores + * information about the Test VM which is later queried for IR matching. The communication between this Driver VM + * and the Test VM is done over a dedicated socket. * * @see TestVM * @see TestFrameworkSocket @@ -62,7 +62,7 @@ public class TestVMProcess { private String hotspotPidFileName; private String commandLine; private OutputAnalyzer oa; - private String irEncoding; + private String applicableIRRules; public TestVMProcess(List additionalFlags, Class testClass, Set> helperClasses, int defaultWarmup, boolean allowNotCompilable, boolean testClassesOnBootClassPath) { @@ -81,8 +81,8 @@ public class TestVMProcess { return commandLine; } - public String getIrEncoding() { - return irEncoding; + public String getApplicableIRRules() { + return applicableIRRules; } public String getHotspotPidFileName() { @@ -98,7 +98,7 @@ public class TestVMProcess { boolean testClassesOnBootClassPath) { // Set java.library.path so JNI tests which rely on jtreg nativepath setting work cmds.add("-Djava.library.path=" + Utils.TEST_NATIVE_PATH); - // Need White Box access in test VM. + // Need White Box access in Test VM. String bootClassPath = "-Xbootclasspath/a:."; if (testClassesOnBootClassPath) { // Add test classes themselves to boot classpath to make them privileged. @@ -112,7 +112,8 @@ public class TestVMProcess { if (!PREFER_COMMAND_LINE_FLAGS) { cmds.addAll(jtregVMFlags); } - // Add server property flag that enables test VM to print encoding for IR verification last and debug messages. + // Add server property flag that enables the Test VM to print the Applicable IR Rules for IR verification and + // debug messages. cmds.add(socket.getPortPropertyFlag()); cmds.addAll(additionalFlags); cmds.addAll(Arrays.asList(getDefaultFlags())); @@ -142,7 +143,7 @@ public class TestVMProcess { } /** - * Default flags that are added used for the test VM. + * Default flags that are added used for the Test VM. */ private static String[] getDefaultFlags() { return new String[] {"-XX:-BackgroundCompilation", "-XX:CompileCommand=quiet"}; @@ -169,7 +170,7 @@ public class TestVMProcess { throw new TestFrameworkException("Error while executing Test VM", e); } - process.command().add(1, "-DReproduce=true"); // Add after "/path/to/bin/java" in order to rerun the test VM directly + process.command().add(1, "-DReproduce=true"); // Add after "/path/to/bin/java" in order to rerun the Test VM directly commandLine = "Command Line:" + System.lineSeparator() + String.join(" ", process.command()) + System.lineSeparator(); hotspotPidFileName = String.format("hotspot_pid%d.log", oa.pid()); @@ -178,7 +179,7 @@ public class TestVMProcess { /** * Process the socket output: All prefixed lines are dumped to the standard output while the remaining lines - * represent the IR encoding used for IR matching later. + * represent the Applicable IR Rules used for IR matching later. */ private void processSocketOutput(TestFrameworkSocket socket) { String output = socket.getOutput(); @@ -215,16 +216,16 @@ public class TestVMProcess { System.out.println("---------------------"); System.out.println(messagesBuilder); } - irEncoding = nonStdOutBuilder.toString(); + applicableIRRules = nonStdOutBuilder.toString(); } else { - irEncoding = output; + applicableIRRules = output; } } private void checkTestVMExitCode() { final int exitCode = oa.getExitValue(); if (EXCLUDE_RANDOM || REPORT_STDOUT || (VERBOSE && exitCode == 0)) { - System.out.println("--- OUTPUT TestFramework test VM ---"); + System.out.println("--- OUTPUT TestFramework Test VM ---"); System.out.println(oa.getOutput()); } @@ -234,7 +235,7 @@ public class TestVMProcess { } /** - * Exit code was non-zero of test VM. Check the stderr to determine what kind of exception that should be thrown to + * Exit code was non-zero of Test VM. Check the stderr to determine what kind of exception that should be thrown to * react accordingly later. */ private void throwTestVMException() { @@ -266,7 +267,7 @@ public class TestVMProcess { stdOut = System.lineSeparator() + System.lineSeparator() + "Standard Output" + System.lineSeparator() + "---------------" + System.lineSeparator() + oa.getOutput(); } - return "TestFramework test VM exited with code " + exitCode + System.lineSeparator() + stdOut + return "TestFramework Test VM exited with code " + exitCode + System.lineSeparator() + stdOut + System.lineSeparator() + commandLine + System.lineSeparator() + System.lineSeparator() + "Error Output" + System.lineSeparator() + "------------" + System.lineSeparator() + stdErr + System.lineSeparator() + System.lineSeparator(); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IREncodingParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java similarity index 60% rename from test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IREncodingParser.java rename to test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java index 28c4213f82d..7aaf0c2b285 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IREncodingParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, 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 @@ -28,7 +28,7 @@ import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.HotSpotPidFileParser; import compiler.lib.ir_framework.shared.TestFormat; import compiler.lib.ir_framework.shared.TestFrameworkException; -import compiler.lib.ir_framework.test.IREncodingPrinter; +import compiler.lib.ir_framework.test.ApplicableIRRulesPrinter; import java.lang.reflect.Method; import java.util.HashMap; @@ -37,34 +37,34 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * Class to parse the IR encoding emitted by the test VM and creating {@link TestMethod} objects for each entry. + * Class to parse the Applicable IR Rules emitted by the Test VM and creating {@link TestMethod} objects for each entry. * * @see TestMethod */ -public class IREncodingParser { +public class ApplicableIRRulesParser { - private static final boolean PRINT_IR_ENCODING = Boolean.parseBoolean(System.getProperty("PrintIREncoding", "false")); - private static final Pattern IR_ENCODING_PATTERN = - Pattern.compile("(?<=" + IREncodingPrinter.START + "\r?\n).*\\R([\\s\\S]*)(?=" + IREncodingPrinter.END + ")"); + private static final boolean PRINT_APPLICABLE_IR_RULES = Boolean.parseBoolean(System.getProperty("PrintApplicableIRRules", "false")); + private static final Pattern APPLICABLE_IR_RULES_PATTERN = + Pattern.compile("(?<=" + ApplicableIRRulesPrinter.START + "\r?\n).*\\R([\\s\\S]*)(?=" + ApplicableIRRulesPrinter.END + ")"); private final Map testMethods; private final Class testClass; - public IREncodingParser(Class testClass) { + public ApplicableIRRulesParser(Class testClass) { this.testClass = testClass; this.testMethods = new HashMap<>(); } /** - * Parse the IR encoding passed as parameter and return a "test name" -> TestMethod map that contains an entry - * for each method that needs to be IR matched on. + * 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 irEncoding) { - if (TestFramework.VERBOSE || PRINT_IR_ENCODING) { - System.out.println("Read IR encoding from test VM:"); - System.out.println(irEncoding); + public TestMethods parse(String applicableIRRules) { + if (TestFramework.VERBOSE || PRINT_APPLICABLE_IR_RULES) { + System.out.println("Read Applicable IR Rules from Test VM:"); + System.out.println(applicableIRRules); } - createTestMethodMap(irEncoding, testClass); + createTestMethodMap(applicableIRRules, testClass); // We could have found format errors in @IR annotations. Report them now with an exception. TestFormat.throwIfAnyFailures(); return new TestMethods(testMethods); @@ -74,22 +74,22 @@ public class IREncodingParser { * 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 irEncoding, Class testClass) { - Map irRulesMap = parseIREncoding(irEncoding); - createTestMethodsWithEncoding(testClass, irRulesMap); + private void createTestMethodMap(String applicableIRRules, Class testClass) { + Map irRulesMap = parseApplicableIRRules(applicableIRRules); + createTestMethodsWithApplicableIRRules(testClass, irRulesMap); } /** - * Read the IR encoding emitted by the test VM to decide if an @IR rule must be checked for a method. + * Read the Applicable IR Rules emitted by the Test VM to decide if an @IR rule must be checked for a method. */ - private Map parseIREncoding(String irEncoding) { + private Map parseApplicableIRRules(String applicableIRRules) { Map irRulesMap = new HashMap<>(); - String[] irEncodingLines = getIREncodingLines(irEncoding); - for (String s : irEncodingLines) { + String[] applicableIRRulesLines = getApplicableIRRulesLines(applicableIRRules); + for (String s : applicableIRRulesLines) { String line = s.trim(); String[] splitLine = line.split(","); if (splitLine.length < 2) { - throw new TestFrameworkException("Invalid IR match rule encoding. No comma found: " + splitLine[0]); + throw new TestFrameworkException("Invalid Applicable IR Rules format. No comma found: " + splitLine[0]); } String testName = splitLine[0]; int[] irRulesIdx = getRuleIndexes(splitLine); @@ -99,11 +99,12 @@ public class IREncodingParser { } /** - * Parse the IR encoding lines without header, explanation line and footer and return them in an array. + * Parse the Applicable IR Rules lines without header, explanation line and footer and return them in an array. */ - private String[] getIREncodingLines(String irEncoding) { - Matcher matcher = IR_ENCODING_PATTERN.matcher(irEncoding); - TestFramework.check(matcher.find(), "Did not find IR encoding in:" + System.lineSeparator() + irEncoding); + private String[] getApplicableIRRulesLines(String applicableIRRules) { + Matcher matcher = APPLICABLE_IR_RULES_PATTERN.matcher(applicableIRRules); + TestFramework.check(matcher.find(), "Did not find Applicable IR Rules in:" + + System.lineSeparator() + applicableIRRules); String lines = matcher.group(1).trim(); if (lines.isEmpty()) { // Nothing to IR match. @@ -113,7 +114,7 @@ public class IREncodingParser { } /** - * Parse rule indexes from IR encoding line of the format: + * Parse rule indexes from a single line of the Applicable IR Rules in the format: */ private int[] getRuleIndexes(String[] splitLine) { int[] irRulesIdx = new int[splitLine.length - 1]; @@ -121,13 +122,13 @@ public class IREncodingParser { try { irRulesIdx[i - 1] = Integer.parseInt(splitLine[i]); } catch (NumberFormatException e) { - throw new TestFrameworkException("Invalid IR match rule encoding. No number found: " + splitLine[i]); + throw new TestFrameworkException("Invalid Applicable IR Rules format. No number found: " + splitLine[i]); } } return irRulesIdx; } - private void createTestMethodsWithEncoding(Class testClass, Map irRulesMap) { + private void createTestMethodsWithApplicableIRRules(Class testClass, Map irRulesMap) { for (Method m : testClass.getDeclaredMethods()) { IR[] irAnnos = m.getAnnotationsByType(IR.class); if (irAnnos.length > 0) { @@ -144,7 +145,7 @@ public class IREncodingParser { private void validateIRRuleIds(Method m, IR[] irAnnos, int[] ids) { TestFramework.check(ids != null, "Should find method name in validIrRulesMap for " + m); TestFramework.check(ids.length > 0, "Did not find any rule indices for " + m); - TestFramework.check((ids[0] >= 1 || ids[0] == IREncodingPrinter.NO_RULE_APPLIED) + TestFramework.check((ids[0] >= 1 || ids[0] == ApplicableIRRulesPrinter.NO_RULE_APPLIED) && ids[ids.length - 1] <= irAnnos.length, "Invalid IR rule index found in validIrRulesMap for " + m); } @@ -153,6 +154,6 @@ public class IREncodingParser { * Does the list of IR rules contain any applicable IR rules for the given conditions? */ private boolean hasAnyApplicableIRRules(int[] irRuleIds) { - return irRuleIds[0] != IREncodingPrinter.NO_RULE_APPLIED; + return irRuleIds[0] != ApplicableIRRulesPrinter.NO_RULE_APPLIED; } } 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 538680496e7..d7a10acd1cd 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 @@ -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 @@ -24,7 +24,6 @@ package compiler.lib.ir_framework.driver.irmatching.parser; import compiler.lib.ir_framework.Test; -import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.driver.irmatching.Compilation; import compiler.lib.ir_framework.driver.irmatching.irmethod.IRMethod; import compiler.lib.ir_framework.driver.irmatching.irmethod.IRMethodMatchable; @@ -53,7 +52,7 @@ class IRMethodBuilder { } /** - * Create IR methods for all test methods identified by {@link IREncodingParser} by combining them with the parsed + * Create IR methods for all test methods identified by {@link ApplicableIRRulesParser} by combining them with the parsed * compilation output from {@link HotSpotPidFileParser}. */ public SortedSet build(VMInfo vmInfo) { 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 b6c9920c2d1..2329b41afbe 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 @@ -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 @@ -50,13 +50,13 @@ public class TestClassParser { } /** - * Parse the IR encoding and hotspot_pid* file to create a collection of {@link IRMethod} objects. + * Parse the Applicable IR Rules and hotspot_pid* file to create a collection of {@link IRMethod} objects. * Return a default/empty TestClass object if there are no applicable @IR rules in any method of the test class. */ - public Matchable parse(String hotspotPidFileName, String irEncoding) { - IREncodingParser irEncodingParser = new IREncodingParser(testClass); - TestMethods testMethods = irEncodingParser.parse(irEncoding); - VMInfo vmInfo = VMInfoParser.parseVMInfo(irEncoding); + public Matchable parse(String hotspotPidFileName, String applicableIRRules) { + ApplicableIRRulesParser applicableIRRulesParser = new ApplicableIRRulesParser(testClass); + TestMethods testMethods = applicableIRRulesParser.parse(applicableIRRules); + VMInfo vmInfo = VMInfoParser.parseVMInfo(applicableIRRules); if (testMethods.hasTestMethods()) { HotSpotPidFileParser hotSpotPidFileParser = new HotSpotPidFileParser(testClass.getName(), testMethods); LoggedMethods loggedMethods = hotSpotPidFileParser.parse(hotspotPidFileName); @@ -66,7 +66,7 @@ public class TestClassParser { } /** - * Create test class with IR methods for all test methods identified by {@link IREncodingParser} by combining them + * Create test class with IR methods for all test methods identified by {@link ApplicableIRRulesParser} 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/TestMethod.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethod.java index 8491b8d61eb..43f27c80ac6 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethod.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, 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 @@ -30,10 +30,10 @@ import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.LoggedMethod; import java.lang.reflect.Method; /** - * This class represents a test method parsed by {@link IREncodingParser}. In combination with the associated + * This class represents a test method parsed by {@link ApplicableIRRulesParser}. In combination with the associated * {@link LoggedMethod}, a new {@link IRMethod} is created to IR match on later. * - * @see IREncodingParser + * @see ApplicableIRRulesParser * @see LoggedMethod * @see IRMethod */ diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethods.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethods.java index ecc8fb4e0b9..d1edfb08fdf 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethods.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethods.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 @@ -29,15 +29,15 @@ import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.HotSpotPidFile import java.util.Map; /** - * This class stores all test methods that need to be IR matched as identified by {@link IREncodingParser}. + * This class stores all test methods that need to be IR matched as identified by {@link ApplicableIRRulesParser}. * - * @see IREncodingParser + * @see ApplicableIRRulesParser * @see HotSpotPidFileParser * @see IRMethod */ public class TestMethods { /** - * "Method name" -> TestMethod map created by {@link IREncodingParser} which contains an entry for each method that + * "Method name" -> TestMethod map created by {@link ApplicableIRRulesParser} which contains an entry for each method that * needs to be IR matched on. */ private final Map testMethods; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.java index db70e7892a2..89b6d610496 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/VMInfo.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 @@ -33,7 +33,7 @@ import java.util.regex.Pattern; /** * This class stores the key value mapping from the VMInfo. * - * @see IREncodingParser + * @see ApplicableIRRulesParser */ public class 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 index a8e6782ab12..2b17303f1a7 100644 --- 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 @@ -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 @@ -33,7 +33,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * Class to parse the VMInfo emitted by the test VM and creating {@link VMInfo} objects for each entry. + * Class to parse the VMInfo emitted by the Test VM and creating {@link VMInfo} objects for each entry. * * @see VMInfo */ @@ -43,11 +43,11 @@ public class VMInfoParser { Pattern.compile("(?<=" + VMInfoPrinter.START_VM_INFO + "\r?\n).*\\R([\\s\\S]*)(?=" + VMInfoPrinter.END_VM_INFO + ")"); /** - * Extract VMInfo from the irEncoding. + * Extract VMInfo from the applicableIRRules. */ - public static VMInfo parseVMInfo(String irEncoding) { + public static VMInfo parseVMInfo(String applicableIRRules) { Map map = new HashMap<>(); - String[] lines = getVMInfoLines(irEncoding); + String[] lines = getVMInfoLines(applicableIRRules); for (String s : lines) { String line = s.trim(); String[] splitLine = line.split(":", 2); @@ -62,11 +62,11 @@ public class VMInfoParser { } /** - * Extract the VMInfo from the irEncoding string, strip away the header and return the individual key-value lines. + * Extract the VMInfo from the applicableIRRules string, strip away the header and return the individual key-value lines. */ - private static String[] getVMInfoLines(String irEncoding) { - Matcher matcher = VM_INFO_PATTERN.matcher(irEncoding); - TestFramework.check(matcher.find(), "Did not find VMInfo in:" + System.lineSeparator() + irEncoding); + private static String[] getVMInfoLines(String applicableIRRules) { + Matcher matcher = VM_INFO_PATTERN.matcher(applicableIRRules); + TestFramework.check(matcher.find(), "Did not find VMInfo in:" + System.lineSeparator() + applicableIRRules); String lines = matcher.group(1).trim(); if (lines.isEmpty()) { // Nothing to IR match. diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/CompileQueueMessages.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/CompileQueueMessages.java index 9ccb78ea892..f9fa0d1433e 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/CompileQueueMessages.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/CompileQueueMessages.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 @@ -24,7 +24,7 @@ package compiler.lib.ir_framework.driver.irmatching.parser.hotspot; import compiler.lib.ir_framework.TestFramework; -import compiler.lib.ir_framework.driver.irmatching.parser.IREncodingParser; +import compiler.lib.ir_framework.driver.irmatching.parser.ApplicableIRRulesParser; import compiler.lib.ir_framework.driver.irmatching.parser.TestMethods; import java.util.HashMap; @@ -34,9 +34,9 @@ import java.util.regex.Pattern; /** * This class parses compile queue messages found in the hotspot_pid* files and keeps track of those that need to be - * IR matched (i.e. identified by {@link IREncodingParser}. + * IR matched (i.e. identified by {@link ApplicableIRRulesParser}. * - * @see IREncodingParser + * @see ApplicableIRRulesParser */ class CompileQueueMessages { private static final Pattern COMPILE_ID_PATTERN = Pattern.compile("compile_id='(\\d+)'"); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/HotSpotPidFileParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/HotSpotPidFileParser.java index a5a5a0b20a9..f39f7cc9a83 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/HotSpotPidFileParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/hotspot/HotSpotPidFileParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, 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 @@ -24,7 +24,7 @@ package compiler.lib.ir_framework.driver.irmatching.parser.hotspot; import compiler.lib.ir_framework.driver.irmatching.irmethod.IRMethod; -import compiler.lib.ir_framework.driver.irmatching.parser.IREncodingParser; +import compiler.lib.ir_framework.driver.irmatching.parser.ApplicableIRRulesParser; import compiler.lib.ir_framework.driver.irmatching.parser.TestMethods; import compiler.lib.ir_framework.shared.TestFrameworkException; @@ -34,10 +34,10 @@ import java.nio.file.Paths; /** * Class to parse the ideal compile phases and PrintOptoAssembly outputs of the test class from the hotspot_pid* file - * of all methods identified by {@link IREncodingParser}. + * of all methods identified by {@link ApplicableIRRulesParser}. * * @see IRMethod - * @see IREncodingParser + * @see ApplicableIRRulesParser */ public class HotSpotPidFileParser { private final State state; @@ -47,8 +47,8 @@ public class HotSpotPidFileParser { } /** - * Parse the hotspot_pid*.log file from the test VM. Read the ideal compile phase and PrintOptoAssembly outputs for - * all methods defined by the IR encoding. + * Parse the hotspot_pid*.log file from the Test VM. Read the ideal compile phase and PrintOptoAssembly outputs for + * all methods defined by the Applicable IR Rules. */ public LoggedMethods parse(String hotspotPidFileName) { try (var reader = Files.newBufferedReader(Paths.get(hotspotPidFileName))) { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/flag/CompilePhaseCollector.java b/test/hotspot/jtreg/compiler/lib/ir_framework/flag/CompilePhaseCollector.java index 86ef31c6277..0488b3f58e6 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/flag/CompilePhaseCollector.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/flag/CompilePhaseCollector.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 @@ -53,7 +53,7 @@ class CompilePhaseCollector { collectCompilePhases(method)); } } catch (TestFormatException e) { - // Create default map and let the IR matcher report the format failures later in the driver VM. + // Create default map and let the IR matcher report the format failures later in the Driver VM. return createDefaultMap(testClass); } return methodNameToCompilePhasesMap; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/flag/FlagVM.java b/test/hotspot/jtreg/compiler/lib/ir_framework/flag/FlagVM.java index 42e4c85a499..4b033eb2c0e 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/flag/FlagVM.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/flag/FlagVM.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 @@ -38,8 +38,8 @@ import java.util.ArrayList; import java.util.Arrays; /** - * This class' main method is called from {@link TestFramework} and represents the so-called "flag VM". It uses the - * Whitebox API to determine the necessary additional flags to run the test VM (e.g. to do IR matching). It returns + * This class' main method is called from {@link TestFramework} and represents the so-called "Flag VM". It uses the + * Whitebox API to determine the necessary additional flags to run the Test VM (e.g. to do IR matching). It returns * the flags over the dedicated TestFramework socket. */ public class FlagVM { @@ -79,12 +79,12 @@ public class FlagVM { private static final boolean VERIFY_IR = REQUESTED_VERIFY_IR && USE_COMPILER && !EXCLUDE_RANDOM && !FLIP_C1_C2 && !TEST_C1 && Platform.isServer(); /** - * Main entry point of the flag VM. + * Main entry point of the Flag VM. */ public static void main(String[] args) { String testClassName = args[0]; if (VERBOSE) { - System.out.println("FlagVM main() called. Prepare test VM flags to run class " + testClassName); + System.out.println("FlagVM main() called. Prepare Test VM flags to run class " + testClassName); } Class testClass; try { @@ -96,8 +96,8 @@ public class FlagVM { } /** - * Emit test VM flags to the dedicated test VM flags file to parse them from the TestFramework "driver" VM again - * which adds them to the test VM. + * Emit Test VM flags to the dedicated Test VM flags file to parse them from the TestFramework Driver VM again + * which adds them to the Test VM. */ private static void emitTestVMFlags(ArrayList flags) { try (var bw = Files.newBufferedWriter(Paths.get(TEST_VM_FLAGS_FILE))) { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/NoTestsRunException.java b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/NoTestsRunException.java index 02afdd4ee4f..4b616f53e4d 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/NoTestsRunException.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/NoTestsRunException.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,17 +24,17 @@ package compiler.lib.ir_framework.shared; /** - * Exception that is thrown by the test VM if no tests are run as a result of specifying {@code -DTest} and/or - * {@code -DExclude} defining an empty set with the used test VM flags. + * Exception that is thrown by the Test VM if no tests are run as a result of specifying {@code -DTest} and/or + * {@code -DExclude} defining an empty set with the used Test VM flags. */ public class NoTestsRunException extends RuntimeException { /** - * Default constructor used by test VM + * Default constructor used by Test VM */ public NoTestsRunException() {} /** - * Constructor used to eventually throw the exception in the driver VM. + * Constructor used to eventually throw the exception in the Driver VM. */ public NoTestsRunException(String message) { super(message); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java index 1fdcb34a6b8..f10540ebc5b 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.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 @@ -37,7 +37,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** - * Dedicated socket to send data from the flag and test VM back to the driver VM. + * Dedicated socket to send data from the flag and Test VM back to the Driver VM. */ public class TestFrameworkSocket implements AutoCloseable { public static final String STDOUT_PREFIX = "[STDOUT]"; @@ -46,7 +46,7 @@ public class TestFrameworkSocket implements AutoCloseable { public static final String PRINT_TIMES_TAG = "[PRINT_TIMES]"; public static final String NOT_COMPILABLE_TAG = "[NOT_COMPILABLE]"; - // Static fields used for test VM only. + // Static fields used for Test VM only. private static final String SERVER_PORT_PROPERTY = "ir.framework.server.port"; private static final int SERVER_PORT = Integer.getInteger(SERVER_PORT_PROPERTY, -1); @@ -85,7 +85,7 @@ public class TestFrameworkSocket implements AutoCloseable { } /** - * Waits for a client (created by flag or test VM) to connect. Return the messages received from the client. + * Waits for a client (created by flag or Test VM) to connect. Return the messages received from the client. */ private FutureTask initSocketTask() { return new FutureTask<>(() -> { @@ -117,18 +117,18 @@ public class TestFrameworkSocket implements AutoCloseable { } /** - * Only called by test VM to write to server socket. + * Only called by Test VM to write to server socket. */ public static void write(String msg, String tag) { write(msg, tag, false); } /** - * Only called by test VM to write to server socket. + * Only called by Test VM to write to server socket. *

      - * The test VM is spawned by the main jtreg VM. The stdout of the test VM is hidden + * The Test VM is spawned by the main jtreg VM. The stdout of the Test VM is hidden * unless the Verbose or ReportStdout flag is used. TestFrameworkSocket is used by the parent jtreg - * VM and the test VM to communicate. By sending the prints through the TestFrameworkSocket with the + * VM and the Test VM to communicate. By sending the prints through the TestFrameworkSocket with the * parameter stdout set to true, the parent VM will print the received messages to its stdout, making it * visible to the user. */ @@ -137,10 +137,10 @@ public class TestFrameworkSocket implements AutoCloseable { System.out.println("Debugging Test VM: Skip writing due to -DReproduce"); return; } - TestFramework.check(SERVER_PORT != -1, "Server port was not set correctly for flag and/or test VM " - + "or method not called from flag or test VM"); + TestFramework.check(SERVER_PORT != -1, "Server port was not set correctly for flag and/or Test VM " + + "or method not called from flag or Test VM"); try { - // Keep the client socket open until the test VM terminates (calls closeClientSocket before exiting main()). + // Keep the client socket open until the Test VM terminates (calls closeClientSocket before exiting main()). if (clientSocket == null) { clientSocket = new Socket(InetAddress.getLoopbackAddress(), SERVER_PORT); clientWriter = new PrintWriter(clientSocket.getOutputStream(), true); @@ -150,11 +150,11 @@ public class TestFrameworkSocket implements AutoCloseable { } clientWriter.println(msg); } catch (Exception e) { - // When the test VM is directly run, we should ignore all messages that would normally be sent to the - // driver VM. + // When the Test VM is directly run, we should ignore all messages that would normally be sent to the + // Driver VM. String failMsg = System.lineSeparator() + System.lineSeparator() + """ ########################################################### - Did you directly run the test VM (TestVM class) + Did you directly run the Test VM (TestVM class) to reproduce a bug? => Append the flag -DReproduce=true and try again! ########################################################### @@ -169,7 +169,7 @@ public class TestFrameworkSocket implements AutoCloseable { /** * Closes (and flushes) the printer to the socket and the socket itself. Is called as last thing before exiting - * the main() method of the flag and the test VM. + * the main() method of the flag and the Test VM. */ public static void closeClientSocket() { if (clientSocket != null) { @@ -183,7 +183,7 @@ public class TestFrameworkSocket implements AutoCloseable { } /** - * Get the socket output of the flag VM. + * Get the socket output of the Flag VM. */ public String getOutput() { try { @@ -197,7 +197,7 @@ public class TestFrameworkSocket implements AutoCloseable { } /** - * Return whether test VM sent messages to be put on stdout (starting with {@link ::STDOUT_PREFIX}). + * Return whether Test VM sent messages to be put on stdout (starting with {@link ::STDOUT_PREFIX}). */ public boolean hasStdOut() { return receivedStdOut; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java similarity index 96% rename from test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java rename to test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java index a24cfbd3e37..4fa1f8f3fe5 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.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 @@ -39,12 +39,13 @@ import java.util.Objects; import java.util.function.Function; /** - * Prints an encoding to the dedicated test framework socket whether @IR rules of @Test methods should be applied or not. - * This is done during the execution of the test VM by checking the active VM flags. This encoding is eventually parsed - * and checked by the IRMatcher class in the driver VM after the termination of the test VM. IR rule indices start at 1. + * Prints all applicable IR rules to the dedicated test framework socket whether @IR rules of @Test methods should be + * applied or not. This is done during the execution of the Test VM by checking the active VM flags. This + * Applicable IR Rules message is eventually parsed and checked by the IRMatcher class in the Driver VM after the + * termination of the Test VM. IR rule indices start at 1. */ -public class IREncodingPrinter { - public static final String START = "##### IRMatchRulesEncoding - used by TestFramework #####"; +public class ApplicableIRRulesPrinter { + public static final String START = "##### ApplicableIRRules - used by TestFramework #####"; public static final String END = "----- END -----"; public static final int NO_RULE_APPLIED = -1; @@ -60,7 +61,7 @@ public class IREncodingPrinter { // Platforms for use in IR preconditions. Please verify that e.g. there is // a corresponding use in a jtreg @requires annotation before adding new platforms, // as adding non-existent platforms can lead to skipped tests. - private static final List irTestingPlatforms = new ArrayList(Arrays.asList( + private static final List irTestingPlatforms = new ArrayList<>(Arrays.asList( // os.family "aix", "linux", @@ -85,7 +86,7 @@ public class IREncodingPrinter { // Please verify new CPU features before adding them. If we allow non-existent features // on this list, we will ignore tests and never execute them. Consult CPU_FEATURE_FLAGS // in corresponding vm_version_.hpp file to find correct cpu feature's name. - private static final List verifiedCPUFeatures = new ArrayList( Arrays.asList( + private static final List verifiedCPUFeatures = new ArrayList<>( Arrays.asList( // x86 "fma", "f16c", @@ -126,7 +127,7 @@ public class IREncodingPrinter { "zvkn" )); - public IREncodingPrinter() { + public ApplicableIRRulesPrinter() { output.append(START).append(System.lineSeparator()); output.append(",{comma separated applied @IR rule ids}").append(System.lineSeparator()); } @@ -136,7 +137,7 @@ public class IREncodingPrinter { * - indices of all @IR rules that should be applied, separated by a comma * - "-1" if no @IR rule should not be applied */ - public void emitRuleEncoding(Method m, boolean skipped) { + public void emitApplicableIRRules(Method m, boolean skipped) { method = m; int i = 0; ArrayList validRules = new ArrayList<>(); @@ -170,7 +171,7 @@ public class IREncodingPrinter { private void printDisableReason(String method, String reason, String[] apply, int ruleIndex, int ruleMax) { TestFrameworkSocket.write("Disabling IR matching for rule " + ruleIndex + " of " + ruleMax + " in " + method + ": " + reason + ": " + String.join(", ", apply), - "[IREncodingPrinter]", true); + "[ApplicableIRRules]", true); } private boolean shouldApplyIrRule(IR irAnno, String m, int ruleIndex, int ruleMax) { @@ -521,7 +522,7 @@ public class IREncodingPrinter { public void emit() { output.append(END); - TestFrameworkSocket.write(output.toString(), "IR rule application encoding"); + TestFrameworkSocket.write(output.toString(), "ApplicableIRRules"); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java index 8ccd0faa013..c5ab318d67d 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.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 @@ -41,7 +41,7 @@ import java.util.stream.Stream; import static compiler.lib.ir_framework.shared.TestFrameworkSocket.PRINT_TIMES_TAG; /** - * This class' main method is called from {@link TestFramework} and represents the so-called "test VM". The class is + * This class' main method is called from {@link TestFramework} and represents the so-called "Test VM". The class is * the heart of the framework and is responsible for executing all the specified tests in the test class. It uses the * Whitebox API and reflection to achieve this task. */ @@ -58,7 +58,7 @@ public class TestVM { TestFramework in main() of your test? Make sure to only call setup/run methods and no checks or assertions from main() of your test! - - Are you rerunning the test VM (TestVM class) + - Are you rerunning the Test VM (TestVM class) directly after a JTreg run? Make sure to start it from within JTwork/scratch and with the flag -DReproduce=true! @@ -100,7 +100,7 @@ public class TestVM { private static final boolean DUMP_REPLAY = Boolean.getBoolean("DumpReplay"); private static final boolean GC_AFTER = Boolean.getBoolean("GCAfter"); private static final boolean SHUFFLE_TESTS = Boolean.parseBoolean(System.getProperty("ShuffleTests", "true")); - // Use separate flag as VERIFY_IR could have been set by user but due to other flags it was disabled by flag VM. + // Use separate flag as VERIFY_IR could have been set by user but due to other flags it was disabled by Flag VM. private static final boolean PRINT_VALID_IR_RULES = Boolean.getBoolean("ShouldDoIRVerification"); protected static final long PER_METHOD_TRAP_LIMIT = (Long)WHITE_BOX.getVMFlag("PerMethodTrapLimit"); protected static final boolean PROFILE_INTERPRETER = (Boolean)WHITE_BOX.getVMFlag("ProfileInterpreter"); @@ -114,7 +114,7 @@ public class TestVM { private final List excludeList; private final List testList; private Set> helperClasses = null; // Helper classes that contain framework annotations to be processed. - private final IREncodingPrinter irMatchRulePrinter; + private final ApplicableIRRulesPrinter irMatchRulePrinter; private final Class testClass; private final Map forceCompileMap = new HashMap<>(); @@ -125,7 +125,7 @@ public class TestVM { this.excludeList = createTestFilterList(EXCLUDELIST, testClass); if (PRINT_VALID_IR_RULES) { - irMatchRulePrinter = new IREncodingPrinter(); + irMatchRulePrinter = new ApplicableIRRulesPrinter(); } else { irMatchRulePrinter = null; } @@ -155,7 +155,7 @@ public class TestVM { } /** - * Main entry point of the test VM. + * Main entry point of the Test VM. */ public static void main(String[] args) { try { @@ -293,7 +293,7 @@ public class TestVM { BaseTest baseTest = new BaseTest(test, shouldExcludeTest(m.getName())); allTests.add(baseTest); if (PRINT_VALID_IR_RULES) { - irMatchRulePrinter.emitRuleEncoding(m, baseTest.isSkipped()); + irMatchRulePrinter.emitApplicableIRRules(m, baseTest.isSkipped()); } } catch (TestFormatException e) { // Failure logged. Continue and report later. @@ -687,7 +687,7 @@ public class TestVM { allTests.add(checkedTest); if (PRINT_VALID_IR_RULES) { // Only need to emit IR verification information if IR verification is actually performed. - irMatchRulePrinter.emitRuleEncoding(testMethod, checkedTest.isSkipped()); + irMatchRulePrinter.emitApplicableIRRules(testMethod, checkedTest.isSkipped()); } } @@ -755,7 +755,7 @@ public class TestVM { CustomRunTest customRunTest = new CustomRunTest(m, getAnnotation(m, Warmup.class), runAnno, tests, shouldExcludeTest); allTests.add(customRunTest); if (PRINT_VALID_IR_RULES) { - tests.forEach(test -> irMatchRulePrinter.emitRuleEncoding(test.getTestMethod(), customRunTest.isSkipped())); + tests.forEach(test -> irMatchRulePrinter.emitApplicableIRRules(test.getTestMethod(), customRunTest.isSkipped())); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.java index 14636da4cbe..470569122dd 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.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 @@ -27,7 +27,7 @@ import compiler.lib.ir_framework.shared.TestFrameworkSocket; import jdk.test.whitebox.WhiteBox; /** - * Prints some test VM info to the socket. + * Prints some Test VM info to the socket. */ public class VMInfoPrinter { public static final String START_VM_INFO = "##### IRMatchingVMInfo - used by TestFramework #####"; 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 142a30f34a9..07a6a03f2a6 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.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 @@ -38,13 +38,13 @@ import java.util.regex.Pattern; /* * @test * @requires vm.debug == true & vm.compMode != "Xint" & vm.compiler1.enabled & vm.compiler2.enabled & vm.flagless - * @summary Test IR matcher with different default IR node regexes. Use -DPrintIREncoding. + * @summary Test IR matcher with different default IR node regexes. Use -DPrintApplicableIRRules. * Normally, the framework should be called with driver. * @library /test/lib /testlibrary_tests / * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm/timeout=240 -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI -DPrintIREncoding=true ir_framework.tests.TestIRMatching + * -XX:+WhiteBoxAPI -DPrintApplicableIRRules=true ir_framework.tests.TestIRMatching */ public class TestIRMatching { @@ -440,7 +440,8 @@ public class TestIRMatching { } } if (!output.contains(builder.toString())) { - addException(new RuntimeException("Could not find encoding: \"" + builder + System.lineSeparator())); + addException(new RuntimeException("Could not find line in Applicable IR Rules: \"" + builder + + System.lineSeparator())); } } } diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java index fe21dce73b5..70e4c463c55 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.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 @@ -70,7 +70,7 @@ public class TestPhaseIRMatching { TestVMProcess testVMProcess = new TestVMProcess(testVMFlags, testClass, null, -1, false, false); TestClassParser testClassParser = new TestClassParser(testClass, false); Matchable testClassMatchable = testClassParser.parse(testVMProcess.getHotspotPidFileName(), - testVMProcess.getIrEncoding()); + testVMProcess.getApplicableIRRules()); MatchResult result = testClassMatchable.match(); List expectedFails = new ExpectedFailsBuilder().build(testClass); List foundFailures = new FailureBuilder().build(result); From c44a99a758f38ceea84e03905d2ffb9c1fd1987a Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Mon, 19 Jan 2026 14:20:18 +0000 Subject: [PATCH 157/204] 8374180: C2 crash in PhaseCCP::verify_type - fatal error: Not monotonic Reviewed-by: hgreule, bmaillard, epeter --- src/hotspot/share/opto/rangeinference.hpp | 41 ++++++++++---- src/hotspot/share/opto/type.hpp | 8 ++- .../gtest/opto/test_rangeinference.cpp | 8 +-- .../compiler/ccp/TestWrongXorIWiden.java | 54 +++++++++++++++++++ 4 files changed, 95 insertions(+), 16 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/ccp/TestWrongXorIWiden.java diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index ebfd98ca4a6..66ea741a2da 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.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 @@ -147,7 +147,7 @@ public: static const Type* int_type_xmeet(const CT* i1, const Type* t2); template - static CTP int_type_union(CTP t1, CTP t2) { + static auto int_type_union(CTP t1, CTP t2) { using CT = std::conditional_t, std::remove_pointer_t, CTP>; using S = std::remove_const_t; using U = std::remove_const_t; @@ -209,7 +209,7 @@ public: KnownBits _bits; int _widen = 0; // dummy field to mimic the same field in TypeInt, useful in testing - static TypeIntMirror make(const TypeIntPrototype& t, int widen) { + static TypeIntMirror make(const TypeIntPrototype& t, int widen = 0) { auto canonicalized_t = t.canonicalize_constraints(); assert(!canonicalized_t.empty(), "must not be empty"); return TypeIntMirror{canonicalized_t._data._srange._lo, canonicalized_t._data._srange._hi, @@ -217,11 +217,15 @@ public: canonicalized_t._data._bits}; } + TypeIntMirror meet(const TypeIntMirror& o) const { + return TypeIntHelper::int_type_union(this, &o); + } + // These allow TypeIntMirror to mimick the behaviors of TypeInt* and TypeLong*, so they can be // passed into RangeInference methods. These are only used in testing, so they are implemented in // the test file. + static TypeIntMirror make(const TypeIntMirror& t, int widen); const TypeIntMirror* operator->() const; - TypeIntMirror meet(const TypeIntMirror& o) const; bool contains(U u) const; bool contains(const TypeIntMirror& o) const; bool operator==(const TypeIntMirror& o) const; @@ -322,7 +326,7 @@ private: // Infer a result given the input types of a binary operation template static CTP infer_binary(CTP t1, CTP t2, Inference infer) { - CTP res; + TypeIntMirror, U> res; bool is_init = false; SimpleIntervalIterable t1_simple_intervals(t1); @@ -330,10 +334,10 @@ private: for (auto& st1 : t1_simple_intervals) { for (auto& st2 : t2_simple_intervals) { - CTP current = infer(st1, st2); + TypeIntMirror, U> current = infer(st1, st2); if (is_init) { - res = res->meet(current)->template cast>(); + res = res.meet(current); } else { is_init = true; res = current; @@ -342,7 +346,22 @@ private: } assert(is_init, "must be initialized"); - return res; + // It is important that widen is computed on the whole result instead of during each step. This + // is because we normalize the widen of small Type instances to 0, so computing the widen value + // for each step and taking the union of them may return a widen value that conflicts with + // other computations, trigerring the monotonicity assert during CCP. + // + // For example, let us consider the operation r = x ^ y: + // - During the first step of CCP, type(x) = {0}, type(y) = [-2, 2], w = 3. + // Since x is a constant that is the identity element of the xor operation, type(r) = type(y) = [-2, 2], w = 3 + // - During the second step, type(x) is widened to [0, 2], w = 0. + // We then compute the range for: + // r1 = x ^ y1, type(x) = [0, 2], w = 0, type(y1) = [0, 2], w = 0 + // r2 = x ^ y2, type(x) = [0, 2], w = 0, type(y2) = [-2, -1], w = 0 + // This results in type(r1) = [0, 3], w = 0, and type(r2) = [-4, -1], w = 0 + // So the union of type(r1) and type(r2) is [-4, 3], w = 0. This widen value is smaller than + // that of the previous step, triggering the monotonicity assert. + return CT::make(res, MAX2(t1->_widen, t2->_widen)); } public: @@ -357,7 +376,7 @@ public: U uhi = MIN2(st1._uhi, st2._uhi); U zeros = st1._bits._zeros | st2._bits._zeros; U ones = st1._bits._ones & st2._bits._ones; - return CT::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}, MAX2(t1->_widen, t2->_widen)); + return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); }); } @@ -372,7 +391,7 @@ public: U uhi = std::numeric_limits>::max(); U zeros = st1._bits._zeros & st2._bits._zeros; U ones = st1._bits._ones | st2._bits._ones; - return CT::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}, MAX2(t1->_widen, t2->_widen)); + return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); }); } @@ -385,7 +404,7 @@ public: U uhi = std::numeric_limits>::max(); U zeros = (st1._bits._zeros & st2._bits._zeros) | (st1._bits._ones & st2._bits._ones); U ones = (st1._bits._zeros & st2._bits._ones) | (st1._bits._ones & st2._bits._zeros); - return CT::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}, MAX2(t1->_widen, t2->_widen)); + return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); }); } }; diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 73e2ba0045a..285b4984cd1 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.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 @@ -799,6 +799,9 @@ public: static const TypeInt* make(jint lo, jint hi, int widen); static const Type* make_or_top(const TypeIntPrototype& t, int widen); static const TypeInt* make(const TypeIntPrototype& t, int widen) { return make_or_top(t, widen)->is_int(); } + static const TypeInt* make(const TypeIntMirror& t, int widen) { + return (new TypeInt(TypeIntPrototype{{t._lo, t._hi}, {t._ulo, t._uhi}, t._bits}, widen, false))->hashcons()->is_int(); + } // Check for single integer bool is_con() const { return _lo == _hi; } @@ -881,6 +884,9 @@ public: static const TypeLong* make(jlong lo, jlong hi, int widen); static const Type* make_or_top(const TypeIntPrototype& t, int widen); static const TypeLong* make(const TypeIntPrototype& t, int widen) { return make_or_top(t, widen)->is_long(); } + static const TypeLong* make(const TypeIntMirror& t, int widen) { + return (new TypeLong(TypeIntPrototype{{t._lo, t._hi}, {t._ulo, t._uhi}, t._bits}, widen, false))->hashcons()->is_long(); + } // Check for single integer bool is_con() const { return _lo == _hi; } diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index 42767e9fcab..fd49050d022 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -216,13 +216,13 @@ TEST_VM(opto, canonicalize_constraints) { // Implementations of TypeIntMirror methods for testing purposes template -const TypeIntMirror* TypeIntMirror::operator->() const { - return this; +TypeIntMirror TypeIntMirror::make(const TypeIntMirror& t, int widen) { + return t; } template -TypeIntMirror TypeIntMirror::meet(const TypeIntMirror& o) const { - return TypeIntHelper::int_type_union(*this, o); +const TypeIntMirror* TypeIntMirror::operator->() const { + return this; } template diff --git a/test/hotspot/jtreg/compiler/ccp/TestWrongXorIWiden.java b/test/hotspot/jtreg/compiler/ccp/TestWrongXorIWiden.java new file mode 100644 index 00000000000..3b058758beb --- /dev/null +++ b/test/hotspot/jtreg/compiler/ccp/TestWrongXorIWiden.java @@ -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. + */ + +package compiler.ccp; + +/* + * @test + * @bug 8374180 + * @summary Test that _widen is set correctly in XorI::add_ring() to ensure monotonicity. + * @run main/othervm -XX:CompileCommand=compileonly,${test.main.class}::* -Xcomp ${test.main.class} + */ +public class TestWrongXorIWiden { + static byte byFld; + + public static void main(String[] strArr) { + test(); + } + + static void test() { + int k, i17 = 0; + long lArr[] = new long[400]; + for (int i = 9; i < 54; ++i) { + for (int j = 7; j > 1; j--) { + for (k = 1; k < 2; k++) { + i17 >>= i; + } + byFld += j ^ i17; + for (int a = 1; a < 2; a++) { + i17 = k; + } + } + } + } +} From f2d5290c29b0b832e64ab2b4dc04cd892a627ca2 Mon Sep 17 00:00:00 2001 From: Casper Norrbin Date: Mon, 19 Jan 2026 14:44:37 +0000 Subject: [PATCH 158/204] 8367319: Add os interfaces to get machine and container values separately Reviewed-by: eosterlund, sgehwolf --- src/hotspot/os/aix/os_aix.cpp | 26 +++- src/hotspot/os/bsd/os_bsd.cpp | 26 +++- .../os/linux/cgroupSubsystem_linux.cpp | 22 ++- .../os/linux/cgroupSubsystem_linux.hpp | 23 +-- src/hotspot/os/linux/cgroupUtil_linux.cpp | 27 ++-- src/hotspot/os/linux/cgroupUtil_linux.hpp | 7 +- .../os/linux/cgroupV1Subsystem_linux.cpp | 6 +- .../os/linux/cgroupV1Subsystem_linux.hpp | 10 +- .../os/linux/cgroupV2Subsystem_linux.cpp | 6 +- .../os/linux/cgroupV2Subsystem_linux.hpp | 10 +- src/hotspot/os/linux/osContainer_linux.cpp | 16 +- src/hotspot/os/linux/osContainer_linux.hpp | 7 +- src/hotspot/os/linux/os_linux.cpp | 141 ++++++++++++++---- src/hotspot/os/windows/os_windows.cpp | 26 +++- src/hotspot/share/jfr/jni/jfrJniMethod.cpp | 27 +--- src/hotspot/share/prims/jvm.cpp | 9 +- src/hotspot/share/prims/whitebox.cpp | 9 +- src/hotspot/share/runtime/os.cpp | 55 +++++-- src/hotspot/share/runtime/os.hpp | 48 +++++- .../containers/docker/TestCPUAwareness.java | 12 +- 20 files changed, 356 insertions(+), 157 deletions(-) diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index f88729cdc66..d7c1911a914 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.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. * @@ -258,10 +258,18 @@ bool os::free_memory(physical_memory_size_type& value) { return Aix::available_memory(value); } +bool os::Machine::free_memory(physical_memory_size_type& value) { + return Aix::available_memory(value); +} + bool os::available_memory(physical_memory_size_type& value) { return Aix::available_memory(value); } +bool os::Machine::available_memory(physical_memory_size_type& value) { + return Aix::available_memory(value); +} + bool os::Aix::available_memory(physical_memory_size_type& value) { os::Aix::meminfo_t mi; if (os::Aix::get_meminfo(&mi)) { @@ -273,6 +281,10 @@ bool os::Aix::available_memory(physical_memory_size_type& value) { } bool os::total_swap_space(physical_memory_size_type& value) { + return Machine::total_swap_space(value); +} + +bool os::Machine::total_swap_space(physical_memory_size_type& value) { perfstat_memory_total_t memory_info; if (libperfstat::perfstat_memory_total(nullptr, &memory_info, sizeof(perfstat_memory_total_t), 1) == -1) { return false; @@ -282,6 +294,10 @@ bool os::total_swap_space(physical_memory_size_type& value) { } bool os::free_swap_space(physical_memory_size_type& value) { + return Machine::free_swap_space(value); +} + +bool os::Machine::free_swap_space(physical_memory_size_type& value) { perfstat_memory_total_t memory_info; if (libperfstat::perfstat_memory_total(nullptr, &memory_info, sizeof(perfstat_memory_total_t), 1) == -1) { return false; @@ -294,6 +310,10 @@ physical_memory_size_type os::physical_memory() { return Aix::physical_memory(); } +physical_memory_size_type os::Machine::physical_memory() { + return Aix::physical_memory(); +} + size_t os::rss() { return (size_t)0; } // Cpu architecture string @@ -2264,6 +2284,10 @@ int os::active_processor_count() { return ActiveProcessorCount; } + return Machine::active_processor_count(); +} + +int os::Machine::active_processor_count() { int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN); assert(online_cpus > 0 && online_cpus <= processor_count(), "sanity check"); return online_cpus; diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 61de48bb7fa..667810bd6ca 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.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 @@ -137,10 +137,18 @@ bool os::available_memory(physical_memory_size_type& value) { return Bsd::available_memory(value); } +bool os::Machine::available_memory(physical_memory_size_type& value) { + return Bsd::available_memory(value); +} + bool os::free_memory(physical_memory_size_type& value) { return Bsd::available_memory(value); } +bool os::Machine::free_memory(physical_memory_size_type& value) { + return Bsd::available_memory(value); +} + // Available here means free. Note that this number is of no much use. As an estimate // for future memory pressure it is far too conservative, since MacOS will use a lot // of unused memory for caches, and return it willingly in case of needs. @@ -181,6 +189,10 @@ void os::Bsd::print_uptime_info(outputStream* st) { } bool os::total_swap_space(physical_memory_size_type& value) { + return Machine::total_swap_space(value); +} + +bool os::Machine::total_swap_space(physical_memory_size_type& value) { #if defined(__APPLE__) struct xsw_usage vmusage; size_t size = sizeof(vmusage); @@ -195,6 +207,10 @@ bool os::total_swap_space(physical_memory_size_type& value) { } bool os::free_swap_space(physical_memory_size_type& value) { + return Machine::free_swap_space(value); +} + +bool os::Machine::free_swap_space(physical_memory_size_type& value) { #if defined(__APPLE__) struct xsw_usage vmusage; size_t size = sizeof(vmusage); @@ -212,6 +228,10 @@ physical_memory_size_type os::physical_memory() { return Bsd::physical_memory(); } +physical_memory_size_type os::Machine::physical_memory() { + return Bsd::physical_memory(); +} + size_t os::rss() { size_t rss = 0; #ifdef __APPLE__ @@ -2189,6 +2209,10 @@ int os::active_processor_count() { return ActiveProcessorCount; } + return Machine::active_processor_count(); +} + +int os::Machine::active_processor_count() { return _processor_count; } diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index f9c6f794ebd..e49d070890e 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.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 @@ -631,22 +631,20 @@ void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) { * return: * true if there were no errors. false otherwise. */ -bool CgroupSubsystem::active_processor_count(int& value) { - int cpu_count; - int result = -1; - +bool CgroupSubsystem::active_processor_count(double& value) { // We use a cache with a timeout to avoid performing expensive // computations in the event this function is called frequently. // [See 8227006]. - CachingCgroupController* contrl = cpu_controller(); - CachedMetric* cpu_limit = contrl->metrics_cache(); + CachingCgroupController* contrl = cpu_controller(); + CachedMetric* cpu_limit = contrl->metrics_cache(); if (!cpu_limit->should_check_metric()) { - value = (int)cpu_limit->value(); - log_trace(os, container)("CgroupSubsystem::active_processor_count (cached): %d", value); + value = cpu_limit->value(); + log_trace(os, container)("CgroupSubsystem::active_processor_count (cached): %.2f", value); return true; } - cpu_count = os::Linux::active_processor_count(); + int cpu_count = os::Linux::active_processor_count(); + double result = -1; if (!CgroupUtil::processor_count(contrl->controller(), cpu_count, result)) { return false; } @@ -671,8 +669,8 @@ bool CgroupSubsystem::active_processor_count(int& value) { */ bool CgroupSubsystem::memory_limit_in_bytes(physical_memory_size_type upper_bound, physical_memory_size_type& value) { - CachingCgroupController* contrl = memory_controller(); - CachedMetric* memory_limit = contrl->metrics_cache(); + CachingCgroupController* contrl = memory_controller(); + CachedMetric* memory_limit = contrl->metrics_cache(); if (!memory_limit->should_check_metric()) { value = memory_limit->value(); return true; diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index 522b64f8816..8aafbf49c63 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.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 @@ -181,9 +181,10 @@ class CgroupController: public CHeapObj { static bool limit_from_str(char* limit_str, physical_memory_size_type& value); }; +template class CachedMetric : public CHeapObj{ private: - volatile physical_memory_size_type _metric; + volatile MetricType _metric; volatile jlong _next_check_counter; public: CachedMetric() { @@ -193,8 +194,8 @@ class CachedMetric : public CHeapObj{ bool should_check_metric() { return os::elapsed_counter() > _next_check_counter; } - physical_memory_size_type value() { return _metric; } - void set_value(physical_memory_size_type value, jlong timeout) { + MetricType value() { return _metric; } + void set_value(MetricType value, jlong timeout) { _metric = value; // Metric is unlikely to change, but we want to remain // responsive to configuration changes. A very short grace time @@ -205,19 +206,19 @@ class CachedMetric : public CHeapObj{ } }; -template +template class CachingCgroupController : public CHeapObj { private: T* _controller; - CachedMetric* _metrics_cache; + CachedMetric* _metrics_cache; public: CachingCgroupController(T* cont) { _controller = cont; - _metrics_cache = new CachedMetric(); + _metrics_cache = new CachedMetric(); } - CachedMetric* metrics_cache() { return _metrics_cache; } + CachedMetric* metrics_cache() { return _metrics_cache; } T* controller() { return _controller; } }; @@ -277,7 +278,7 @@ class CgroupMemoryController: public CHeapObj { class CgroupSubsystem: public CHeapObj { public: bool memory_limit_in_bytes(physical_memory_size_type upper_bound, physical_memory_size_type& value); - bool active_processor_count(int& value); + bool active_processor_count(double& value); virtual bool pids_max(uint64_t& value) = 0; virtual bool pids_current(uint64_t& value) = 0; @@ -286,8 +287,8 @@ class CgroupSubsystem: public CHeapObj { virtual char * cpu_cpuset_cpus() = 0; virtual char * cpu_cpuset_memory_nodes() = 0; virtual const char * container_type() = 0; - virtual CachingCgroupController* memory_controller() = 0; - virtual CachingCgroupController* cpu_controller() = 0; + virtual CachingCgroupController* memory_controller() = 0; + virtual CachingCgroupController* cpu_controller() = 0; virtual CgroupCpuacctController* cpuacct_controller() = 0; bool cpu_quota(int& value); diff --git a/src/hotspot/os/linux/cgroupUtil_linux.cpp b/src/hotspot/os/linux/cgroupUtil_linux.cpp index 7aa07d53148..570b335940b 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.cpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, 2025, Red Hat, Inc. + * 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,9 +26,8 @@ #include "cgroupUtil_linux.hpp" #include "os_linux.hpp" -bool CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int upper_bound, int& value) { +bool CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int upper_bound, double& value) { assert(upper_bound > 0, "upper bound of cpus must be positive"); - int limit_count = upper_bound; int quota = -1; int period = -1; if (!cpu_ctrl->cpu_quota(quota)) { @@ -37,20 +37,15 @@ bool CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int upper_bound, return false; } int quota_count = 0; - int result = upper_bound; + double result = upper_bound; - if (quota > -1 && period > 0) { - quota_count = ceilf((float)quota / (float)period); - log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count); + if (quota > 0 && period > 0) { // Use quotas + double cpu_quota = static_cast(quota) / period; + log_trace(os, container)("CPU Quota based on quota/period: %.2f", cpu_quota); + result = MIN2(result, cpu_quota); } - // Use quotas - if (quota_count != 0) { - limit_count = quota_count; - } - - result = MIN2(upper_bound, limit_count); - log_trace(os, container)("OSContainer::active_processor_count: %d", result); + log_trace(os, container)("OSContainer::active_processor_count: %.2f", result); value = result; return true; } @@ -73,11 +68,11 @@ physical_memory_size_type CgroupUtil::get_updated_mem_limit(CgroupMemoryControll // Get an updated cpu limit. The return value is strictly less than or equal to the // passed in 'lowest' value. -int CgroupUtil::get_updated_cpu_limit(CgroupCpuController* cpu, +double CgroupUtil::get_updated_cpu_limit(CgroupCpuController* cpu, int lowest, int upper_bound) { assert(lowest > 0 && lowest <= upper_bound, "invariant"); - int cpu_limit_val = -1; + double cpu_limit_val = -1; if (CgroupUtil::processor_count(cpu, upper_bound, cpu_limit_val) && cpu_limit_val != upper_bound) { assert(cpu_limit_val <= upper_bound, "invariant"); if (lowest > cpu_limit_val) { @@ -172,7 +167,7 @@ void CgroupUtil::adjust_controller(CgroupCpuController* cpu) { assert(cg_path[0] == '/', "cgroup path must start with '/'"); int host_cpus = os::Linux::active_processor_count(); int lowest_limit = host_cpus; - int cpus = get_updated_cpu_limit(cpu, lowest_limit, host_cpus); + double cpus = get_updated_cpu_limit(cpu, lowest_limit, host_cpus); int orig_limit = lowest_limit != host_cpus ? lowest_limit : host_cpus; char* limit_cg_path = nullptr; while ((last_slash = strrchr(cg_path, '/')) != cg_path) { diff --git a/src/hotspot/os/linux/cgroupUtil_linux.hpp b/src/hotspot/os/linux/cgroupUtil_linux.hpp index d72bbd1cf1e..1fd2a7d872b 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.hpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.hpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2024, Red Hat, Inc. + * 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 @@ -31,7 +32,7 @@ class CgroupUtil: AllStatic { public: - static bool processor_count(CgroupCpuController* cpu, int upper_bound, int& value); + static bool processor_count(CgroupCpuController* cpu, int upper_bound, double& value); // Given a memory controller, adjust its path to a point in the hierarchy // that represents the closest memory limit. static void adjust_controller(CgroupMemoryController* m); @@ -42,9 +43,7 @@ class CgroupUtil: AllStatic { static physical_memory_size_type get_updated_mem_limit(CgroupMemoryController* m, physical_memory_size_type lowest, physical_memory_size_type upper_bound); - static int get_updated_cpu_limit(CgroupCpuController* c, - int lowest, - int upper_bound); + static double get_updated_cpu_limit(CgroupCpuController* c, int lowest, int upper_bound); }; #endif // CGROUP_UTIL_LINUX_HPP diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 2df604083d2..c8f5a290c99 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.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 @@ -328,8 +328,8 @@ CgroupV1Subsystem::CgroupV1Subsystem(CgroupV1Controller* cpuset, _pids(pids) { CgroupUtil::adjust_controller(memory); CgroupUtil::adjust_controller(cpu); - _memory = new CachingCgroupController(memory); - _cpu = new CachingCgroupController(cpu); + _memory = new CachingCgroupController(memory); + _cpu = new CachingCgroupController(cpu); } bool CgroupV1Subsystem::is_containerized() { diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index f556bc57f26..af8d0efd378 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.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 @@ -214,15 +214,15 @@ class CgroupV1Subsystem: public CgroupSubsystem { const char * container_type() override { return "cgroupv1"; } - CachingCgroupController* memory_controller() override { return _memory; } - CachingCgroupController* cpu_controller() override { return _cpu; } + CachingCgroupController* memory_controller() override { return _memory; } + CachingCgroupController* cpu_controller() override { return _cpu; } CgroupCpuacctController* cpuacct_controller() override { return _cpuacct; } private: /* controllers */ - CachingCgroupController* _memory = nullptr; + CachingCgroupController* _memory = nullptr; CgroupV1Controller* _cpuset = nullptr; - CachingCgroupController* _cpu = nullptr; + CachingCgroupController* _cpu = nullptr; CgroupV1CpuacctController* _cpuacct = nullptr; CgroupV1Controller* _pids = nullptr; diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index c61d30e9236..30e1affc646 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2025, Red Hat Inc. - * 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 @@ -156,8 +156,8 @@ CgroupV2Subsystem::CgroupV2Subsystem(CgroupV2MemoryController * memory, _unified(unified) { CgroupUtil::adjust_controller(memory); CgroupUtil::adjust_controller(cpu); - _memory = new CachingCgroupController(memory); - _cpu = new CachingCgroupController(cpu); + _memory = new CachingCgroupController(memory); + _cpu = new CachingCgroupController(cpu); _cpuacct = cpuacct; } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 39a4fabe9f6..998145c0ff9 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2024, Red Hat Inc. - * 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 @@ -152,8 +152,8 @@ class CgroupV2Subsystem: public CgroupSubsystem { /* One unified controller */ CgroupV2Controller _unified; /* Caching wrappers for cpu/memory metrics */ - CachingCgroupController* _memory = nullptr; - CachingCgroupController* _cpu = nullptr; + CachingCgroupController* _memory = nullptr; + CachingCgroupController* _cpu = nullptr; CgroupCpuacctController* _cpuacct = nullptr; @@ -175,8 +175,8 @@ class CgroupV2Subsystem: public CgroupSubsystem { const char * container_type() override { return "cgroupv2"; } - CachingCgroupController* memory_controller() override { return _memory; } - CachingCgroupController* cpu_controller() override { return _cpu; } + CachingCgroupController* memory_controller() override { return _memory; } + CachingCgroupController* cpu_controller() override { return _cpu; } CgroupCpuacctController* cpuacct_controller() override { return _cpuacct; }; }; diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp index 15a6403d07f..fe1dbc17239 100644 --- a/src/hotspot/os/linux/osContainer_linux.cpp +++ b/src/hotspot/os/linux/osContainer_linux.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 @@ -86,8 +86,8 @@ void OSContainer::init() { // 2.) On a physical Linux system with a limit enforced by other means (like systemd slice) physical_memory_size_type mem_limit_val = value_unlimited; (void)memory_limit_in_bytes(mem_limit_val); // discard error and use default - int host_cpus = os::Linux::active_processor_count(); - int cpus = host_cpus; + double host_cpus = os::Linux::active_processor_count(); + double cpus = host_cpus; (void)active_processor_count(cpus); // discard error and use default any_mem_cpu_limit_present = mem_limit_val != value_unlimited || host_cpus != cpus; if (any_mem_cpu_limit_present) { @@ -127,8 +127,7 @@ bool OSContainer::available_memory_in_bytes(physical_memory_size_type& value) { return false; } -bool OSContainer::available_swap_in_bytes(physical_memory_size_type host_free_swap, - physical_memory_size_type& value) { +bool OSContainer::available_swap_in_bytes(physical_memory_size_type& value) { physical_memory_size_type mem_limit = 0; physical_memory_size_type mem_swap_limit = 0; if (memory_limit_in_bytes(mem_limit) && @@ -179,8 +178,7 @@ bool OSContainer::available_swap_in_bytes(physical_memory_size_type host_free_sw assert(num < 25, "buffer too small"); mem_limit_buf[num] = '\0'; log_trace(os,container)("OSContainer::available_swap_in_bytes: container_swap_limit=%s" - " container_mem_limit=%s, host_free_swap: " PHYS_MEM_TYPE_FORMAT, - mem_swap_buf, mem_limit_buf, host_free_swap); + " container_mem_limit=%s", mem_swap_buf, mem_limit_buf); } return false; } @@ -252,7 +250,7 @@ char * OSContainer::cpu_cpuset_memory_nodes() { return cgroup_subsystem->cpu_cpuset_memory_nodes(); } -bool OSContainer::active_processor_count(int& value) { +bool OSContainer::active_processor_count(double& value) { assert(cgroup_subsystem != nullptr, "cgroup subsystem not available"); return cgroup_subsystem->active_processor_count(value); } @@ -291,11 +289,13 @@ template struct metric_fmt; template<> struct metric_fmt { static constexpr const char* fmt = "%llu"; }; template<> struct metric_fmt { static constexpr const char* fmt = "%lu"; }; template<> struct metric_fmt { static constexpr const char* fmt = "%d"; }; +template<> struct metric_fmt { static constexpr const char* fmt = "%.2f"; }; template<> struct metric_fmt { static constexpr const char* fmt = "%s"; }; template void OSContainer::print_container_metric(outputStream*, const char*, unsigned long long int, const char*); template void OSContainer::print_container_metric(outputStream*, const char*, unsigned long int, const char*); template void OSContainer::print_container_metric(outputStream*, const char*, int, const char*); +template void OSContainer::print_container_metric(outputStream*, const char*, double, const char*); template void OSContainer::print_container_metric(outputStream*, const char*, const char*, const char*); template diff --git a/src/hotspot/os/linux/osContainer_linux.hpp b/src/hotspot/os/linux/osContainer_linux.hpp index 11c3e086feb..96b59b98db8 100644 --- a/src/hotspot/os/linux/osContainer_linux.hpp +++ b/src/hotspot/os/linux/osContainer_linux.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 @@ -72,8 +72,7 @@ class OSContainer: AllStatic { static const char * container_type(); static bool available_memory_in_bytes(physical_memory_size_type& value); - static bool available_swap_in_bytes(physical_memory_size_type host_free_swap, - physical_memory_size_type& value); + static bool available_swap_in_bytes(physical_memory_size_type& value); static bool memory_limit_in_bytes(physical_memory_size_type& value); static bool memory_and_swap_limit_in_bytes(physical_memory_size_type& value); static bool memory_and_swap_usage_in_bytes(physical_memory_size_type& value); @@ -84,7 +83,7 @@ class OSContainer: AllStatic { static bool rss_usage_in_bytes(physical_memory_size_type& value); static bool cache_usage_in_bytes(physical_memory_size_type& value); - static bool active_processor_count(int& value); + static bool active_processor_count(double& value); static char * cpu_cpuset_cpus(); static char * cpu_cpuset_memory_nodes(); diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 6a2a3974a16..48529c6ce17 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -211,15 +211,58 @@ static bool suppress_primordial_thread_resolution = false; // utility functions +bool os::is_containerized() { + return OSContainer::is_containerized(); +} + +bool os::Container::memory_limit(physical_memory_size_type& value) { + physical_memory_size_type result = 0; + if (OSContainer::memory_limit_in_bytes(result) && result != value_unlimited) { + value = result; + return true; + } + return false; +} + +bool os::Container::memory_soft_limit(physical_memory_size_type& value) { + physical_memory_size_type result = 0; + if (OSContainer::memory_soft_limit_in_bytes(result) && result != 0 && result != value_unlimited) { + value = result; + return true; + } + return false; +} + +bool os::Container::memory_throttle_limit(physical_memory_size_type& value) { + physical_memory_size_type result = 0; + if (OSContainer::memory_throttle_limit_in_bytes(result) && result != value_unlimited) { + value = result; + return true; + } + return false; +} + +bool os::Container::used_memory(physical_memory_size_type& value) { + return OSContainer::memory_usage_in_bytes(value); +} + bool os::available_memory(physical_memory_size_type& value) { - if (OSContainer::is_containerized() && OSContainer::available_memory_in_bytes(value)) { + if (is_containerized() && Container::available_memory(value)) { log_trace(os)("available container memory: " PHYS_MEM_TYPE_FORMAT, value); return true; } + return Machine::available_memory(value); +} + +bool os::Machine::available_memory(physical_memory_size_type& value) { return Linux::available_memory(value); } +bool os::Container::available_memory(physical_memory_size_type& value) { + return OSContainer::available_memory_in_bytes(value); +} + bool os::Linux::available_memory(physical_memory_size_type& value) { physical_memory_size_type avail_mem = 0; @@ -251,11 +294,15 @@ bool os::Linux::available_memory(physical_memory_size_type& value) { } bool os::free_memory(physical_memory_size_type& value) { - if (OSContainer::is_containerized() && OSContainer::available_memory_in_bytes(value)) { + if (is_containerized() && Container::available_memory(value)) { log_trace(os)("free container memory: " PHYS_MEM_TYPE_FORMAT, value); return true; } + return Machine::free_memory(value); +} + +bool os::Machine::free_memory(physical_memory_size_type& value) { return Linux::free_memory(value); } @@ -274,21 +321,30 @@ bool os::Linux::free_memory(physical_memory_size_type& value) { } bool os::total_swap_space(physical_memory_size_type& value) { - if (OSContainer::is_containerized()) { - physical_memory_size_type mem_swap_limit = value_unlimited; - physical_memory_size_type memory_limit = value_unlimited; - if (OSContainer::memory_and_swap_limit_in_bytes(mem_swap_limit) && - OSContainer::memory_limit_in_bytes(memory_limit)) { - if (memory_limit != value_unlimited && mem_swap_limit != value_unlimited && - mem_swap_limit >= memory_limit /* ensure swap is >= 0 */) { - value = mem_swap_limit - memory_limit; - return true; - } - } - } // fallback to the host swap space if the container returned unlimited + if (is_containerized() && Container::total_swap_space(value)) { + return true; + } // fallback to the host swap space if the container value fails + return Machine::total_swap_space(value); +} + +bool os::Machine::total_swap_space(physical_memory_size_type& value) { return Linux::host_swap(value); } +bool os::Container::total_swap_space(physical_memory_size_type& value) { + physical_memory_size_type mem_swap_limit = value_unlimited; + physical_memory_size_type memory_limit = value_unlimited; + if (OSContainer::memory_and_swap_limit_in_bytes(mem_swap_limit) && + OSContainer::memory_limit_in_bytes(memory_limit)) { + if (memory_limit != value_unlimited && mem_swap_limit != value_unlimited && + mem_swap_limit >= memory_limit /* ensure swap is >= 0 */) { + value = mem_swap_limit - memory_limit; + return true; + } + } + return false; +} + static bool host_free_swap_f(physical_memory_size_type& value) { struct sysinfo si; int ret = sysinfo(&si); @@ -309,32 +365,45 @@ bool os::free_swap_space(physical_memory_size_type& value) { return false; } physical_memory_size_type host_free_swap_val = MIN2(total_swap_space, host_free_swap); - if (OSContainer::is_containerized()) { - if (OSContainer::available_swap_in_bytes(host_free_swap_val, value)) { + if (is_containerized()) { + if (Container::free_swap_space(value)) { return true; } // Fall through to use host value log_trace(os,container)("os::free_swap_space: containerized value unavailable" " returning host value: " PHYS_MEM_TYPE_FORMAT, host_free_swap_val); } + value = host_free_swap_val; return true; } +bool os::Machine::free_swap_space(physical_memory_size_type& value) { + return host_free_swap_f(value); +} + +bool os::Container::free_swap_space(physical_memory_size_type& value) { + return OSContainer::available_swap_in_bytes(value); +} + physical_memory_size_type os::physical_memory() { - if (OSContainer::is_containerized()) { + if (is_containerized()) { physical_memory_size_type mem_limit = value_unlimited; - if (OSContainer::memory_limit_in_bytes(mem_limit) && mem_limit != value_unlimited) { + if (Container::memory_limit(mem_limit) && mem_limit != value_unlimited) { log_trace(os)("total container memory: " PHYS_MEM_TYPE_FORMAT, mem_limit); return mem_limit; } } - physical_memory_size_type phys_mem = Linux::physical_memory(); + physical_memory_size_type phys_mem = Machine::physical_memory(); log_trace(os)("total system memory: " PHYS_MEM_TYPE_FORMAT, phys_mem); return phys_mem; } +physical_memory_size_type os::Machine::physical_memory() { + return Linux::physical_memory(); +} + // Returns the resident set size (RSS) of the process. // Falls back to using VmRSS from /proc/self/status if /proc/self/smaps_rollup is unavailable. // Note: On kernels with memory cgroups or shared memory, VmRSS may underreport RSS. @@ -2439,20 +2508,21 @@ bool os::Linux::print_container_info(outputStream* st) { OSContainer::print_container_metric(st, "cpu_memory_nodes", p != nullptr ? p : "not supported"); free(p); - int i = -1; - bool supported = OSContainer::active_processor_count(i); + double cpus = -1; + bool supported = OSContainer::active_processor_count(cpus); if (supported) { - assert(i > 0, "must be"); + assert(cpus > 0, "must be"); if (ActiveProcessorCount > 0) { OSContainer::print_container_metric(st, "active_processor_count", ActiveProcessorCount, "(from -XX:ActiveProcessorCount)"); } else { - OSContainer::print_container_metric(st, "active_processor_count", i); + OSContainer::print_container_metric(st, "active_processor_count", cpus); } } else { OSContainer::print_container_metric(st, "active_processor_count", "not supported"); } + int i = -1; supported = OSContainer::cpu_quota(i); if (supported && i > 0) { OSContainer::print_container_metric(st, "cpu_quota", i); @@ -4737,15 +4807,26 @@ int os::active_processor_count() { return ActiveProcessorCount; } - int active_cpus = -1; - if (OSContainer::is_containerized() && OSContainer::active_processor_count(active_cpus)) { - log_trace(os)("active_processor_count: determined by OSContainer: %d", - active_cpus); - } else { - active_cpus = os::Linux::active_processor_count(); + if (is_containerized()) { + double cpu_quota; + if (Container::processor_count(cpu_quota)) { + int active_cpus = ceilf(cpu_quota); // Round fractional CPU quota up. + assert(active_cpus <= Machine::active_processor_count(), "must be"); + log_trace(os)("active_processor_count: determined by OSContainer: %d", + active_cpus); + return active_cpus; + } } - return active_cpus; + return Machine::active_processor_count(); +} + +int os::Machine::active_processor_count() { + return os::Linux::active_processor_count(); +} + +bool os::Container::processor_count(double& value) { + return OSContainer::active_processor_count(value); } static bool should_warn_invalid_processor_id() { diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index efbd1fe7c68..b0b7ae18106 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -839,10 +839,18 @@ bool os::available_memory(physical_memory_size_type& value) { return win32::available_memory(value); } +bool os::Machine::available_memory(physical_memory_size_type& value) { + return win32::available_memory(value); +} + bool os::free_memory(physical_memory_size_type& value) { return win32::available_memory(value); } +bool os::Machine::free_memory(physical_memory_size_type& value) { + return win32::available_memory(value); +} + bool os::win32::available_memory(physical_memory_size_type& value) { // Use GlobalMemoryStatusEx() because GlobalMemoryStatus() may return incorrect // value if total memory is larger than 4GB @@ -858,7 +866,11 @@ bool os::win32::available_memory(physical_memory_size_type& value) { } } -bool os::total_swap_space(physical_memory_size_type& value) { +bool os::total_swap_space(physical_memory_size_type& value) { + return Machine::total_swap_space(value); +} + +bool os::Machine::total_swap_space(physical_memory_size_type& value) { MEMORYSTATUSEX ms; ms.dwLength = sizeof(ms); BOOL res = GlobalMemoryStatusEx(&ms); @@ -872,6 +884,10 @@ bool os::total_swap_space(physical_memory_size_type& value) { } bool os::free_swap_space(physical_memory_size_type& value) { + return Machine::free_swap_space(value); +} + +bool os::Machine::free_swap_space(physical_memory_size_type& value) { MEMORYSTATUSEX ms; ms.dwLength = sizeof(ms); BOOL res = GlobalMemoryStatusEx(&ms); @@ -888,6 +904,10 @@ physical_memory_size_type os::physical_memory() { return win32::physical_memory(); } +physical_memory_size_type os::Machine::physical_memory() { + return win32::physical_memory(); +} + size_t os::rss() { size_t rss = 0; PROCESS_MEMORY_COUNTERS_EX pmex; @@ -911,6 +931,10 @@ int os::active_processor_count() { return ActiveProcessorCount; } + return Machine::active_processor_count(); +} + +int os::Machine::active_processor_count() { bool schedules_all_processor_groups = win32::is_windows_11_or_greater() || win32::is_windows_server_2022_or_greater(); if (UseAllWindowsProcessorGroups && !schedules_all_processor_groups && !win32::processor_group_warning_displayed()) { win32::set_processor_group_warning_displayed(true); diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp index d8a30f9b5ee..885484020bd 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp @@ -66,10 +66,6 @@ #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" -#ifdef LINUX -#include "os_linux.hpp" -#include "osContainer_linux.hpp" -#endif #define NO_TRANSITION(result_type, header) extern "C" { result_type JNICALL header { #define NO_TRANSITION_END } } @@ -400,35 +396,18 @@ JVM_ENTRY_NO_ENV(jboolean, jfr_is_class_instrumented(JNIEnv* env, jclass jvm, jc JVM_END JVM_ENTRY_NO_ENV(jboolean, jfr_is_containerized(JNIEnv* env, jclass jvm)) -#ifdef LINUX - return OSContainer::is_containerized(); -#else - return false; -#endif + return os::is_containerized(); JVM_END JVM_ENTRY_NO_ENV(jlong, jfr_host_total_memory(JNIEnv* env, jclass jvm)) -#ifdef LINUX - // We want the host memory, not the container limit. - // os::physical_memory() would return the container limit. - return static_cast(os::Linux::physical_memory()); -#else - return static_cast(os::physical_memory()); -#endif + return static_cast(os::Machine::physical_memory()); JVM_END JVM_ENTRY_NO_ENV(jlong, jfr_host_total_swap_memory(JNIEnv* env, jclass jvm)) -#ifdef LINUX - // We want the host swap memory, not the container value. - physical_memory_size_type host_swap = 0; - (void)os::Linux::host_swap(host_swap); // Discard return value and treat as no swap - return static_cast(host_swap); -#else physical_memory_size_type total_swap_space = 0; // Return value ignored - defaulting to 0 on failure. - (void)os::total_swap_space(total_swap_space); + (void)os::Machine::total_swap_space(total_swap_space); return static_cast(total_swap_space); -#endif JVM_END JVM_ENTRY_NO_ENV(void, jfr_emit_data_loss(JNIEnv* env, jclass jvm, jlong bytes)) diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index ef5aca96a57..6d6937a97e9 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.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 @@ -115,9 +115,6 @@ #if INCLUDE_MANAGEMENT #include "services/finalizerService.hpp" #endif -#ifdef LINUX -#include "osContainer_linux.hpp" -#endif #include @@ -500,11 +497,9 @@ JVM_LEAF(jboolean, JVM_IsUseContainerSupport(void)) JVM_END JVM_LEAF(jboolean, JVM_IsContainerized(void)) -#ifdef LINUX - if (OSContainer::is_containerized()) { + if (os::is_containerized()) { return JNI_TRUE; } -#endif return JNI_FALSE; JVM_END diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 1c2f5527ff9..de5bc9ea58f 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.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 @@ -129,7 +129,6 @@ #ifdef LINUX #include "cgroupSubsystem_linux.hpp" #include "os_linux.hpp" -#include "osContainer_linux.hpp" #endif #define CHECK_JNI_EXCEPTION_(env, value) \ @@ -2582,14 +2581,12 @@ WB_ENTRY(jboolean, WB_CheckLibSpecifiesNoexecstack(JNIEnv* env, jobject o, jstri WB_END WB_ENTRY(jboolean, WB_IsContainerized(JNIEnv* env, jobject o)) - LINUX_ONLY(return OSContainer::is_containerized();) - return false; + return os::is_containerized(); WB_END // Physical memory of the host machine (including containers) WB_ENTRY(jlong, WB_HostPhysicalMemory(JNIEnv* env, jobject o)) - LINUX_ONLY(return static_cast(os::Linux::physical_memory());) - return static_cast(os::physical_memory()); + return static_cast(os::Machine::physical_memory()); WB_END // Available memory of the host machine (container-aware) diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index cf18388c625..1c06bf3c521 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.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 @@ -81,10 +81,6 @@ #include "utilities/permitForbiddenFunctions.hpp" #include "utilities/powerOfTwo.hpp" -#ifdef LINUX -#include "osContainer_linux.hpp" -#endif - #ifndef _WINDOWS # include #endif @@ -2205,11 +2201,14 @@ static void assert_nonempty_range(const char* addr, size_t bytes) { } bool os::used_memory(physical_memory_size_type& value) { -#ifdef LINUX - if (OSContainer::is_containerized()) { - return OSContainer::memory_usage_in_bytes(value); + if (is_containerized()) { + return Container::used_memory(value); } -#endif + + return Machine::used_memory(value); +} + +bool os::Machine::used_memory(physical_memory_size_type& value) { physical_memory_size_type avail_mem = 0; // Return value ignored - defaulting to 0 on failure. (void)os::available_memory(avail_mem); @@ -2218,6 +2217,44 @@ bool os::used_memory(physical_memory_size_type& value) { return true; } +#ifndef LINUX +bool os::is_containerized() { + return false; +} + +bool os::Container::processor_count(double& value) { + return false; +} + +bool os::Container::available_memory(physical_memory_size_type& value) { + return false; +} + +bool os::Container::used_memory(physical_memory_size_type& value) { + return false; +} + +bool os::Container::total_swap_space(physical_memory_size_type& value) { + return false; +} + +bool os::Container::free_swap_space(physical_memory_size_type& value) { + return false; +} + +bool os::Container::memory_limit(physical_memory_size_type& value) { + return false; +} + +bool os::Container::memory_soft_limit(physical_memory_size_type& value) { + return false; +} + +bool os::Container::memory_throttle_limit(physical_memory_size_type& value) { + return false; +} +#endif + bool os::commit_memory(char* addr, size_t bytes, bool executable) { assert_nonempty_range(addr, bytes); diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index d585d3e5fc0..29c872157fd 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.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 @@ -342,6 +342,52 @@ class os: AllStatic { static bool is_server_class_machine(); static size_t rss(); + // On platforms with container support (currently only Linux) we combine machine values with + // potential container values in os:: methods, abstracting which value is actually used. + // The os::Machine and os::Container classes and containing methods are used to get machine + // and container values (when available) separately. + static bool is_containerized(); + + // The os::Machine class reports system resource metrics from the perspective of the operating + // system, without considering container-imposed limits. The values returned by these methods + // reflect the resources visible to the process as reported by the OS, and may already be + // affected by mechanisms such as virtualization, hypervisor limits, or process affinity, + // but do NOT consider further restrictions imposed by container runtimes (e.g., cgroups) + class Machine : AllStatic { + public: + static int active_processor_count(); + + [[nodiscard]] static bool available_memory(physical_memory_size_type& value); + [[nodiscard]] static bool used_memory(physical_memory_size_type& value); + [[nodiscard]] static bool free_memory(physical_memory_size_type& value); + + [[nodiscard]] static bool total_swap_space(physical_memory_size_type& value); + [[nodiscard]] static bool free_swap_space(physical_memory_size_type& value); + + static physical_memory_size_type physical_memory(); + }; + + // The os::Container class reports resource limits as imposed by a supported container runtime + // (currently only cgroup-based Linux runtimes). If the process is running inside a + // containerized environment, methods from this class report the effective limits imposed + // by the container, which may be more restrictive than what os::Machine reports. + // Methods return true and set the out-parameter if a limit is found, + // or false if no limit exists or it cannot be determined. + class Container : AllStatic { + public: + [[nodiscard]] static bool processor_count(double& value); // Returns the core-equivalent CPU quota + + [[nodiscard]] static bool available_memory(physical_memory_size_type& value); + [[nodiscard]] static bool used_memory(physical_memory_size_type& value); + + [[nodiscard]] static bool total_swap_space(physical_memory_size_type& value); + [[nodiscard]] static bool free_swap_space(physical_memory_size_type& value); + + [[nodiscard]] static bool memory_limit(physical_memory_size_type& value); + [[nodiscard]] static bool memory_soft_limit(physical_memory_size_type& value); + [[nodiscard]] static bool memory_throttle_limit(physical_memory_size_type& value); + }; + // Returns the id of the processor on which the calling thread is currently executing. // The returned value is guaranteed to be between 0 and (os::processor_count() - 1). static uint processor_id(); diff --git a/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java b/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java index 6b0a536e4d4..3064b320c0d 100644 --- a/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java +++ b/test/hotspot/jtreg/containers/docker/TestCPUAwareness.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 @@ -143,9 +143,9 @@ public class TestCPUAwareness { // Expected active processor count can not exceed available CPU count - private static int adjustExpectedAPCForAvailableCPUs(int expectedAPC) { - if (expectedAPC > availableCPUs) { - expectedAPC = availableCPUs; + private static double adjustExpectedAPCForAvailableCPUs(double expectedAPC) { + if (expectedAPC > (double)availableCPUs) { + expectedAPC = (double)availableCPUs; System.out.println("Adjusted expectedAPC = " + expectedAPC); } return expectedAPC; @@ -158,7 +158,7 @@ public class TestCPUAwareness { System.out.println("quota = " + quota); System.out.println("period = " + period); - int expectedAPC = (int) Math.ceil((float) quota / (float) period); + double expectedAPC = (double) quota / (double) period; System.out.println("expectedAPC = " + expectedAPC); expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC); @@ -178,7 +178,7 @@ public class TestCPUAwareness { private static void testAPCCombo(String cpuset, int quota, int period, int shares, - int expectedAPC) throws Exception { + double expectedAPC) throws Exception { Common.logNewTestCase("test APC Combo"); System.out.println("cpuset = " + cpuset); System.out.println("quota = " + quota); From 496af3cf4769b78fa0928450a87928d259511c51 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Mon, 19 Jan 2026 18:05:22 +0000 Subject: [PATCH 159/204] 8375093: Convert GlobalCounter to use Atomic Reviewed-by: dholmes, iwalulya --- src/hotspot/share/runtime/thread.cpp | 5 +++-- src/hotspot/share/runtime/thread.hpp | 7 ++++--- src/hotspot/share/utilities/globalCounter.cpp | 12 ++++++------ src/hotspot/share/utilities/globalCounter.hpp | 5 +++-- .../share/utilities/globalCounter.inline.hpp | 15 +++++++-------- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index f3c08ae62f7..bfbd4727e9a 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.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) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -36,6 +36,7 @@ #include "memory/resourceArea.hpp" #include "nmt/memTracker.hpp" #include "oops/oop.inline.hpp" +#include "runtime/atomic.hpp" #include "runtime/atomicAccess.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaThread.inline.hpp" @@ -82,7 +83,7 @@ Thread::Thread(MemTag mem_tag) { _threads_hazard_ptr = nullptr; _threads_list_ptr = nullptr; _nested_threads_hazard_ptr_cnt = 0; - _rcu_counter = 0; + _rcu_counter.store_relaxed(0); // the handle mark links itself to last_handle_mark new HandleMark(this); diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 181dfc46f87..dcd6fb2d3fd 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.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. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,6 +30,7 @@ #include "gc/shared/threadLocalAllocBuffer.hpp" #include "jni.h" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "runtime/atomicAccess.hpp" #include "runtime/globals.hpp" #include "runtime/os.hpp" @@ -238,9 +239,9 @@ class Thread: public ThreadShadow { // Support for GlobalCounter private: - volatile uintx _rcu_counter; + Atomic _rcu_counter; public: - volatile uintx* get_rcu_counter() { + Atomic* get_rcu_counter() { return &_rcu_counter; } diff --git a/src/hotspot/share/utilities/globalCounter.cpp b/src/hotspot/share/utilities/globalCounter.cpp index 7019273d937..114d3b5fed1 100644 --- a/src/hotspot/share/utilities/globalCounter.cpp +++ b/src/hotspot/share/utilities/globalCounter.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,7 +23,7 @@ */ #include "memory/iterator.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" #include "runtime/threadSMR.inline.hpp" #include "runtime/vmThread.hpp" @@ -41,7 +41,7 @@ class GlobalCounter::CounterThreadCheck : public ThreadClosure { SpinYield yield; // Loops on this thread until it has exited the critical read section. while(true) { - uintx cnt = AtomicAccess::load_acquire(thread->get_rcu_counter()); + uintx cnt = thread->get_rcu_counter()->load_acquire(); // This checks if the thread's counter is active. And if so is the counter // for a pre-existing reader (belongs to this grace period). A pre-existing // reader will have a lower counter than the global counter version for this @@ -57,9 +57,9 @@ class GlobalCounter::CounterThreadCheck : public ThreadClosure { }; void GlobalCounter::write_synchronize() { - assert((*Thread::current()->get_rcu_counter() & COUNTER_ACTIVE) == 0x0, "must be outside a critcal section"); - // AtomicAccess::add must provide fence since we have storeload dependency. - uintx gbl_cnt = AtomicAccess::add(&_global_counter._counter, COUNTER_INCREMENT); + assert((Thread::current()->get_rcu_counter()->load_relaxed() & COUNTER_ACTIVE) == 0x0, "must be outside a critcal section"); + // Atomic add must provide fence since we have storeload dependency. + uintx gbl_cnt = _global_counter._counter.add_then_fetch(COUNTER_INCREMENT); // Do all RCU threads. CounterThreadCheck ctc(gbl_cnt); diff --git a/src/hotspot/share/utilities/globalCounter.hpp b/src/hotspot/share/utilities/globalCounter.hpp index c78831acfa5..80fc1b3e94e 100644 --- a/src/hotspot/share/utilities/globalCounter.hpp +++ b/src/hotspot/share/utilities/globalCounter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, 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,6 +27,7 @@ #include "memory/allStatic.hpp" #include "memory/padded.hpp" +#include "runtime/atomic.hpp" class Thread; @@ -47,7 +48,7 @@ class GlobalCounter : public AllStatic { // counter is on a separate cacheline. struct PaddedCounter { DEFINE_PAD_MINUS_SIZE(0, DEFAULT_PADDING_SIZE, 0); - volatile uintx _counter; + Atomic _counter; DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(volatile uintx)); }; diff --git a/src/hotspot/share/utilities/globalCounter.inline.hpp b/src/hotspot/share/utilities/globalCounter.inline.hpp index 9cc746173b8..ed37b8a878d 100644 --- a/src/hotspot/share/utilities/globalCounter.inline.hpp +++ b/src/hotspot/share/utilities/globalCounter.inline.hpp @@ -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 @@ -27,30 +27,29 @@ #include "utilities/globalCounter.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" inline GlobalCounter::CSContext GlobalCounter::critical_section_begin(Thread *thread) { assert(thread == Thread::current(), "must be current thread"); - uintx old_cnt = AtomicAccess::load(thread->get_rcu_counter()); + uintx old_cnt = thread->get_rcu_counter()->load_relaxed(); // Retain the old counter value if already active, e.g. nested. // Otherwise, set the counter to the current version + active bit. uintx new_cnt = old_cnt; if ((new_cnt & COUNTER_ACTIVE) == 0) { - new_cnt = AtomicAccess::load(&_global_counter._counter) | COUNTER_ACTIVE; + new_cnt = _global_counter._counter.load_relaxed() | COUNTER_ACTIVE; } - AtomicAccess::release_store_fence(thread->get_rcu_counter(), new_cnt); + thread->get_rcu_counter()->release_store_fence(new_cnt); return static_cast(old_cnt); } inline void GlobalCounter::critical_section_end(Thread *thread, CSContext context) { assert(thread == Thread::current(), "must be current thread"); - assert((*thread->get_rcu_counter() & COUNTER_ACTIVE) == COUNTER_ACTIVE, "must be in critical section"); + assert((thread->get_rcu_counter()->load_relaxed() & COUNTER_ACTIVE) == COUNTER_ACTIVE, "must be in critical section"); // Restore the counter value from before the associated begin. - AtomicAccess::release_store(thread->get_rcu_counter(), - static_cast(context)); + thread->get_rcu_counter()->release_store(static_cast(context)); } class GlobalCounter::CriticalSection { From 303de9a3f2ba93f0bbe42044483a0b48c82b70cb Mon Sep 17 00:00:00 2001 From: Xiaohong Gong Date: Tue, 20 Jan 2026 01:43:40 +0000 Subject: [PATCH 160/204] 8370666: VectorAPI: Add clear comments for vector relative code in c2 Reviewed-by: epeter, jbhateja, qamai --- src/hotspot/share/opto/matcher.hpp | 4 +- src/hotspot/share/opto/node.hpp | 5 +- src/hotspot/share/opto/type.cpp | 8 +- src/hotspot/share/opto/type.hpp | 15 +- src/hotspot/share/opto/vectornode.hpp | 343 ++++++++++++++------------ 5 files changed, 214 insertions(+), 161 deletions(-) diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index a071cff9e3c..9579af84f24 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.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 @@ -327,6 +327,8 @@ public: // e.g. Op_ vector nodes and other intrinsics while guarding with vlen static bool match_rule_supported_vector(int opcode, int vlen, BasicType bt); + // Returns true if the platform efficiently implements the given masked vector + // operation using predicate features, false otherwise. static bool match_rule_supported_vector_masked(int opcode, int vlen, BasicType bt); // Determines if a vector operation needs to be partially implemented with a mask diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index f1d9785a746..5ddc4236b3e 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.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. * Copyright (c) 2024, 2025, Alibaba Group Holding Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -845,7 +845,8 @@ public: Flag_has_swapped_edges = 1ULL << 11, Flag_is_scheduled = 1ULL << 12, Flag_is_expensive = 1ULL << 13, - Flag_is_predicated_vector = 1ULL << 14, + Flag_is_predicated_vector = 1ULL << 14, // Marked on a vector node that has an additional + // mask input controlling the lane operations. Flag_for_post_loop_opts_igvn = 1ULL << 15, Flag_for_merge_stores_igvn = 1ULL << 16, Flag_is_removed_by_peephole = 1ULL << 17, diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp index ecb8c2c1cd8..d3271e79f2f 100644 --- a/src/hotspot/share/opto/type.cpp +++ b/src/hotspot/share/opto/type.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 @@ -2443,6 +2443,12 @@ const TypeVect* TypeVect::make(BasicType elem_bt, uint length, bool is_mask) { return nullptr; } +// Create a vector mask type with the given element basic type and length. +// - Returns "TypeVectMask" (PVectMask) for platforms that support the predicate +// feature and it is implemented properly in the backend, allowing the mask to +// be stored in a predicate/mask register. +// - Returns a normal vector type "TypeVectA ~ TypeVectZ" (NVectMask) otherwise, +// where the vector mask is stored in a vector register. const TypeVect* TypeVect::makemask(BasicType elem_bt, uint length) { if (Matcher::has_predicated_vectors() && Matcher::match_rule_supported_vector_masked(Op_VectorLoadMask, length, elem_bt)) { diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp index 285b4984cd1..7c7ff035a54 100644 --- a/src/hotspot/share/opto/type.hpp +++ b/src/hotspot/share/opto/type.hpp @@ -1018,7 +1018,7 @@ public: }; //------------------------------TypeVect--------------------------------------- -// Class of Vector Types +// Basic class of vector (mask) types. class TypeVect : public Type { const BasicType _elem_bt; // Vector's element type const uint _length; // Elements in vector (power of 2) @@ -1058,6 +1058,16 @@ public: #endif }; +// TypeVect subclasses representing vectors or vector masks with "BVectMask" or "NVectMask" +// layout (see vectornode.hpp for detailed notes on vector mask representations), mapped +// to vector registers and distinguished by vector register size: +// +// - TypeVectA: Scalable vector type (variable size, e.g., AArch64 SVE, RISC-V RVV) +// - TypeVectS: 32-bit vector type +// - TypeVectD: 64-bit vector type +// - TypeVectX: 128-bit vector type +// - TypeVectY: 256-bit vector type +// - TypeVectZ: 512-bit vector type class TypeVectA : public TypeVect { friend class TypeVect; TypeVectA(BasicType elem_bt, uint length) : TypeVect(VectorA, elem_bt, length) {} @@ -1088,6 +1098,9 @@ class TypeVectZ : public TypeVect { TypeVectZ(BasicType elem_bt, uint length) : TypeVect(VectorZ, elem_bt, length) {} }; +// Class of TypeVectMask, representing vector masks with "PVectMask" layout (see +// vectornode.hpp for detailed notes on vector mask representations), mapped to +// dedicated hardware predicate/mask registers. class TypeVectMask : public TypeVect { public: friend class TypeVect; diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index dc7aa13cf36..b1976d8f0db 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * 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,63 @@ #include "opto/opcodes.hpp" #include "prims/vectorSupport.hpp" -//------------------------------VectorNode------------------------------------- +//=======================Notes-for-VectorMask-Representation======================= +// +// There are three distinct representations for vector masks based on platform and +// use scenarios: +// +// - BVectMask: Platform-independent mask stored in a vector register with 8-bit +// lanes containing 1/0 values. The corresponding type is TypeVectA ~ TypeVectZ. +// +// - NVectMask: Platform-specific mask stored in vector registers with N-bit lanes, +// where all bits in each lane are either set (true) or unset (false). Generated +// on architectures without predicate/mask feature, such as AArch64 NEON, x86 +// AVX2, etc. The corresponding type is TypeVectA ~ TypeVectZ. +// +// - PVectMask: Platform-specific mask stored in predicate/mask registers. Generated +// on architectures with predicate/mask feature, such as AArch64 SVE, x86 AVX-512, +// and RISC-V Vector Extension (RVV). The corresponding type is TypeVectMask. +// +// NVectMask and PVectMask encode element data type and vector length information. +// They are the primary mask representations used in most mask and masked vector +// operations. BVectMask primarily represents mask values loaded from or stored to +// Java boolean memory (mask backing storage). VectorLoadMask/VectorStoreMask nodes +// are needed to transform it to/from P/NVectMask. It is also used in certain mask +// operations (e.g. VectorMaskOpNode). +// +//=========================Notes-for-Masked-Vector-Nodes=========================== +// +// Each lane-wise and cross-lane (reduction) ALU node supports both non-masked +// and masked operations. +// +// Currently masked vector nodes are only used to implement the Vector API's masked +// operations (which might also be used for auto-vectorization in future), such as: +// Vector lanewise(VectorOperators.Binary op, Vector v, VectorMask m) +// +// They are generated during intrinsification for Vector API, only on architectures +// that support the relevant predicated instructions. The compiler uses +// "Matcher::match_rule_supported_vector_masked()" to check whether the current +// platform supports the predicated/masked vector instructions for an operation. It +// generates the masked vector node for the operation if supported. Otherwise, it +// generates the unpredicated vector node and implements the masked operation with +// the help of a VectorBlendNode. Please see more details from API intrinsification +// in vectorIntrinsics.cpp. +// +// To differentiate the masked and non-masked nodes, flag "Flag_is_predicated_vector" +// is set for the masked version. Meanwhile, there is an additional mask input for +// the masked nodes. +// +// For example: +// - Non-masked version: +// in1 in2 +// \ / +// AddVBNode +// +// - Masked version (with "Flag_is_predicated_vector" being set): +// in1 in2 mask +// \ | / +// AddVBNode + // Vector Operation class VectorNode : public TypeNode { public: @@ -103,6 +159,8 @@ class VectorNode : public TypeNode { static bool implemented(int opc, uint vlen, BasicType bt); static bool is_shift(Node* n); static bool is_vshift_cnt(Node* n); + // Returns true if the lower vlen bits (bits [0, vlen-1]) of the long value + // are all 1s or all 0s, indicating a "mask all" or "mask none" pattern. static bool is_maskall_type(const TypeLong* type, int vlen); static bool is_muladds2i(const Node* n); static bool is_roundopD(Node* n); @@ -176,7 +234,6 @@ class SaturatingVectorNode : public VectorNode { bool is_unsigned() { return _is_unsigned; } }; -//------------------------------AddVBNode-------------------------------------- // Vector add byte class AddVBNode : public VectorNode { public: @@ -184,7 +241,6 @@ class AddVBNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AddVSNode-------------------------------------- // Vector add char/short class AddVSNode : public VectorNode { public: @@ -192,7 +248,6 @@ class AddVSNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AddVINode-------------------------------------- // Vector add int class AddVINode : public VectorNode { public: @@ -200,7 +255,6 @@ class AddVINode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AddVLNode-------------------------------------- // Vector add long class AddVLNode : public VectorNode { public: @@ -208,7 +262,6 @@ public: virtual int Opcode() const; }; -//------------------------------AddVHFNode-------------------------------------- // Vector add float class AddVHFNode : public VectorNode { public: @@ -216,7 +269,6 @@ public: virtual int Opcode() const; }; -//------------------------------AddVFNode-------------------------------------- // Vector add float class AddVFNode : public VectorNode { public: @@ -224,7 +276,6 @@ public: virtual int Opcode() const; }; -//------------------------------AddVDNode-------------------------------------- // Vector add double class AddVDNode : public VectorNode { public: @@ -232,7 +283,6 @@ public: virtual int Opcode() const; }; -//------------------------------ReductionNode------------------------------------ // Perform reduction of a vector class ReductionNode : public Node { private: @@ -294,7 +344,6 @@ class ReductionNode : public Node { #endif }; -//------------------------------AddReductionVINode-------------------------------------- // Vector add byte, short and int as a reduction class AddReductionVINode : public ReductionNode { public: @@ -302,7 +351,6 @@ public: virtual int Opcode() const; }; -//------------------------------AddReductionVLNode-------------------------------------- // Vector add long as a reduction class AddReductionVLNode : public ReductionNode { public: @@ -310,7 +358,6 @@ public: virtual int Opcode() const; }; -//------------------------------AddReductionVFNode-------------------------------------- // Vector add float as a reduction class AddReductionVFNode : public ReductionNode { private: @@ -337,7 +384,6 @@ public: virtual uint size_of() const { return sizeof(*this); } }; -//------------------------------AddReductionVDNode-------------------------------------- // Vector add double as a reduction class AddReductionVDNode : public ReductionNode { private: @@ -364,7 +410,6 @@ public: virtual uint size_of() const { return sizeof(*this); } }; -//------------------------------SubVBNode-------------------------------------- // Vector subtract byte class SubVBNode : public VectorNode { public: @@ -372,7 +417,6 @@ class SubVBNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SubVSNode-------------------------------------- // Vector subtract short class SubVSNode : public VectorNode { public: @@ -380,7 +424,6 @@ class SubVSNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SubVINode-------------------------------------- // Vector subtract int class SubVINode : public VectorNode { public: @@ -388,7 +431,6 @@ class SubVINode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SubVLNode-------------------------------------- // Vector subtract long class SubVLNode : public VectorNode { public: @@ -396,7 +438,6 @@ class SubVLNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SaturatingAddVNode----------------------------- // Vector saturating addition. class SaturatingAddVNode : public SaturatingVectorNode { public: @@ -404,7 +445,6 @@ class SaturatingAddVNode : public SaturatingVectorNode { virtual int Opcode() const; }; -//------------------------------SaturatingSubVNode----------------------------- // Vector saturating subtraction. class SaturatingSubVNode : public SaturatingVectorNode { public: @@ -412,7 +452,6 @@ class SaturatingSubVNode : public SaturatingVectorNode { virtual int Opcode() const; }; -//------------------------------SubVHFNode-------------------------------------- // Vector subtract half float class SubVHFNode : public VectorNode { public: @@ -420,8 +459,6 @@ public: virtual int Opcode() const; }; - -//------------------------------SubVFNode-------------------------------------- // Vector subtract float class SubVFNode : public VectorNode { public: @@ -429,7 +466,6 @@ class SubVFNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SubVDNode-------------------------------------- // Vector subtract double class SubVDNode : public VectorNode { public: @@ -437,7 +473,6 @@ class SubVDNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------MulVBNode-------------------------------------- // Vector multiply byte class MulVBNode : public VectorNode { public: @@ -445,7 +480,6 @@ class MulVBNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------MulVSNode-------------------------------------- // Vector multiply short class MulVSNode : public VectorNode { public: @@ -453,7 +487,6 @@ class MulVSNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------MulVINode-------------------------------------- // Vector multiply int class MulVINode : public VectorNode { public: @@ -461,7 +494,6 @@ class MulVINode : public VectorNode { virtual int Opcode() const; }; -//------------------------------MulVLNode-------------------------------------- // Vector multiply long class MulVLNode : public VectorNode { public: @@ -473,7 +505,6 @@ public: bool has_uint_inputs() const; }; -//------------------------------MulVFNode-------------------------------------- // Vector multiply half float class MulVHFNode : public VectorNode { public: @@ -481,7 +512,6 @@ public: virtual int Opcode() const; }; -//------------------------------MulVFNode-------------------------------------- // Vector multiply float class MulVFNode : public VectorNode { public: @@ -489,7 +519,6 @@ public: virtual int Opcode() const; }; -//------------------------------MulVDNode-------------------------------------- // Vector multiply double class MulVDNode : public VectorNode { public: @@ -497,7 +526,6 @@ public: virtual int Opcode() const; }; -//------------------------------MulAddVS2VINode-------------------------------- // Vector multiply shorts to int and add adjacent ints. class MulAddVS2VINode : public VectorNode { public: @@ -505,7 +533,6 @@ class MulAddVS2VINode : public VectorNode { virtual int Opcode() const; }; -//------------------------------FmaVNode-------------------------------------- // Vector fused-multiply-add class FmaVNode : public VectorNode { public: @@ -515,7 +542,6 @@ public: virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); }; -//------------------------------FmaVDNode-------------------------------------- // Vector fused-multiply-add double class FmaVDNode : public FmaVNode { public: @@ -523,7 +549,6 @@ public: virtual int Opcode() const; }; -//------------------------------FmaVFNode-------------------------------------- // Vector fused-multiply-add float class FmaVFNode : public FmaVNode { public: @@ -531,7 +556,6 @@ public: virtual int Opcode() const; }; -//------------------------------FmaVHFNode------------------------------------- // Vector fused-multiply-add half-precision float class FmaVHFNode : public FmaVNode { public: @@ -539,7 +563,6 @@ public: virtual int Opcode() const; }; -//------------------------------MulReductionVINode-------------------------------------- // Vector multiply byte, short and int as a reduction class MulReductionVINode : public ReductionNode { public: @@ -547,7 +570,6 @@ public: virtual int Opcode() const; }; -//------------------------------MulReductionVLNode-------------------------------------- // Vector multiply int as a reduction class MulReductionVLNode : public ReductionNode { public: @@ -555,7 +577,6 @@ public: virtual int Opcode() const; }; -//------------------------------MulReductionVFNode-------------------------------------- // Vector multiply float as a reduction class MulReductionVFNode : public ReductionNode { // True if mul reduction operation for floats requires strict ordering. @@ -581,7 +602,6 @@ public: virtual uint size_of() const { return sizeof(*this); } }; -//------------------------------MulReductionVDNode-------------------------------------- // Vector multiply double as a reduction class MulReductionVDNode : public ReductionNode { // True if mul reduction operation for doubles requires strict ordering. @@ -607,7 +627,6 @@ public: virtual uint size_of() const { return sizeof(*this); } }; -//------------------------------DivVHFNode------------------------------------- // Vector divide half float class DivVHFNode : public VectorNode { public: @@ -615,7 +634,6 @@ public: virtual int Opcode() const; }; -//------------------------------DivVFNode-------------------------------------- // Vector divide float class DivVFNode : public VectorNode { public: @@ -623,7 +641,6 @@ class DivVFNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------DivVDNode-------------------------------------- // Vector Divide double class DivVDNode : public VectorNode { public: @@ -631,7 +648,6 @@ class DivVDNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AbsVBNode-------------------------------------- // Vector Abs byte class AbsVBNode : public VectorNode { public: @@ -639,7 +655,6 @@ public: virtual int Opcode() const; }; -//------------------------------AbsVSNode-------------------------------------- // Vector Abs short class AbsVSNode : public VectorNode { public: @@ -647,7 +662,6 @@ public: virtual int Opcode() const; }; -//------------------------------MinVNode-------------------------------------- // Vector Min class MinVNode : public VectorNode { public: @@ -655,7 +669,6 @@ public: virtual int Opcode() const; }; -//------------------------------MinVHFNode------------------------------------ // Vector Min for half floats class MinVHFNode : public VectorNode { public: @@ -663,7 +676,6 @@ public: virtual int Opcode() const; }; -//------------------------------MaxVHFNode------------------------------------ // Vector Max for half floats class MaxVHFNode : public VectorNode { public: @@ -671,6 +683,7 @@ public: virtual int Opcode() const; }; +// Vector Unsigned Min class UMinVNode : public VectorNode { public: UMinVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2 ,vt) { @@ -681,8 +694,6 @@ class UMinVNode : public VectorNode { virtual int Opcode() const; }; - -//------------------------------MaxVNode-------------------------------------- // Vector Max class MaxVNode : public VectorNode { public: @@ -690,6 +701,7 @@ class MaxVNode : public VectorNode { virtual int Opcode() const; }; +// Vector Unsigned Max class UMaxVNode : public VectorNode { public: UMaxVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) { @@ -700,7 +712,6 @@ class UMaxVNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AbsVINode-------------------------------------- // Vector Abs int class AbsVINode : public VectorNode { public: @@ -708,7 +719,6 @@ class AbsVINode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AbsVLNode-------------------------------------- // Vector Abs long class AbsVLNode : public VectorNode { public: @@ -716,7 +726,6 @@ public: virtual int Opcode() const; }; -//------------------------------AbsVFNode-------------------------------------- // Vector Abs float class AbsVFNode : public VectorNode { public: @@ -724,7 +733,6 @@ class AbsVFNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AbsVDNode-------------------------------------- // Vector Abs double class AbsVDNode : public VectorNode { public: @@ -732,7 +740,6 @@ class AbsVDNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------NegVNode--------------------------------------- // Vector Neg parent class (not for code generation). class NegVNode : public VectorNode { public: @@ -746,7 +753,6 @@ class NegVNode : public VectorNode { Node* degenerate_integral_negate(PhaseGVN* phase, bool is_predicated); }; -//------------------------------NegVINode-------------------------------------- // Vector Neg byte/short/int class NegVINode : public NegVNode { public: @@ -754,7 +760,6 @@ class NegVINode : public NegVNode { virtual int Opcode() const; }; -//------------------------------NegVLNode-------------------------------------- // Vector Neg long class NegVLNode : public NegVNode { public: @@ -762,7 +767,6 @@ class NegVLNode : public NegVNode { virtual int Opcode() const; }; -//------------------------------NegVFNode-------------------------------------- // Vector Neg float class NegVFNode : public NegVNode { public: @@ -770,7 +774,6 @@ class NegVFNode : public NegVNode { virtual int Opcode() const; }; -//------------------------------NegVDNode-------------------------------------- // Vector Neg double class NegVDNode : public NegVNode { public: @@ -778,7 +781,6 @@ class NegVDNode : public NegVNode { virtual int Opcode() const; }; -//------------------------------PopCountVINode--------------------------------- // Vector popcount integer bits class PopCountVINode : public VectorNode { public: @@ -786,7 +788,6 @@ class PopCountVINode : public VectorNode { virtual int Opcode() const; }; -//------------------------------PopCountVLNode--------------------------------- // Vector popcount long bits class PopCountVLNode : public VectorNode { public: @@ -796,7 +797,6 @@ class PopCountVLNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SqrtVHFNode------------------------------------- // Vector Sqrt half-precision float class SqrtVHFNode : public VectorNode { public: @@ -804,14 +804,13 @@ public: virtual int Opcode() const; }; -//------------------------------SqrtVFNode-------------------------------------- // Vector Sqrt float class SqrtVFNode : public VectorNode { public: SqrtVFNode(Node* in, const TypeVect* vt) : VectorNode(in,vt) {} virtual int Opcode() const; }; -//------------------------------RoundDoubleVNode-------------------------------- + // Vector round double class RoundDoubleModeVNode : public VectorNode { public: @@ -819,7 +818,6 @@ class RoundDoubleModeVNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------SqrtVDNode-------------------------------------- // Vector Sqrt double class SqrtVDNode : public VectorNode { public: @@ -827,8 +825,7 @@ class SqrtVDNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------ShiftVNode----------------------------------- -// Class ShiftV functionality. This covers the common behaviors for all kinds +// Class ShiftV functionality. This covers the common behaviors for all kinds // of vector shifts. class ShiftVNode : public VectorNode { private: @@ -848,7 +845,6 @@ class ShiftVNode : public VectorNode { virtual uint size_of() const { return sizeof(ShiftVNode); } }; -//------------------------------LShiftVBNode----------------------------------- // Vector left shift bytes class LShiftVBNode : public ShiftVNode { public: @@ -857,7 +853,6 @@ class LShiftVBNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------LShiftVSNode----------------------------------- // Vector left shift shorts class LShiftVSNode : public ShiftVNode { public: @@ -866,7 +861,6 @@ class LShiftVSNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------LShiftVINode----------------------------------- // Vector left shift ints class LShiftVINode : public ShiftVNode { public: @@ -875,7 +869,6 @@ class LShiftVINode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------LShiftVLNode----------------------------------- // Vector left shift longs class LShiftVLNode : public ShiftVNode { public: @@ -884,7 +877,6 @@ class LShiftVLNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------RShiftVBNode----------------------------------- // Vector right arithmetic (signed) shift bytes class RShiftVBNode : public ShiftVNode { public: @@ -893,7 +885,6 @@ class RShiftVBNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------RShiftVSNode----------------------------------- // Vector right arithmetic (signed) shift shorts class RShiftVSNode : public ShiftVNode { public: @@ -902,7 +893,6 @@ class RShiftVSNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------RShiftVINode----------------------------------- // Vector right arithmetic (signed) shift ints class RShiftVINode : public ShiftVNode { public: @@ -911,7 +901,6 @@ class RShiftVINode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------RShiftVLNode----------------------------------- // Vector right arithmetic (signed) shift longs class RShiftVLNode : public ShiftVNode { public: @@ -920,7 +909,6 @@ class RShiftVLNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------URShiftVBNode---------------------------------- // Vector right logical (unsigned) shift bytes class URShiftVBNode : public ShiftVNode { public: @@ -929,7 +917,6 @@ class URShiftVBNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------URShiftVSNode---------------------------------- // Vector right logical (unsigned) shift shorts class URShiftVSNode : public ShiftVNode { public: @@ -938,7 +925,6 @@ class URShiftVSNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------URShiftVINode---------------------------------- // Vector right logical (unsigned) shift ints class URShiftVINode : public ShiftVNode { public: @@ -947,7 +933,6 @@ class URShiftVINode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------URShiftVLNode---------------------------------- // Vector right logical (unsigned) shift longs class URShiftVLNode : public ShiftVNode { public: @@ -956,7 +941,6 @@ class URShiftVLNode : public ShiftVNode { virtual int Opcode() const; }; -//------------------------------LShiftCntVNode--------------------------------- // Vector left shift count class LShiftCntVNode : public VectorNode { public: @@ -964,7 +948,6 @@ class LShiftCntVNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------RShiftCntVNode--------------------------------- // Vector right shift count class RShiftCntVNode : public VectorNode { public: @@ -972,7 +955,6 @@ class RShiftCntVNode : public VectorNode { virtual int Opcode() const; }; -//------------------------------AndVNode--------------------------------------- // Vector and integer class AndVNode : public VectorNode { public: @@ -981,7 +963,6 @@ class AndVNode : public VectorNode { virtual Node* Identity(PhaseGVN* phase); }; -//------------------------------AndReductionVNode-------------------------------------- // Vector and byte, short, int, long as a reduction class AndReductionVNode : public ReductionNode { public: @@ -989,8 +970,7 @@ class AndReductionVNode : public ReductionNode { virtual int Opcode() const; }; -//------------------------------OrVNode--------------------------------------- -// Vector or byte, short, int, long as a reduction +// Vector or integer class OrVNode : public VectorNode { public: OrVNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1,in2,vt) {} @@ -998,15 +978,13 @@ class OrVNode : public VectorNode { virtual Node* Identity(PhaseGVN* phase); }; -//------------------------------OrReductionVNode-------------------------------------- -// Vector xor byte, short, int, long as a reduction +// Vector or byte, short, int, long as a reduction class OrReductionVNode : public ReductionNode { public: OrReductionVNode(Node* ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; }; -//------------------------------XorVNode--------------------------------------- // Vector xor integer class XorVNode : public VectorNode { public: @@ -1016,15 +994,13 @@ class XorVNode : public VectorNode { Node* Ideal_XorV_VectorMaskCmp(PhaseGVN* phase, bool can_reshape); }; -//------------------------------XorReductionVNode-------------------------------------- -// Vector and int, long as a reduction +// Vector xor byte, short, int, long as a reduction class XorReductionVNode : public ReductionNode { public: XorReductionVNode(Node* ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; }; -//------------------------------MinReductionVNode-------------------------------------- // Vector min byte, short, int, long, float, double as a reduction class MinReductionVNode : public ReductionNode { public: @@ -1032,15 +1008,13 @@ public: virtual int Opcode() const; }; -//------------------------------MaxReductionVNode-------------------------------------- -// Vector min byte, short, int, long, float, double as a reduction +// Vector max byte, short, int, long, float, double as a reduction class MaxReductionVNode : public ReductionNode { public: MaxReductionVNode(Node* ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} virtual int Opcode() const; }; -//------------------------------CompressVNode-------------------------------------- // Vector compress class CompressVNode: public VectorNode { public: @@ -1051,6 +1025,7 @@ class CompressVNode: public VectorNode { virtual int Opcode() const; }; +// Vector mask compress class CompressMNode: public VectorNode { public: CompressMNode(Node* mask, const TypeVect* vt) : @@ -1060,7 +1035,6 @@ class CompressMNode: public VectorNode { virtual int Opcode() const; }; -//------------------------------ExpandVNode-------------------------------------- // Vector expand class ExpandVNode: public VectorNode { public: @@ -1073,7 +1047,6 @@ class ExpandVNode: public VectorNode { //================================= M E M O R Y =============================== -//------------------------------LoadVectorNode--------------------------------- // Load Vector from memory class LoadVectorNode : public LoadNode { private: @@ -1115,7 +1088,6 @@ class LoadVectorNode : public LoadNode { #endif }; -//------------------------------LoadVectorGatherNode------------------------------ // Load Vector from memory via index map class LoadVectorGatherNode : public LoadVectorNode { public: @@ -1138,7 +1110,6 @@ class LoadVectorGatherNode : public LoadVectorNode { } }; -//------------------------------StoreVectorNode-------------------------------- // Store Vector to memory class StoreVectorNode : public StoreNode { private: @@ -1180,9 +1151,7 @@ class StoreVectorNode : public StoreNode { #endif }; -//------------------------------StoreVectorScatterNode------------------------------ // Store Vector into memory via index map - class StoreVectorScatterNode : public StoreVectorNode { public: enum { Indices = 4 }; @@ -1200,7 +1169,6 @@ class StoreVectorNode : public StoreNode { virtual Node* indices() const { return in(Indices); } }; -//------------------------------StoreVectorMaskedNode-------------------------------- // Store Vector to memory under the influence of a predicate register(mask). class StoreVectorMaskedNode : public StoreVectorNode { public: @@ -1221,7 +1189,6 @@ class StoreVectorMaskedNode : public StoreVectorNode { virtual Node* mask() const { return in(Mask); } }; -//------------------------------LoadVectorMaskedNode-------------------------------- // Load Vector from memory under the influence of a predicate register(mask). class LoadVectorMaskedNode : public LoadVectorNode { public: @@ -1245,7 +1212,6 @@ class LoadVectorMaskedNode : public LoadVectorNode { } }; -//-------------------------------LoadVectorGatherMaskedNode--------------------------------- // Load Vector from memory via index map under the influence of a predicate register(mask). class LoadVectorGatherMaskedNode : public LoadVectorNode { public: @@ -1268,7 +1234,6 @@ class LoadVectorGatherMaskedNode : public LoadVectorNode { } }; -//------------------------------StoreVectorScatterMaskedNode-------------------------------- // Store Vector into memory via index map under the influence of a predicate register(mask). class StoreVectorScatterMaskedNode : public StoreVectorNode { public: @@ -1312,7 +1277,6 @@ public: virtual const Type *bottom_type() const { return in(1)->bottom_type(); } }; -//------------------------------VectorCmpMaskedNode-------------------------------- // Vector Comparison under the influence of a predicate register(mask). class VectorCmpMaskedNode : public TypeNode { public: @@ -1325,7 +1289,8 @@ class VectorCmpMaskedNode : public TypeNode { virtual int Opcode() const; }; -//------------------------------VectorMaskGenNode---------------------------------- +// Generate a vector mask based on the given length. Lanes with indices in +// [0, length) are set to true, while the remaining lanes are set to false. class VectorMaskGenNode : public TypeNode { public: VectorMaskGenNode(Node* length, const Type* ty): TypeNode(ty, 2) { @@ -1338,7 +1303,8 @@ class VectorMaskGenNode : public TypeNode { static Node* make(Node* length, BasicType vmask_bt, int vmask_len); }; -//------------------------------VectorMaskOpNode----------------------------------- +// Base class for certain vector mask operations. The supported input mask can +// be either "BVectMask" or "PVectMask" depending on the platform. class VectorMaskOpNode : public TypeNode { private: int _mopc; @@ -1359,6 +1325,7 @@ class VectorMaskOpNode : public TypeNode { static Node* make(Node* mask, const Type* ty, int mopc); }; +// Count the number of true (set) lanes in the vector mask. class VectorMaskTrueCountNode : public VectorMaskOpNode { public: VectorMaskTrueCountNode(Node* mask, const Type* ty): @@ -1366,6 +1333,8 @@ class VectorMaskTrueCountNode : public VectorMaskOpNode { virtual int Opcode() const; }; +// Returns the index of the first true (set) lane in the vector mask. +// If no lanes are set, returns the vector length. class VectorMaskFirstTrueNode : public VectorMaskOpNode { public: VectorMaskFirstTrueNode(Node* mask, const Type* ty): @@ -1373,6 +1342,8 @@ class VectorMaskFirstTrueNode : public VectorMaskOpNode { virtual int Opcode() const; }; +// Returns the index of the last true (set) lane in the vector mask. +// If no lanes are set, returns -1 . class VectorMaskLastTrueNode : public VectorMaskOpNode { public: VectorMaskLastTrueNode(Node* mask, const Type* ty): @@ -1380,6 +1351,10 @@ class VectorMaskLastTrueNode : public VectorMaskOpNode { virtual int Opcode() const; }; +// Pack the mask lane values into a long value, supporting at most the +// first 64 lanes. Each mask lane is packed into one bit in the long +// value, ordered from the least significant bit to the most significant +// bit. class VectorMaskToLongNode : public VectorMaskOpNode { public: VectorMaskToLongNode(Node* mask, const Type* ty): @@ -1391,6 +1366,9 @@ class VectorMaskToLongNode : public VectorMaskOpNode { virtual Node* Identity(PhaseGVN* phase); }; +// Unpack bits from a long value into vector mask lane values. Each bit +// in the long value is unpacked into one mask lane, ordered from the +// least significant bit to the sign bit. class VectorLongToMaskNode : public VectorNode { public: VectorLongToMaskNode(Node* mask, const TypeVect* ty): @@ -1400,36 +1378,40 @@ class VectorLongToMaskNode : public VectorNode { virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); }; -//-------------------------- Vector mask broadcast ----------------------------------- +// Broadcast a scalar value to all lanes of a vector mask. All lanes are set +// to true if the input value is non-zero, or false if the input value is +// zero. This node is only used to generate a mask with "PVectMask" layout. class MaskAllNode : public VectorNode { public: MaskAllNode(Node* in, const TypeVect* vt) : VectorNode(in, vt) {} virtual int Opcode() const; }; -//--------------------------- Vector mask logical and -------------------------------- +// Perform a bitwise AND operation between two vector masks. This node is +// only used for vector masks with "PVectMask" layout. class AndVMaskNode : public AndVNode { public: AndVMaskNode(Node* in1, Node* in2, const TypeVect* vt) : AndVNode(in1, in2, vt) {} virtual int Opcode() const; }; -//--------------------------- Vector mask logical or --------------------------------- +// Perform a bitwise OR operation between two vector masks. This node is +// only used for vector masks with "PVectMask" layout. class OrVMaskNode : public OrVNode { public: OrVMaskNode(Node* in1, Node* in2, const TypeVect* vt) : OrVNode(in1, in2, vt) {} virtual int Opcode() const; }; -//--------------------------- Vector mask logical xor -------------------------------- +// Perform a bitwise XOR operation between two vector masks. This node is +// only used for vector masks with "PVectMask" layout. class XorVMaskNode : public XorVNode { public: XorVMaskNode(Node* in1, Node* in2, const TypeVect* vt) : XorVNode(in1, in2, vt) {} virtual int Opcode() const; }; -//=========================Promote_Scalar_to_Vector============================ - +// Replicate a scalar value to all lanes of a vector. class ReplicateNode : public VectorNode { public: ReplicateNode(Node* in1, const TypeVect* vt) : VectorNode(in1, vt) { @@ -1439,7 +1421,7 @@ class ReplicateNode : public VectorNode { virtual int Opcode() const; }; -//======================Populate_Indices_into_a_Vector========================= +// Populate indices into a vector. class PopulateIndexNode : public VectorNode { public: PopulateIndexNode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} @@ -1448,7 +1430,6 @@ class PopulateIndexNode : public VectorNode { //========================Pack_Scalars_into_a_Vector=========================== -//------------------------------PackNode--------------------------------------- // Pack parent class (not for code generation). class PackNode : public VectorNode { public: @@ -1466,7 +1447,6 @@ class PackNode : public VectorNode { static PackNode* make(Node* s, uint vlen, BasicType bt); }; -//------------------------------PackBNode-------------------------------------- // Pack byte scalars into vector class PackBNode : public PackNode { public: @@ -1474,7 +1454,6 @@ class PackBNode : public PackNode { virtual int Opcode() const; }; -//------------------------------PackSNode-------------------------------------- // Pack short scalars into a vector class PackSNode : public PackNode { public: @@ -1483,7 +1462,6 @@ class PackSNode : public PackNode { virtual int Opcode() const; }; -//------------------------------PackINode-------------------------------------- // Pack integer scalars into a vector class PackINode : public PackNode { public: @@ -1492,7 +1470,6 @@ class PackINode : public PackNode { virtual int Opcode() const; }; -//------------------------------PackLNode-------------------------------------- // Pack long scalars into a vector class PackLNode : public PackNode { public: @@ -1501,7 +1478,6 @@ class PackLNode : public PackNode { virtual int Opcode() const; }; -//------------------------------Pack2LNode------------------------------------- // Pack 2 long scalars into a vector class Pack2LNode : public PackNode { public: @@ -1509,7 +1485,6 @@ class Pack2LNode : public PackNode { virtual int Opcode() const; }; -//------------------------------PackFNode-------------------------------------- // Pack float scalars into vector class PackFNode : public PackNode { public: @@ -1518,7 +1493,6 @@ class PackFNode : public PackNode { virtual int Opcode() const; }; -//------------------------------PackDNode-------------------------------------- // Pack double scalars into a vector class PackDNode : public PackNode { public: @@ -1527,7 +1501,6 @@ class PackDNode : public PackNode { virtual int Opcode() const; }; -//------------------------------Pack2DNode------------------------------------- // Pack 2 double scalars into a vector class Pack2DNode : public PackNode { public: @@ -1535,7 +1508,10 @@ class Pack2DNode : public PackNode { virtual int Opcode() const; }; - +// Load the IOTA constant vector containing sequential indices starting from 0 +// and incrementing by 1 up to "VLENGTH - 1". So far, the first input is an int +// constant 0. For example, a 128-bit vector with int (32-bit) elements produces +// a vector like "[0, 1, 2, 3]". class VectorLoadConstNode : public VectorNode { public: VectorLoadConstNode(Node* in1, const TypeVect* vt) : VectorNode(in1, vt) {} @@ -1544,8 +1520,7 @@ class VectorLoadConstNode : public VectorNode { //========================Extract_Scalar_from_Vector=========================== -//------------------------------ExtractNode------------------------------------ -// Extract a scalar from a vector at position "pos" +// The base class for all extract nodes. class ExtractNode : public Node { public: ExtractNode(Node* src, Node* pos) : Node(nullptr, src, pos) {} @@ -1554,8 +1529,7 @@ class ExtractNode : public Node { static int opcode(BasicType bt); }; -//------------------------------ExtractBNode----------------------------------- -// Extract a byte from a vector at position "pos" +// Extract a byte from a vector at position "pos". class ExtractBNode : public ExtractNode { public: ExtractBNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1564,8 +1538,7 @@ class ExtractBNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegI; } }; -//------------------------------ExtractUBNode---------------------------------- -// Extract a boolean from a vector at position "pos" +// Extract a boolean from a vector at position "pos". class ExtractUBNode : public ExtractNode { public: ExtractUBNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1574,8 +1547,7 @@ class ExtractUBNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegI; } }; -//------------------------------ExtractCNode----------------------------------- -// Extract a char from a vector at position "pos" +// Extract a char from a vector at position "pos". class ExtractCNode : public ExtractNode { public: ExtractCNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1584,8 +1556,7 @@ class ExtractCNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegI; } }; -//------------------------------ExtractSNode----------------------------------- -// Extract a short from a vector at position "pos" +// Extract a short from a vector at position "pos". class ExtractSNode : public ExtractNode { public: ExtractSNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1594,8 +1565,7 @@ class ExtractSNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegI; } }; -//------------------------------ExtractINode----------------------------------- -// Extract an int from a vector at position "pos" +// Extract an int from a vector at position "pos". class ExtractINode : public ExtractNode { public: ExtractINode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1604,8 +1574,7 @@ class ExtractINode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegI; } }; -//------------------------------ExtractLNode----------------------------------- -// Extract a long from a vector at position "pos" +// Extract a long from a vector at position "pos". class ExtractLNode : public ExtractNode { public: ExtractLNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1614,8 +1583,7 @@ class ExtractLNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegL; } }; -//------------------------------ExtractFNode----------------------------------- -// Extract a float from a vector at position "pos" +// Extract a float from a vector at position "pos". class ExtractFNode : public ExtractNode { public: ExtractFNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1624,8 +1592,7 @@ class ExtractFNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegF; } }; -//------------------------------ExtractDNode----------------------------------- -// Extract a double from a vector at position "pos" +// Extract a double from a vector at position "pos". class ExtractDNode : public ExtractNode { public: ExtractDNode(Node* src, Node* pos) : ExtractNode(src, pos) {} @@ -1634,7 +1601,6 @@ class ExtractDNode : public ExtractNode { virtual uint ideal_reg() const { return Op_RegD; } }; -//------------------------------MacroLogicVNode------------------------------- // Vector logical operations packing node. class MacroLogicVNode : public VectorNode { private: @@ -1653,6 +1619,8 @@ public: Node* mask, uint truth_table, const TypeVect* vt); }; +// Compare two vectors lane-wise using the specified predicate and produce a +// vector mask. class VectorMaskCmpNode : public VectorNode { private: BoolTest::mask _predicate; @@ -1697,6 +1665,8 @@ class VectorMaskWrapperNode : public VectorNode { Node* vector_mask() const { return in(2); } }; +// Test whether all or any lanes in the first input vector mask is true, +// based on the specified predicate. class VectorTestNode : public CmpNode { private: BoolTest::mask _predicate; @@ -1719,6 +1689,9 @@ class VectorTestNode : public CmpNode { } }; +// Blend two vectors based on a vector mask. For each lane, select the value +// from the first input vector (vec1) if the corresponding mask lane is set, +// otherwise select from the second input vector (vec2). class VectorBlendNode : public VectorNode { public: VectorBlendNode(Node* vec1, Node* vec2, Node* mask) @@ -1732,11 +1705,14 @@ class VectorBlendNode : public VectorNode { Node* vec_mask() const { return in(3); } }; +// Rearrange lane elements from a source vector under the control of a shuffle +// (indexes) vector. Each lane in the shuffle vector specifies which lane from +// the source vector to select for the corresponding output lane. All indexes +// are in the range [0, VLENGTH). class VectorRearrangeNode : public VectorNode { public: VectorRearrangeNode(Node* vec1, Node* shuffle) : VectorNode(vec1, shuffle, vec1->bottom_type()->is_vect()) { - // assert(mask->is_VectorMask(), "VectorBlendNode requires that third argument be a mask"); } virtual int Opcode() const; @@ -1744,9 +1720,12 @@ class VectorRearrangeNode : public VectorNode { Node* vec_shuffle() const { return in(2); } }; - -// Select elements from two source vectors based on the wrapped indexes held in -// the first vector. +// Select lane elements from two source vectors ("src1" and "src2") under the +// control of an "indexes" vector. The two source vectors are logically concatenated +// to form a table of 2*VLENGTH elements, where src1 occupies indices [0, VLENGTH) +// and src2 occupies indices [VLENGTH, 2*VLENGTH). Each lane in the "indexes" +// vector specifies which element from this table to select for the corresponding +// output lane. class SelectFromTwoVectorNode : public VectorNode { public: SelectFromTwoVectorNode(Node* indexes, Node* src1, Node* src2, const TypeVect* vt) @@ -1758,13 +1737,19 @@ public: virtual int Opcode() const; }; -// The target may not directly support the rearrange operation for an element type. In those cases, -// we can transform the rearrange into a different element type. For example, on x86 before AVX512, -// there is no rearrange instruction for short elements, what we will then do is to transform the -// shuffle vector into one that we can do byte rearrange such that it would provide the same -// result. This could have been done in VectorRearrangeNode during code emission but we eagerly -// expand this out because it is often the case that an index vector is reused in many rearrange -// operations. This allows the index preparation to be GVN-ed as well as hoisted out of loops, etc. +// Transform a shuffle vector when the target does not directly support rearrange +// operations for the original element type. In such cases, the rearrange can be +// transformed to use a different element type. +// +// For example, on x86 before AVX512, there are no rearrange instructions for short +// elements. The shuffle vector is transformed into one suitable for byte rearrange +// that produces the same result. This could have been done in VectorRearrangeNode +// during code emission, but we eagerly expand it out because shuffle vectors are +// often reused in many rearrange operations. This allows the transformation to be +// GVN-ed and hoisted out of loops. +// +// Input: Original shuffle vector (indices for the desired element type) +// Output: Transformed shuffle vector (indices for the supported element type) class VectorLoadShuffleNode : public VectorNode { public: VectorLoadShuffleNode(Node* in, const TypeVect* vt) @@ -1773,6 +1758,8 @@ class VectorLoadShuffleNode : public VectorNode { virtual int Opcode() const; }; +// Convert a "BVectMask" into a platform-specific vector mask (either "NVectMask" +// or "PVectMask"). class VectorLoadMaskNode : public VectorNode { public: VectorLoadMaskNode(Node* in, const TypeVect* vt) : VectorNode(in, vt) { @@ -1784,6 +1771,8 @@ class VectorLoadMaskNode : public VectorNode { Node* Ideal(PhaseGVN* phase, bool can_reshape); }; +// Convert a platform-specific vector mask (either "NVectMask" or "PVectMask") +// into a "BVectMask". class VectorStoreMaskNode : public VectorNode { protected: VectorStoreMaskNode(Node* in1, ConINode* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {} @@ -1795,6 +1784,8 @@ class VectorStoreMaskNode : public VectorNode { static VectorStoreMaskNode* make(PhaseGVN& gvn, Node* in, BasicType in_type, uint num_elem); }; +// Lane-wise type cast a vector mask to the given vector type. The vector length +// of the input and output must be the same. class VectorMaskCastNode : public VectorNode { public: VectorMaskCastNode(Node* in, const TypeVect* vt) : VectorNode(in, vt) { @@ -1831,6 +1822,8 @@ class VectorReinterpretNode : public VectorNode { virtual int Opcode() const; }; +// Lane-wise type cast a vector to the given vector type. This is the base +// class for all vector type cast operations. class VectorCastNode : public VectorNode { public: VectorCastNode(Node* in, const TypeVect* vt) : VectorNode(in, vt) {} @@ -1843,6 +1836,7 @@ class VectorCastNode : public VectorNode { virtual Node* Identity(PhaseGVN* phase); }; +// Cast a byte vector to the given vector type. class VectorCastB2XNode : public VectorCastNode { public: VectorCastB2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1851,6 +1845,7 @@ class VectorCastB2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast a short vector to the given vector type. class VectorCastS2XNode : public VectorCastNode { public: VectorCastS2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1859,6 +1854,7 @@ class VectorCastS2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast an int vector to the given vector type. class VectorCastI2XNode : public VectorCastNode { public: VectorCastI2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1867,6 +1863,7 @@ class VectorCastI2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast a long vector to the given vector type. class VectorCastL2XNode : public VectorCastNode { public: VectorCastL2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1875,6 +1872,7 @@ class VectorCastL2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast a float vector to the given vector type. class VectorCastF2XNode : public VectorCastNode { public: VectorCastF2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1883,6 +1881,7 @@ class VectorCastF2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast a double vector to the given vector type. class VectorCastD2XNode : public VectorCastNode { public: VectorCastD2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1891,6 +1890,7 @@ class VectorCastD2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast a half float vector to float vector type. class VectorCastHF2FNode : public VectorCastNode { public: VectorCastHF2FNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1899,6 +1899,7 @@ class VectorCastHF2FNode : public VectorCastNode { virtual int Opcode() const; }; +// Cast a float vector to a half float vector type. class VectorCastF2HFNode : public VectorCastNode { public: VectorCastF2HFNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1907,8 +1908,12 @@ class VectorCastF2HFNode : public VectorCastNode { virtual int Opcode() const; }; -// So far, VectorUCastNode can only be used in Vector API unsigned extensions -// between integral types. E.g., extending byte to float is not supported now. +// Unsigned vector cast operations can only be used in unsigned (zero) +// extensions between integral types so far. E.g., extending byte to +// float is not supported now. + +// Unsigned cast a byte vector to the given vector type with short, int, +// or long element type. class VectorUCastB2XNode : public VectorCastNode { public: VectorUCastB2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1920,6 +1925,8 @@ class VectorUCastB2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Unsigned cast a short vector to the given vector type with int or long +// element type. class VectorUCastS2XNode : public VectorCastNode { public: VectorUCastS2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1930,6 +1937,7 @@ class VectorUCastS2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Unsigned cast an int vector to the given vector type with long element type. class VectorUCastI2XNode : public VectorCastNode { public: VectorUCastI2XNode(Node* in, const TypeVect* vt) : VectorCastNode(in, vt) { @@ -1939,6 +1947,7 @@ class VectorUCastI2XNode : public VectorCastNode { virtual int Opcode() const; }; +// Vector round float to nearest integer. class RoundVFNode : public VectorNode { public: RoundVFNode(Node* in, const TypeVect* vt) :VectorNode(in, vt) { @@ -1947,6 +1956,7 @@ class RoundVFNode : public VectorNode { virtual int Opcode() const; }; +// Vector round double to nearest integer. class RoundVDNode : public VectorNode { public: RoundVDNode(Node* in, const TypeVect* vt) : VectorNode(in, vt) { @@ -1955,6 +1965,7 @@ class RoundVDNode : public VectorNode { virtual int Opcode() const; }; +// Insert a new value into a vector lane at the specified position. class VectorInsertNode : public VectorNode { public: VectorInsertNode(Node* vsrc, Node* new_val, ConINode* pos, const TypeVect* vt) : VectorNode(vsrc, new_val, (Node*)pos, vt) { @@ -1968,6 +1979,9 @@ class VectorInsertNode : public VectorNode { static Node* make(Node* vec, Node* new_val, int position, PhaseGVN& gvn); }; +// Box a vector value into a Vector API object (e.g., IntMaxVector). +// This is a macro node that gets expanded during vector optimization +// phase. class VectorBoxNode : public Node { private: const TypeInstPtr* const _box_type; @@ -1995,6 +2009,8 @@ class VectorBoxNode : public Node { static const TypeFunc* vec_box_type(const TypeInstPtr* box_type); }; +// Allocate storage for boxing a vector value. This is used during vector +// box expansion. class VectorBoxAllocateNode : public CallStaticJavaNode { public: VectorBoxAllocateNode(Compile* C, const TypeInstPtr* vbox_type) @@ -2009,6 +2025,9 @@ class VectorBoxAllocateNode : public CallStaticJavaNode { #endif // !PRODUCT }; +// Unbox a Vector API object (e.g., IntMaxVector) to extract the underlying +// vector value. This is a macro node expanded during vector optimization +// phase. class VectorUnboxNode : public VectorNode { protected: uint size_of() const { return sizeof(*this); } @@ -2027,6 +2046,7 @@ class VectorUnboxNode : public VectorNode { Node* Ideal(PhaseGVN* phase, bool can_reshape); }; +// Lane-wise right rotation of the first input by the second input. class RotateRightVNode : public VectorNode { public: RotateRightVNode(Node* in1, Node* in2, const TypeVect* vt) @@ -2036,6 +2056,7 @@ public: Node* Ideal(PhaseGVN* phase, bool can_reshape); }; +// Lane-wise left rotation of the first input by the second input. class RotateLeftVNode : public VectorNode { public: RotateLeftVNode(Node* in1, Node* in2, const TypeVect* vt) @@ -2045,6 +2066,7 @@ public: Node* Ideal(PhaseGVN* phase, bool can_reshape); }; +// Count the number of leading zeros in each lane of the input. class CountLeadingZerosVNode : public VectorNode { public: CountLeadingZerosVNode(Node* in, const TypeVect* vt) @@ -2056,6 +2078,7 @@ class CountLeadingZerosVNode : public VectorNode { virtual int Opcode() const; }; +// Count the number of trailing zeros in each lane of the input. class CountTrailingZerosVNode : public VectorNode { public: CountTrailingZerosVNode(Node* in, const TypeVect* vt) @@ -2067,6 +2090,7 @@ class CountTrailingZerosVNode : public VectorNode { virtual int Opcode() const; }; +// Reverse the bits within each lane (e.g., 0b10110010 becomes 0b01001101). class ReverseVNode : public VectorNode { public: ReverseVNode(Node* in, const TypeVect* vt) @@ -2076,6 +2100,7 @@ public: virtual int Opcode() const; }; +// Reverse the byte order within each lane (e.g., 0x12345678 becomes 0x78563412). class ReverseBytesVNode : public VectorNode { public: ReverseBytesVNode(Node* in, const TypeVect* vt) @@ -2085,6 +2110,7 @@ public: virtual int Opcode() const; }; +// Vector signum float. class SignumVFNode : public VectorNode { public: SignumVFNode(Node* in1, Node* zero, Node* one, const TypeVect* vt) @@ -2093,6 +2119,7 @@ public: virtual int Opcode() const; }; +// Vector signum double. class SignumVDNode : public VectorNode { public: SignumVDNode(Node* in1, Node* zero, Node* one, const TypeVect* vt) @@ -2101,6 +2128,8 @@ public: virtual int Opcode() const; }; +// Compress (extract and pack) bits in each lane of the first input +// based on the mask input. class CompressBitsVNode : public VectorNode { public: CompressBitsVNode(Node* in, Node* mask, const TypeVect* vt) @@ -2108,6 +2137,8 @@ public: virtual int Opcode() const; }; +// Expand (deposit) bits in each lane of the first input based on the +// mask input. class ExpandBitsVNode : public VectorNode { public: ExpandBitsVNode(Node* in, Node* mask, const TypeVect* vt) From ca6925ec6bf44cf7d4704becc194389e4c87b74f Mon Sep 17 00:00:00 2001 From: David Holmes Date: Tue, 20 Jan 2026 06:18:07 +0000 Subject: [PATCH 161/204] 8370112: Remove VM_Version::supports_fast_class_init_checks() in platform-specific code Reviewed-by: shade, fyang --- .../cpu/aarch64/sharedRuntime_aarch64.cpp | 29 ++++++++------- .../cpu/aarch64/templateTable_aarch64.cpp | 9 ++--- src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp | 35 +++++++++--------- src/hotspot/cpu/ppc/templateTable_ppc_64.cpp | 9 ++--- src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp | 29 ++++++++------- src/hotspot/cpu/riscv/templateTable_riscv.cpp | 9 ++--- src/hotspot/cpu/s390/sharedRuntime_s390.cpp | 31 ++++++++-------- src/hotspot/cpu/s390/templateTable_s390.cpp | 9 ++--- src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp | 36 +++++++++---------- src/hotspot/cpu/x86/templateTable_x86.cpp | 9 ++--- 10 files changed, 102 insertions(+), 103 deletions(-) diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index 89ae6bc10e0..73b631029a0 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_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, 2021, Red Hat Inc. All rights reserved. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -722,22 +722,20 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, // Class initialization barrier for static methods entry_address[AdapterBlob::C2I_No_Clinit_Check] = nullptr; - if (VM_Version::supports_fast_class_init_checks()) { - Label L_skip_barrier; + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); + Label L_skip_barrier; - { // Bypass the barrier for non-static methods - __ ldrh(rscratch1, Address(rmethod, Method::access_flags_offset())); - __ andsw(zr, rscratch1, JVM_ACC_STATIC); - __ br(Assembler::EQ, L_skip_barrier); // non-static - } + // Bypass the barrier for non-static methods + __ ldrh(rscratch1, Address(rmethod, Method::access_flags_offset())); + __ andsw(zr, rscratch1, JVM_ACC_STATIC); + __ br(Assembler::EQ, L_skip_barrier); // non-static - __ load_method_holder(rscratch2, rmethod); - __ clinit_barrier(rscratch2, rscratch1, &L_skip_barrier); - __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); + __ load_method_holder(rscratch2, rmethod); + __ clinit_barrier(rscratch2, rscratch1, &L_skip_barrier); + __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); - __ bind(L_skip_barrier); - entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); - } + __ bind(L_skip_barrier); + entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->c2i_entry_barrier(masm); @@ -1508,7 +1506,8 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, // SVC, HVC, or SMC. Make it a NOP. __ nop(); - if (VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) { + if (method->needs_clinit_barrier()) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; __ mov_metadata(rscratch2, method->method_holder()); // InstanceKlass* __ clinit_barrier(rscratch2, rscratch1, &L_skip_barrier); diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index cde142b39ac..07b469650f0 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_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, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -2290,7 +2290,8 @@ void TemplateTable::resolve_cache_and_index_for_method(int byte_no, __ subs(zr, temp, (int) code); // have we resolved this bytecode? // Class initialization barrier for static methods - if (VM_Version::supports_fast_class_init_checks() && bytecode() == Bytecodes::_invokestatic) { + if (bytecode() == Bytecodes::_invokestatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); __ br(Assembler::NE, L_clinit_barrier_slow); __ ldr(temp, Address(Rcache, in_bytes(ResolvedMethodEntry::method_offset()))); __ load_method_holder(temp, temp); @@ -2340,8 +2341,8 @@ void TemplateTable::resolve_cache_and_index_for_field(int byte_no, __ subs(zr, temp, (int) code); // have we resolved this bytecode? // Class initialization barrier for static fields - if (VM_Version::supports_fast_class_init_checks() && - (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic)) { + if (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register field_holder = temp; __ br(Assembler::NE, L_clinit_barrier_slow); diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 4e427ace404..4eb2028f529 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.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) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1237,26 +1237,24 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, // Class initialization barrier for static methods entry_address[AdapterBlob::C2I_No_Clinit_Check] = nullptr; - if (VM_Version::supports_fast_class_init_checks()) { - Label L_skip_barrier; + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); + Label L_skip_barrier; - { // Bypass the barrier for non-static methods - __ lhz(R0, in_bytes(Method::access_flags_offset()), R19_method); - __ andi_(R0, R0, JVM_ACC_STATIC); - __ beq(CR0, L_skip_barrier); // non-static - } + // Bypass the barrier for non-static methods + __ lhz(R0, in_bytes(Method::access_flags_offset()), R19_method); + __ andi_(R0, R0, JVM_ACC_STATIC); + __ beq(CR0, L_skip_barrier); // non-static - Register klass = R11_scratch1; - __ load_method_holder(klass, R19_method); - __ clinit_barrier(klass, R16_thread, &L_skip_barrier /*L_fast_path*/); + Register klass = R11_scratch1; + __ load_method_holder(klass, R19_method); + __ clinit_barrier(klass, R16_thread, &L_skip_barrier /*L_fast_path*/); - __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub(), R0); - __ mtctr(klass); - __ bctr(); + __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub(), R0); + __ mtctr(klass); + __ bctr(); - __ bind(L_skip_barrier); - entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); - } + __ bind(L_skip_barrier); + entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->c2i_entry_barrier(masm, /* tmp register*/ ic_klass, /* tmp register*/ receiver_klass, /* tmp register*/ code); @@ -2210,7 +2208,8 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // -------------------------------------------------------------------------- vep_start_pc = (intptr_t)__ pc(); - if (VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) { + if (method->needs_clinit_barrier()) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; Register klass = r_temp_1; // Notify OOP recorder (don't need the relocation) diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index 8d61ba1b2d7..8a3af748fa1 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.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. * Copyright (c) 2013, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -2199,7 +2199,8 @@ void TemplateTable::resolve_cache_and_index_for_method(int byte_no, Register Rca __ isync(); // Order load wrt. succeeding loads. // Class initialization barrier for static methods - if (VM_Version::supports_fast_class_init_checks() && bytecode() == Bytecodes::_invokestatic) { + if (bytecode() == Bytecodes::_invokestatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register method = Rscratch; const Register klass = Rscratch; @@ -2244,8 +2245,8 @@ void TemplateTable::resolve_cache_and_index_for_field(int byte_no, Register Rcac __ isync(); // Order load wrt. succeeding loads. // Class initialization barrier for static fields - if (VM_Version::supports_fast_class_init_checks() && - (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic)) { + if (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register field_holder = R4_ARG2; // InterpreterRuntime::resolve_get_put sets field_holder and finally release-stores put_code. diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index eeb6fad1b59..44a6f6c0dc0 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_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. @@ -637,22 +637,20 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, // Class initialization barrier for static methods entry_address[AdapterBlob::C2I_No_Clinit_Check] = nullptr; - if (VM_Version::supports_fast_class_init_checks()) { - Label L_skip_barrier; + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); + Label L_skip_barrier; - { // Bypass the barrier for non-static methods - __ load_unsigned_short(t0, Address(xmethod, Method::access_flags_offset())); - __ test_bit(t1, t0, exact_log2(JVM_ACC_STATIC)); - __ beqz(t1, L_skip_barrier); // non-static - } + // Bypass the barrier for non-static methods + __ load_unsigned_short(t0, Address(xmethod, Method::access_flags_offset())); + __ test_bit(t1, t0, exact_log2(JVM_ACC_STATIC)); + __ beqz(t1, L_skip_barrier); // non-static - __ load_method_holder(t1, xmethod); - __ clinit_barrier(t1, t0, &L_skip_barrier); - __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); + __ load_method_holder(t1, xmethod); + __ clinit_barrier(t1, t0, &L_skip_barrier); + __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); - __ bind(L_skip_barrier); - entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); - } + __ bind(L_skip_barrier); + entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->c2i_entry_barrier(masm); @@ -1443,7 +1441,8 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, __ nop(); // 4 bytes } - if (VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) { + if (method->needs_clinit_barrier()) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; __ mov_metadata(t1, method->method_holder()); // InstanceKlass* __ clinit_barrier(t1, t0, &L_skip_barrier); diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index ca41583e4bc..5a3644f70bb 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_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, 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. @@ -2192,7 +2192,8 @@ void TemplateTable::resolve_cache_and_index_for_method(int byte_no, __ mv(t0, (int) code); // Class initialization barrier for static methods - if (VM_Version::supports_fast_class_init_checks() && bytecode() == Bytecodes::_invokestatic) { + if (bytecode() == Bytecodes::_invokestatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); __ bne(temp, t0, L_clinit_barrier_slow); // have we resolved this bytecode? __ ld(temp, Address(Rcache, in_bytes(ResolvedMethodEntry::method_offset()))); __ load_method_holder(temp, temp); @@ -2243,8 +2244,8 @@ void TemplateTable::resolve_cache_and_index_for_field(int byte_no, __ mv(t0, (int) code); // have we resolved this bytecode? // Class initialization barrier for static fields - if (VM_Version::supports_fast_class_init_checks() && - (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic)) { + if (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register field_holder = temp; __ bne(temp, t0, L_clinit_barrier_slow); diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index 5b6f7dcd984..00a830a80cd 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_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. * @@ -1567,7 +1567,8 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, //--------------------------------------------------------------------- wrapper_VEPStart = __ offset(); - if (VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) { + if (method->needs_clinit_barrier()) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; Register klass = Z_R1_scratch; // Notify OOP recorder (don't need the relocation) @@ -2378,24 +2379,22 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, // Class initialization barrier for static methods entry_address[AdapterBlob::C2I_No_Clinit_Check] = nullptr; - if (VM_Version::supports_fast_class_init_checks()) { - Label L_skip_barrier; + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); + Label L_skip_barrier; - { // Bypass the barrier for non-static methods - __ testbit_ushort(Address(Z_method, Method::access_flags_offset()), JVM_ACC_STATIC_BIT); - __ z_bfalse(L_skip_barrier); // non-static - } + // Bypass the barrier for non-static methods + __ testbit_ushort(Address(Z_method, Method::access_flags_offset()), JVM_ACC_STATIC_BIT); + __ z_bfalse(L_skip_barrier); // non-static - Register klass = Z_R11; - __ load_method_holder(klass, Z_method); - __ clinit_barrier(klass, Z_thread, &L_skip_barrier /*L_fast_path*/); + Register klass = Z_R11; + __ load_method_holder(klass, Z_method); + __ clinit_barrier(klass, Z_thread, &L_skip_barrier /*L_fast_path*/); - __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub()); - __ z_br(klass); + __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub()); + __ z_br(klass); - __ bind(L_skip_barrier); - entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); - } + __ bind(L_skip_barrier); + entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); return; diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp index 4e8fdf275e4..647915ef4fa 100644 --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_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. * @@ -2377,7 +2377,8 @@ void TemplateTable::resolve_cache_and_index_for_method(int byte_no, __ z_cli(Address(Rcache, bc_offset), code); // Class initialization barrier for static methods - if (VM_Version::supports_fast_class_init_checks() && bytecode() == Bytecodes::_invokestatic) { + if (bytecode() == Bytecodes::_invokestatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register method = Z_R1_scratch; const Register klass = Z_R1_scratch; __ z_brne(L_clinit_barrier_slow); @@ -2427,8 +2428,8 @@ void TemplateTable::resolve_cache_and_index_for_field(int byte_no, __ z_cli(Address(cache, code_offset), code); // Class initialization barrier for static fields - if (VM_Version::supports_fast_class_init_checks() && - (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic)) { + if (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register field_holder = index; __ z_brne(L_clinit_barrier_slow); diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index 5a4a5b1809e..bbd43c1a0e8 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1043,26 +1043,24 @@ void SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, // Class initialization barrier for static methods entry_address[AdapterBlob::C2I_No_Clinit_Check] = nullptr; - if (VM_Version::supports_fast_class_init_checks()) { - Label L_skip_barrier; - Register method = rbx; + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); + Label L_skip_barrier; + Register method = rbx; - { // Bypass the barrier for non-static methods - Register flags = rscratch1; - __ load_unsigned_short(flags, Address(method, Method::access_flags_offset())); - __ testl(flags, JVM_ACC_STATIC); - __ jcc(Assembler::zero, L_skip_barrier); // non-static - } + // Bypass the barrier for non-static methods + Register flags = rscratch1; + __ load_unsigned_short(flags, Address(method, Method::access_flags_offset())); + __ testl(flags, JVM_ACC_STATIC); + __ jcc(Assembler::zero, L_skip_barrier); // non-static - Register klass = rscratch1; - __ load_method_holder(klass, method); - __ clinit_barrier(klass, &L_skip_barrier /*L_fast_path*/); + Register klass = rscratch1; + __ load_method_holder(klass, method); + __ clinit_barrier(klass, &L_skip_barrier /*L_fast_path*/); - __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path + __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path - __ bind(L_skip_barrier); - entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); - } + __ bind(L_skip_barrier); + entry_address[AdapterBlob::C2I_No_Clinit_Check] = __ pc(); BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->c2i_entry_barrier(masm); @@ -1904,7 +1902,8 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm, int vep_offset = ((intptr_t)__ pc()) - start; - if (VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) { + if (method->needs_clinit_barrier()) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); Label L_skip_barrier; Register klass = r10; __ mov_metadata(klass, method->method_holder()); // InstanceKlass* @@ -3602,4 +3601,3 @@ RuntimeStub* SharedRuntime::generate_jfr_return_lease() { } #endif // INCLUDE_JFR - diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp index 42392b84833..db7749ec482 100644 --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_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 @@ -2216,7 +2216,8 @@ void TemplateTable::resolve_cache_and_index_for_method(int byte_no, __ cmpl(temp, code); // have we resolved this bytecode? // Class initialization barrier for static methods - if (VM_Version::supports_fast_class_init_checks() && bytecode() == Bytecodes::_invokestatic) { + if (bytecode() == Bytecodes::_invokestatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register method = temp; const Register klass = temp; @@ -2264,8 +2265,8 @@ void TemplateTable::resolve_cache_and_index_for_field(int byte_no, __ cmpl(temp, code); // have we resolved this bytecode? // Class initialization barrier for static fields - if (VM_Version::supports_fast_class_init_checks() && - (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic)) { + if (bytecode() == Bytecodes::_getstatic || bytecode() == Bytecodes::_putstatic) { + assert(VM_Version::supports_fast_class_init_checks(), "sanity"); const Register field_holder = temp; __ jcc(Assembler::notEqual, L_clinit_barrier_slow); From e45f5656bc90421c9acb0cbf87164162039ddf81 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Tue, 20 Jan 2026 07:10:46 +0000 Subject: [PATCH 162/204] 8373650: Test "javax/swing/JMenuItem/6458123/ManualBug6458123.java" fails because the check icons are not aligned properly as expected Reviewed-by: tr, dnguyen --- .../plaf/windows/WindowsIconFactory.java | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java index 915a361a3a1..91c2cbcd61d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.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 @@ -905,10 +905,46 @@ public final class WindowsIconFactory implements Serializable XPStyle xp = XPStyle.getXP(); if (xp != null) { Skin skin = xp.getSkin(c, part); - if (icon == null || icon.getIconHeight() <= 16) { - skin.paintSkin(g, x + OFFSET, y + OFFSET, state); + if (WindowsGraphicsUtils.isLeftToRight(c)) { + if (icon == null || icon.getIconHeight() <= 16) { + skin.paintSkin(g, x + OFFSET, y + OFFSET, state); + } else { + skin.paintSkin(g, x + OFFSET, y + icon.getIconHeight() / 2, state); + } } else { - skin.paintSkin(g, x + OFFSET, y + icon.getIconHeight() / 2, state); + if (icon == null) { + skin.paintSkin(g, x + 4 * OFFSET, y + OFFSET, state); + } else { + int ycoord = (icon.getIconHeight() <= 16) + ? y + OFFSET + : (y + icon.getIconHeight() / 2); + if (icon.getIconWidth() <= 8) { + skin.paintSkin(g, x + OFFSET, ycoord, state); + } else if (icon.getIconWidth() <= 16) { + if (menuItem.getText().isEmpty()) { + skin.paintSkin(g, + (menuItem.getAccelerator() != null) + ? (x + 2 * OFFSET) : (x + 3 * OFFSET), + ycoord, state); + } else { + skin.paintSkin(g, + (type == JRadioButtonMenuItem.class) + ? (x + 4 * OFFSET) : (x + 3 * OFFSET), + ycoord, state); + } + } else { + if (menuItem.getText().isEmpty() + || menuItem.getAccelerator() != null) { + skin.paintSkin(g, + (type == JRadioButtonMenuItem.class) + ? (x + 3 * OFFSET) : (x + 4 * OFFSET), + ycoord, state); + } else { + skin.paintSkin(g, x + 7 * OFFSET, + ycoord, state); + } + } + } } } } From d9db4fb36e4f90546dc3fc19b5923b8be6a2f518 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 20 Jan 2026 08:01:54 +0000 Subject: [PATCH 163/204] 8373894: G1: Count evacuation-failed garbage collections in gc cpu usage Reviewed-by: iwalulya, kbarrett --- src/hotspot/share/gc/g1/g1Policy.cpp | 11 ++++------- src/hotspot/share/gc/g1/g1Policy.hpp | 5 ++--- .../jtreg/gc/stress/TestMultiThreadStressRSet.java | 6 +++--- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 6eef6cbfa87..2847a25c5b4 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.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 @@ -943,7 +943,7 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar phase_times()->sum_thread_work_items(G1GCPhaseTimes::MergePSS, G1GCPhaseTimes::MergePSSToYoungGenCards)); } - record_pause(this_pause, start_time_sec, end_time_sec, allocation_failure); + 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), @@ -1389,16 +1389,13 @@ void G1Policy::update_gc_pause_time_ratios(G1GCPauseType gc_type, double start_t void G1Policy::record_pause(G1GCPauseType gc_type, double start, - double end, - bool allocation_failure) { + double end) { // Manage the MMU tracker. For some reason it ignores Full GCs. if (gc_type != G1GCPauseType::FullGC) { _mmu_tracker->add_pause(start, end); } - if (!allocation_failure) { - update_gc_pause_time_ratios(gc_type, start, end); - } + update_gc_pause_time_ratios(gc_type, start, end); update_time_to_mixed_tracking(gc_type, start, end); diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 72fdc6deb5b..cf12a7a8027 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -275,8 +275,7 @@ private: // Record the given STW pause with the given start and end times (in s). void record_pause(G1GCPauseType gc_type, double start, - double end, - bool allocation_failure = false); + double end); void update_gc_pause_time_ratios(G1GCPauseType gc_type, double start_sec, double end_sec); diff --git a/test/hotspot/jtreg/gc/stress/TestMultiThreadStressRSet.java b/test/hotspot/jtreg/gc/stress/TestMultiThreadStressRSet.java index a323ebd945a..ffd5203dfb8 100644 --- a/test/hotspot/jtreg/gc/stress/TestMultiThreadStressRSet.java +++ b/test/hotspot/jtreg/gc/stress/TestMultiThreadStressRSet.java @@ -44,15 +44,15 @@ import jdk.test.lib.Utils; * @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:G1SummarizeRSetStatsPeriod=1 -Xlog:gc + * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=1 -Xlog:gc -XX:-UseGCOverheadLimit * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 gc.stress.TestMultiThreadStressRSet 10 4 * * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=100 -Xlog:gc + * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=100 -Xlog:gc -XX:-UseGCOverheadLimit * -Xmx1G -XX:G1HeapRegionSize=8m -XX:MaxGCPauseMillis=1000 gc.stress.TestMultiThreadStressRSet 60 16 * * @run main/othervm/timeout=1200 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=100 -Xlog:gc + * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=100 -Xlog:gc -XX:-UseGCOverheadLimit * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 gc.stress.TestMultiThreadStressRSet 600 32 */ public class TestMultiThreadStressRSet { From c5f288e2ae2ebe6ee4a0d39d91348f746bd0e353 Mon Sep 17 00:00:00 2001 From: Leo Korinth Date: Tue, 20 Jan 2026 09:30:12 +0000 Subject: [PATCH 164/204] 8373253: Re-work InjectGCWorkerCreationFailure for future changes Reviewed-by: stefank, tschatzl, iwalulya, sjohanss --- src/hotspot/share/gc/shared/workerThread.cpp | 16 +++++++++++++++- src/hotspot/share/gc/shared/workerThread.hpp | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/shared/workerThread.cpp b/src/hotspot/share/gc/shared/workerThread.cpp index 7a9404a195a..e4831d25d26 100644 --- a/src/hotspot/share/gc/shared/workerThread.cpp +++ b/src/hotspot/share/gc/shared/workerThread.cpp @@ -96,8 +96,22 @@ void WorkerThreads::initialize_workers() { } } +bool WorkerThreads::allow_inject_creation_failure() const { + if (!is_init_completed()) { + // Never allow creation failures during VM init + return false; + } + + if (_created_workers == 0) { + // Never allow creation failures of the first worker, it will cause the VM to exit + return false; + } + + return true; +} + WorkerThread* WorkerThreads::create_worker(uint name_suffix) { - if (is_init_completed() && InjectGCWorkerCreationFailure) { + if (InjectGCWorkerCreationFailure && allow_inject_creation_failure()) { return nullptr; } diff --git a/src/hotspot/share/gc/shared/workerThread.hpp b/src/hotspot/share/gc/shared/workerThread.hpp index a1f7282abe4..003ce8a2959 100644 --- a/src/hotspot/share/gc/shared/workerThread.hpp +++ b/src/hotspot/share/gc/shared/workerThread.hpp @@ -104,6 +104,7 @@ public: WorkerThreads(const char* name, uint max_workers); void initialize_workers(); + bool allow_inject_creation_failure() const; uint max_workers() const { return _max_workers; } uint created_workers() const { return _created_workers; } From afbb3a041545ea11ee1514d329c1a6cc4cb969d2 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 20 Jan 2026 10:31:22 +0000 Subject: [PATCH 165/204] 8375620: G1: Convert G1CardTableClaimTable to use Atomic Reviewed-by: kbarrett, shade --- src/hotspot/share/gc/g1/g1CardTableClaimTable.cpp | 8 ++++---- src/hotspot/share/gc/g1/g1CardTableClaimTable.hpp | 5 +++-- .../share/gc/g1/g1CardTableClaimTable.inline.hpp | 11 +++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CardTableClaimTable.cpp b/src/hotspot/share/gc/g1/g1CardTableClaimTable.cpp index e0cadbdd907..d8cabaa00a4 100644 --- a/src/hotspot/share/gc/g1/g1CardTableClaimTable.cpp +++ b/src/hotspot/share/gc/g1/g1CardTableClaimTable.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 @@ -44,20 +44,20 @@ G1CardTableClaimTable::~G1CardTableClaimTable() { void G1CardTableClaimTable::initialize(uint max_reserved_regions) { assert(_card_claims == nullptr, "Must not be initialized twice"); - _card_claims = NEW_C_HEAP_ARRAY(uint, max_reserved_regions, mtGC); + _card_claims = NEW_C_HEAP_ARRAY(Atomic, max_reserved_regions, mtGC); _max_reserved_regions = max_reserved_regions; reset_all_to_unclaimed(); } void G1CardTableClaimTable::reset_all_to_unclaimed() { for (uint i = 0; i < _max_reserved_regions; i++) { - _card_claims[i] = 0; + _card_claims[i].store_relaxed(0); } } void G1CardTableClaimTable::reset_all_to_claimed() { for (uint i = 0; i < _max_reserved_regions; i++) { - _card_claims[i] = (uint)G1HeapRegion::CardsPerRegion; + _card_claims[i].store_relaxed((uint)G1HeapRegion::CardsPerRegion); } } diff --git a/src/hotspot/share/gc/g1/g1CardTableClaimTable.hpp b/src/hotspot/share/gc/g1/g1CardTableClaimTable.hpp index 4f524b83f97..822ef45c722 100644 --- a/src/hotspot/share/gc/g1/g1CardTableClaimTable.hpp +++ b/src/hotspot/share/gc/g1/g1CardTableClaimTable.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 @@ -27,6 +27,7 @@ #include "gc/g1/g1CardTable.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" class G1HeapRegionClosure; @@ -45,7 +46,7 @@ class G1CardTableClaimTable : public CHeapObj { // Card table iteration claim values for every heap region, from 0 (completely unclaimed) // to (>=) G1HeapRegion::CardsPerRegion (completely claimed). - uint volatile* _card_claims; + Atomic* _card_claims; uint _cards_per_chunk; // For conversion between card index and chunk index. diff --git a/src/hotspot/share/gc/g1/g1CardTableClaimTable.inline.hpp b/src/hotspot/share/gc/g1/g1CardTableClaimTable.inline.hpp index d682f0d17ae..35b2484982c 100644 --- a/src/hotspot/share/gc/g1/g1CardTableClaimTable.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CardTableClaimTable.inline.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 @@ -29,26 +29,25 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" -#include "runtime/atomicAccess.hpp" bool G1CardTableClaimTable::has_unclaimed_cards(uint region) { assert(region < _max_reserved_regions, "Tried to access invalid region %u", region); - return AtomicAccess::load(&_card_claims[region]) < G1HeapRegion::CardsPerRegion; + return _card_claims[region].load_relaxed() < G1HeapRegion::CardsPerRegion; } void G1CardTableClaimTable::reset_to_unclaimed(uint region) { assert(region < _max_reserved_regions, "Tried to access invalid region %u", region); - AtomicAccess::store(&_card_claims[region], 0u); + _card_claims[region].store_relaxed(0u); } uint G1CardTableClaimTable::claim_cards(uint region, uint increment) { assert(region < _max_reserved_regions, "Tried to access invalid region %u", region); - return AtomicAccess::fetch_then_add(&_card_claims[region], increment, memory_order_relaxed); + return _card_claims[region].fetch_then_add(increment, memory_order_relaxed); } uint G1CardTableClaimTable::claim_chunk(uint region) { assert(region < _max_reserved_regions, "Tried to access invalid region %u", region); - return AtomicAccess::fetch_then_add(&_card_claims[region], cards_per_chunk(), memory_order_relaxed); + return _card_claims[region].fetch_then_add(cards_per_chunk(), memory_order_relaxed); } uint G1CardTableClaimTable::claim_all_cards(uint region) { From 8c615190e69ee6e521990595fc23197f38ad6f14 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 20 Jan 2026 10:34:00 +0000 Subject: [PATCH 166/204] 8375624: G1: Convert G1JavaThreadsListClaimer to use Atomic Reviewed-by: kbarrett, shade --- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 5 +++-- src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp | 7 +++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index aff7166d391..a0104d04f4f 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.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 @@ -54,6 +54,7 @@ #include "memory/allocation.hpp" #include "memory/iterator.hpp" #include "memory/memRegion.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/threadSMR.hpp" #include "utilities/bitMap.hpp" @@ -124,7 +125,7 @@ class G1JavaThreadsListClaimer : public StackObj { ThreadsListHandle _list; uint _claim_step; - volatile uint _cur_claim; + Atomic _cur_claim; // Attempts to claim _claim_step JavaThreads, returning an array of claimed // JavaThread* with count elements. Returns null (and a zero count) if there diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index abd61e72d57..577450b3be9 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.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 @@ -41,7 +41,6 @@ #include "gc/shared/markBitMap.inline.hpp" #include "gc/shared/taskqueue.inline.hpp" #include "oops/stackChunkOop.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/threadSMR.inline.hpp" #include "utilities/bitMap.inline.hpp" @@ -53,10 +52,10 @@ inline bool G1STWIsAliveClosure::do_object_b(oop p) { inline JavaThread* const* G1JavaThreadsListClaimer::claim(uint& count) { count = 0; - if (AtomicAccess::load(&_cur_claim) >= _list.length()) { + if (_cur_claim.load_relaxed() >= _list.length()) { return nullptr; } - uint claim = AtomicAccess::fetch_then_add(&_cur_claim, _claim_step); + uint claim = _cur_claim.fetch_then_add(_claim_step); if (claim >= _list.length()) { return nullptr; } From fe102918dd4f33ba030c4c4301a676ac8497fd90 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 20 Jan 2026 10:34:16 +0000 Subject: [PATCH 167/204] 8375630: G1: Convert G1ConcurrentMark to use Atomic Reviewed-by: kbarrett, shade --- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 27 ++++++++++---------- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 9 ++++--- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 456d543fa10..1077939f953 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.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 @@ -67,7 +67,6 @@ #include "nmt/memTracker.hpp" #include "oops/access.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/globals_extension.hpp" #include "runtime/handles.inline.hpp" #include "runtime/java.hpp" @@ -148,25 +147,25 @@ bool G1CMMarkStack::initialize() { } G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::ChunkAllocator::allocate_new_chunk() { - if (_size >= _max_capacity) { + if (_size.load_relaxed() >= _max_capacity) { return nullptr; } - size_t cur_idx = AtomicAccess::fetch_then_add(&_size, 1u); + size_t cur_idx = _size.fetch_then_add(1u); if (cur_idx >= _max_capacity) { return nullptr; } size_t bucket = get_bucket(cur_idx); - if (AtomicAccess::load_acquire(&_buckets[bucket]) == nullptr) { + if (_buckets[bucket].load_acquire() == nullptr) { if (!_should_grow) { // Prefer to restart the CM. return nullptr; } MutexLocker x(G1MarkStackChunkList_lock, Mutex::_no_safepoint_check_flag); - if (AtomicAccess::load_acquire(&_buckets[bucket]) == nullptr) { + if (_buckets[bucket].load_acquire() == nullptr) { size_t desired_capacity = bucket_size(bucket) * 2; if (!try_expand_to(desired_capacity)) { return nullptr; @@ -175,7 +174,7 @@ G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::ChunkAllocator::allocate_new_ } size_t bucket_idx = get_bucket_index(cur_idx); - TaskQueueEntryChunk* result = ::new (&_buckets[bucket][bucket_idx]) TaskQueueEntryChunk; + TaskQueueEntryChunk* result = ::new (&_buckets[bucket].load_relaxed()[bucket_idx]) TaskQueueEntryChunk; result->next = nullptr; return result; } @@ -197,10 +196,10 @@ bool G1CMMarkStack::ChunkAllocator::initialize(size_t initial_capacity, size_t m _max_capacity = max_capacity; _num_buckets = get_bucket(_max_capacity) + 1; - _buckets = NEW_C_HEAP_ARRAY(TaskQueueEntryChunk*, _num_buckets, mtGC); + _buckets = NEW_C_HEAP_ARRAY(Atomic, _num_buckets, mtGC); for (size_t i = 0; i < _num_buckets; i++) { - _buckets[i] = nullptr; + _buckets[i].store_relaxed(nullptr); } size_t new_capacity = bucket_size(0); @@ -240,9 +239,9 @@ G1CMMarkStack::ChunkAllocator::~ChunkAllocator() { } for (size_t i = 0; i < _num_buckets; i++) { - if (_buckets[i] != nullptr) { - MmapArrayAllocator::free(_buckets[i], bucket_size(i)); - _buckets[i] = nullptr; + if (_buckets[i].load_relaxed() != nullptr) { + MmapArrayAllocator::free(_buckets[i].load_relaxed(), bucket_size(i)); + _buckets[i].store_relaxed(nullptr); } } @@ -259,7 +258,7 @@ bool G1CMMarkStack::ChunkAllocator::reserve(size_t new_capacity) { // and the new capacity (new_capacity). This step ensures that there are no gaps in the // array and that the capacity accurately reflects the reserved memory. for (; i <= highest_bucket; i++) { - if (AtomicAccess::load_acquire(&_buckets[i]) != nullptr) { + if (_buckets[i].load_acquire() != nullptr) { continue; // Skip over already allocated buckets. } @@ -279,7 +278,7 @@ bool G1CMMarkStack::ChunkAllocator::reserve(size_t new_capacity) { return false; } _capacity += bucket_capacity; - AtomicAccess::release_store(&_buckets[i], bucket_base); + _buckets[i].release_store(bucket_base); } return true; } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 752082ce629..1a0cfcd8caa 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.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 @@ -37,6 +37,7 @@ #include "gc/shared/workerThread.hpp" #include "gc/shared/workerUtils.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "utilities/compilerWarnings.hpp" #include "utilities/numberSeq.hpp" @@ -172,9 +173,9 @@ private: size_t _capacity; size_t _num_buckets; bool _should_grow; - TaskQueueEntryChunk* volatile* _buckets; + Atomic* _buckets; char _pad0[DEFAULT_PADDING_SIZE]; - volatile size_t _size; + Atomic _size; char _pad4[DEFAULT_PADDING_SIZE - sizeof(size_t)]; size_t bucket_size(size_t bucket) { @@ -212,7 +213,7 @@ private: bool initialize(size_t initial_capacity, size_t max_capacity); void reset() { - _size = 0; + _size.store_relaxed(0); _should_grow = false; } From 3cc713fa296dfb59bbc03f2cfd4fc7d8f4b44be2 Mon Sep 17 00:00:00 2001 From: Jonas Norlinder Date: Tue, 20 Jan 2026 11:40:19 +0000 Subject: [PATCH 168/204] 8374945: Avoid fstat in os::open Reviewed-by: dholmes, jsjolen, redestad --- src/hotspot/os/linux/os_linux.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 48529c6ce17..7190845a8ba 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -4963,9 +4963,14 @@ int os::open(const char *path, int oflag, int mode) { oflag |= O_CLOEXEC; int fd = ::open(path, oflag, mode); - if (fd == -1) return -1; + // No further checking is needed if open() returned an error or + // access mode is not read only. + if (fd == -1 || (oflag & O_ACCMODE) != O_RDONLY) { + return fd; + } - //If the open succeeded, the file might still be a directory + // If the open succeeded and is read only, the file might be a directory + // which the JVM doesn't allow to be read. { struct stat buf; int ret = ::fstat(fd, &buf); From 037040129e82958bd023e0b24d962627e8653710 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 20 Jan 2026 13:22:25 +0000 Subject: [PATCH 169/204] 8375643: G1: Convert G1RegionMarkStatsCache to use Atomic Reviewed-by: shade, kbarrett --- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 8 +++---- src/hotspot/share/gc/g1/g1FullCollector.hpp | 4 ++-- .../share/gc/g1/g1RegionMarkStatsCache.hpp | 24 ++++++++++++------- .../gc/g1/g1RegionMarkStatsCache.inline.hpp | 12 ++++------ 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 1a0cfcd8caa..7ea9151c6f1 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -557,14 +557,14 @@ public: // mark_in_bitmap call. Updates various statistics data. void add_to_liveness(uint worker_id, oop const obj, size_t size); // Did the last marking find a live object between bottom and TAMS? - bool contains_live_object(uint region) const { return _region_mark_stats[region]._live_words != 0; } + bool contains_live_object(uint region) const { return _region_mark_stats[region].live_words() != 0; } // Live bytes in the given region as determined by concurrent marking, i.e. the amount of // live bytes between bottom and TAMS. - size_t live_bytes(uint region) const { return _region_mark_stats[region]._live_words * HeapWordSize; } + size_t live_bytes(uint region) const { return _region_mark_stats[region].live_words() * HeapWordSize; } // Set live bytes for concurrent marking. - void set_live_bytes(uint region, size_t live_bytes) { _region_mark_stats[region]._live_words = live_bytes / HeapWordSize; } + void set_live_bytes(uint region, size_t live_bytes) { _region_mark_stats[region]._live_words.store_relaxed(live_bytes / HeapWordSize); } // Approximate number of incoming references found during marking. - size_t incoming_refs(uint region) const { return _region_mark_stats[region]._incoming_refs; } + size_t incoming_refs(uint region) const { return _region_mark_stats[region].incoming_refs(); } // Update the TAMS for the given region to the current top. inline void update_top_at_mark_start(G1HeapRegion* r); diff --git a/src/hotspot/share/gc/g1/g1FullCollector.hpp b/src/hotspot/share/gc/g1/g1FullCollector.hpp index ed8225fc004..1fb3af17032 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.hpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.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 @@ -122,7 +122,7 @@ public: ReferenceProcessor* reference_processor(); size_t live_words(uint region_index) const { assert(region_index < _heap->max_num_regions(), "sanity"); - return _live_stats[region_index]._live_words; + return _live_stats[region_index].live_words(); } void before_marking_update_attribute_table(G1HeapRegion* hr); diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp index 3c1a6ed4667..4dcdd33846e 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp @@ -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 @@ -27,6 +27,7 @@ #include "memory/allocation.hpp" #include "oops/oop.hpp" +#include "runtime/atomic.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/pair.hpp" @@ -40,20 +41,23 @@ // * the number of incoming references found during marking. This is an approximate // value because we do not mark through all objects. struct G1RegionMarkStats { - size_t _live_words; - size_t _incoming_refs; + Atomic _live_words; + Atomic _incoming_refs; // Clear all members. void clear() { - _live_words = 0; - _incoming_refs = 0; + _live_words.store_relaxed(0); + _incoming_refs.store_relaxed(0); } // Clear all members after a marking overflow. Only needs to clear the number of // incoming references as all objects will be rescanned, while the live words are // gathered whenever a thread can mark an object, which is synchronized. void clear_during_overflow() { - _incoming_refs = 0; + _incoming_refs.store_relaxed(0); } + + size_t live_words() const { return _live_words.load_relaxed(); } + size_t incoming_refs() const { return _incoming_refs.load_relaxed(); } }; // Per-marking thread cache for the region mark statistics. @@ -112,12 +116,16 @@ public: void add_live_words(oop obj); void add_live_words(uint region_idx, size_t live_words) { G1RegionMarkStatsCacheEntry* const cur = find_for_add(region_idx); - cur->_stats._live_words += live_words; + // This method is only ever called single-threaded, so we do not need atomic + // update here. + cur->_stats._live_words.store_relaxed(cur->_stats.live_words() + live_words); } void inc_incoming_refs(uint region_idx) { G1RegionMarkStatsCacheEntry* const cur = find_for_add(region_idx); - cur->_stats._incoming_refs++; + // This method is only ever called single-threaded, so we do not need atomic + // update here. + cur->_stats._incoming_refs.store_relaxed(cur->_stats.incoming_refs() + 1u); } void reset(uint region_idx) { diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.inline.hpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.inline.hpp index 6b0ebb34e0d..71cd33e71ae 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.inline.hpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.inline.hpp @@ -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 @@ -27,8 +27,6 @@ #include "gc/g1/g1RegionMarkStatsCache.hpp" -#include "runtime/atomicAccess.hpp" - inline G1RegionMarkStatsCache::G1RegionMarkStatsCacheEntry* G1RegionMarkStatsCache::find_for_add(uint region_idx) { uint const cache_idx = hash(region_idx); @@ -46,12 +44,12 @@ inline G1RegionMarkStatsCache::G1RegionMarkStatsCacheEntry* G1RegionMarkStatsCac inline void G1RegionMarkStatsCache::evict(uint idx) { G1RegionMarkStatsCacheEntry* cur = &_cache[idx]; - if (cur->_stats._live_words != 0) { - AtomicAccess::add(&_target[cur->_region_idx]._live_words, cur->_stats._live_words); + if (cur->_stats.live_words() != 0) { + _target[cur->_region_idx]._live_words.add_then_fetch(cur->_stats.live_words()); } - if (cur->_stats._incoming_refs != 0) { - AtomicAccess::add(&_target[cur->_region_idx]._incoming_refs, cur->_stats._incoming_refs); + if (cur->_stats.incoming_refs() != 0) { + _target[cur->_region_idx]._incoming_refs.add_then_fetch(cur->_stats.incoming_refs()); } cur->clear(); From 5ba91fed345b078a67ad6bead1d8893bd9289f58 Mon Sep 17 00:00:00 2001 From: Christian Heilmann Date: Tue, 20 Jan 2026 15:00:14 +0000 Subject: [PATCH 170/204] 8297191: [macos] Printing a page range with starting page > 1 results in missing pages Reviewed-by: aivanov, prr --- .../classes/sun/lwawt/macosx/CPrinterJob.java | 24 +++++-------------- .../native/libawt_lwawt/awt/CPrinterJob.m | 8 +++---- .../native/libawt_lwawt/awt/PrinterView.h | 6 ++--- .../native/libawt_lwawt/awt/PrinterView.m | 13 +++++----- .../java/awt/print/PrinterJob/PageRanges.java | 4 ++-- 5 files changed, 21 insertions(+), 34 deletions(-) diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java index 979eeb36239..508b1a843ef 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java @@ -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. * 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,20 +355,9 @@ public final class CPrinterJob extends RasterPrinterJob { validateDestination(destinationAttr); } - /* Get the range of pages we are to print. If the - * last page to print is unknown, then we print to - * the end of the document. Note that firstPage - * and lastPage are 0 based page indices. - */ - + // Note that firstPage is 0 based page index. int firstPage = getFirstPage(); - int lastPage = getLastPage(); - if(lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES) { - int totalPages = mDocument.getNumberOfPages(); - if (totalPages != Pageable.UNKNOWN_NUMBER_OF_PAGES) { - lastPage = mDocument.getNumberOfPages() - 1; - } - } + int totalPages = mDocument.getNumberOfPages(); try { synchronized (this) { @@ -393,7 +382,7 @@ public final class CPrinterJob extends RasterPrinterJob { try { // Fire off the print rendering loop on the AppKit thread, and don't have // it wait and block this thread. - if (printLoop(false, firstPage, lastPage)) { + if (printLoop(false, firstPage, totalPages)) { // Start a secondary loop on EDT until printing operation is finished or cancelled printingLoop.enter(); } @@ -407,7 +396,7 @@ public final class CPrinterJob extends RasterPrinterJob { onEventThread = false; try { - printLoop(true, firstPage, lastPage); + printLoop(true, firstPage, totalPages); } catch (Exception e) { e.printStackTrace(); } @@ -417,7 +406,6 @@ public final class CPrinterJob extends RasterPrinterJob { } if (++loopi < prMembers.length) { firstPage = prMembers[loopi][0]-1; - lastPage = prMembers[loopi][1] -1; } } while (loopi < prMembers.length); } finally { @@ -693,7 +681,7 @@ public final class CPrinterJob extends RasterPrinterJob { } } - private native boolean printLoop(boolean waitUntilDone, int firstPage, int lastPage) throws PrinterException; + private native boolean printLoop(boolean waitUntilDone, int firstPage, int totalPages) throws PrinterException; private PageFormat getPageFormat(int pageIndex) { // This is called from the native side. diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m index 9cc0a18564f..555a2746f43 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CPrinterJob.m @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -656,7 +656,7 @@ JNI_COCOA_EXIT(env); * Signature: ()V */ JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPrinterJob_printLoop - (JNIEnv *env, jobject jthis, jboolean blocks, jint firstPage, jint lastPage) + (JNIEnv *env, jobject jthis, jboolean blocks, jint firstPage, jint totalPages) { AWT_ASSERT_NOT_APPKIT_THREAD; @@ -672,14 +672,14 @@ JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPrinterJob_printLoop JNI_COCOA_ENTER(env); // Get the first page's PageFormat for setting things up (This introduces // and is a facet of the same problem in Radar 2818593/2708932). - jobject page = (*env)->CallObjectMethod(env, jthis, jm_getPageFormat, 0); // AWT_THREADING Safe (!appKit) + jobject page = (*env)->CallObjectMethod(env, jthis, jm_getPageFormat, firstPage); // AWT_THREADING Safe (!appKit) CHECK_EXCEPTION(); if (page != NULL) { jobject pageFormatArea = (*env)->CallObjectMethod(env, jthis, jm_getPageFormatArea, page); // AWT_THREADING Safe (!appKit) CHECK_EXCEPTION(); PrinterView* printerView = [[PrinterView alloc] initWithFrame:JavaToNSRect(env, pageFormatArea) withEnv:env withPrinterJob:jthis]; - [printerView setFirstPage:firstPage lastPage:lastPage]; + [printerView setTotalPages:totalPages]; GET_NSPRINTINFO_METHOD_RETURN(NO) NSPrintInfo* printInfo = (NSPrintInfo*)jlong_to_ptr((*env)->CallLongMethod(env, jthis, sjm_getNSPrintInfo)); // AWT_THREADING Safe (known object) diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.h index 43472bee920..95a8055cdb0 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 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 @@ -32,12 +32,12 @@ jobject fCurPainter; jobject fCurPeekGraphics; - jint fFirstPage, fLastPage; + jint fTotalPages; } - (id)initWithFrame:(NSRect)aRect withEnv:(JNIEnv*)env withPrinterJob:(jobject)printerJob; -- (void)setFirstPage:(jint)firstPage lastPage:(jint)lastPage; +- (void)setTotalPages:(jint)totalPages; - (void)releaseReferences:(JNIEnv*)env; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.m index d19948d9f0f..f219e8082b4 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/PrinterView.m @@ -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. * 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,9 +72,8 @@ static jclass sjc_PAbortEx = NULL; } } -- (void)setFirstPage:(jint)firstPage lastPage:(jint)lastPage { - fFirstPage = firstPage; - fLastPage = lastPage; +- (void)setTotalPages:(jint)totalPages { + fTotalPages = totalPages; } - (void)drawRect:(NSRect)aRect @@ -156,15 +155,15 @@ static jclass sjc_PAbortEx = NULL; return NO; } - aRange->location = fFirstPage + 1; + aRange->location = 1; - if (fLastPage == java_awt_print_Pageable_UNKNOWN_NUMBER_OF_PAGES) + if (fTotalPages == java_awt_print_Pageable_UNKNOWN_NUMBER_OF_PAGES) { aRange->length = NSIntegerMax; } else { - aRange->length = (fLastPage + 1) - fFirstPage; + aRange->length = fTotalPages; } return YES; diff --git a/test/jdk/java/awt/print/PrinterJob/PageRanges.java b/test/jdk/java/awt/print/PrinterJob/PageRanges.java index e80330bae6c..aea60516f78 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageRanges.java +++ b/test/jdk/java/awt/print/PrinterJob/PageRanges.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * 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 6575331 + * @bug 6575331 8297191 * @key printer * @summary The specified pages should be printed. * @library /java/awt/regtesthelpers From 21dc41f744edd138e77970d4e25e3a7eda41621f Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Tue, 20 Jan 2026 16:16:38 +0000 Subject: [PATCH 171/204] 8314323: Implement JEP 527: TLS 1.3 Hybrid Key Exchange Co-authored-by: Jamil Nimeh Co-authored-by: Weijun Wang Reviewed-by: wetmore, mullan --- .../classes/sun/security/ssl/DHasKEM.java | 254 ++++++++++ .../classes/sun/security/ssl/Hybrid.java | 474 ++++++++++++++++++ .../sun/security/ssl/HybridProvider.java | 130 +++++ .../sun/security/ssl/KAKeyDerivation.java | 127 ++++- .../sun/security/ssl/KEMKeyExchange.java | 223 ++++++++ .../sun/security/ssl/KeyShareExtension.java | 113 +++-- .../classes/sun/security/ssl/NamedGroup.java | 131 ++++- .../sun/security/ssl/SSLKeyExchange.java | 6 +- .../classes/sun/security/ssl/ServerHello.java | 51 +- .../classes/sun/security/x509/X509Key.java | 4 + .../net/ssl/SSLParameters/NamedGroups.java | 57 ++- .../javax/net/ssl/TLSCommon/NamedGroup.java | 8 +- .../net/ssl/TLSv13/ClientHelloKeyShares.java | 15 +- .../javax/net/ssl/TLSv13/HRRKeyShares.java | 25 +- .../security/pkcs11/tls/fips/FipsModeTLS.java | 8 +- .../ssl/CipherSuite/DisabledCurve.java | 49 +- .../NamedGroupsWithCipherSuite.java | 76 ++- .../ssl/CipherSuite/RestrictNamedGroup.java | 7 +- .../ssl/CipherSuite/SupportedGroups.java | 32 +- .../bench/java/security/SSLHandshake.java | 25 +- .../bench/javax/crypto/full/KEMBench.java | 110 +++- .../crypto/full/KeyPairGeneratorBench.java | 34 +- 22 files changed, 1839 insertions(+), 120 deletions(-) create mode 100644 src/java.base/share/classes/sun/security/ssl/DHasKEM.java create mode 100644 src/java.base/share/classes/sun/security/ssl/Hybrid.java create mode 100644 src/java.base/share/classes/sun/security/ssl/HybridProvider.java create mode 100644 src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java diff --git a/src/java.base/share/classes/sun/security/ssl/DHasKEM.java b/src/java.base/share/classes/sun/security/ssl/DHasKEM.java new file mode 100644 index 00000000000..763013f280c --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/DHasKEM.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import sun.security.util.ArrayUtil; +import sun.security.util.CurveDB; +import sun.security.util.ECUtil; +import sun.security.util.NamedCurve; + +import javax.crypto.DecapsulateException; +import javax.crypto.KEM; +import javax.crypto.KEMSpi; +import javax.crypto.KeyAgreement; +import javax.crypto.SecretKey; +import java.io.IOException; +import java.math.BigInteger; +import java.security.*; +import java.security.interfaces.ECKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.XECKey; +import java.security.interfaces.XECPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.NamedParameterSpec; +import java.security.spec.XECPublicKeySpec; +import java.util.Arrays; + +/** + * The DHasKEM class presents a KEM abstraction layer over traditional + * DH-based key exchange, which can be used for either straight + * ECDH/XDH or TLS hybrid key exchanges. + * + * This class can be alongside standard full post-quantum KEMs + * when hybrid implementations are required. + */ +public class DHasKEM implements KEMSpi { + + @Override + public EncapsulatorSpi engineNewEncapsulator( + PublicKey publicKey, AlgorithmParameterSpec spec, + SecureRandom secureRandom) throws InvalidKeyException { + return new Handler(publicKey, null, secureRandom); + } + + @Override + public DecapsulatorSpi engineNewDecapsulator(PrivateKey privateKey, + AlgorithmParameterSpec spec) throws InvalidKeyException { + return new Handler(null, privateKey, null); + } + + private static final class Handler + implements KEMSpi.EncapsulatorSpi, KEMSpi.DecapsulatorSpi { + private final PublicKey pkR; + private final PrivateKey skR; + private final SecureRandom sr; + private final Params params; + + Handler(PublicKey pk, PrivateKey sk, SecureRandom sr) + throws InvalidKeyException { + this.pkR = pk; + this.skR = sk; + this.sr = sr; + this.params = paramsFromKey(pk == null ? sk : pk); + } + + @Override + public KEM.Encapsulated engineEncapsulate(int from, int to, + String algorithm) { + KeyPair kpE = params.generateKeyPair(sr); + PrivateKey skE = kpE.getPrivate(); + PublicKey pkE = kpE.getPublic(); + byte[] pkEm = params.SerializePublicKey(pkE); + try { + SecretKey dh = params.DH(algorithm, skE, pkR); + return new KEM.Encapsulated( + sub(dh, from, to), + pkEm, null); + } catch (Exception e) { + throw new ProviderException("internal error", e); + } + } + + @Override + public int engineSecretSize() { + return params.secretLen; + } + + @Override + public int engineEncapsulationSize() { + return params.publicKeyLen; + } + + @Override + public SecretKey engineDecapsulate(byte[] encapsulation, int from, + int to, String algorithm) throws DecapsulateException { + if (encapsulation.length != params.publicKeyLen) { + throw new DecapsulateException("incorrect encapsulation size"); + } + try { + PublicKey pkE = params.DeserializePublicKey(encapsulation); + SecretKey dh = params.DH(algorithm, skR, pkE); + return sub(dh, from, to); + } catch (IOException | InvalidKeyException e) { + throw new DecapsulateException("Cannot decapsulate", e); + } catch (Exception e) { + throw new ProviderException("internal error", e); + } + } + + private SecretKey sub(SecretKey key, int from, int to) { + if (from == 0 && to == params.secretLen) { + return key; + } + + // Key slicing should never happen. Otherwise, there might be + // a programming error. + throw new AssertionError( + "Unexpected key slicing: from=" + from + ", to=" + to); + } + + // This KEM is designed to be able to represent every ECDH and XDH + private Params paramsFromKey(Key k) throws InvalidKeyException { + if (k instanceof ECKey eckey) { + if (ECUtil.equals(eckey.getParams(), CurveDB.P_256)) { + return Params.P256; + } else if (ECUtil.equals(eckey.getParams(), CurveDB.P_384)) { + return Params.P384; + } else if (ECUtil.equals(eckey.getParams(), CurveDB.P_521)) { + return Params.P521; + } + } else if (k instanceof XECKey xkey + && xkey.getParams() instanceof NamedParameterSpec ns) { + if (ns.getName().equalsIgnoreCase( + NamedParameterSpec.X25519.getName())) { + return Params.X25519; + } else if (ns.getName().equalsIgnoreCase( + NamedParameterSpec.X448.getName())) { + return Params.X448; + } + } + throw new InvalidKeyException("Unsupported key"); + } + } + + private enum Params { + + P256(32, 2 * 32 + 1, + "ECDH", "EC", CurveDB.P_256), + + P384(48, 2 * 48 + 1, + "ECDH", "EC", CurveDB.P_384), + + P521(66, 2 * 66 + 1, + "ECDH", "EC", CurveDB.P_521), + + X25519(32, 32, + "XDH", "XDH", NamedParameterSpec.X25519), + + X448(56, 56, + "XDH", "XDH", NamedParameterSpec.X448); + + private final int secretLen; + private final int publicKeyLen; + private final String kaAlgorithm; + private final String keyAlgorithm; + private final AlgorithmParameterSpec spec; + + Params(int secretLen, int publicKeyLen, String kaAlgorithm, + String keyAlgorithm, AlgorithmParameterSpec spec) { + this.spec = spec; + this.secretLen = secretLen; + this.publicKeyLen = publicKeyLen; + this.kaAlgorithm = kaAlgorithm; + this.keyAlgorithm = keyAlgorithm; + } + + private boolean isEC() { + return this == P256 || this == P384 || this == P521; + } + + private KeyPair generateKeyPair(SecureRandom sr) { + try { + KeyPairGenerator g = KeyPairGenerator.getInstance(keyAlgorithm); + g.initialize(spec, sr); + return g.generateKeyPair(); + } catch (Exception e) { + throw new ProviderException("internal error", e); + } + } + + private byte[] SerializePublicKey(PublicKey k) { + if (isEC()) { + ECPoint w = ((ECPublicKey) k).getW(); + return ECUtil.encodePoint(w, ((NamedCurve) spec).getCurve()); + } else { + byte[] uArray = ((XECPublicKey) k).getU().toByteArray(); + ArrayUtil.reverse(uArray); + return Arrays.copyOf(uArray, publicKeyLen); + } + } + + private PublicKey DeserializePublicKey(byte[] data) throws + IOException, NoSuchAlgorithmException, + InvalidKeySpecException { + KeySpec keySpec; + if (isEC()) { + NamedCurve curve = (NamedCurve) this.spec; + keySpec = new ECPublicKeySpec( + ECUtil.decodePoint(data, curve.getCurve()), curve); + } else { + data = data.clone(); + ArrayUtil.reverse(data); + keySpec = new XECPublicKeySpec( + this.spec, new BigInteger(1, data)); + } + return KeyFactory.getInstance(keyAlgorithm). + generatePublic(keySpec); + } + + private SecretKey DH(String alg, PrivateKey skE, PublicKey pkR) + throws NoSuchAlgorithmException, InvalidKeyException { + KeyAgreement ka = KeyAgreement.getInstance(kaAlgorithm); + ka.init(skE); + ka.doPhase(pkR, true); + return ka.generateSecret(alg); + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/Hybrid.java b/src/java.base/share/classes/sun/security/ssl/Hybrid.java new file mode 100644 index 00000000000..e3e2cfa0b23 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/Hybrid.java @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import sun.security.util.ArrayUtil; +import sun.security.util.CurveDB; +import sun.security.util.ECUtil; +import sun.security.util.RawKeySpec; +import sun.security.x509.X509Key; + +import javax.crypto.DecapsulateException; +import javax.crypto.KEM; +import javax.crypto.KEMSpi; +import javax.crypto.SecretKey; +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyFactorySpi; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyPairGeneratorSpi; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.*; +import java.util.Arrays; +import java.util.Locale; + +// The Hybrid class wraps two underlying algorithms (left and right sides) +// in a single TLS hybrid named group. +// It implements: +// - Hybrid KeyPair generation +// - Hybrid KeyFactory for decoding concatenated hybrid public keys +// - Hybrid KEM implementation for performing encapsulation and +// decapsulation over two underlying algorithms (traditional +// algorithm and post-quantum KEM algorithm) + +public class Hybrid { + + public static final NamedParameterSpec X25519_MLKEM768 = + new NamedParameterSpec("X25519MLKEM768"); + + public static final NamedParameterSpec SECP256R1_MLKEM768 = + new NamedParameterSpec("SecP256r1MLKEM768"); + + public static final NamedParameterSpec SECP384R1_MLKEM1024 = + new NamedParameterSpec("SecP384r1MLKEM1024"); + + private static AlgorithmParameterSpec getSpec(String name) { + if (name.startsWith("secp")) { + return new ECGenParameterSpec(name); + } else { + return new NamedParameterSpec(name); + } + } + + private static KeyPairGenerator getKeyPairGenerator(String name) throws + NoSuchAlgorithmException { + if (name.startsWith("secp")) { + name = "EC"; + } + return KeyPairGenerator.getInstance(name); + } + + private static KeyFactory getKeyFactory(String name) throws + NoSuchAlgorithmException { + if (name.startsWith("secp")) { + name = "EC"; + } + return KeyFactory.getInstance(name); + } + + /** + * Returns a KEM instance for each side of the hybrid algorithm. + * For traditional key exchange algorithms, we use the DH-based KEM + * implementation provided by DHasKEM class. + * For ML-KEM post-quantum algorithms, we obtain a KEM instance + * with "ML-KEM". This is done to work with 3rd-party providers that + * only have "ML-KEM" KEM algorithm. + */ + private static KEM getKEM(String name) throws NoSuchAlgorithmException { + if (name.startsWith("secp") || name.equals("X25519")) { + return KEM.getInstance("DH", HybridProvider.PROVIDER); + } else { + return KEM.getInstance("ML-KEM"); + } + } + + public static class KeyPairGeneratorImpl extends KeyPairGeneratorSpi { + private final KeyPairGenerator left; + private final KeyPairGenerator right; + private final AlgorithmParameterSpec leftSpec; + private final AlgorithmParameterSpec rightSpec; + + public KeyPairGeneratorImpl(String leftAlg, String rightAlg) + throws NoSuchAlgorithmException { + left = getKeyPairGenerator(leftAlg); + right = getKeyPairGenerator(rightAlg); + leftSpec = getSpec(leftAlg); + rightSpec = getSpec(rightAlg); + } + + @Override + public void initialize(AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException { + left.initialize(leftSpec, random); + right.initialize(rightSpec, random); + } + + @Override + public void initialize(int keysize, SecureRandom random) { + // NO-OP (do nothing) + } + + @Override + public KeyPair generateKeyPair() { + var kp1 = left.generateKeyPair(); + var kp2 = right.generateKeyPair(); + return new KeyPair( + new PublicKeyImpl("Hybrid", kp1.getPublic(), + kp2.getPublic()), + new PrivateKeyImpl("Hybrid", kp1.getPrivate(), + kp2.getPrivate())); + } + } + + public static class KeyFactoryImpl extends KeyFactorySpi { + private final KeyFactory left; + private final KeyFactory right; + private final int leftlen; + private final String leftname; + private final String rightname; + + public KeyFactoryImpl(String left, String right) + throws NoSuchAlgorithmException { + this.left = getKeyFactory(left); + this.right = getKeyFactory(right); + this.leftlen = leftPublicLength(left); + this.leftname = left; + this.rightname = right; + } + + @Override + protected PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException { + if (keySpec == null) { + throw new InvalidKeySpecException("keySpec must not be null"); + } + + if (keySpec instanceof RawKeySpec rks) { + byte[] key = rks.getKeyArr(); + if (key == null) { + throw new InvalidKeySpecException( + "RawkeySpec contains null key data"); + } + if (key.length <= leftlen) { + throw new InvalidKeySpecException( + "Hybrid key length " + key.length + + " is too short and its left key length is " + + leftlen); + } + + byte[] leftKeyBytes = Arrays.copyOfRange(key, 0, leftlen); + byte[] rightKeyBytes = Arrays.copyOfRange(key, leftlen, + key.length); + PublicKey leftKey, rightKey; + + try { + if (leftname.startsWith("secp")) { + var curve = CurveDB.lookup(leftname); + var ecSpec = new ECPublicKeySpec( + ECUtil.decodePoint(leftKeyBytes, + curve.getCurve()), curve); + leftKey = left.generatePublic(ecSpec); + } else if (leftname.startsWith("ML-KEM")) { + leftKey = left.generatePublic(new RawKeySpec( + leftKeyBytes)); + } else { + throw new InvalidKeySpecException("Unsupported left" + + " algorithm" + leftname); + } + + if (rightname.equals("X25519")) { + ArrayUtil.reverse(rightKeyBytes); + var xecSpec = new XECPublicKeySpec( + new NamedParameterSpec(rightname), + new BigInteger(1, rightKeyBytes)); + rightKey = right.generatePublic(xecSpec); + } else if (rightname.startsWith("ML-KEM")) { + rightKey = right.generatePublic(new RawKeySpec( + rightKeyBytes)); + } else { + throw new InvalidKeySpecException("Unsupported right" + + " algorithm: " + rightname); + } + + return new PublicKeyImpl("Hybrid", leftKey, rightKey); + } catch (Exception e) { + throw new InvalidKeySpecException("Failed to decode " + + "hybrid key", e); + } + } + + throw new InvalidKeySpecException( + "KeySpec type:" + + keySpec.getClass().getName() + " not supported"); + } + + private static int leftPublicLength(String name) { + return switch (name.toLowerCase(Locale.ROOT)) { + case "secp256r1" -> 65; + case "secp384r1" -> 97; + case "ml-kem-768" -> 1184; + default -> throw new IllegalArgumentException( + "Unknown named group: " + name); + }; + } + + @Override + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws + InvalidKeySpecException { + throw new UnsupportedOperationException(); + } + + @Override + protected T engineGetKeySpec(Key key, + Class keySpec) throws InvalidKeySpecException { + throw new UnsupportedOperationException(); + } + + @Override + protected Key engineTranslateKey(Key key) throws InvalidKeyException { + throw new UnsupportedOperationException(); + } + } + + public static class KEMImpl implements KEMSpi { + private final KEM left; + private final KEM right; + + public KEMImpl(String left, String right) + throws NoSuchAlgorithmException { + this.left = getKEM(left); + this.right = getKEM(right); + } + + @Override + public EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey, + AlgorithmParameterSpec spec, SecureRandom secureRandom) throws + InvalidAlgorithmParameterException, InvalidKeyException { + if (publicKey instanceof PublicKeyImpl pk) { + return new Handler(left.newEncapsulator(pk.left, secureRandom), + right.newEncapsulator(pk.right, secureRandom), + null, null); + } + throw new InvalidKeyException(); + } + + @Override + public DecapsulatorSpi engineNewDecapsulator(PrivateKey privateKey, + AlgorithmParameterSpec spec) + throws InvalidAlgorithmParameterException, InvalidKeyException { + if (privateKey instanceof PrivateKeyImpl pk) { + return new Handler(null, null, left.newDecapsulator(pk.left), + right.newDecapsulator(pk.right)); + } + throw new InvalidKeyException(); + } + } + + private static byte[] concat(byte[]... inputs) { + int outLen = 0; + for (byte[] in : inputs) { + outLen += in.length; + } + byte[] out = new byte[outLen]; + int pos = 0; + for (byte[] in : inputs) { + System.arraycopy(in, 0, out, pos, in.length); + pos += in.length; + } + return out; + } + + private record Handler(KEM.Encapsulator le, KEM.Encapsulator re, + KEM.Decapsulator ld, KEM.Decapsulator rd) + implements KEMSpi.EncapsulatorSpi, KEMSpi.DecapsulatorSpi { + @Override + public KEM.Encapsulated engineEncapsulate(int from, int to, + String algorithm) { + int expectedSecretSize = engineSecretSize(); + if (!(from == 0 && to == expectedSecretSize)) { + throw new IllegalArgumentException( + "Invalid range for encapsulation: from = " + from + + " to = " + to + ", expected total secret size = " + + expectedSecretSize); + } + + var left = le.encapsulate(); + var right = re.encapsulate(); + return new KEM.Encapsulated( + new SecretKeyImpl(left.key(), right.key()), + concat(left.encapsulation(), right.encapsulation()), + null); + } + + @Override + public int engineSecretSize() { + if (le != null) { + return le.secretSize() + re.secretSize(); + } else { + return ld.secretSize() + rd.secretSize(); + } + } + + @Override + public int engineEncapsulationSize() { + if (le != null) { + return le.encapsulationSize() + re.encapsulationSize(); + } else { + return ld.encapsulationSize() + rd.encapsulationSize(); + } + } + + @Override + public SecretKey engineDecapsulate(byte[] encapsulation, int from, + int to, String algorithm) throws DecapsulateException { + int expectedEncSize = engineEncapsulationSize(); + if (encapsulation.length != expectedEncSize) { + throw new IllegalArgumentException( + "Invalid key encapsulation message length: " + + encapsulation.length + + ", expected = " + expectedEncSize); + } + + int expectedSecretSize = engineSecretSize(); + if (!(from == 0 && to == expectedSecretSize)) { + throw new IllegalArgumentException( + "Invalid range for decapsulation: from = " + from + + " to = " + to + ", expected total secret size = " + + expectedSecretSize); + } + + var left = Arrays.copyOf(encapsulation, ld.encapsulationSize()); + var right = Arrays.copyOfRange(encapsulation, + ld.encapsulationSize(), encapsulation.length); + return new SecretKeyImpl( + ld.decapsulate(left), + rd.decapsulate(right) + ); + } + } + + // Package-private + record SecretKeyImpl(SecretKey k1, SecretKey k2) + implements SecretKey { + @Override + public String getAlgorithm() { + return "Generic"; + } + + @Override + public String getFormat() { + return null; + } + + @Override + public byte[] getEncoded() { + return null; + } + } + + /** + * Hybrid public key combines two underlying public keys (left and right). + * Public keys can be transmitted/encoded because the hybrid protocol + * requires the public component to be sent. + */ + // Package-private + record PublicKeyImpl(String algorithm, PublicKey left, + PublicKey right) implements PublicKey { + @Override + public String getAlgorithm() { + return algorithm; + } + + // getFormat() returns "RAW" as hybrid key uses RAW concatenation + // of underlying encodings. + @Override + public String getFormat() { + return "RAW"; + } + + // getEncoded() returns the concatenation of the encoded bytes of the + // left and right public keys. + @Override + public byte[] getEncoded() { + return concat(onlyKey(left), onlyKey(right)); + } + + static byte[] onlyKey(PublicKey key) { + if (key instanceof X509Key xk) { + return xk.getKeyAsBytes(); + } + + // Fallback for 3rd-party providers + if (!"X.509".equalsIgnoreCase(key.getFormat())) { + throw new ProviderException("Invalid public key encoding " + + "format"); + } + var xk = new X509Key(); + try { + xk.decode(key.getEncoded()); + } catch (InvalidKeyException e) { + throw new ProviderException("Invalid public key encoding", e); + } + return xk.getKeyAsBytes(); + } + } + + /** + * Hybrid private key combines two underlying private keys (left and right). + * It is for internal use only. The private keys should never be exported. + */ + private record PrivateKeyImpl(String algorithm, PrivateKey left, + PrivateKey right) implements PrivateKey { + + @Override + public String getAlgorithm() { + return algorithm; + } + + // getFormat() returns null because there is no standard + // format for a hybrid private key. + @Override + public String getFormat() { + return null; + } + + // getEncoded() returns an empty byte array because there is no + // standard encoding format for a hybrid private key. + @Override + public byte[] getEncoded() { + return null; + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/HybridProvider.java b/src/java.base/share/classes/sun/security/ssl/HybridProvider.java new file mode 100644 index 00000000000..c77d6f66273 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/HybridProvider.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.security.Provider; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Map; + +import static sun.security.util.SecurityConstants.PROVIDER_VER; + +// This is an internal provider used in the JSSE code for DH-as-KEM +// and Hybrid KEM support. It doesn't actually get installed in the +// system's list of security providers that is searched at runtime. +// JSSE loads this provider internally. +// It registers Hybrid KeyPairGenerator, KeyFactory, and KEM +// implementations for hybrid named groups as Provider services. + +public class HybridProvider { + + public static final Provider PROVIDER = new ProviderImpl(); + + private static final class ProviderImpl extends Provider { + @java.io.Serial + private static final long serialVersionUID = 0L; + + ProviderImpl() { + super("HybridAndDHAsKEM", PROVIDER_VER, + "Hybrid and DHAsKEM provider"); + put("KEM.DH", DHasKEM.class.getName()); + + // Hybrid KeyPairGenerator/KeyFactory/KEM + + // The order of shares in the concatenation for group name + // X25519MLKEM768 has been reversed as per the current + // draft RFC. + var attrs = Map.of("name", "X25519MLKEM768", "left", "ML-KEM-768", + "right", "X25519"); + putService(new HybridService(this, "KeyPairGenerator", + "X25519MLKEM768", + "sun.security.ssl.Hybrid$KeyPairGeneratorImpl", + null, attrs)); + putService(new HybridService(this, "KEM", + "X25519MLKEM768", + "sun.security.ssl.Hybrid$KEMImpl", + null, attrs)); + putService(new HybridService(this, "KeyFactory", + "X25519MLKEM768", + "sun.security.ssl.Hybrid$KeyFactoryImpl", + null, attrs)); + + attrs = Map.of("name", "SecP256r1MLKEM768", "left", "secp256r1", + "right", "ML-KEM-768"); + putService(new HybridService(this, "KeyPairGenerator", + "SecP256r1MLKEM768", + "sun.security.ssl.Hybrid$KeyPairGeneratorImpl", + null, attrs)); + putService(new HybridService(this, "KEM", + "SecP256r1MLKEM768", + "sun.security.ssl.Hybrid$KEMImpl", + null, attrs)); + putService(new HybridService(this, "KeyFactory", + "SecP256r1MLKEM768", + "sun.security.ssl.Hybrid$KeyFactoryImpl", + null, attrs)); + + attrs = Map.of("name", "SecP384r1MLKEM1024", "left", "secp384r1", + "right", "ML-KEM-1024"); + putService(new HybridService(this, "KeyPairGenerator", + "SecP384r1MLKEM1024", + "sun.security.ssl.Hybrid$KeyPairGeneratorImpl", + null, attrs)); + putService(new HybridService(this, "KEM", + "SecP384r1MLKEM1024", + "sun.security.ssl.Hybrid$KEMImpl", + null, attrs)); + putService(new HybridService(this, "KeyFactory", + "SecP384r1MLKEM1024", + "sun.security.ssl.Hybrid$KeyFactoryImpl", + null, attrs)); + } + } + + private static class HybridService extends Provider.Service { + + HybridService(Provider p, String type, String algo, String cn, + List aliases, Map attrs) { + super(p, type, algo, cn, aliases, attrs); + } + + @Override + public Object newInstance(Object ctrParamObj) + throws NoSuchAlgorithmException { + String type = getType(); + return switch (type) { + case "KeyPairGenerator" -> new Hybrid.KeyPairGeneratorImpl( + getAttribute("left"), getAttribute("right")); + case "KeyFactory" -> new Hybrid.KeyFactoryImpl( + getAttribute("left"), getAttribute("right")); + case "KEM" -> new Hybrid.KEMImpl( + getAttribute("left"), getAttribute("right")); + default -> throw new NoSuchAlgorithmException( + "Unexpected value: " + type); + }; + } + } +} 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 623f83f547a..39e82b50435 100644 --- a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java @@ -24,7 +24,10 @@ */ package sun.security.ssl; +import sun.security.util.RawKeySpec; + import javax.crypto.KDF; +import javax.crypto.KEM; import javax.crypto.KeyAgreement; import javax.crypto.SecretKey; import javax.crypto.spec.HKDFParameterSpec; @@ -32,9 +35,11 @@ import javax.net.ssl.SSLHandshakeException; import java.io.IOException; import java.security.GeneralSecurityException; +import java.security.KeyFactory; import java.security.PrivateKey; +import java.security.Provider; import java.security.PublicKey; -import java.security.spec.AlgorithmParameterSpec; +import java.security.SecureRandom; import sun.security.util.KeyUtil; /** @@ -46,15 +51,32 @@ public class KAKeyDerivation implements SSLKeyDerivation { private final HandshakeContext context; private final PrivateKey localPrivateKey; private final PublicKey peerPublicKey; + private final byte[] keyshare; + private final Provider provider; + // Constructor called by Key Agreement KAKeyDerivation(String algorithmName, HandshakeContext context, PrivateKey localPrivateKey, PublicKey peerPublicKey) { + this(algorithmName, null, context, localPrivateKey, + peerPublicKey, null); + } + + // When the constructor called by KEM: store the client's public key or the + // encapsulated message in keyshare. + KAKeyDerivation(String algorithmName, + NamedGroup namedGroup, + HandshakeContext context, + PrivateKey localPrivateKey, + PublicKey peerPublicKey, + byte[] keyshare) { this.algorithmName = algorithmName; this.context = context; this.localPrivateKey = localPrivateKey; this.peerPublicKey = peerPublicKey; + this.keyshare = keyshare; + this.provider = (namedGroup != null) ? namedGroup.getProvider() : null; } @Override @@ -94,22 +116,15 @@ public class KAKeyDerivation implements SSLKeyDerivation { } } - /** - * Handle the TLSv1.3 objects, which use the HKDF algorithms. - */ - private SecretKey t13DeriveKey(String type) - throws IOException { - SecretKey sharedSecret = null; + private SecretKey deriveHandshakeSecret(String label, + SecretKey sharedSecret) + throws GeneralSecurityException, IOException { SecretKey earlySecret = null; SecretKey saltSecret = null; - try { - KeyAgreement ka = KeyAgreement.getInstance(algorithmName); - ka.init(localPrivateKey); - ka.doPhase(peerPublicKey, true); - sharedSecret = ka.generateSecret("TlsPremasterSecret"); - CipherSuite.HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg; - SSLKeyDerivation kd = context.handshakeKeyDerivation; + CipherSuite.HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg; + SSLKeyDerivation kd = context.handshakeKeyDerivation; + try { if (kd == null) { // No PSK is in use. // If PSK is not in use, Early Secret will still be // HKDF-Extract(0, 0). @@ -129,12 +144,90 @@ public class KAKeyDerivation implements SSLKeyDerivation { // the handshake secret key derivation (below) as it may not // work with the "sharedSecret" obj. KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm); - return hkdf.deriveKey(type, HKDFParameterSpec.ofExtract() - .addSalt(saltSecret).addIKM(sharedSecret).extractOnly()); + var spec = HKDFParameterSpec.ofExtract().addSalt(saltSecret); + if (sharedSecret instanceof Hybrid.SecretKeyImpl hsk) { + spec = spec.addIKM(hsk.k1()).addIKM(hsk.k2()); + } else { + spec = spec.addIKM(sharedSecret); + } + + return hkdf.deriveKey(label, spec.extractOnly()); + } finally { + KeyUtil.destroySecretKeys(earlySecret, saltSecret); + } + } + /** + * This method is called by the server to perform KEM encapsulation. + * It uses the client's public key (sent by the client as a keyshare) + * to encapsulate a shared secret and returns the encapsulated message. + * + * Package-private, used from KeyShareExtension.SHKeyShareProducer:: + * produce(). + */ + KEM.Encapsulated encapsulate(String algorithm, SecureRandom random) + throws IOException { + SecretKey sharedSecret = null; + + if (keyshare == null) { + throw new IOException("No keyshare available for KEM " + + "encapsulation"); + } + + try { + KeyFactory kf = (provider != null) ? + KeyFactory.getInstance(algorithmName, provider) : + KeyFactory.getInstance(algorithmName); + var pk = kf.generatePublic(new RawKeySpec(keyshare)); + + KEM kem = (provider != null) ? + KEM.getInstance(algorithmName, provider) : + KEM.getInstance(algorithmName); + KEM.Encapsulator e = kem.newEncapsulator(pk, random); + KEM.Encapsulated enc = e.encapsulate(); + sharedSecret = enc.key(); + + SecretKey derived = deriveHandshakeSecret(algorithm, sharedSecret); + + return new KEM.Encapsulated(derived, enc.encapsulation(), null); } catch (GeneralSecurityException gse) { throw new SSLHandshakeException("Could not generate secret", gse); } finally { - KeyUtil.destroySecretKeys(sharedSecret, earlySecret, saltSecret); + KeyUtil.destroySecretKeys(sharedSecret); + } + } + + /** + * Handle the TLSv1.3 objects, which use the HKDF algorithms. + */ + private SecretKey t13DeriveKey(String type) + throws IOException { + SecretKey sharedSecret = null; + + try { + if (keyshare != null) { + // 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"); + } else { + // Using traditional DH-style Key Agreement + KeyAgreement ka = KeyAgreement.getInstance(algorithmName); + ka.init(localPrivateKey); + ka.doPhase(peerPublicKey, true); + sharedSecret = ka.generateSecret("TlsPremasterSecret"); + } + + return deriveHandshakeSecret(type, sharedSecret); + } catch (GeneralSecurityException gse) { + throw new SSLHandshakeException("Could not generate secret", gse); + } finally { + KeyUtil.destroySecretKeys(sharedSecret); } } } diff --git a/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java new file mode 100644 index 00000000000..fb8de6cb104 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/KEMKeyExchange.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.ssl; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.NamedParameterSpec; +import javax.crypto.SecretKey; + +import sun.security.ssl.NamedGroup.NamedGroupSpec; +import sun.security.x509.X509Key; + +/** + * Specifics for single or hybrid Key exchanges based on KEM + */ +final class KEMKeyExchange { + + static final SSLKeyAgreementGenerator kemKAGenerator + = new KEMKAGenerator(); + + static final class KEMCredentials implements NamedGroupCredentials { + + final NamedGroup namedGroup; + // Unlike other credentials, we directly store the key share + // value here, no need to convert to a key + private final byte[] keyshare; + + KEMCredentials(byte[] keyshare, NamedGroup namedGroup) { + this.keyshare = keyshare; + this.namedGroup = namedGroup; + } + + // For KEM, server performs encapsulation and the resulting + // encapsulated message becomes the key_share value sent to + // the client. It is not a public key, so no PublicKey object + // to return. + @Override + public PublicKey getPublicKey() { + throw new UnsupportedOperationException( + "KEMCredentials stores raw keyshare, not a PublicKey"); + } + + public byte[] getKeyShare() { + return keyshare; + } + + @Override + public NamedGroup getNamedGroup() { + return namedGroup; + } + + /** + * Instantiates a KEMCredentials object + */ + static KEMCredentials valueOf(NamedGroup namedGroup, + byte[] encodedPoint) { + + if (namedGroup.spec != NamedGroupSpec.NAMED_GROUP_KEM) { + throw new RuntimeException( + "Credentials decoding: Not KEM named group"); + } + + if (encodedPoint == null || encodedPoint.length == 0) { + return null; + } + + return new KEMCredentials(encodedPoint, namedGroup); + } + } + + private static class KEMPossession implements SSLPossession { + private final NamedGroup namedGroup; + + public KEMPossession(NamedGroup ng) { + this.namedGroup = ng; + } + public NamedGroup getNamedGroup() { + return namedGroup; + } + } + + static final class KEMReceiverPossession extends KEMPossession { + + private final PrivateKey privateKey; + private final PublicKey publicKey; + + KEMReceiverPossession(NamedGroup namedGroup, SecureRandom random) { + super(namedGroup); + String algName = null; + try { + // For KEM: This receiver side (client) generates a key pair. + algName = ((NamedParameterSpec)namedGroup.keAlgParamSpec). + getName(); + Provider provider = namedGroup.getProvider(); + KeyPairGenerator kpg = (provider != null) ? + KeyPairGenerator.getInstance(algName, provider) : + KeyPairGenerator.getInstance(algName); + + kpg.initialize(namedGroup.keAlgParamSpec, random); + KeyPair kp = kpg.generateKeyPair(); + privateKey = kp.getPrivate(); + publicKey = kp.getPublic(); + } catch (GeneralSecurityException e) { + throw new RuntimeException( + "Could not generate keypair for algorithm: " + + algName, e); + } + } + + @Override + public byte[] encode() { + if (publicKey instanceof X509Key xk) { + return xk.getKeyAsBytes(); + } else if (publicKey instanceof Hybrid.PublicKeyImpl hk) { + return hk.getEncoded(); + } + throw new ProviderException("Unsupported key type: " + publicKey); + } + + // Package-private + PublicKey getPublicKey() { + return publicKey; + } + + // Package-private + PrivateKey getPrivateKey() { + return privateKey; + } + } + + static final class KEMSenderPossession extends KEMPossession { + + private SecretKey key; + private final SecureRandom random; + + KEMSenderPossession(NamedGroup namedGroup, SecureRandom random) { + super(namedGroup); + this.random = random; + } + + // Package-private + SecureRandom getRandom() { + return random; + } + + // Package-private + SecretKey getKey() { + return key; + } + + // Package-private + void setKey(SecretKey key) { + this.key = key; + } + + @Override + public byte[] encode() { + throw new UnsupportedOperationException("encode() not supported"); + } + } + + private static final class KEMKAGenerator + implements SSLKeyAgreementGenerator { + + // Prevent instantiation of this class. + private KEMKAGenerator() { + // blank + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context) throws IOException { + for (SSLPossession poss : context.handshakePossessions) { + if (poss instanceof KEMReceiverPossession kposs) { + NamedGroup ng = kposs.getNamedGroup(); + for (SSLCredentials cred : context.handshakeCredentials) { + if (cred instanceof KEMCredentials kcred && + ng.equals(kcred.namedGroup)) { + String name = ((NamedParameterSpec) + ng.keAlgParamSpec).getName(); + return new KAKeyDerivation(name, ng, context, + kposs.getPrivateKey(), null, + kcred.getKeyShare()); + } + } + } + } + context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No suitable KEM key agreement " + + "parameters negotiated"); + return null; + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java index 8d785f7515a..0d2cbb8f529 100644 --- a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java @@ -27,8 +27,11 @@ package sun.security.ssl; import java.io.IOException; import java.nio.ByteBuffer; +import java.security.AlgorithmConstraints; import java.security.CryptoPrimitive; import java.security.GeneralSecurityException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.NamedParameterSpec; import java.text.MessageFormat; import java.util.*; import javax.net.ssl.SSLProtocolException; @@ -297,7 +300,9 @@ final class KeyShareExtension { // update the context chc.handshakePossessions.add(pos); // May need more possession types in the future. - if (pos instanceof NamedGroupPossession) { + if (pos instanceof NamedGroupPossession || + pos instanceof + KEMKeyExchange.KEMReceiverPossession) { return pos.encode(); } } @@ -358,24 +363,16 @@ final class KeyShareExtension { try { SSLCredentials kaCred = ng.decodeCredentials(entry.keyExchange); - if (shc.algorithmConstraints != null && - kaCred instanceof - NamedGroupCredentials namedGroupCredentials) { - if (!shc.algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - namedGroupCredentials.getPublicKey())) { - if (SSLLogger.isOn() && - SSLLogger.isOn("ssl,handshake")) { - SSLLogger.warning( + + if (!isCredentialPermitted(shc.algorithmConstraints, + kaCred)) { + if (SSLLogger.isOn() && + SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( "key share entry of " + ng + " does not " + - " comply with algorithm constraints"); - } - - kaCred = null; + "comply with algorithm constraints"); } - } - - if (kaCred != null) { + } else { credentials.add(kaCred); } } catch (GeneralSecurityException ex) { @@ -513,7 +510,8 @@ final class KeyShareExtension { @Override public byte[] produce(ConnectionContext context, HandshakeMessage message) throws IOException { - // The producing happens in client side only. + // The producing happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; // In response to key_share request only @@ -571,7 +569,9 @@ final class KeyShareExtension { SSLPossession[] poses = ke.createPossessions(shc); for (SSLPossession pos : poses) { - if (!(pos instanceof NamedGroupPossession)) { + if (!(pos instanceof NamedGroupPossession || + pos instanceof + KEMKeyExchange.KEMSenderPossession)) { // May need more possession types in the future. continue; } @@ -579,7 +579,34 @@ final class KeyShareExtension { // update the context shc.handshakeKeyExchange = ke; shc.handshakePossessions.add(pos); - keyShare = new KeyShareEntry(ng.id, pos.encode()); + + // For KEM, perform encapsulation using the client’s public + // key (KEMCredentials). The resulting encapsulated message + // becomes the key_share value sent to the client. The + // shared secret derived from encapsulation is stored in + // the KEMSenderPossession for later use in the TLS key + // schedule. + + // SSLKeyExchange.createPossessions() returns at most one + // key-agreement possession or one KEMSenderPossession + // per handshake. + if (pos instanceof KEMKeyExchange.KEMSenderPossession xp) { + if (cd instanceof KEMKeyExchange.KEMCredentials kcred + && ng.equals(kcred.namedGroup)) { + String name = ((NamedParameterSpec) + ng.keAlgParamSpec).getName(); + KAKeyDerivation handshakeKD = new KAKeyDerivation( + name, ng, shc, null, null, + kcred.getKeyShare()); + var encaped = handshakeKD.encapsulate( + "TlsHandshakeSecret", xp.getRandom()); + xp.setKey(encaped.key()); + keyShare = new KeyShareEntry(ng.id, + encaped.encapsulation()); + } + } else { + keyShare = new KeyShareEntry(ng.id, pos.encode()); + } break; } @@ -663,19 +690,13 @@ final class KeyShareExtension { try { SSLCredentials kaCred = ng.decodeCredentials(keyShare.keyExchange); - if (chc.algorithmConstraints != null && - kaCred instanceof - NamedGroupCredentials namedGroupCredentials) { - if (!chc.algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - namedGroupCredentials.getPublicKey())) { - chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, - "key share entry of " + ng + " does not " + - " comply with algorithm constraints"); - } - } - if (kaCred != null) { + if (!isCredentialPermitted(chc.algorithmConstraints, + kaCred)) { + chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, + "key share entry of " + ng + " does not " + + "comply with algorithm constraints"); + } else { credentials = kaCred; } } catch (GeneralSecurityException ex) { @@ -696,6 +717,34 @@ final class KeyShareExtension { } } + private static boolean isCredentialPermitted( + AlgorithmConstraints constraints, + SSLCredentials cred) { + + if (constraints == null) return true; + if (cred == null) return false; + + if (cred instanceof NamedGroupCredentials namedGroupCred) { + if (namedGroupCred instanceof KEMKeyExchange.KEMCredentials + kemCred) { + AlgorithmParameterSpec paramSpec = kemCred.getNamedGroup(). + keAlgParamSpec; + String algName = (paramSpec instanceof NamedParameterSpec nps) ? + nps.getName() : null; + return algName != null && constraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + algName, + null); + } else { + return constraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + namedGroupCred.getPublicKey()); + } + } + + return true; + } + /** * The absence processing if the extension is not present in * the ServerHello handshake message. diff --git a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java index 877236ebfad..abf973727f3 100644 --- a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java +++ b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java @@ -214,6 +214,39 @@ enum NamedGroup { ProtocolVersion.PROTOCOLS_TO_13, PredefinedDHParameterSpecs.ffdheParams.get(8192)), + ML_KEM_512(0x0200, "MLKEM512", + NamedGroupSpec.NAMED_GROUP_KEM, + ProtocolVersion.PROTOCOLS_OF_13, + null), + + ML_KEM_768(0x0201, "MLKEM768", + NamedGroupSpec.NAMED_GROUP_KEM, + ProtocolVersion.PROTOCOLS_OF_13, + null), + + ML_KEM_1024(0x0202, "MLKEM1024", + NamedGroupSpec.NAMED_GROUP_KEM, + ProtocolVersion.PROTOCOLS_OF_13, + null), + + X25519MLKEM768(0x11ec, "X25519MLKEM768", + NamedGroupSpec.NAMED_GROUP_KEM, + ProtocolVersion.PROTOCOLS_OF_13, + Hybrid.X25519_MLKEM768, + HybridProvider.PROVIDER), + + SECP256R1MLKEM768(0x11eb, "SecP256r1MLKEM768", + NamedGroupSpec.NAMED_GROUP_KEM, + ProtocolVersion.PROTOCOLS_OF_13, + Hybrid.SECP256R1_MLKEM768, + HybridProvider.PROVIDER), + + SECP384R1MLKEM1024(0x11ed, "SecP384r1MLKEM1024", + NamedGroupSpec.NAMED_GROUP_KEM, + ProtocolVersion.PROTOCOLS_OF_13, + Hybrid.SECP384R1_MLKEM1024, + HybridProvider.PROVIDER), + // Elliptic Curves (RFC 4492) // // arbitrary prime and characteristic-2 curves @@ -234,22 +267,33 @@ enum NamedGroup { final AlgorithmParameterSpec keAlgParamSpec; final AlgorithmParameters keAlgParams; final boolean isAvailable; + final Provider defaultProvider; // performance optimization private static final Set KEY_AGREEMENT_PRIMITIVE_SET = Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT)); - // Constructor used for all NamedGroup types NamedGroup(int id, String name, NamedGroupSpec namedGroupSpec, ProtocolVersion[] supportedProtocols, AlgorithmParameterSpec keAlgParamSpec) { + this(id, name, namedGroupSpec, supportedProtocols, keAlgParamSpec, + null); + } + + // Constructor used for all NamedGroup types + NamedGroup(int id, String name, + NamedGroupSpec namedGroupSpec, + ProtocolVersion[] supportedProtocols, + AlgorithmParameterSpec keAlgParamSpec, + Provider defaultProvider) { this.id = id; this.name = name; this.spec = namedGroupSpec; this.algorithm = namedGroupSpec.algorithm; this.supportedProtocols = supportedProtocols; this.keAlgParamSpec = keAlgParamSpec; + this.defaultProvider = defaultProvider; // Check if it is a supported named group. AlgorithmParameters algParams = null; @@ -266,16 +310,28 @@ enum NamedGroup { // Check the specific algorithm parameters. if (mediator) { try { - algParams = - AlgorithmParameters.getInstance(namedGroupSpec.algorithm); - algParams.init(keAlgParamSpec); + // Skip AlgorithmParameters for KEMs (not supported) + // Check KEM's availability via KeyFactory + if (namedGroupSpec == NamedGroupSpec.NAMED_GROUP_KEM) { + if (defaultProvider == null) { + KeyFactory.getInstance(name); + } else { + KeyFactory.getInstance(name, defaultProvider); + } + } else { + // ECDHE or others: use AlgorithmParameters as before + algParams = AlgorithmParameters.getInstance( + namedGroupSpec.algorithm); + algParams.init(keAlgParamSpec); + } } catch (InvalidParameterSpecException | NoSuchAlgorithmException exp) { if (namedGroupSpec != NamedGroupSpec.NAMED_GROUP_XDH) { mediator = false; if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning( - "No AlgorithmParameters for " + name, exp); + "No AlgorithmParameters or KeyFactory for " + name, + exp); } } else { // Please remove the following code if the XDH/X25519/X448 @@ -307,6 +363,10 @@ enum NamedGroup { this.keAlgParams = mediator ? algParams : null; } + Provider getProvider() { + return defaultProvider; + } + // // The next set of methods search & retrieve NamedGroups. // @@ -545,6 +605,10 @@ enum NamedGroup { return spec.decodeCredentials(this, encoded); } + SSLPossession createPossession(boolean isClient, SecureRandom random) { + return spec.createPossession(this, isClient, random); + } + SSLPossession createPossession(SecureRandom random) { return spec.createPossession(this, random); } @@ -566,6 +630,11 @@ enum NamedGroup { SSLKeyDerivation createKeyDerivation( HandshakeContext hc) throws IOException; + + default SSLPossession createPossession(NamedGroup ng, boolean isClient, + SecureRandom random) { + return createPossession(ng, random); + } } enum NamedGroupSpec implements NamedGroupScheme { @@ -578,6 +647,10 @@ enum NamedGroup { // Finite Field Groups (XDH) NAMED_GROUP_XDH("XDH", XDHScheme.instance), + // Post-Quantum Cryptography (PQC) KEM groups + // Currently used for hybrid named groups + NAMED_GROUP_KEM("KEM", KEMScheme.instance), + // arbitrary prime and curves (ECDHE) NAMED_GROUP_ARBITRARY("EC", null), @@ -634,6 +707,15 @@ enum NamedGroup { return null; } + public SSLPossession createPossession( + NamedGroup ng, boolean isClient, SecureRandom random) { + if (scheme != null) { + return scheme.createPossession(ng, isClient, random); + } + + return null; + } + @Override public SSLPossession createPossession( NamedGroup ng, SecureRandom random) { @@ -739,6 +821,42 @@ enum NamedGroup { } } + private static class KEMScheme implements NamedGroupScheme { + private static final KEMScheme instance = new KEMScheme(); + + @Override + public byte[] encodePossessionPublicKey(NamedGroupPossession poss) { + return poss.encode(); + } + + @Override + public SSLCredentials decodeCredentials(NamedGroup ng, + byte[] encoded) throws IOException, GeneralSecurityException { + return KEMKeyExchange.KEMCredentials.valueOf(ng, encoded); + } + + @Override + public SSLPossession createPossession(NamedGroup ng, + SecureRandom random) { + // Must call createPossession with isClient + throw new UnsupportedOperationException(); + } + + @Override + public SSLPossession createPossession( + NamedGroup ng, boolean isClient, SecureRandom random) { + return isClient + ? new KEMKeyExchange.KEMReceiverPossession(ng, random) + : new KEMKeyExchange.KEMSenderPossession(ng, random); + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext hc) throws IOException { + return KEMKeyExchange.kemKAGenerator.createKeyDerivation(hc); + } + } + static final class SupportedGroups { // the supported named groups, non-null immutable list static final String[] namedGroups; @@ -784,6 +902,9 @@ enum NamedGroup { } else { // default groups NamedGroup[] groups = new NamedGroup[] { + // Hybrid key agreement + X25519MLKEM768, + // Primary XDH (RFC 7748) curves X25519, diff --git a/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java b/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java index 22a44590ce3..263308f0659 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -570,7 +570,9 @@ final class SSLKeyExchange implements SSLKeyAgreementGenerator, @Override public SSLPossession createPossession(HandshakeContext hc) { - return namedGroup.createPossession(hc.sslContext.getSecureRandom()); + return namedGroup.createPossession( + hc instanceof ClientHandshakeContext, + hc.sslContext.getSecureRandom()); } @Override diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHello.java b/src/java.base/share/classes/sun/security/ssl/ServerHello.java index 76c266a628a..0567c861e18 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java @@ -565,6 +565,34 @@ final class ServerHello { clientHello); shc.serverHelloRandom = shm.serverRandom; + // For key derivation, we will either use the traditional Key + // Agreement (KA) model or the Key Encapsulation Mechanism (KEM) + // model, depending on what key exchange group is used. + // + // For KA flows, the server first receives the client's share, + // then generates its key share, and finally comes here. + // However, this is changed for KEM: the server + // must perform both actions — derive the secret and generate + // the key encapsulation message at the same time during + // encapsulation in SHKeyShareProducer. + // + // Traditional Key Agreement (KA): + // - Both peers generate a key share and exchange it. + // - Each peer computes a shared secret sometime after + // receiving the other's key share. + // + // Key Encapsulation Mechanism (KEM): + // The client publishes a public key via a KeyShareExtension, + // which the server uses to: + // + // - generate the shared secret + // - encapsulate the message which is sent to the client in + // another KeyShareExtension + // + // The derived shared secret must be stored in a + // KEMSenderPossession so it can be retrieved for handshake + // traffic secret derivation later. + // Produce extensions for ServerHello handshake message. SSLExtension[] serverHelloExtensions = shc.sslConfig.getEnabledExtensions( @@ -590,9 +618,26 @@ final class ServerHello { "Not negotiated key shares"); } - SSLKeyDerivation handshakeKD = ke.createKeyDerivation(shc); - SecretKey handshakeSecret = handshakeKD.deriveKey( - "TlsHandshakeSecret"); + SecretKey handshakeSecret = null; + + // For KEM, the shared secret has already been generated and + // stored in the server’s possession (KEMSenderPossession) + // during encapsulation in SHKeyShareProducer. + // + // Only one key share is selected by the server, so at most one + // possession will contain the pre-derived shared secret. + for (var pos : shc.handshakePossessions) { + if (pos instanceof KEMKeyExchange.KEMSenderPossession xp) { + handshakeSecret = xp.getKey(); + break; + } + } + + if (handshakeSecret == null) { + SSLKeyDerivation handshakeKD = ke.createKeyDerivation(shc); + handshakeSecret = handshakeKD.deriveKey( + "TlsHandshakeSecret"); + } SSLTrafficKeyDerivation kdg = SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); diff --git a/src/java.base/share/classes/sun/security/x509/X509Key.java b/src/java.base/share/classes/sun/security/x509/X509Key.java index c83e06f651e..1cfe3f9d95d 100644 --- a/src/java.base/share/classes/sun/security/x509/X509Key.java +++ b/src/java.base/share/classes/sun/security/x509/X509Key.java @@ -104,6 +104,10 @@ public class X509Key implements PublicKey, DerEncoder { return (BitArray)bitStringKey.clone(); } + public byte[] getKeyAsBytes() { + return bitStringKey.toByteArray(); + } + /** * Construct X.509 subject public key from a DER value. If * the runtime environment is configured with a specific class for diff --git a/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java b/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java index 25f73606b96..786b907b79a 100644 --- a/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java +++ b/test/jdk/javax/net/ssl/SSLParameters/NamedGroups.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (C) 2022, Tencent. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,7 +27,7 @@ /* * @test - * @bug 8281236 + * @bug 8281236 8314323 * @summary Check TLS connection behaviors for named groups configuration * @library /javax/net/ssl/templates * @run main/othervm NamedGroups @@ -136,6 +137,60 @@ public class NamedGroups extends SSLSocketTemplate { "secp256r1" }, true); + + runTest(new String[] { + "X25519MLKEM768" + }, + new String[] { + "X25519MLKEM768" + }, + false); + + runTest(new String[] { + "SecP256r1MLKEM768" + }, + new String[] { + "SecP256r1MLKEM768" + }, + false); + + runTest(new String[] { + "SecP384r1MLKEM1024" + }, + new String[] { + "SecP384r1MLKEM1024" + }, + false); + + runTest(new String[] { + "X25519MLKEM768" + }, + new String[] { + "SecP256r1MLKEM768" + }, + true); + + runTest(new String[] { + "X25519MLKEM768" + }, + new String[0], + true); + + runTest(new String[] { + "SecP256r1MLKEM768" + }, + null, + true); + + runTest(new String[] { + "X25519MLKEM768", + "x25519" + }, + new String[] { + "X25519MLKEM768", + "x25519" + }, + false); } private static void runTest(String[] serverNamedGroups, diff --git a/test/jdk/javax/net/ssl/TLSCommon/NamedGroup.java b/test/jdk/javax/net/ssl/TLSCommon/NamedGroup.java index ec89fe0d5b5..432a2bd1b0d 100644 --- a/test/jdk/javax/net/ssl/TLSCommon/NamedGroup.java +++ b/test/jdk/javax/net/ssl/TLSCommon/NamedGroup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,11 @@ public enum NamedGroup { FFDHE3072("ffdhe3072"), FFDHE4096("ffdhe4096"), FFDHE6144("ffdhe6144"), - FFDHE8192("ffdhe8192"); + FFDHE8192("ffdhe8192"), + + X25519MLKEM768("X25519MLKEM768"), + SECP256R1MLKEM768("SecP256r1MLKEM768"), + SECP384R1MLKEM1024("SecP384r1MLKEM1024"); public final String name; diff --git a/test/jdk/javax/net/ssl/TLSv13/ClientHelloKeyShares.java b/test/jdk/javax/net/ssl/TLSv13/ClientHelloKeyShares.java index efb9895b33c..ed26cf90a8c 100644 --- a/test/jdk/javax/net/ssl/TLSv13/ClientHelloKeyShares.java +++ b/test/jdk/javax/net/ssl/TLSv13/ClientHelloKeyShares.java @@ -26,16 +26,21 @@ /* * @test - * @bug 8247630 + * @bug 8247630 8314323 * @summary Use two key share entries - * @run main/othervm ClientHelloKeyShares 29 23 + * @run main/othervm ClientHelloKeyShares 4588 29 * @run main/othervm -Djdk.tls.namedGroups=secp384r1,secp521r1,x448,ffdhe2048 ClientHelloKeyShares 24 30 * @run main/othervm -Djdk.tls.namedGroups=sect163k1,sect163r1,x25519 ClientHelloKeyShares 29 * @run main/othervm -Djdk.tls.namedGroups=sect163k1,sect163r1,secp256r1 ClientHelloKeyShares 23 * @run main/othervm -Djdk.tls.namedGroups=sect163k1,sect163r1,ffdhe2048,ffdhe3072,ffdhe4096 ClientHelloKeyShares 256 * @run main/othervm -Djdk.tls.namedGroups=sect163k1,ffdhe2048,x25519,secp256r1 ClientHelloKeyShares 256 29 * @run main/othervm -Djdk.tls.namedGroups=secp256r1,secp384r1,ffdhe2048,x25519 ClientHelloKeyShares 23 256 - */ + * @run main/othervm -Djdk.tls.namedGroups=X25519MLKEM768 ClientHelloKeyShares 4588 + * @run main/othervm -Djdk.tls.namedGroups=x25519,X25519MLKEM768 ClientHelloKeyShares 29 4588 + * @run main/othervm -Djdk.tls.namedGroups=SecP256r1MLKEM768,x25519 ClientHelloKeyShares 4587 29 + * @run main/othervm -Djdk.tls.namedGroups=SecP384r1MLKEM1024,secp256r1 ClientHelloKeyShares 4589 23 + * @run main/othervm -Djdk.tls.namedGroups=X25519MLKEM768,SecP256r1MLKEM768,X25519,secp256r1 ClientHelloKeyShares 4588 29 +*/ import javax.net.ssl.*; import javax.net.ssl.SSLEngineResult.*; @@ -62,10 +67,6 @@ public class ClientHelloKeyShares { private static final int HELLO_EXT_SUPP_VERS = 43; private static final int HELLO_EXT_KEY_SHARE = 51; private static final int TLS_PROT_VER_13 = 0x0304; - private static final int NG_SECP256R1 = 0x0017; - private static final int NG_SECP384R1 = 0x0018; - private static final int NG_X25519 = 0x001D; - private static final int NG_X448 = 0x001E; public static void main(String args[]) throws Exception { if (debug) { diff --git a/test/jdk/javax/net/ssl/TLSv13/HRRKeyShares.java b/test/jdk/javax/net/ssl/TLSv13/HRRKeyShares.java index 560faf87049..bd14e465e65 100644 --- a/test/jdk/javax/net/ssl/TLSv13/HRRKeyShares.java +++ b/test/jdk/javax/net/ssl/TLSv13/HRRKeyShares.java @@ -26,10 +26,12 @@ /* * @test - * @bug 8247630 + * @bug 8247630 8314323 * @summary Use two key share entries * @library /test/lib - * @run main/othervm -Djdk.tls.namedGroups=x25519,secp256r1,secp384r1 HRRKeyShares + * @run main/othervm + * -Djdk.tls.namedGroups=x25519,secp256r1,secp384r1,X25519MLKEM768,SecP256r1MLKEM768,SecP384r1MLKEM1024 + * HRRKeyShares */ import java.io.ByteArrayOutputStream; @@ -72,6 +74,10 @@ public class HRRKeyShares { private static final int NG_SECP384R1 = 0x0018; private static final int NG_X25519 = 0x001D; private static final int NG_X448 = 0x001E; + private static final int NG_X25519_MLKEM768 = 0x11EC; + private static final int NG_SECP256R1_MLKEM768 = 0x11EB; + private static final int NG_SECP384R1_MLKEM1024 = 0x11ED; + private static final int NG_GC512A = 0x0026; private static final int COMP_NONE = 0; private static final int ALERT_TYPE_FATAL = 2; @@ -238,6 +244,18 @@ public class HRRKeyShares { System.out.println("Test 4: Bad HRR using known / unasserted x448"); hrrKeyShareTest(NG_X448, false); System.out.println(); + + System.out.println("Test 5: Good HRR exchange using X25519MLKEM768"); + hrrKeyShareTest(NG_X25519_MLKEM768, true); + System.out.println(); + + System.out.println("Test 6: Good HRR exchange using SecP256r1MLKEM768"); + hrrKeyShareTest(NG_SECP256R1_MLKEM768, true); + System.out.println(); + + System.out.println("Test 7: Good HRR exchange using SecP384r1MLKEM1024"); + hrrKeyShareTest(NG_SECP384R1_MLKEM1024, true); + System.out.println(); } private static void logResult(String str, SSLEngineResult result) { @@ -348,7 +366,8 @@ public class HRRKeyShares { try { // Now we're expecting to reissue the ClientHello, this time - // with a secp384r1 share. + // with a key share for the HRR requested named + // group (hrrNamedGroup). cTOs.compact(); clientResult = engine.wrap(clientOut, cTOs); logResult("client wrap: ", clientResult); diff --git a/test/jdk/sun/security/pkcs11/tls/fips/FipsModeTLS.java b/test/jdk/sun/security/pkcs11/tls/fips/FipsModeTLS.java index 8799f2305bf..a54bb501f78 100644 --- a/test/jdk/sun/security/pkcs11/tls/fips/FipsModeTLS.java +++ b/test/jdk/sun/security/pkcs11/tls/fips/FipsModeTLS.java @@ -34,8 +34,12 @@ * -Djdk.tls.useExtendedMasterSecret=false * -Djdk.tls.client.enableSessionTicketExtension=false FipsModeTLS * @comment SunPKCS11 does not support (TLS1.2) SunTlsExtendedMasterSecret yet. - * Stateless resumption doesn't currently work with NSS-FIPS, see JDK-8368669 - * @run main/othervm/timeout=120 -Djdk.tls.client.protocols=TLSv1.3 FipsModeTLS + * Stateless resumption doesn't currently work with NSS-FIPS, see JDK-8368669. + * NSS-FIPS does not support ML-KEM, so configures the list of named groups. + * @run main/othervm/timeout=120 + * -Djdk.tls.client.protocols=TLSv1.3 + * -Djdk.tls.namedGroups=x25519,secp256r1,secp384r1,secp521r1,x448,ffdhe2048,ffdhe3072,ffdhe4096,ffdhe6144,ffdhe8192 + * FipsModeTLS */ import java.io.File; diff --git a/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java b/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java index 26304c5df95..a13f8570f14 100644 --- a/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java +++ b/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,12 +23,24 @@ /* * @test - * @bug 8246330 + * @bug 8246330 8314323 * @library /javax/net/ssl/templates /test/lib * @run main/othervm -Djdk.tls.namedGroups="secp384r1" DisabledCurve DISABLE_NONE PASS * @run main/othervm -Djdk.tls.namedGroups="secp384r1" DisabledCurve secp384r1 FAIL + * @run main/othervm -Djdk.tls.namedGroups="X25519MLKEM768" + DisabledCurve DISABLE_NONE PASS + * @run main/othervm -Djdk.tls.namedGroups="X25519MLKEM768" + DisabledCurve X25519MLKEM768 FAIL + * @run main/othervm -Djdk.tls.namedGroups="SecP256r1MLKEM768" + DisabledCurve DISABLE_NONE PASS + * @run main/othervm -Djdk.tls.namedGroups="SecP256r1MLKEM768" + DisabledCurve SecP256r1MLKEM768 FAIL + * @run main/othervm -Djdk.tls.namedGroups="SecP384r1MLKEM1024" + DisabledCurve DISABLE_NONE PASS + * @run main/othervm -Djdk.tls.namedGroups="SecP384r1MLKEM1024" + DisabledCurve SecP384r1MLKEM1024 FAIL */ import java.security.Security; import java.util.Arrays; @@ -45,8 +57,10 @@ public class DisabledCurve extends SSLSocketTemplate { private static final String[][][] protocols = { { { "TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1" }, { "TLSv1.2" } }, { { "TLSv1.2" }, { "TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1" } }, - { { "TLSv1.2" }, { "TLSv1.2" } }, { { "TLSv1.1" }, { "TLSv1.1" } }, - { { "TLSv1" }, { "TLSv1" } } }; + { { "TLSv1.2" }, { "TLSv1.2" } }, + { { "TLSv1.1" }, { "TLSv1.1" } }, + { { "TLSv1" }, { "TLSv1" } }, + { { "TLSv1.3" }, { "TLSv1.3" } } }; @Override protected SSLContext createClientSSLContext() throws Exception { @@ -94,17 +108,36 @@ public class DisabledCurve extends SSLSocketTemplate { String expected = args[1]; String disabledName = ("DISABLE_NONE".equals(args[0]) ? "" : args[0]); boolean disabled = false; - if (disabledName.equals("")) { + + if (disabledName.isEmpty()) { Security.setProperty("jdk.disabled.namedCurves", ""); + Security.setProperty("jdk.certpath.disabledAlgorithms", ""); } else { disabled = true; - Security.setProperty("jdk.certpath.disabledAlgorithms", "secp384r1"); + Security.setProperty("jdk.certpath.disabledAlgorithms", disabledName); + if (!disabledName.contains("MLKEM")) { + Security.setProperty("jdk.disabled.namedCurves", disabledName); + } else { + Security.setProperty("jdk.disabled.namedCurves", ""); + } } // Re-enable TLSv1 and TLSv1.1 since test depends on it. SecurityUtils.removeFromDisabledTlsAlgs("TLSv1", "TLSv1.1"); + String namedGroups = System.getProperty("jdk.tls.namedGroups", ""); + boolean hybridGroup = namedGroups.contains("MLKEM"); + for (index = 0; index < protocols.length; index++) { + if (hybridGroup) { + String[] clientProtos = protocols[index][0]; + String[] serverProtos = protocols[index][1]; + + if (!(isTLS13(clientProtos) && isTLS13(serverProtos))) { + continue; + } + } + try { (new DisabledCurve()).run(); if (expected.equals("FAIL")) { @@ -123,4 +156,8 @@ public class DisabledCurve extends SSLSocketTemplate { } } + + private static boolean isTLS13(String[] protocols) { + return protocols.length == 1 && "TLSv1.3".equals(protocols[0]); + } } diff --git a/test/jdk/sun/security/ssl/CipherSuite/NamedGroupsWithCipherSuite.java b/test/jdk/sun/security/ssl/CipherSuite/NamedGroupsWithCipherSuite.java index 5732f42982c..9080f549683 100644 --- a/test/jdk/sun/security/ssl/CipherSuite/NamedGroupsWithCipherSuite.java +++ b/test/jdk/sun/security/ssl/CipherSuite/NamedGroupsWithCipherSuite.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,6 +21,8 @@ * questions. */ +import java.util.Arrays; +import java.util.List; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLSocket; @@ -29,7 +31,7 @@ import jdk.test.lib.security.SecurityUtils; /* * @test - * @bug 8224650 8242929 + * @bug 8224650 8242929 8314323 * @library /javax/net/ssl/templates * /javax/net/ssl/TLSCommon * /test/lib @@ -44,17 +46,20 @@ import jdk.test.lib.security.SecurityUtils; * @run main/othervm NamedGroupsWithCipherSuite ffdhe4096 * @run main/othervm NamedGroupsWithCipherSuite ffdhe6144 * @run main/othervm NamedGroupsWithCipherSuite ffdhe8192 + * @run main/othervm NamedGroupsWithCipherSuite X25519MLKEM768 + * @run main/othervm NamedGroupsWithCipherSuite SecP256r1MLKEM768 + * @run main/othervm NamedGroupsWithCipherSuite SecP384r1MLKEM1024 */ public class NamedGroupsWithCipherSuite extends SSLSocketTemplate { - private static final Protocol[] PROTOCOLS = new Protocol[] { + private static final List PROTOCOLS = List.of( Protocol.TLSV1_3, Protocol.TLSV1_2, Protocol.TLSV1_1, Protocol.TLSV1 - }; + ); - private static final CipherSuite[] CIPHER_SUITES = new CipherSuite[] { + private static final List CIPHER_SUITES = List.of( CipherSuite.TLS_AES_128_GCM_SHA256, CipherSuite.TLS_AES_256_GCM_SHA384, CipherSuite.TLS_CHACHA20_POLY1305_SHA256, @@ -75,7 +80,23 @@ public class NamedGroupsWithCipherSuite extends SSLSocketTemplate { CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - }; + ); + + private static final List HYBRID_NAMEDGROUPS = List.of( + "X25519MLKEM768", + "SecP256r1MLKEM768", + "SecP384r1MLKEM1024" + ); + + private static final List HYBRID_PROTOCOL = List.of( + Protocol.TLSV1_3 + ); + + private static final List HYBRID_CIPHER_SUITES = List.of( + CipherSuite.TLS_AES_128_GCM_SHA256, + CipherSuite.TLS_AES_256_GCM_SHA384, + CipherSuite.TLS_CHACHA20_POLY1305_SHA256 + ); private String protocol; private String cipher; @@ -151,48 +172,59 @@ public class NamedGroupsWithCipherSuite extends SSLSocketTemplate { // Re-enable TLSv1 and TLSv1.1 since test depends on it. SecurityUtils.removeFromDisabledTlsAlgs("TLSv1", "TLSv1.1"); - for (Protocol protocol : PROTOCOLS) { - for (CipherSuite cipherSuite : CIPHER_SUITES) { - // Named group converted to lower case just - // to satisfy Test condition + boolean hybridGroup = HYBRID_NAMEDGROUPS.contains(namedGroup); + List protocolList = hybridGroup ? + HYBRID_PROTOCOL : PROTOCOLS; + List cipherList = hybridGroup ? + HYBRID_CIPHER_SUITES : CIPHER_SUITES; + + // non-Hybrid named group converted to lower case just + // to satisfy Test condition + String normalizedGroup = hybridGroup ? + namedGroup : namedGroup.toLowerCase(); + + for (Protocol protocol : protocolList) { + for (CipherSuite cipherSuite : cipherList) { if (cipherSuite.supportedByProtocol(protocol) - && groupSupportdByCipher(namedGroup.toLowerCase(), - cipherSuite)) { + && groupSupportedByCipher(normalizedGroup, + cipherSuite)) { System.out.printf("Protocol: %s, cipher suite: %s%n", protocol, cipherSuite); - // Named group converted to lower case just - // to satisfy Test condition new NamedGroupsWithCipherSuite(protocol, - cipherSuite, namedGroup.toLowerCase()).run(); + cipherSuite, normalizedGroup).run(); } } } } - private static boolean groupSupportdByCipher(String group, + private static boolean groupSupportedByCipher(String group, CipherSuite cipherSuite) { + if (HYBRID_NAMEDGROUPS.contains(group)) { + return cipherSuite.keyExAlgorithm == null; + } + return (group.startsWith("x") - && xdhGroupSupportdByCipher(cipherSuite)) + && xdhGroupSupportedByCipher(cipherSuite)) || (group.startsWith("secp") - && ecdhGroupSupportdByCipher(cipherSuite)) + && ecdhGroupSupportedByCipher(cipherSuite)) || (group.startsWith("ffdhe") - && ffdhGroupSupportdByCipher(cipherSuite)); + && ffdhGroupSupportedByCipher(cipherSuite)); } - private static boolean xdhGroupSupportdByCipher( + private static boolean xdhGroupSupportedByCipher( CipherSuite cipherSuite) { return cipherSuite.keyExAlgorithm == null || cipherSuite.keyExAlgorithm == KeyExAlgorithm.ECDHE_RSA; } - private static boolean ecdhGroupSupportdByCipher( + private static boolean ecdhGroupSupportedByCipher( CipherSuite cipherSuite) { return cipherSuite.keyExAlgorithm == null || cipherSuite.keyExAlgorithm == KeyExAlgorithm.ECDHE_RSA || cipherSuite.keyExAlgorithm == KeyExAlgorithm.ECDHE_ECDSA; } - private static boolean ffdhGroupSupportdByCipher( + private static boolean ffdhGroupSupportedByCipher( CipherSuite cipherSuite) { return cipherSuite.keyExAlgorithm == null || cipherSuite.keyExAlgorithm == KeyExAlgorithm.DHE_DSS diff --git a/test/jdk/sun/security/ssl/CipherSuite/RestrictNamedGroup.java b/test/jdk/sun/security/ssl/CipherSuite/RestrictNamedGroup.java index c4c343bf84e..4ff0c6e6e15 100644 --- a/test/jdk/sun/security/ssl/CipherSuite/RestrictNamedGroup.java +++ b/test/jdk/sun/security/ssl/CipherSuite/RestrictNamedGroup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8226374 8242929 + * @bug 8226374 8242929 8314323 * @library /javax/net/ssl/templates * @summary Restrict signature algorithms and named groups * @run main/othervm RestrictNamedGroup x25519 @@ -36,6 +36,9 @@ * @run main/othervm RestrictNamedGroup ffdhe4096 * @run main/othervm RestrictNamedGroup ffdhe6144 * @run main/othervm RestrictNamedGroup ffdhe8192 + * @run main/othervm RestrictNamedGroup X25519MLKEM768 + * @run main/othervm RestrictNamedGroup SecP256r1MLKEM768 + * @run main/othervm RestrictNamedGroup SecP384r1MLKEM1024 */ import java.security.Security; diff --git a/test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java b/test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java index 88b0bf2489a..8cf6ee2b5e6 100644 --- a/test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java +++ b/test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8171279 + * @bug 8171279 8314323 * @library /javax/net/ssl/templates * @summary Test TLS connection with each individual supported group * @run main/othervm SupportedGroups x25519 @@ -36,6 +36,9 @@ * @run main/othervm SupportedGroups ffdhe4096 * @run main/othervm SupportedGroups ffdhe6144 * @run main/othervm SupportedGroups ffdhe8192 + * @run main/othervm SupportedGroups X25519MLKEM768 + * @run main/othervm SupportedGroups SecP256r1MLKEM768 + * @run main/othervm SupportedGroups SecP384r1MLKEM1024 */ import java.net.InetAddress; import java.util.Arrays; @@ -45,15 +48,24 @@ import javax.net.ssl.SSLServerSocket; public class SupportedGroups extends SSLSocketTemplate { private static volatile int index; - private static final String[][][] protocols = { + private static final String[][][] protocolsForClassic = { {{"TLSv1.3"}, {"TLSv1.3"}}, {{"TLSv1.3", "TLSv1.2"}, {"TLSv1.2"}}, {{"TLSv1.2"}, {"TLSv1.3", "TLSv1.2"}}, {{"TLSv1.2"}, {"TLSv1.2"}} }; - public SupportedGroups() { + private static final String[][][] protocolsForHybrid = { + {{"TLSv1.3"}, {"TLSv1.3"}}, + {{"TLSv1.3", "TLSv1.2"}, {"TLSv1.3"}}, + {{"TLSv1.3"}, {"TLSv1.3", "TLSv1.2"}} + }; + + private final String[][][] protocols; + + public SupportedGroups(String[][][] protocols) { this.serverAddress = InetAddress.getLoopbackAddress(); + this.protocols = protocols; } // Servers are configured before clients, increment test case after. @@ -85,8 +97,18 @@ public class SupportedGroups extends SSLSocketTemplate { public static void main(String[] args) throws Exception { System.setProperty("jdk.tls.namedGroups", args[0]); + boolean hybridGroup = hybridNamedGroup(args[0]); + String[][][] protocols = hybridGroup ? + protocolsForHybrid : protocolsForClassic; + for (index = 0; index < protocols.length; index++) { - (new SupportedGroups()).run(); + (new SupportedGroups(protocols)).run(); } } + + private static boolean hybridNamedGroup(String namedGroup) { + return namedGroup.equals("X25519MLKEM768") || + namedGroup.equals("SecP256r1MLKEM768") || + namedGroup.equals("SecP384r1MLKEM1024"); + } } diff --git a/test/micro/org/openjdk/bench/java/security/SSLHandshake.java b/test/micro/org/openjdk/bench/java/security/SSLHandshake.java index d8773781b58..b46704a01de 100644 --- a/test/micro/org/openjdk/bench/java/security/SSLHandshake.java +++ b/test/micro/org/openjdk/bench/java/security/SSLHandshake.java @@ -44,6 +44,7 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManagerFactory; @@ -75,8 +76,15 @@ public class SSLHandshake { @Param({"true", "false"}) boolean resume; - @Param({"TLSv1.2", "TLS"}) - String tlsVersion; + @Param({ + "TLSv1.2-secp256r1", + "TLSv1.3-x25519", "TLSv1.3-secp256r1", "TLSv1.3-secp384r1", + "TLSv1.3-X25519MLKEM768", "TLSv1.3-SecP256r1MLKEM768", "TLSv1.3-SecP384r1MLKEM1024" + }) + String versionAndGroup; + + private String tlsVersion; + private String namedGroup; private static SSLContext getServerContext() { try { @@ -96,6 +104,10 @@ public class SSLHandshake { @Setup(Level.Trial) public void init() throws Exception { + String[] components = versionAndGroup.split("-", 2); + tlsVersion = components[0]; + namedGroup = components[1]; + KeyStore ts = TestCertificates.getTrustStore(); TrustManagerFactory tmf = TrustManagerFactory.getInstance( @@ -195,5 +207,14 @@ public class SSLHandshake { */ clientEngine = sslClientCtx.createSSLEngine("client", 80); clientEngine.setUseClientMode(true); + + // Set the key exchange named group in client and server engines + SSLParameters clientParams = clientEngine.getSSLParameters(); + clientParams.setNamedGroups(new String[]{namedGroup}); + clientEngine.setSSLParameters(clientParams); + + SSLParameters serverParams = serverEngine.getSSLParameters(); + serverParams.setNamedGroups(new String[]{namedGroup}); + serverEngine.setSSLParameters(serverParams); } } diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/KEMBench.java b/test/micro/org/openjdk/bench/javax/crypto/full/KEMBench.java index 3386039c62e..dc6d2060f5e 100644 --- a/test/micro/org/openjdk/bench/javax/crypto/full/KEMBench.java +++ b/test/micro/org/openjdk/bench/javax/crypto/full/KEMBench.java @@ -23,34 +23,69 @@ package org.openjdk.bench.javax.crypto.full; import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.OperationsPerInvocation; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.infra.Blackhole; import javax.crypto.DecapsulateException; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; import javax.crypto.KEM; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.security.spec.ECGenParameterSpec; -public class KEMBench extends CryptoBase { +public abstract class KEMBench extends CryptoBase { public static final int SET_SIZE = 128; - @Param({"ML-KEM-512", "ML-KEM-768", "ML-KEM-1024" }) + @Param({}) private String algorithm; + @Param({""}) // Used when the KeyPairGenerator Alg != KEM Alg + private String kpgSpec; + private KeyPair[] keys; private byte[][] messages; private KEM kem; @Setup - public void setup() throws NoSuchAlgorithmException, InvalidKeyException { - kem = (prov == null) ? KEM.getInstance(algorithm) : KEM.getInstance(algorithm, prov); - KeyPairGenerator generator = (prov == null) ? KeyPairGenerator.getInstance(algorithm) : KeyPairGenerator.getInstance(algorithm, prov); + public void setup() throws NoSuchAlgorithmException, InvalidKeyException, + InvalidAlgorithmParameterException { + String kpgAlg; + String kpgParams; + kem = (prov == null) ? KEM.getInstance(algorithm) : + KEM.getInstance(algorithm, prov); + + // By default use the same provider for KEM and KPG + Provider kpgProv = prov; + if (kpgSpec.isEmpty()) { + kpgAlg = algorithm; + kpgParams = ""; + } else { + // The key pair generation spec is broken down from a colon- + // delimited string spec into 3 fields: + // [0] - the provider name + // [1] - the algorithm name + // [2] - the parameters (i.e. the name of the curve) + String[] kpgTok = kpgSpec.split(":"); + kpgProv = Security.getProvider(kpgTok[0]); + kpgAlg = kpgTok[1]; + kpgParams = kpgTok[2]; + } + KeyPairGenerator generator = (kpgProv == null) ? + KeyPairGenerator.getInstance(kpgAlg) : + KeyPairGenerator.getInstance(kpgAlg, kpgProv); + if (kpgParams != null && !kpgParams.isEmpty()) { + generator.initialize(new ECGenParameterSpec(kpgParams)); + } keys = new KeyPair[SET_SIZE]; for (int i = 0; i < keys.length; i++) { keys[i] = generator.generateKeyPair(); @@ -63,20 +98,79 @@ public class KEMBench extends CryptoBase { } } + private static Provider getInternalJce() { + try { + Class dhClazz = Class.forName("sun.security.ssl.HybridProvider"); + return (Provider) dhClazz.getField("PROVIDER").get(null); + } catch (ReflectiveOperationException exc) { + throw new RuntimeException(exc); + } + } + @Benchmark @OperationsPerInvocation(SET_SIZE) public void encapsulate(Blackhole bh) throws InvalidKeyException { for (KeyPair kp : keys) { - bh.consume(kem.newEncapsulator(kp.getPublic()).encapsulate().encapsulation()); + bh.consume(kem.newEncapsulator(kp.getPublic()).encapsulate(). + encapsulation()); } } @Benchmark @OperationsPerInvocation(SET_SIZE) - public void decapsulate(Blackhole bh) throws InvalidKeyException, DecapsulateException { + public void decapsulate(Blackhole bh) throws InvalidKeyException, + DecapsulateException { for (int i = 0; i < messages.length; i++) { - bh.consume(kem.newDecapsulator(keys[i].getPrivate()).decapsulate(messages[i])); + bh.consume(kem.newDecapsulator(keys[i].getPrivate()). + decapsulate(messages[i])); } } + public static class MLKEM extends KEMBench { + @Param({"ML-KEM-512", "ML-KEM-768", "ML-KEM-1024" }) + private String algorithm; + + @Param({""}) // ML-KEM uses the same alg for KPG and KEM + private String kpgSpec; + } + + @Fork(value = 5, jvmArgs = {"-XX:+AlwaysPreTouch", "--add-opens", + "java.base/sun.security.ssl=ALL-UNNAMED"}) + public static class JSSE_DHasKEM extends KEMBench { + @Setup + public void init() { + try { + prov = getInternalJce(); + super.setup(); + } catch (GeneralSecurityException gse) { + throw new RuntimeException(gse); + } + } + + @Param({"DH"}) + private String algorithm; + + @Param({"SunEC:XDH:x25519", "SunEC:EC:secp256r1", "SunEC:EC:secp384r1"}) + private String kpgSpec; + } + + @Fork(value = 5, jvmArgs = {"-XX:+AlwaysPreTouch", "--add-opens", + "java.base/sun.security.ssl=ALL-UNNAMED"}) + public static class JSSE_Hybrid extends KEMBench { + @Setup + public void init() { + try { + prov = getInternalJce(); + super.setup(); + } catch (GeneralSecurityException gse) { + throw new RuntimeException(gse); + } + } + + @Param({"X25519MLKEM768", "SecP256r1MLKEM768", "SecP384r1MLKEM1024"}) + private String algorithm; + + @Param({""}) // ML-KEM uses the same alg for KPG and KEM + private String kpgSpec; + } } diff --git a/test/micro/org/openjdk/bench/javax/crypto/full/KeyPairGeneratorBench.java b/test/micro/org/openjdk/bench/javax/crypto/full/KeyPairGeneratorBench.java index 58daff28d88..5a3c72fb263 100644 --- a/test/micro/org/openjdk/bench/javax/crypto/full/KeyPairGeneratorBench.java +++ b/test/micro/org/openjdk/bench/javax/crypto/full/KeyPairGeneratorBench.java @@ -22,13 +22,16 @@ */ package org.openjdk.bench.javax.crypto.full; +import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Setup; +import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; +import java.security.Provider; public class KeyPairGeneratorBench extends CryptoBase { @@ -45,11 +48,21 @@ public class KeyPairGeneratorBench extends CryptoBase { setupProvider(); generator = (prov == null) ? KeyPairGenerator.getInstance(algorithm) : KeyPairGenerator.getInstance(algorithm, prov); - if (keyLength > 0) { // not all key pair generators allow the use of key length + // not all key pair generators allow the use of key length + if (keyLength > 0) { generator.initialize(keyLength); } } + private static Provider getInternalJce() { + try { + Class dhClazz = Class.forName("sun.security.ssl.HybridProvider"); + return (Provider) dhClazz.getField("PROVIDER").get(null); + } catch (ReflectiveOperationException exc) { + throw new RuntimeException(exc); + } + } + @Benchmark public KeyPair generateKeyPair() { return generator.generateKeyPair(); @@ -118,4 +131,23 @@ public class KeyPairGeneratorBench extends CryptoBase { private int keyLength; } + @Fork(value = 5, jvmArgs = {"-XX:+AlwaysPreTouch", "--add-opens", + "java.base/sun.security.ssl=ALL-UNNAMED"}) + public static class JSSE_Hybrid extends KeyPairGeneratorBench { + @Setup + public void init() { + try { + prov = getInternalJce(); + super.setup(); + } catch (GeneralSecurityException gse) { + throw new RuntimeException(gse); + } + } + + @Param({"X25519MLKEM768", "SecP256r1MLKEM768", "SecP384r1MLKEM1024"}) + private String algorithm; + + @Param({"0"}) // Hybrid KPGs don't need key lengths + private int keyLength; + } } From b2b4729ba2dbbb7cecb177612bd08927ccb085f2 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Tue, 20 Jan 2026 16:28:23 +0000 Subject: [PATCH 172/204] 8375015: CompletionAPITest::testDocumentation failed - AssertionFailedError: expected: but was: Reviewed-by: jlahoda --- test/langtools/jdk/jshell/CompletionAPITest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/langtools/jdk/jshell/CompletionAPITest.java b/test/langtools/jdk/jshell/CompletionAPITest.java index ad5dea90a95..932482ce6dc 100644 --- a/test/langtools/jdk/jshell/CompletionAPITest.java +++ b/test/langtools/jdk/jshell/CompletionAPITest.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,7 @@ /* * @test - * @bug 8366691 + * @bug 8366691 8375015 * @summary Test JShell Completion API * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -67,7 +67,7 @@ import org.junit.jupiter.api.Test; public class CompletionAPITest extends KullaTesting { - private static final long TIMEOUT = 2_000; + private static final long TIMEOUT = 20_000; @Test public void testAPI() { @@ -144,7 +144,7 @@ public class CompletionAPITest extends KullaTesting { } @Test - public void testDocumentation() { + public void testDocumentation() throws Exception { waitIndexingFinished(); Path classes = prepareZip(); @@ -171,6 +171,7 @@ public class CompletionAPITest extends KullaTesting { while (clazz.get().get() != null && (System.currentTimeMillis() - start) < TIMEOUT) { System.gc(); + Thread.sleep(100); } assertNull(clazz.get().get()); From 72bf0bb6f6eaf61b3800d885733e23b7b42bf9c9 Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Tue, 20 Jan 2026 16:49:02 +0000 Subject: [PATCH 173/204] 8353115: GenShen: mixed evacuation candidate regions need accurate live_data Reviewed-by: wkemper --- .../heuristics/shenandoahHeuristics.hpp | 7 +++++++ .../heuristics/shenandoahOldHeuristics.cpp | 15 +++++++++++++++ .../gc/shenandoah/shenandoahHeapRegion.cpp | 2 ++ .../gc/shenandoah/shenandoahHeapRegion.hpp | 11 +++++++++++ .../shenandoah/shenandoahHeapRegion.inline.hpp | 17 +++++++++++++++++ 5 files changed, 52 insertions(+) diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp index fb8cfb36353..e1139765022 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -129,6 +129,13 @@ protected: #endif } + inline void update_livedata(size_t live) { + _region_union._live_data = live; +#ifdef ASSERT + _union_tag = is_live_data; +#endif + } + inline ShenandoahHeapRegion* get_region() const { assert(_union_tag != is_uninitialized, "Cannot fetch region from uninitialized RegionData"); return _region; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index 029a4dd98fb..f2c6e427ea8 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -89,6 +89,17 @@ bool ShenandoahOldHeuristics::prime_collection_set(ShenandoahCollectionSet* coll return false; } + // Between consecutive mixed-evacuation cycles, the live data within each candidate region may change due to + // promotions and old-gen evacuations. Re-sort the candidate regions in order to first evacuate regions that have + // the smallest amount of live data. These are easiest to evacuate with least effort. Doing these first allows + // us to more quickly replenish free memory with empty regions. + for (uint i = _next_old_collection_candidate; i < _last_old_collection_candidate; i++) { + ShenandoahHeapRegion* r = _region_data[i].get_region(); + _region_data[i].update_livedata(r->get_mixed_candidate_live_data_bytes()); + } + QuickSort::sort(_region_data + _next_old_collection_candidate, unprocessed_old_collection_candidates(), + compare_by_live); + _first_pinned_candidate = NOT_FOUND; uint included_old_regions = 0; @@ -414,6 +425,8 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() { ShenandoahHeapRegion* r = candidates[i].get_region(); size_t region_garbage = r->garbage(); size_t region_free = r->free(); + + r->capture_mixed_candidate_garbage(); candidates_garbage += region_garbage; unfragmented += region_free; } @@ -456,6 +469,8 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() { r->index(), ShenandoahHeapRegion::region_state_to_string(r->state())); const size_t region_garbage = r->garbage(); const size_t region_free = r->free(); + + r->capture_mixed_candidate_garbage(); candidates_garbage += region_garbage; unfragmented += region_free; defrag_count++; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp index 3cd5cdd2ec3..e794a86e473 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp @@ -75,6 +75,7 @@ ShenandoahHeapRegion::ShenandoahHeapRegion(HeapWord* start, size_t index, bool c _plab_allocs(0), _live_data(0), _critical_pins(0), + _mixed_candidate_garbage_words(0), _update_watermark(start), _age(0), #ifdef SHENANDOAH_CENSUS_NOISE @@ -565,6 +566,7 @@ void ShenandoahHeapRegion::recycle_internal() { assert(_recycling.is_set() && is_trash(), "Wrong state"); ShenandoahHeap* heap = ShenandoahHeap::heap(); + _mixed_candidate_garbage_words = 0; set_top(bottom()); clear_live_data(); reset_alloc_metadata(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp index cf0dc5476d0..9da2816e2c9 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp @@ -43,6 +43,7 @@ class ShenandoahHeapRegion { friend class VMStructs; friend class ShenandoahHeapRegionStateConstant; private: + /* Region state is described by a state machine. Transitions are guarded by heap lock, which allows changing the state of several regions atomically. @@ -259,6 +260,8 @@ private: volatile size_t _live_data; volatile size_t _critical_pins; + size_t _mixed_candidate_garbage_words; + HeapWord* volatile _update_watermark; uint _age; @@ -398,6 +401,14 @@ public: // above TAMS. inline size_t get_live_data_words() const; + inline size_t get_mixed_candidate_live_data_bytes() const; + inline size_t get_mixed_candidate_live_data_words() const; + + inline void capture_mixed_candidate_garbage(); + + // Returns garbage by calculating difference between used and get_live_data_words. The value returned is only + // meaningful immediately following completion of marking. If there have been subsequent allocations in this region, + // use a different approach to determine garbage, such as (used() - get_mixed_candidate_live_data_bytes()) inline size_t garbage() const; void print_on(outputStream* st) const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp index b9304ee9daa..be982433885 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp @@ -163,6 +163,23 @@ inline size_t ShenandoahHeapRegion::get_live_data_bytes() const { return get_live_data_words() * HeapWordSize; } +inline size_t ShenandoahHeapRegion::get_mixed_candidate_live_data_bytes() const { + shenandoah_assert_heaplocked_or_safepoint(); + assert(used() >= _mixed_candidate_garbage_words * HeapWordSize, "used must exceed garbage"); + return used() - _mixed_candidate_garbage_words * HeapWordSize; +} + +inline size_t ShenandoahHeapRegion::get_mixed_candidate_live_data_words() const { + shenandoah_assert_heaplocked_or_safepoint(); + assert(used() >= _mixed_candidate_garbage_words * HeapWordSize, "used must exceed garbage"); + return used() / HeapWordSize - _mixed_candidate_garbage_words; +} + +inline void ShenandoahHeapRegion::capture_mixed_candidate_garbage() { + shenandoah_assert_heaplocked_or_safepoint(); + _mixed_candidate_garbage_words = garbage() / HeapWordSize; +} + inline bool ShenandoahHeapRegion::has_live() const { return get_live_data_words() != 0; } From 5f8cb30fc0296a2b487edf9dee63e810f4861e8e Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 20 Jan 2026 18:16:39 +0000 Subject: [PATCH 174/204] 8375626: G1: Convert G1CollectionSetChooser to use Atomic Reviewed-by: kbarrett, shade --- .../share/gc/g1/g1CollectionSetChooser.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp b/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp index 954ca40a77f..d9496410c12 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetChooser.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 @@ -27,7 +27,7 @@ #include "gc/g1/g1CollectionSetChooser.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" #include "gc/shared/space.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "utilities/quickSort.hpp" // Determine collection set candidates (from marking): For all regions determine @@ -50,7 +50,7 @@ class G1BuildCandidateRegionsTask : public WorkerTask { G1HeapRegion** _data; - uint volatile _cur_claim_idx; + Atomic _cur_claim_idx; static int compare_region_gc_efficiency(G1HeapRegion** rr1, G1HeapRegion** rr2) { G1HeapRegion* r1 = *rr1; @@ -105,7 +105,7 @@ class G1BuildCandidateRegionsTask : public WorkerTask { // Claim a new chunk, returning its bounds [from, to[. void claim_chunk(uint& from, uint& to) { - uint result = AtomicAccess::add(&_cur_claim_idx, _chunk_size); + 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); @@ -121,14 +121,15 @@ class G1BuildCandidateRegionsTask : public WorkerTask { } void sort_by_gc_efficiency() { - if (_cur_claim_idx == 0) { + uint length = _cur_claim_idx.load_relaxed(); + if (length == 0) { return; } - for (uint i = _cur_claim_idx; i < _max_size; i++) { + for (uint i = length; i < _max_size; i++) { assert(_data[i] == nullptr, "must be"); } - qsort(_data, _cur_claim_idx, sizeof(_data[0]), (_sort_Fn)compare_region_gc_efficiency); - for (uint i = _cur_claim_idx; i < _max_size; i++) { + 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"); } } From 42439eb60c4488711f182d0d6ee5165b4972b99d Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 20 Jan 2026 18:30:42 +0000 Subject: [PATCH 175/204] 8374889: C2 VectorAPI: must handle impossible combination of signed cast from float Reviewed-by: dlong, qamai --- src/hotspot/share/opto/graphKit.cpp | 23 +++-- src/hotspot/share/opto/graphKit.hpp | 2 + src/hotspot/share/opto/parse1.cpp | 3 +- src/hotspot/share/opto/vectorIntrinsics.cpp | 18 +++- .../vectorapi/TestCastShapeBadOpc.java | 91 +++++++++++++++++++ 5 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 71ae8fe44a7..bc8ebaf1869 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -1489,8 +1489,7 @@ Node* GraphKit::must_be_not_null(Node* value, bool do_replace_in_map) { } Node *if_f = _gvn.transform(new IfFalseNode(iff)); Node *frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr)); - Node* halt = _gvn.transform(new HaltNode(if_f, frame, "unexpected null in intrinsic")); - C->root()->add_req(halt); + halt(if_f, frame, "unexpected null in intrinsic"); Node *if_t = _gvn.transform(new IfTrueNode(iff)); set_control(if_t); return cast_not_null(value, do_replace_in_map); @@ -2073,6 +2072,12 @@ void GraphKit::increment_counter(Node* counter_addr) { store_to_memory(ctrl, counter_addr, incr, T_LONG, MemNode::unordered); } +void GraphKit::halt(Node* ctrl, Node* frameptr, const char* reason, bool generate_code_in_product) { + Node* halt = new HaltNode(ctrl, frameptr, reason + PRODUCT_ONLY(COMMA generate_code_in_product)); + halt = _gvn.transform(halt); + root()->add_req(halt); +} //------------------------------uncommon_trap---------------------------------- // Bail out to the interpreter in mid-method. Implemented by calling the @@ -2195,11 +2200,15 @@ Node* GraphKit::uncommon_trap(int trap_request, // The debug info is the only real input to this call. // Halt-and-catch fire here. The above call should never return! - HaltNode* halt = new HaltNode(control(), frameptr(), "uncommon trap returned which should never happen" - PRODUCT_ONLY(COMMA /*reachable*/false)); - _gvn.set_type_bottom(halt); - root()->add_req(halt); - + // We only emit code for the HaltNode in debug, which is enough for + // verifying correctness. In product, we don't want to emit it so + // that we can save on code space. HaltNode often get folded because + // the compiler can prove that the unreachable path is dead. But we + // cannot generally expect that for uncommon traps, which are often + // reachable and occasionally taken. + halt(control(), frameptr(), + "uncommon trap returned which should never happen", + false /* don't emit code in product */); stop_and_kill_map(); return call; } diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index 56e9a949e6f..0537d31ae36 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -709,6 +709,8 @@ class GraphKit : public Phase { void increment_counter(address counter_addr); // increment a debug counter void increment_counter(Node* counter_addr); // increment a debug counter + void halt(Node* ctrl, Node* frameptr, const char* reason, bool generate_code_in_product = true); + // Bail out to the interpreter right now // The optional klass is the one causing the trap. // The optional reason is debug information written to the compile log. diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 7aa96d2ace3..6122f7e7bfc 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -1229,8 +1229,7 @@ void Parse::do_method_entry() { Node* not_subtype_ctrl = gen_subtype_check(receiver_obj, holder_klass); assert(!stopped(), "not a subtype"); - Node* halt = _gvn.transform(new HaltNode(not_subtype_ctrl, frameptr(), "failed receiver subtype check")); - C->root()->add_req(halt); + halt(not_subtype_ctrl, frameptr(), "failed receiver subtype check"); } } #endif // ASSERT diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index b48b5f2cd05..6dcf4615b10 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.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 @@ -25,6 +25,7 @@ #include "ci/ciSymbols.hpp" #include "classfile/vmSymbols.hpp" #include "opto/library_call.hpp" +#include "opto/rootnode.hpp" #include "opto/runtime.hpp" #include "opto/vectornode.hpp" #include "prims/vectorSupport.hpp" @@ -2330,6 +2331,21 @@ bool LibraryCallKit::inline_vector_convert() { Node* op = opd1; if (is_cast) { assert(!is_mask || num_elem_from == num_elem_to, "vector mask cast needs the same elem num"); + + // Make sure the precondition of VectorCastNode::opcode holds: we can only have + // unsigned casts for integral types (excluding long). VectorAPI code is not + // expected to violate this at runtime, but we may compile unreachable code + // where such impossible combinations arise. + if (is_ucast && (!is_integral_type(elem_bt_from) || elem_bt_from == T_LONG)) { + // Halt-and-catch fire here. This condition should never happen at runtime. + stringStream ss; + ss.print("impossible combination: unsigned vector cast from %s", type2name(elem_bt_from)); + halt(control(), frameptr(), ss.as_string(C->comp_arena())); + stop_and_kill_map(); + log_if_needed(" ** impossible combination: unsigned cast from %s", type2name(elem_bt_from)); + return true; + } + int cast_vopc = VectorCastNode::opcode(-1, elem_bt_from, !is_ucast); // Make sure that vector cast is implemented to particular type/size combination if it is diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java b/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java new file mode 100644 index 00000000000..4c20c84bc50 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java @@ -0,0 +1,91 @@ +/* + * 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 id=all-flags + * @bug 8374889 + * @summary Test case that can compile unexpected code paths in VectorAPI cast intrinsification. + * @modules jdk.incubator.vector + * @library /test/lib / + * @run main/othervm + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:-TieredCompilation -Xbatch + * -XX:StressSeed=1462975402 + * -XX:+StressIncrementalInlining + * -XX:CompileCommand=compileonly,${test.main.class}::test2 + * ${test.main.class} + */ + +/* + * @test id=no-stress-seed + * @bug 8374889 + * @modules jdk.incubator.vector + * @library /test/lib / + * @run main/othervm + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:-TieredCompilation -Xbatch + * -XX:+StressIncrementalInlining + * -XX:CompileCommand=compileonly,${test.main.class}::test2 + * ${test.main.class} + */ + +/* + * @test id=vanilla + * @bug 8374889 + * @modules jdk.incubator.vector + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.vectorapi; + +import jdk.incubator.vector.*; + +public class TestCastShapeBadOpc { + public static void main(String[] args) { + for (int i = 0; i < 100_000; ++i) { + test1(); + test2(); + } + } + + // This code does not trigger the bug itself, but seems to be important for profiling, + // so that test2 fails. + public static Object test1() { + LongVector v0 = LongVector.broadcast(LongVector.SPECIES_512, -15L); + var v1 = (ByteVector)v0.convertShape(VectorOperators.Conversion.ofReinterpret(long.class, byte.class), ByteVector.SPECIES_128, 0); + var v2 = (ByteVector)v1.castShape(ByteVector.SPECIES_256, 0); + return v2; + } + + public static Object test2() { + var v0 = ShortVector.broadcast(ShortVector.SPECIES_64, (short)7729); + var v1 = (FloatVector)v0.reinterpretShape(FloatVector.SPECIES_64, 0); + // The castShape below should take the "C" path in AbstractVector::convert0, but sometimes + // we also compile the "Z" case because of profiling. This means we attempt to create + // a vector cast from float -> long, but unfortunately with a UCAST (float -> long is signed). + // This triggered an assert in VectorCastNode::opcode. + var v2 = (LongVector)v1.castShape(LongVector.SPECIES_256, 0); + return v2; + } +} From aaca0a2c1f3de06a1349ae9084e9e9dbec991421 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 20 Jan 2026 21:54:56 +0000 Subject: [PATCH 176/204] 8375742: Test java/lang/invoke/MethodHandleProxies/Driver.java does not run Unnamed.java Reviewed-by: jvernee --- test/jdk/java/lang/invoke/MethodHandleProxies/Driver.java | 6 +++--- test/jdk/java/lang/invoke/MethodHandleProxies/Unnamed.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/jdk/java/lang/invoke/MethodHandleProxies/Driver.java b/test/jdk/java/lang/invoke/MethodHandleProxies/Driver.java index 6acd4fb30e1..0e9c708e8e9 100644 --- a/test/jdk/java/lang/invoke/MethodHandleProxies/Driver.java +++ b/test/jdk/java/lang/invoke/MethodHandleProxies/Driver.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 @@ -21,12 +21,12 @@ * questions. */ -/** +/* * @test * @bug 8280377 * @build m1/* m2/* Unnamed * @run testng/othervm m1/p1.Main - * @run testng/othervm Unnamed + * @run main/othervm Unnamed * @summary Test MethodHandleProxies::asInterfaceInstance with a default * method with varargs */ diff --git a/test/jdk/java/lang/invoke/MethodHandleProxies/Unnamed.java b/test/jdk/java/lang/invoke/MethodHandleProxies/Unnamed.java index f42071f0427..f60f36ca9de 100644 --- a/test/jdk/java/lang/invoke/MethodHandleProxies/Unnamed.java +++ b/test/jdk/java/lang/invoke/MethodHandleProxies/Unnamed.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 @@ -39,7 +39,7 @@ public class Unnamed { // verify that the caller has no access to the proxy created on an // inaccessible interface - Method m = intf.getMethod("test", Object[].class); - assertFalse(m.canAccess(null)); + Method m = intf.getMethod("test"); + assertFalse(m.canAccess(t)); } } From 4fd7595f1b607588d9854471a701c2992c6bec60 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Tue, 20 Jan 2026 22:45:39 +0000 Subject: [PATCH 177/204] 8374905: Clarify ZonedDateTime#toString() documentation regarding omitted zero seconds Reviewed-by: rriggs, bpb --- src/java.base/share/classes/java/time/ZonedDateTime.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/time/ZonedDateTime.java b/src/java.base/share/classes/java/time/ZonedDateTime.java index 57dc98d5c68..b1ffe7b87d6 100644 --- a/src/java.base/share/classes/java/time/ZonedDateTime.java +++ b/src/java.base/share/classes/java/time/ZonedDateTime.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2207,7 +2207,10 @@ public final class ZonedDateTime * Outputs this date-time as a {@code String}, such as * {@code 2007-12-03T10:15:30+01:00[Europe/Paris]}. *

      - * The format consists of the {@code LocalDateTime} followed by the {@code ZoneOffset}. + * The format consists of the output of {@link LocalDateTime#toString()}, + * followed by the output of {@link ZoneOffset#toString()}. + * If the time has zero seconds and/or nanoseconds, they are + * omitted to produce the shortest representation. * If the {@code ZoneId} is not the same as the offset, then the ID is output. * The output is compatible with ISO-8601 if the offset and ID are the same, * and the seconds in the offset are zero. From ca3e6236a28794156cc2acf697755229c47735a8 Mon Sep 17 00:00:00 2001 From: Dingli Zhang Date: Tue, 20 Jan 2026 23:48:42 +0000 Subject: [PATCH 178/204] 8375657: RISC-V: Need to check size in SharedRuntime::is_wide_vector Reviewed-by: fjiang, fyang --- src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index 44a6f6c0dc0..8c343f6ab2b 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -213,7 +213,7 @@ void RegisterSaver::restore_live_registers(MacroAssembler* masm) { // Is vector's size (in bytes) bigger than a size saved by default? // riscv does not ovlerlay the floating-point registers on vector registers like aarch64. bool SharedRuntime::is_wide_vector(int size) { - return UseRVV; + return UseRVV && size > 0; } // --------------------------------------------------------------------------- From a2e749572e03dd394d123b701e163e3837472dd0 Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Wed, 21 Jan 2026 03:12:18 +0000 Subject: [PATCH 179/204] 8375063: Update Libpng to 1.6.54 Reviewed-by: serb, prr --- src/java.desktop/share/legal/libpng.md | 10 +- .../native/libsplashscreen/libpng/CHANGES | 27 + .../native/libsplashscreen/libpng/LICENSE | 4 +- .../native/libsplashscreen/libpng/README | 2 +- .../share/native/libsplashscreen/libpng/png.c | 21 +- .../share/native/libsplashscreen/libpng/png.h | 1121 ++++++++++------- .../native/libsplashscreen/libpng/pngconf.h | 4 +- .../native/libsplashscreen/libpng/pngerror.c | 17 +- .../native/libsplashscreen/libpng/pngget.c | 13 +- .../libsplashscreen/libpng/pnglibconf.h | 4 +- .../native/libsplashscreen/libpng/pngmem.c | 19 +- .../native/libsplashscreen/libpng/pngpriv.h | 921 ++++++++------ .../native/libsplashscreen/libpng/pngread.c | 188 +-- .../native/libsplashscreen/libpng/pngrtran.c | 5 +- .../native/libsplashscreen/libpng/pngrutil.c | 6 +- .../native/libsplashscreen/libpng/pngtrans.c | 4 +- 16 files changed, 1401 insertions(+), 965 deletions(-) diff --git a/src/java.desktop/share/legal/libpng.md b/src/java.desktop/share/legal/libpng.md index 8899491c6c0..80d12248ec4 100644 --- a/src/java.desktop/share/legal/libpng.md +++ b/src/java.desktop/share/legal/libpng.md @@ -1,4 +1,4 @@ -## libpng v1.6.51 +## libpng v1.6.54 ### libpng License

      @@ -9,8 +9,8 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
       PNG Reference Library License version 2
       ---------------------------------------
       
      -Copyright (C) 1995-2025 The PNG Reference Library Authors.
      -Copyright (C) 2018-2025 Cosmin Truta
      +Copyright (C) 1995-2026 The PNG Reference Library Authors.
      +Copyright (C) 2018-2026 Cosmin Truta
       Copyright (C) 1998-2018 Glenn Randers-Pehrson
       Copyright (C) 1996-1997 Andreas Dilger
       Copyright (C) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
      @@ -158,6 +158,7 @@ This is the list of PNG Reference Library ("libpng") Contributing
       Authors, for copyright and licensing purposes.
       
        * Adam Richter
      + * Alexander Smorkalov
        * Andreas Dilger
        * Chris Blume
        * Cosmin Truta
      @@ -179,6 +180,7 @@ Authors, for copyright and licensing purposes.
        * Mike Klein
        * Pascal Massimino
        * Paul Schmidt
      + * Petr Simecek
        * Philippe Antoine
        * Qiang Zhou
        * Sam Bushell
      @@ -209,6 +211,8 @@ Authors, for copyright and licensing purposes.
           - ZhangLixia (张利霞)
        * Samsung Group
           - Filip Wasil
      + * SpacemiT Hangzhou Technology, Co.
      +    - Liang Junzhao (梁俊钊)
       
       The build projects, the build scripts, the test scripts, and other
       files in the "projects", "scripts" and "tests" directories, have
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
      index 2478fd0fc08..3bb1baecd23 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/CHANGES
      @@ -6304,6 +6304,33 @@ Version 1.6.51 [November 21, 2025]
         Added GitHub Actions workflows for automated testing.
         Performed various refactorings and cleanups.
       
      +Version 1.6.52 [December 3, 2025]
      +  Fixed CVE-2025-66293 (high severity):
      +    Out-of-bounds read in `png_image_read_composite`.
      +    (Reported by flyfish101 .)
      +  Fixed the Paeth filter handling in the RISC-V RVV implementation.
      +    (Reported by Filip Wasil; fixed by Liang Junzhao.)
      +  Improved the performance of the RISC-V RVV implementation.
      +    (Contributed by Liang Junzhao.)
      +  Added allocation failure fuzzing to oss-fuzz.
      +    (Contributed by Philippe Antoine.)
      +
      +Version 1.6.53 [December 5, 2025]
      +  Fixed a build failure on RISC-V RVV caused by a misspelled intrinsic.
      +    (Contributed by Alexander Smorkalov.)
      +  Fixed a build failure with CMake 4.1 or newer, on Windows, when using
      +    Visual C++ without MASM installed.
      +
      +Version 1.6.54 [January 12, 2026]
      +  Fixed CVE-2026-22695 (medium severity):
      +    Heap buffer over-read in `png_image_read_direct_scaled.
      +    (Reported and fixed by Petr Simecek.)
      +  Fixed CVE-2026-22801 (medium severity):
      +    Integer truncation causing heap buffer over-read in `png_image_write_*`.
      +  Implemented various improvements in oss-fuzz.
      +    (Contributed by Philippe Antoine.)
      +
      +
       Send comments/corrections/commendations to png-mng-implement at lists.sf.net.
       Subscription is required; visit
       https://lists.sourceforge.net/lists/listinfo/png-mng-implement
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE b/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE
      index ea6df986cb6..1b765ae9f96 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/LICENSE
      @@ -4,8 +4,8 @@ COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
       PNG Reference Library License version 2
       ---------------------------------------
       
      - * Copyright (c) 1995-2025 The PNG Reference Library Authors.
      - * Copyright (c) 2018-2025 Cosmin Truta.
      + * Copyright (c) 1995-2026 The PNG Reference Library Authors.
      + * Copyright (c) 2018-2026 Cosmin Truta.
        * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
        * Copyright (c) 1996-1997 Andreas Dilger.
        * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/README b/src/java.desktop/share/native/libsplashscreen/libpng/README
      index 5ea329ee3da..63d1376edf7 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/README
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/README
      @@ -1,4 +1,4 @@
      -README for libpng version 1.6.51
      +README for libpng version 1.6.54
       ================================
       
       See the note about version numbers near the top of `png.h`.
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.c b/src/java.desktop/share/native/libsplashscreen/libpng/png.c
      index 7d85e7c8d5f..5636b4a754e 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/png.c
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.c
      @@ -29,7 +29,7 @@
        * However, the following notice accompanied the original version of this
        * file and, per its terms, should not be removed:
        *
      - * Copyright (c) 2018-2025 Cosmin Truta
      + * Copyright (c) 2018-2026 Cosmin Truta
        * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
        * Copyright (c) 1996-1997 Andreas Dilger
        * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
      @@ -42,7 +42,7 @@
       #include "pngpriv.h"
       
       /* Generate a compiler error if there is an old png.h in the search path. */
      -typedef png_libpng_version_1_6_51 Your_png_h_is_not_version_1_6_51;
      +typedef png_libpng_version_1_6_54 Your_png_h_is_not_version_1_6_54;
       
       /* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the
        * corresponding macro definitions.  This causes a compile time failure if
      @@ -130,7 +130,8 @@ png_sig_cmp(png_const_bytep sig, size_t start, size_t num_to_check)
       #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
       /* Function to allocate memory for zlib */
       PNG_FUNCTION(voidpf /* PRIVATE */,
      -png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED)
      +png_zalloc,(voidpf png_ptr, uInt items, uInt size),
      +    PNG_ALLOCATED)
       {
          png_alloc_size_t num_bytes = size;
       
      @@ -286,7 +287,8 @@ png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver)
       PNG_FUNCTION(png_structp /* PRIVATE */,
       png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
           png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
      -    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
      +    png_malloc_ptr malloc_fn, png_free_ptr free_fn),
      +    PNG_ALLOCATED)
       {
          png_struct create_struct;
       #  ifdef PNG_SETJMP_SUPPORTED
      @@ -390,7 +392,8 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
       
       /* Allocate the memory for an info_struct for the application. */
       PNG_FUNCTION(png_infop,PNGAPI
      -png_create_info_struct,(png_const_structrp png_ptr),PNG_ALLOCATED)
      +png_create_info_struct,(png_const_structrp png_ptr),
      +    PNG_ALLOCATED)
       {
          png_inforp info_ptr;
       
      @@ -846,8 +849,8 @@ png_get_copyright(png_const_structrp png_ptr)
          return PNG_STRING_COPYRIGHT
       #else
          return PNG_STRING_NEWLINE \
      -      "libpng version 1.6.51" PNG_STRING_NEWLINE \
      -      "Copyright (c) 2018-2025 Cosmin Truta" PNG_STRING_NEWLINE \
      +      "libpng version 1.6.54" PNG_STRING_NEWLINE \
      +      "Copyright (c) 2018-2026 Cosmin Truta" PNG_STRING_NEWLINE \
             "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \
             PNG_STRING_NEWLINE \
             "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \
      @@ -2286,8 +2289,8 @@ PNG_FP_End:
       int
       png_check_fp_string(png_const_charp string, size_t size)
       {
      -   int        state=0;
      -   size_t char_index=0;
      +   int state = 0;
      +   size_t char_index = 0;
       
          if (png_check_fp_number(string, size, &state, &char_index) != 0 &&
             (char_index == size || string[char_index] == 0))
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/png.h b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
      index d39ff73552c..ab8876a9626 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/png.h
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/png.h
      @@ -29,9 +29,9 @@
        * However, the following notice accompanied the original version of this
        * file and, per its terms, should not be removed:
        *
      - * libpng version 1.6.51
      + * libpng version 1.6.54
        *
      - * Copyright (c) 2018-2025 Cosmin Truta
      + * Copyright (c) 2018-2026 Cosmin Truta
        * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
        * Copyright (c) 1996-1997 Andreas Dilger
        * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
      @@ -43,7 +43,7 @@
        *   libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
        *   libpng versions 0.97, January 1998, through 1.6.35, July 2018:
        *     Glenn Randers-Pehrson
      - *   libpng versions 1.6.36, December 2018, through 1.6.51, November 2025:
      + *   libpng versions 1.6.36, December 2018, through 1.6.54, January 2026:
        *     Cosmin Truta
        *   See also "Contributing Authors", below.
        */
      @@ -55,8 +55,8 @@
        * PNG Reference Library License version 2
        * ---------------------------------------
        *
      - *  * Copyright (c) 1995-2025 The PNG Reference Library Authors.
      - *  * Copyright (c) 2018-2025 Cosmin Truta.
      + *  * Copyright (c) 1995-2026 The PNG Reference Library Authors.
      + *  * Copyright (c) 2018-2026 Cosmin Truta.
        *  * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
        *  * Copyright (c) 1996-1997 Andreas Dilger.
        *  * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
      @@ -267,7 +267,7 @@
        *    ...
        *    1.5.30                  15    10530  15.so.15.30[.0]
        *    ...
      - *    1.6.51                  16    10651  16.so.16.51[.0]
      + *    1.6.54                  16    10654  16.so.16.54[.0]
        *
        *    Henceforth the source version will match the shared-library major and
        *    minor numbers; the shared-library major version number will be used for
      @@ -303,7 +303,7 @@
        */
       
       /* Version information for png.h - this should match the version in png.c */
      -#define PNG_LIBPNG_VER_STRING "1.6.51"
      +#define PNG_LIBPNG_VER_STRING "1.6.54"
       #define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n"
       
       /* The versions of shared library builds should stay in sync, going forward */
      @@ -314,7 +314,7 @@
       /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
       #define PNG_LIBPNG_VER_MAJOR   1
       #define PNG_LIBPNG_VER_MINOR   6
      -#define PNG_LIBPNG_VER_RELEASE 51
      +#define PNG_LIBPNG_VER_RELEASE 54
       
       /* This should be zero for a public release, or non-zero for a
        * development version.
      @@ -345,7 +345,7 @@
        * From version 1.0.1 it is:
        * XXYYZZ, where XX=major, YY=minor, ZZ=release
        */
      -#define PNG_LIBPNG_VER 10651 /* 1.6.51 */
      +#define PNG_LIBPNG_VER 10654 /* 1.6.54 */
       
       /* Library configuration: these options cannot be changed after
        * the library has been built.
      @@ -455,7 +455,7 @@ extern "C" {
       /* This triggers a compiler error in png.c, if png.c and png.h
        * do not agree upon the version number.
        */
      -typedef char* png_libpng_version_1_6_51;
      +typedef char *png_libpng_version_1_6_54;
       
       /* Basic control structions.  Read libpng-manual.txt or libpng.3 for more info.
        *
      @@ -814,17 +814,22 @@ typedef png_row_info * * png_row_infopp;
        * modify the buffer it is passed. The 'read' function, on the other hand, is
        * expected to return the read data in the buffer.
        */
      -typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp));
      -typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t));
      -typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp));
      -typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32,
      -    int));
      -typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32,
      -    int));
      +typedef PNG_CALLBACK(void, *png_error_ptr,
      +   (png_structp, png_const_charp));
      +typedef PNG_CALLBACK(void, *png_rw_ptr,
      +   (png_structp, png_bytep, size_t));
      +typedef PNG_CALLBACK(void, *png_flush_ptr,
      +   (png_structp));
      +typedef PNG_CALLBACK(void, *png_read_status_ptr,
      +   (png_structp, png_uint_32, int));
      +typedef PNG_CALLBACK(void, *png_write_status_ptr,
      +   (png_structp, png_uint_32, int));
       
       #ifdef PNG_PROGRESSIVE_READ_SUPPORTED
      -typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop));
      -typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop));
      +typedef PNG_CALLBACK(void, *png_progressive_info_ptr,
      +   (png_structp, png_infop));
      +typedef PNG_CALLBACK(void, *png_progressive_end_ptr,
      +   (png_structp, png_infop));
       
       /* The following callback receives png_uint_32 row_number, int pass for the
        * png_bytep data of the row.  When transforming an interlaced image the
      @@ -836,19 +841,19 @@ typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop));
        * find the output pixel (x,y) given an interlaced sub-image pixel
        * (row,col,pass).  (See below for these macros.)
        */
      -typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep,
      -    png_uint_32, int));
      +typedef PNG_CALLBACK(void, *png_progressive_row_ptr,
      +   (png_structp, png_bytep, png_uint_32, int));
       #endif
       
       #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
           defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
      -typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop,
      -    png_bytep));
      +typedef PNG_CALLBACK(void, *png_user_transform_ptr,
      +   (png_structp, png_row_infop, png_bytep));
       #endif
       
       #ifdef PNG_USER_CHUNKS_SUPPORTED
      -typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp,
      -    png_unknown_chunkp));
      +typedef PNG_CALLBACK(int, *png_user_chunk_ptr,
      +   (png_structp, png_unknown_chunkp));
       #endif
       #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
       /* not used anywhere */
      @@ -906,9 +911,10 @@ PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), (jmp_buf, int), typedef);
        * ignores the first argument) should be completely compatible with the
        * following.
        */
      -typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp,
      -    png_alloc_size_t));
      -typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp));
      +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr,
      +   (png_structp, png_alloc_size_t));
      +typedef PNG_CALLBACK(void, *png_free_ptr,
      +   (png_structp, png_voidp));
       
       /* Section 4: exported functions
        * Here are the function definitions most commonly used.  This is not
      @@ -940,20 +946,22 @@ typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp));
        */
       
       /* Returns the version number of the library */
      -PNG_EXPORT(1, png_uint_32, png_access_version_number, (void));
      +PNG_EXPORT(1, png_uint_32, png_access_version_number,
      +   (void));
       
       /* Tell lib we have already handled the first  magic bytes.
        * Handling more than 8 bytes from the beginning of the file is an error.
        */
      -PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes));
      +PNG_EXPORT(2, void, png_set_sig_bytes,
      +   (png_structrp png_ptr, int num_bytes));
       
       /* Check sig[start] through sig[start + num_to_check - 1] to see if it's a
        * PNG file.  Returns zero if the supplied bytes match the 8-byte PNG
        * signature, and non-zero otherwise.  Having num_to_check == 0 or
        * start > 7 will always fail (i.e. return non-zero).
        */
      -PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start,
      -    size_t num_to_check));
      +PNG_EXPORT(3, int, png_sig_cmp,
      +   (png_const_bytep sig, size_t start, size_t num_to_check));
       
       /* Simple signature checking function.  This is the same as calling
        * png_check_sig(sig, n) := (png_sig_cmp(sig, 0, n) == 0).
      @@ -962,21 +970,21 @@ PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start,
       
       /* Allocate and initialize png_ptr struct for reading, and any other memory. */
       PNG_EXPORTA(4, png_structp, png_create_read_struct,
      -    (png_const_charp user_png_ver, png_voidp error_ptr,
      -    png_error_ptr error_fn, png_error_ptr warn_fn),
      -    PNG_ALLOCATED);
      +   (png_const_charp user_png_ver,
      +    png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn),
      +   PNG_ALLOCATED);
       
       /* Allocate and initialize png_ptr struct for writing, and any other memory */
       PNG_EXPORTA(5, png_structp, png_create_write_struct,
      -    (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
      -    png_error_ptr warn_fn),
      -    PNG_ALLOCATED);
      +   (png_const_charp user_png_ver,
      +    png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn),
      +   PNG_ALLOCATED);
       
       PNG_EXPORT(6, size_t, png_get_compression_buffer_size,
      -    (png_const_structrp png_ptr));
      +   (png_const_structrp png_ptr));
       
      -PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr,
      -    size_t size));
      +PNG_EXPORT(7, void, png_set_compression_buffer_size,
      +   (png_structrp png_ptr, size_t size));
       
       /* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp
        * match up.
      @@ -989,8 +997,8 @@ PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr,
        * allocated by the library - the call will return NULL on a mismatch
        * indicating an ABI mismatch.
        */
      -PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr,
      -    png_longjmp_ptr longjmp_fn, size_t jmp_buf_size));
      +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn,
      +   (png_structrp png_ptr, png_longjmp_ptr longjmp_fn, size_t jmp_buf_size));
       #  define png_jmpbuf(png_ptr) \
             (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf))))
       #else
      @@ -1002,67 +1010,77 @@ PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr,
        * will use it; otherwise it will call PNG_ABORT().  This function was
        * added in libpng-1.5.0.
        */
      -PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val),
      -    PNG_NORETURN);
      +PNG_EXPORTA(9, void, png_longjmp,
      +   (png_const_structrp png_ptr, int val),
      +   PNG_NORETURN);
       
       #ifdef PNG_READ_SUPPORTED
       /* Reset the compression stream */
      -PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED);
      +PNG_EXPORTA(10, int, png_reset_zstream,
      +   (png_structrp png_ptr),
      +   PNG_DEPRECATED);
       #endif
       
       /* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */
       #ifdef PNG_USER_MEM_SUPPORTED
       PNG_EXPORTA(11, png_structp, png_create_read_struct_2,
      -    (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
      -    png_error_ptr warn_fn,
      +   (png_const_charp user_png_ver,
      +    png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn,
           png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),
      -    PNG_ALLOCATED);
      +   PNG_ALLOCATED);
       PNG_EXPORTA(12, png_structp, png_create_write_struct_2,
      -    (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
      -    png_error_ptr warn_fn,
      +   (png_const_charp user_png_ver,
      +    png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn,
           png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),
      -    PNG_ALLOCATED);
      +   PNG_ALLOCATED);
       #endif
       
       /* Write the PNG file signature. */
      -PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr));
      +PNG_EXPORT(13, void, png_write_sig,
      +   (png_structrp png_ptr));
       
       /* Write a PNG chunk - size, type, (optional) data, CRC. */
      -PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep
      -    chunk_name, png_const_bytep data, size_t length));
      +PNG_EXPORT(14, void, png_write_chunk,
      +   (png_structrp png_ptr,
      +    png_const_bytep chunk_name, png_const_bytep data, size_t length));
       
       /* Write the start of a PNG chunk - length and chunk name. */
      -PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr,
      +PNG_EXPORT(15, void, png_write_chunk_start,
      +   (png_structrp png_ptr,
           png_const_bytep chunk_name, png_uint_32 length));
       
       /* Write the data of a PNG chunk started with png_write_chunk_start(). */
      -PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr,
      +PNG_EXPORT(16, void, png_write_chunk_data,
      +   (png_structrp png_ptr,
           png_const_bytep data, size_t length));
       
       /* Finish a chunk started with png_write_chunk_start() (includes CRC). */
      -PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr));
      +PNG_EXPORT(17, void, png_write_chunk_end,
      +   (png_structrp png_ptr));
       
       /* Allocate and initialize the info structure */
      -PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr),
      -    PNG_ALLOCATED);
      +PNG_EXPORTA(18, png_infop, png_create_info_struct,
      +   (png_const_structrp png_ptr),
      +   PNG_ALLOCATED);
       
       /* DEPRECATED: this function allowed init structures to be created using the
        * default allocation method (typically malloc).  Use is deprecated in 1.6.0 and
        * the API will be removed in the future.
        */
      -PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr,
      -    size_t png_info_struct_size), PNG_DEPRECATED);
      +PNG_EXPORTA(19, void, png_info_init_3,
      +   (png_infopp info_ptr, size_t png_info_struct_size),
      +   PNG_DEPRECATED);
       
       /* Writes all the PNG information before the image. */
       PNG_EXPORT(20, void, png_write_info_before_PLTE,
      -    (png_structrp png_ptr, png_const_inforp info_ptr));
      +   (png_structrp png_ptr, png_const_inforp info_ptr));
       PNG_EXPORT(21, void, png_write_info,
      -    (png_structrp png_ptr, png_const_inforp info_ptr));
      +   (png_structrp png_ptr, png_const_inforp info_ptr));
       
       #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
       /* Read the information before the actual image data. */
       PNG_EXPORT(22, void, png_read_info,
      -    (png_structrp png_ptr, png_inforp info_ptr));
      +   (png_structrp png_ptr, png_inforp info_ptr));
       #endif
       
       #ifdef PNG_TIME_RFC1123_SUPPORTED
      @@ -1072,45 +1090,54 @@ PNG_EXPORT(22, void, png_read_info,
           */
       #if PNG_LIBPNG_VER < 10700
       /* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */
      -PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr,
      -    png_const_timep ptime),PNG_DEPRECATED);
      +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123,
      +   (png_structrp png_ptr, png_const_timep ptime),
      +   PNG_DEPRECATED);
       #endif
      -PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29],
      -    png_const_timep ptime));
      +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer,
      +   (char out[29], png_const_timep ptime));
       #endif
       
       #ifdef PNG_CONVERT_tIME_SUPPORTED
       /* Convert from a struct tm to png_time */
      -PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime,
      -    const struct tm * ttime));
      +PNG_EXPORT(24, void, png_convert_from_struct_tm,
      +   (png_timep ptime, const struct tm * ttime));
       
       /* Convert from time_t to png_time.  Uses gmtime() */
      -PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime));
      +PNG_EXPORT(25, void, png_convert_from_time_t,
      +   (png_timep ptime, time_t ttime));
       #endif /* CONVERT_tIME */
       
       #ifdef PNG_READ_EXPAND_SUPPORTED
       /* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */
      -PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr));
      -PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr));
      -PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr));
      -PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr));
      +PNG_EXPORT(26, void, png_set_expand,
      +   (png_structrp png_ptr));
      +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8,
      +   (png_structrp png_ptr));
      +PNG_EXPORT(28, void, png_set_palette_to_rgb,
      +   (png_structrp png_ptr));
      +PNG_EXPORT(29, void, png_set_tRNS_to_alpha,
      +   (png_structrp png_ptr));
       #endif
       
       #ifdef PNG_READ_EXPAND_16_SUPPORTED
       /* Expand to 16-bit channels, forces conversion of palette to RGB and expansion
        * of a tRNS chunk if present.
        */
      -PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr));
      +PNG_EXPORT(221, void, png_set_expand_16,
      +   (png_structrp png_ptr));
       #endif
       
       #if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
       /* Use blue, green, red order for pixels. */
      -PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr));
      +PNG_EXPORT(30, void, png_set_bgr,
      +   (png_structrp png_ptr));
       #endif
       
       #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
       /* Expand the grayscale to 24-bit RGB if necessary. */
      -PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr));
      +PNG_EXPORT(31, void, png_set_gray_to_rgb,
      +   (png_structrp png_ptr));
       #endif
       
       #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
      @@ -1120,18 +1147,20 @@ PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr));
       #define PNG_ERROR_ACTION_ERROR 3
       #define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/
       
      -PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr,
      +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray,
      +   (png_structrp png_ptr,
           int error_action, double red, double green))
      -PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr,
      +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed,
      +   (png_structrp png_ptr,
           int error_action, png_fixed_point red, png_fixed_point green))
       
      -PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp
      -    png_ptr));
      +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status,
      +   (png_const_structrp png_ptr));
       #endif
       
       #ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
      -PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth,
      -    png_colorp palette));
      +PNG_EXPORT(35, void, png_build_grayscale_palette,
      +   (int bit_depth, png_colorp palette));
       #endif
       
       #ifdef PNG_READ_ALPHA_MODE_SUPPORTED
      @@ -1176,10 +1205,10 @@ PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth,
       #define PNG_ALPHA_OPTIMIZED     2 /* 'PNG' for opaque pixels, else 'STANDARD' */
       #define PNG_ALPHA_BROKEN        3 /* the alpha channel is gamma encoded */
       
      -PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode,
      -    double output_gamma))
      -PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr,
      -    int mode, png_fixed_point output_gamma))
      +PNG_FP_EXPORT(227, void, png_set_alpha_mode,
      +   (png_structrp png_ptr, int mode, double output_gamma))
      +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed,
      +   (png_structrp png_ptr, int mode, png_fixed_point output_gamma))
       #endif
       
       #if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED)
      @@ -1269,51 +1298,57 @@ PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr,
        */
       
       #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
      -PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr));
      +PNG_EXPORT(36, void, png_set_strip_alpha,
      +   (png_structrp png_ptr));
       #endif
       
       #if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
           defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
      -PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr));
      +PNG_EXPORT(37, void, png_set_swap_alpha,
      +   (png_structrp png_ptr));
       #endif
       
       #if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
           defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
      -PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr));
      +PNG_EXPORT(38, void, png_set_invert_alpha,
      +   (png_structrp png_ptr));
       #endif
       
       #if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
       /* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */
      -PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler,
      -    int flags));
      +PNG_EXPORT(39, void, png_set_filler,
      +   (png_structrp png_ptr, png_uint_32 filler, int flags));
       /* The values of the PNG_FILLER_ defines should NOT be changed */
       #  define PNG_FILLER_BEFORE 0
       #  define PNG_FILLER_AFTER 1
       /* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */
      -PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr,
      -    png_uint_32 filler, int flags));
      +PNG_EXPORT(40, void, png_set_add_alpha,
      +   (png_structrp png_ptr, png_uint_32 filler, int flags));
       #endif /* READ_FILLER || WRITE_FILLER */
       
       #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
       /* Swap bytes in 16-bit depth files. */
      -PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr));
      +PNG_EXPORT(41, void, png_set_swap,
      +   (png_structrp png_ptr));
       #endif
       
       #if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
       /* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */
      -PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr));
      +PNG_EXPORT(42, void, png_set_packing,
      +   (png_structrp png_ptr));
       #endif
       
       #if defined(PNG_READ_PACKSWAP_SUPPORTED) || \
           defined(PNG_WRITE_PACKSWAP_SUPPORTED)
       /* Swap packing order of pixels in bytes. */
      -PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr));
      +PNG_EXPORT(43, void, png_set_packswap,
      +   (png_structrp png_ptr));
       #endif
       
       #if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
       /* Converts files to legal bit depths. */
      -PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p
      -    true_bits));
      +PNG_EXPORT(44, void, png_set_shift,
      +   (png_structrp png_ptr, png_const_color_8p true_bits));
       #endif
       
       #if defined(PNG_READ_INTERLACING_SUPPORTED) || \
      @@ -1324,12 +1359,14 @@ PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p
        * necessary to call png_read_row or png_read_rows png_get_image_height
        * times for each pass.
       */
      -PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr));
      +PNG_EXPORT(45, int, png_set_interlace_handling,
      +   (png_structrp png_ptr));
       #endif
       
       #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
       /* Invert monochrome files */
      -PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr));
      +PNG_EXPORT(46, void, png_set_invert_mono,
      +   (png_structrp png_ptr));
       #endif
       
       #ifdef PNG_READ_BACKGROUND_SUPPORTED
      @@ -1338,10 +1375,12 @@ PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr));
        * read.  Doing so will result in unexpected behavior and possible warnings or
        * errors if the PNG file contains a bKGD chunk.
        */
      -PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr,
      +PNG_FP_EXPORT(47, void, png_set_background,
      +   (png_structrp png_ptr,
           png_const_color_16p background_color, int background_gamma_code,
           int need_expand, double background_gamma))
      -PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr,
      +PNG_FIXED_EXPORT(215, void, png_set_background_fixed,
      +   (png_structrp png_ptr,
           png_const_color_16p background_color, int background_gamma_code,
           int need_expand, png_fixed_point background_gamma))
       #endif
      @@ -1354,20 +1393,23 @@ PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr,
       
       #ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED
       /* Scale a 16-bit depth file down to 8-bit, accurately. */
      -PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr));
      +PNG_EXPORT(229, void, png_set_scale_16,
      +   (png_structrp png_ptr));
       #endif
       
       #ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
       #define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */
       /* Strip the second byte of information from a 16-bit depth file. */
      -PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr));
      +PNG_EXPORT(48, void, png_set_strip_16,
      +   (png_structrp png_ptr));
       #endif
       
       #ifdef PNG_READ_QUANTIZE_SUPPORTED
       /* Turn on quantizing, and reduce the palette to the number of colors
        * available.
        */
      -PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr,
      +PNG_EXPORT(49, void, png_set_quantize,
      +   (png_structrp png_ptr,
           png_colorp palette, int num_palette, int maximum_colors,
           png_const_uint_16p histogram, int full_quantize));
       #endif
      @@ -1389,82 +1431,92 @@ PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr,
        * API (floating point or fixed.)  Notice, however, that the 'file_gamma' value
        * is the inverse of a 'screen gamma' value.
        */
      -PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr,
      +PNG_FP_EXPORT(50, void, png_set_gamma,
      +   (png_structrp png_ptr,
           double screen_gamma, double override_file_gamma))
      -PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr,
      +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed,
      +   (png_structrp png_ptr,
           png_fixed_point screen_gamma, png_fixed_point override_file_gamma))
       #endif
       
       #ifdef PNG_WRITE_FLUSH_SUPPORTED
       /* Set how many lines between output flushes - 0 for no flushing */
      -PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows));
      +PNG_EXPORT(51, void, png_set_flush,
      +   (png_structrp png_ptr, int nrows));
       /* Flush the current PNG output buffer */
      -PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr));
      +PNG_EXPORT(52, void, png_write_flush,
      +   (png_structrp png_ptr));
       #endif
       
       /* Optional update palette with requested transformations */
      -PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr));
      +PNG_EXPORT(53, void, png_start_read_image,
      +   (png_structrp png_ptr));
       
       /* Optional call to update the users info structure */
      -PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr,
      -    png_inforp info_ptr));
      +PNG_EXPORT(54, void, png_read_update_info,
      +   (png_structrp png_ptr, png_inforp info_ptr));
       
       #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
       /* Read one or more rows of image data. */
      -PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row,
      +PNG_EXPORT(55, void, png_read_rows,
      +   (png_structrp png_ptr, png_bytepp row,
           png_bytepp display_row, png_uint_32 num_rows));
       #endif
       
       #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
       /* Read a row of data. */
      -PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row,
      -    png_bytep display_row));
      +PNG_EXPORT(56, void, png_read_row,
      +   (png_structrp png_ptr, png_bytep row, png_bytep display_row));
       #endif
       
       #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
       /* Read the whole image into memory at once. */
      -PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image));
      +PNG_EXPORT(57, void, png_read_image,
      +   (png_structrp png_ptr, png_bytepp image));
       #endif
       
       /* Write a row of image data */
      -PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr,
      -    png_const_bytep row));
      +PNG_EXPORT(58, void, png_write_row,
      +   (png_structrp png_ptr, png_const_bytep row));
       
       /* Write a few rows of image data: (*row) is not written; however, the type
        * is declared as writeable to maintain compatibility with previous versions
        * of libpng and to allow the 'display_row' array from read_rows to be passed
        * unchanged to write_rows.
        */
      -PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row,
      -    png_uint_32 num_rows));
      +PNG_EXPORT(59, void, png_write_rows,
      +   (png_structrp png_ptr, png_bytepp row, png_uint_32 num_rows));
       
       /* Write the image data */
      -PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image));
      +PNG_EXPORT(60, void, png_write_image,
      +   (png_structrp png_ptr, png_bytepp image));
       
       /* Write the end of the PNG file. */
      -PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr,
      -    png_inforp info_ptr));
      +PNG_EXPORT(61, void, png_write_end,
      +   (png_structrp png_ptr, png_inforp info_ptr));
       
       #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
       /* Read the end of the PNG file. */
      -PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr));
      +PNG_EXPORT(62, void, png_read_end,
      +   (png_structrp png_ptr, png_inforp info_ptr));
       #endif
       
       /* Free any memory associated with the png_info_struct */
      -PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr,
      -    png_infopp info_ptr_ptr));
      +PNG_EXPORT(63, void, png_destroy_info_struct,
      +   (png_const_structrp png_ptr, png_infopp info_ptr_ptr));
       
       /* Free any memory associated with the png_struct and the png_info_structs */
      -PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr,
      +PNG_EXPORT(64, void, png_destroy_read_struct,
      +   (png_structpp png_ptr_ptr,
           png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr));
       
       /* Free any memory associated with the png_struct and the png_info_structs */
      -PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr,
      -    png_infopp info_ptr_ptr));
      +PNG_EXPORT(65, void, png_destroy_write_struct,
      +   (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr));
       
       /* Set the libpng method of handling chunk CRC errors */
      -PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action,
      -    int ancil_action));
      +PNG_EXPORT(66, void, png_set_crc_action,
      +   (png_structrp png_ptr, int crit_action, int ancil_action));
       
       /* Values for png_set_crc_action() say how to handle CRC errors in
        * ancillary and critical chunks, and whether to use the data contained
      @@ -1494,8 +1546,8 @@ PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action,
       /* Set the filtering method(s) used by libpng.  Currently, the only valid
        * value for "method" is 0.
        */
      -PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method,
      -    int filters));
      +PNG_EXPORT(67, void, png_set_filter,
      +   (png_structrp png_ptr, int method, int filters));
       #endif /* WRITE */
       
       /* Flags for png_set_filter() to say which filters to use.  The flags
      @@ -1524,11 +1576,14 @@ PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method,
       
       #ifdef PNG_WRITE_SUPPORTED
       #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */
      -PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr,
      -    int heuristic_method, int num_weights, png_const_doublep filter_weights,
      +PNG_FP_EXPORT(68, void, png_set_filter_heuristics,
      +   (png_structrp png_ptr,
      +    int heuristic_method, int num_weights,
      +    png_const_doublep filter_weights,
           png_const_doublep filter_costs))
       PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed,
      -    (png_structrp png_ptr, int heuristic_method, int num_weights,
      +   (png_structrp png_ptr,
      +    int heuristic_method, int num_weights,
           png_const_fixed_point_p filter_weights,
           png_const_fixed_point_p filter_costs))
       #endif /* WRITE_WEIGHTED_FILTER */
      @@ -1547,44 +1602,44 @@ PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed,
        * these values may not correspond directly to the zlib compression levels.
        */
       #ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
      -PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr,
      -    int level));
      +PNG_EXPORT(69, void, png_set_compression_level,
      +   (png_structrp png_ptr, int level));
       
      -PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr,
      -    int mem_level));
      +PNG_EXPORT(70, void, png_set_compression_mem_level,
      +   (png_structrp png_ptr, int mem_level));
       
      -PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr,
      -    int strategy));
      +PNG_EXPORT(71, void, png_set_compression_strategy,
      +   (png_structrp png_ptr, int strategy));
       
       /* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
        * smaller value of window_bits if it can do so safely.
        */
      -PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr,
      -    int window_bits));
      +PNG_EXPORT(72, void, png_set_compression_window_bits,
      +   (png_structrp png_ptr, int window_bits));
       
      -PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr,
      -    int method));
      +PNG_EXPORT(73, void, png_set_compression_method,
      +   (png_structrp png_ptr, int method));
       #endif /* WRITE_CUSTOMIZE_COMPRESSION */
       
       #ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
       /* Also set zlib parameters for compressing non-IDAT chunks */
      -PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr,
      -    int level));
      +PNG_EXPORT(222, void, png_set_text_compression_level,
      +   (png_structrp png_ptr, int level));
       
      -PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr,
      -    int mem_level));
      +PNG_EXPORT(223, void, png_set_text_compression_mem_level,
      +   (png_structrp png_ptr, int mem_level));
       
      -PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr,
      -    int strategy));
      +PNG_EXPORT(224, void, png_set_text_compression_strategy,
      +   (png_structrp png_ptr, int strategy));
       
       /* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a
        * smaller value of window_bits if it can do so safely.
        */
       PNG_EXPORT(225, void, png_set_text_compression_window_bits,
      -    (png_structrp png_ptr, int window_bits));
      +   (png_structrp png_ptr, int window_bits));
       
      -PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr,
      -    int method));
      +PNG_EXPORT(226, void, png_set_text_compression_method,
      +   (png_structrp png_ptr, int method));
       #endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
       #endif /* WRITE */
       
      @@ -1599,7 +1654,8 @@ PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr,
       
       #ifdef PNG_STDIO_SUPPORTED
       /* Initialize the input/output for the PNG file to the default functions. */
      -PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, FILE *fp));
      +PNG_EXPORT(74, void, png_init_io,
      +   (png_structrp png_ptr, FILE *fp));
       #endif
       
       /* Replace the (error and abort), and warning functions with user
      @@ -1610,11 +1666,13 @@ PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, FILE *fp));
        * default function will be used.
        */
       
      -PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr,
      +PNG_EXPORT(75, void, png_set_error_fn,
      +   (png_structrp png_ptr,
           png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn));
       
       /* Return the user pointer associated with the error functions */
      -PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr));
      +PNG_EXPORT(76, png_voidp, png_get_error_ptr,
      +   (png_const_structrp png_ptr));
       
       /* Replace the default data output functions with a user supplied one(s).
        * If buffered output is not used, then output_flush_fn can be set to NULL.
      @@ -1626,47 +1684,54 @@ PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr));
        * default flush function, which uses the standard *FILE structure, will
        * be used.
        */
      -PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr,
      +PNG_EXPORT(77, void, png_set_write_fn,
      +   (png_structrp png_ptr,
      +    png_voidp io_ptr,
           png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn));
       
       /* Replace the default data input function with a user supplied one. */
      -PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr,
      -    png_rw_ptr read_data_fn));
      +PNG_EXPORT(78, void, png_set_read_fn,
      +   (png_structrp png_ptr,
      +    png_voidp io_ptr, png_rw_ptr read_data_fn));
       
       /* Return the user pointer associated with the I/O functions */
      -PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr));
      +PNG_EXPORT(79, png_voidp, png_get_io_ptr,
      +   (png_const_structrp png_ptr));
       
      -PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr,
      -    png_read_status_ptr read_row_fn));
      +PNG_EXPORT(80, void, png_set_read_status_fn,
      +   (png_structrp png_ptr, png_read_status_ptr read_row_fn));
       
      -PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr,
      -    png_write_status_ptr write_row_fn));
      +PNG_EXPORT(81, void, png_set_write_status_fn,
      +   (png_structrp png_ptr, png_write_status_ptr write_row_fn));
       
       #ifdef PNG_USER_MEM_SUPPORTED
       /* Replace the default memory allocation functions with user supplied one(s). */
      -PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr,
      -    png_malloc_ptr malloc_fn, png_free_ptr free_fn));
      +PNG_EXPORT(82, void, png_set_mem_fn,
      +   (png_structrp png_ptr,
      +    png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn));
       /* Return the user pointer associated with the memory functions */
      -PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr));
      +PNG_EXPORT(83, png_voidp, png_get_mem_ptr,
      +   (png_const_structrp png_ptr));
       #endif
       
       #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
      -PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr,
      -    png_user_transform_ptr read_user_transform_fn));
      +PNG_EXPORT(84, void, png_set_read_user_transform_fn,
      +   (png_structrp png_ptr, png_user_transform_ptr read_user_transform_fn));
       #endif
       
       #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
      -PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr,
      -    png_user_transform_ptr write_user_transform_fn));
      +PNG_EXPORT(85, void, png_set_write_user_transform_fn,
      +   (png_structrp png_ptr, png_user_transform_ptr write_user_transform_fn));
       #endif
       
       #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
      -PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr,
      -    png_voidp user_transform_ptr, int user_transform_depth,
      -    int user_transform_channels));
      +PNG_EXPORT(86, void, png_set_user_transform_info,
      +   (png_structrp png_ptr,
      +    png_voidp user_transform_ptr,
      +    int user_transform_depth, int user_transform_channels));
       /* Return the user pointer associated with the user transform functions */
       PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr,
      -    (png_const_structrp png_ptr));
      +   (png_const_structrp png_ptr));
       #endif
       
       #ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
      @@ -1681,8 +1746,10 @@ PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr,
        * find the output pixel (x,y) given an interlaced sub-image pixel
        * (row,col,pass).  (See below for these macros.)
        */
      -PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp));
      -PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp));
      +PNG_EXPORT(217, png_uint_32, png_get_current_row_number,
      +   (png_const_structrp));
      +PNG_EXPORT(218, png_byte, png_get_current_pass_number,
      +   (png_const_structrp));
       #endif
       
       #ifdef PNG_READ_USER_CHUNKS_SUPPORTED
      @@ -1705,28 +1772,32 @@ PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp));
        * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about
        * how this behavior will change in libpng 1.7
        */
      -PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr,
      +PNG_EXPORT(88, void, png_set_read_user_chunk_fn,
      +   (png_structrp png_ptr,
           png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn));
       #endif
       
       #ifdef PNG_USER_CHUNKS_SUPPORTED
      -PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr));
      +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr,
      +   (png_const_structrp png_ptr));
       #endif
       
       #ifdef PNG_PROGRESSIVE_READ_SUPPORTED
       /* Sets the function callbacks for the push reader, and a pointer to a
        * user-defined structure available to the callback functions.
        */
      -PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr,
      +PNG_EXPORT(90, void, png_set_progressive_read_fn,
      +   (png_structrp png_ptr,
           png_voidp progressive_ptr, png_progressive_info_ptr info_fn,
           png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn));
       
       /* Returns the user pointer associated with the push read functions */
       PNG_EXPORT(91, png_voidp, png_get_progressive_ptr,
      -    (png_const_structrp png_ptr));
      +   (png_const_structrp png_ptr));
       
       /* Function to be called when data becomes available */
      -PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr,
      +PNG_EXPORT(92, void, png_process_data,
      +   (png_structrp png_ptr,
           png_inforp info_ptr, png_bytep buffer, size_t buffer_size));
       
       /* A function which may be called *only* within png_process_data to stop the
      @@ -1736,7 +1807,8 @@ PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr,
        * 'save' is set to true the routine will first save all the pending data and
        * will always return 0.
        */
      -PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save));
      +PNG_EXPORT(219, size_t, png_process_data_pause,
      +   (png_structrp, int save));
       
       /* A function which may be called *only* outside (after) a call to
        * png_process_data.  It returns the number of bytes of data to skip in the
      @@ -1744,45 +1816,53 @@ PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save));
        * application must skip than number of bytes of input data and pass the
        * following data to the next call to png_process_data.
        */
      -PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp));
      +PNG_EXPORT(220, png_uint_32, png_process_data_skip,
      +   (png_structrp));
       
       /* Function that combines rows.  'new_row' is a flag that should come from
        * the callback and be non-NULL if anything needs to be done; the library
        * stores its own version of the new data internally and ignores the passed
        * in value.
        */
      -PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr,
      +PNG_EXPORT(93, void, png_progressive_combine_row,
      +   (png_const_structrp png_ptr,
           png_bytep old_row, png_const_bytep new_row));
       #endif /* PROGRESSIVE_READ */
       
      -PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr,
      -    png_alloc_size_t size), PNG_ALLOCATED);
      +PNG_EXPORTA(94, png_voidp, png_malloc,
      +   (png_const_structrp png_ptr, png_alloc_size_t size),
      +   PNG_ALLOCATED);
       /* Added at libpng version 1.4.0 */
      -PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr,
      -    png_alloc_size_t size), PNG_ALLOCATED);
      +PNG_EXPORTA(95, png_voidp, png_calloc,
      +   (png_const_structrp png_ptr, png_alloc_size_t size),
      +   PNG_ALLOCATED);
       
       /* Added at libpng version 1.2.4 */
      -PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr,
      -    png_alloc_size_t size), PNG_ALLOCATED);
      +PNG_EXPORTA(96, png_voidp, png_malloc_warn,
      +   (png_const_structrp png_ptr, png_alloc_size_t size),
      +   PNG_ALLOCATED);
       
       /* Frees a pointer allocated by png_malloc() */
      -PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr));
      +PNG_EXPORT(97, void, png_free,
      +   (png_const_structrp png_ptr, png_voidp ptr));
       
       /* Free data that was allocated internally */
      -PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_uint_32 free_me, int num));
      +PNG_EXPORT(98, void, png_free_data,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_uint_32 free_me, int num));
       
       /* Reassign the responsibility for freeing existing data, whether allocated
        * by libpng or by the application; this works on the png_info structure passed
        * in, without changing the state for other png_info structures.
        */
      -PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, int freer, png_uint_32 mask));
      +PNG_EXPORT(99, void, png_data_freer,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    int freer, png_uint_32 mask));
       
       /* Assignments for png_data_freer */
       #define PNG_DESTROY_WILL_FREE_DATA 1
      -#define PNG_SET_WILL_FREE_DATA 1
      -#define PNG_USER_WILL_FREE_DATA 2
      +#define PNG_SET_WILL_FREE_DATA     1
      +#define PNG_USER_WILL_FREE_DATA    2
       /* Flags for png_ptr->free_me and info_ptr->free_me */
       #define PNG_FREE_HIST 0x0008U
       #define PNG_FREE_ICCP 0x0010U
      @@ -1802,36 +1882,42 @@ PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr,
       #define PNG_FREE_MUL  0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */
       
       #ifdef PNG_USER_MEM_SUPPORTED
      -PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr,
      -    png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED);
      -PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr,
      -    png_voidp ptr), PNG_DEPRECATED);
      +PNG_EXPORTA(100, png_voidp, png_malloc_default,
      +   (png_const_structrp png_ptr, png_alloc_size_t size),
      +   PNG_ALLOCATED PNG_DEPRECATED);
      +PNG_EXPORTA(101, void, png_free_default,
      +   (png_const_structrp png_ptr, png_voidp ptr),
      +   PNG_DEPRECATED);
       #endif
       
       #ifdef PNG_ERROR_TEXT_SUPPORTED
       /* Fatal error in PNG image of libpng - can't continue */
      -PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr,
      -    png_const_charp error_message), PNG_NORETURN);
      +PNG_EXPORTA(102, void, png_error,
      +   (png_const_structrp png_ptr, png_const_charp error_message),
      +   PNG_NORETURN);
       
       /* The same, but the chunk name is prepended to the error string. */
      -PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr,
      -    png_const_charp error_message), PNG_NORETURN);
      +PNG_EXPORTA(103, void, png_chunk_error,
      +   (png_const_structrp png_ptr, png_const_charp error_message),
      +   PNG_NORETURN);
       
       #else
       /* Fatal error in PNG image of libpng - can't continue */
      -PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN);
      +PNG_EXPORTA(104, void, png_err,
      +   (png_const_structrp png_ptr),
      +   PNG_NORETURN);
       #  define png_error(s1,s2) png_err(s1)
       #  define png_chunk_error(s1,s2) png_err(s1)
       #endif
       
       #ifdef PNG_WARNINGS_SUPPORTED
       /* Non-fatal error in libpng.  Can continue, but may have a problem. */
      -PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr,
      -    png_const_charp warning_message));
      +PNG_EXPORT(105, void, png_warning,
      +   (png_const_structrp png_ptr, png_const_charp warning_message));
       
       /* Non-fatal error in libpng, chunk name is prepended to message. */
      -PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr,
      -    png_const_charp warning_message));
      +PNG_EXPORT(106, void, png_chunk_warning,
      +   (png_const_structrp png_ptr, png_const_charp warning_message));
       #else
       #  define png_warning(s1,s2) ((void)(s1))
       #  define png_chunk_warning(s1,s2) ((void)(s1))
      @@ -1840,17 +1926,17 @@ PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr,
       #ifdef PNG_BENIGN_ERRORS_SUPPORTED
       /* Benign error in libpng.  Can continue, but may have a problem.
        * User can choose whether to handle as a fatal error or as a warning. */
      -PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr,
      -    png_const_charp warning_message));
      +PNG_EXPORT(107, void, png_benign_error,
      +   (png_const_structrp png_ptr, png_const_charp warning_message));
       
       #ifdef PNG_READ_SUPPORTED
       /* Same, chunk name is prepended to message (only during read) */
      -PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr,
      -    png_const_charp warning_message));
      +PNG_EXPORT(108, void, png_chunk_benign_error,
      +   (png_const_structrp png_ptr, png_const_charp warning_message));
       #endif
       
       PNG_EXPORT(109, void, png_set_benign_errors,
      -    (png_structrp png_ptr, int allowed));
      +   (png_structrp png_ptr, int allowed));
       #else
       #  ifdef PNG_ALLOW_BENIGN_ERRORS
       #    define png_benign_error png_warning
      @@ -1874,169 +1960,181 @@ PNG_EXPORT(109, void, png_set_benign_errors,
        * png_info_struct.
        */
       /* Returns "flag" if chunk data is valid in info_ptr. */
      -PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr, png_uint_32 flag));
      +PNG_EXPORT(110, png_uint_32, png_get_valid,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 flag));
       
       /* Returns number of bytes needed to hold a transformed row. */
      -PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr));
      +PNG_EXPORT(111, size_t, png_get_rowbytes,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       #ifdef PNG_INFO_IMAGE_SUPPORTED
       /* Returns row_pointers, which is an array of pointers to scanlines that was
        * returned from png_read_png().
        */
      -PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr));
      +PNG_EXPORT(112, png_bytepp, png_get_rows,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       /* Set row_pointers, which is an array of pointers to scanlines for use
        * by png_write_png().
        */
      -PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_bytepp row_pointers));
      +PNG_EXPORT(113, void, png_set_rows,
      +   (png_const_structrp png_ptr, png_inforp info_ptr, png_bytepp row_pointers));
       #endif
       
       /* Returns number of color channels in image. */
      -PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr));
      +PNG_EXPORT(114, png_byte, png_get_channels,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       #ifdef PNG_EASY_ACCESS_SUPPORTED
       /* Returns image width in pixels. */
      -PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr));
      +PNG_EXPORT(115, png_uint_32, png_get_image_width,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       /* Returns image height in pixels. */
      -PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr));
      +PNG_EXPORT(116, png_uint_32, png_get_image_height,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       /* Returns image bit_depth. */
      -PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr));
      +PNG_EXPORT(117, png_byte, png_get_bit_depth,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       /* Returns image color_type. */
      -PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr));
      +PNG_EXPORT(118, png_byte, png_get_color_type,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       /* Returns image filter_type. */
      -PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr));
      +PNG_EXPORT(119, png_byte, png_get_filter_type,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       /* Returns image interlace_type. */
      -PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr));
      +PNG_EXPORT(120, png_byte, png_get_interlace_type,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       /* Returns image compression_type. */
      -PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr));
      +PNG_EXPORT(121, png_byte, png_get_compression_type,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       /* Returns image resolution in pixels per meter, from pHYs chunk data. */
       PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr));
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr));
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr));
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       /* Returns pixel aspect ratio, computed from pHYs chunk data.  */
       PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr))
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr))
       PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr))
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr))
       
       /* Returns image x, y offset in pixels or microns, from oFFs chunk data. */
       PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr));
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr));
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       PNG_EXPORT(128, png_int_32, png_get_x_offset_microns,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr));
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       PNG_EXPORT(129, png_int_32, png_get_y_offset_microns,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr));
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       #endif /* EASY_ACCESS */
       
       #ifdef PNG_READ_SUPPORTED
       /* Returns pointer to signature string read from PNG header */
      -PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr));
      +PNG_EXPORT(130, png_const_bytep, png_get_signature,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       #endif
       
       #ifdef PNG_bKGD_SUPPORTED
      -PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_color_16p *background));
      +PNG_EXPORT(131, png_uint_32, png_get_bKGD,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_color_16p *background));
       #endif
       
       #ifdef PNG_bKGD_SUPPORTED
      -PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_const_color_16p background));
      +PNG_EXPORT(132, void, png_set_bKGD,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_const_color_16p background));
       #endif
       
       #ifdef PNG_cHRM_SUPPORTED
      -PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x,
      -    double *red_y, double *green_x, double *green_y, double *blue_x,
      -    double *blue_y))
      -PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z,
      -    double *green_X, double *green_Y, double *green_Z, double *blue_X,
      -    double *blue_Y, double *blue_Z))
      +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    double *white_x, double *white_y,
      +    double *red_x, double *red_y,
      +    double *green_x, double *green_y,
      +    double *blue_x, double *blue_y))
      +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    double *red_X, double *red_Y, double *red_Z,
      +    double *green_X, double *green_Y, double *green_Z,
      +    double *blue_X, double *blue_Y, double *blue_Z))
       PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
           png_fixed_point *int_white_x, png_fixed_point *int_white_y,
           png_fixed_point *int_red_x, png_fixed_point *int_red_y,
           png_fixed_point *int_green_x, png_fixed_point *int_green_y,
           png_fixed_point *int_blue_x, png_fixed_point *int_blue_y))
       PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
           png_fixed_point *int_red_X, png_fixed_point *int_red_Y,
      -    png_fixed_point *int_red_Z, png_fixed_point *int_green_X,
      -    png_fixed_point *int_green_Y, png_fixed_point *int_green_Z,
      +    png_fixed_point *int_red_Z,
      +    png_fixed_point *int_green_X, png_fixed_point *int_green_Y,
      +    png_fixed_point *int_green_Z,
           png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y,
           png_fixed_point *int_blue_Z))
       #endif
       
       #ifdef PNG_cHRM_SUPPORTED
      -PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr,
      -    png_inforp info_ptr,
      -    double white_x, double white_y, double red_x, double red_y, double green_x,
      -    double green_y, double blue_x, double blue_y))
      -PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, double red_X, double red_Y, double red_Z,
      -    double green_X, double green_Y, double green_Z, double blue_X,
      -    double blue_Y, double blue_Z))
      -PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_fixed_point int_white_x,
      -    png_fixed_point int_white_y, png_fixed_point int_red_x,
      -    png_fixed_point int_red_y, png_fixed_point int_green_x,
      -    png_fixed_point int_green_y, png_fixed_point int_blue_x,
      -    png_fixed_point int_blue_y))
      -PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y,
      -    png_fixed_point int_red_Z, png_fixed_point int_green_X,
      -    png_fixed_point int_green_Y, png_fixed_point int_green_Z,
      +PNG_FP_EXPORT(135, void, png_set_cHRM,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    double white_x, double white_y,
      +    double red_x, double red_y,
      +    double green_x, double green_y,
      +    double blue_x, double blue_y))
      +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    double red_X, double red_Y, double red_Z,
      +    double green_X, double green_Y, double green_Z,
      +    double blue_X, double blue_Y, double blue_Z))
      +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_fixed_point int_white_x, png_fixed_point int_white_y,
      +    png_fixed_point int_red_x, png_fixed_point int_red_y,
      +    png_fixed_point int_green_x, png_fixed_point int_green_y,
      +    png_fixed_point int_blue_x, png_fixed_point int_blue_y))
      +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_fixed_point int_red_X, png_fixed_point int_red_Y,
      +    png_fixed_point int_red_Z,
      +    png_fixed_point int_green_X, png_fixed_point int_green_Y,
      +    png_fixed_point int_green_Z,
           png_fixed_point int_blue_X, png_fixed_point int_blue_Y,
           png_fixed_point int_blue_Z))
       #endif
       
       #ifdef PNG_cICP_SUPPORTED
      -PNG_EXPORT(250, png_uint_32, png_get_cICP, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr, png_bytep colour_primaries,
      -    png_bytep transfer_function, png_bytep matrix_coefficients,
      -    png_bytep video_full_range_flag));
      +PNG_EXPORT(250, png_uint_32, png_get_cICP,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    png_bytep colour_primaries, png_bytep transfer_function,
      +    png_bytep matrix_coefficients, png_bytep video_full_range_flag));
       #endif
       
       #ifdef PNG_cICP_SUPPORTED
      -PNG_EXPORT(251, void, png_set_cICP, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_byte colour_primaries,
      -    png_byte transfer_function, png_byte matrix_coefficients,
      -    png_byte video_full_range_flag));
      +PNG_EXPORT(251, void, png_set_cICP,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_byte colour_primaries, png_byte transfer_function,
      +    png_byte matrix_coefficients, png_byte video_full_range_flag));
       #endif
       
       #ifdef PNG_cLLI_SUPPORTED
      -PNG_FP_EXPORT(252, png_uint_32, png_get_cLLI, (png_const_structrp png_ptr,
      -         png_const_inforp info_ptr, double *maximum_content_light_level,
      -         double *maximum_frame_average_light_level))
      +PNG_FP_EXPORT(252, png_uint_32, png_get_cLLI,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    double *maximum_content_light_level,
      +    double *maximum_frame_average_light_level))
       PNG_FIXED_EXPORT(253, png_uint_32, png_get_cLLI_fixed,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
           /* The values below are in cd/m2 (nits) and are scaled by 10,000; not
            * 100,000 as in the case of png_fixed_point.
            */
      @@ -2045,11 +2143,12 @@ PNG_FIXED_EXPORT(253, png_uint_32, png_get_cLLI_fixed,
       #endif
       
       #ifdef PNG_cLLI_SUPPORTED
      -PNG_FP_EXPORT(254, void, png_set_cLLI, (png_const_structrp png_ptr,
      -         png_inforp info_ptr, double maximum_content_light_level,
      -         double maximum_frame_average_light_level))
      -PNG_FIXED_EXPORT(255, void, png_set_cLLI_fixed, (png_const_structrp png_ptr,
      -    png_inforp info_ptr,
      +PNG_FP_EXPORT(254, void, png_set_cLLI,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    double maximum_content_light_level,
      +    double maximum_frame_average_light_level))
      +PNG_FIXED_EXPORT(255, void, png_set_cLLI_fixed,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
           /* The values below are in cd/m2 (nits) and are scaled by 10,000; not
            * 100,000 as in the case of png_fixed_point.
            */
      @@ -2058,64 +2157,73 @@ PNG_FIXED_EXPORT(255, void, png_set_cLLI_fixed, (png_const_structrp png_ptr,
       #endif
       
       #ifdef PNG_eXIf_SUPPORTED
      -PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_bytep *exif));
      -PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_bytep exif));
      +PNG_EXPORT(246, png_uint_32, png_get_eXIf,
      +   (png_const_structrp png_ptr, png_inforp info_ptr, png_bytep *exif));
      +PNG_EXPORT(247, void, png_set_eXIf,
      +   (png_const_structrp png_ptr, png_inforp info_ptr, png_bytep exif));
       
      -PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif));
      -PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif));
      +PNG_EXPORT(248, png_uint_32, png_get_eXIf_1,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    png_uint_32 *num_exif, png_bytep *exif));
      +PNG_EXPORT(249, void, png_set_eXIf_1,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_uint_32 num_exif, png_bytep exif));
       #endif
       
       #ifdef PNG_gAMA_SUPPORTED
      -PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr, double *file_gamma))
      +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    double *file_gamma))
       PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
           png_fixed_point *int_file_gamma))
       #endif
       
       #ifdef PNG_gAMA_SUPPORTED
      -PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, double file_gamma))
      -PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_fixed_point int_file_gamma))
      +PNG_FP_EXPORT(139, void, png_set_gAMA,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    double file_gamma))
      +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_fixed_point int_file_gamma))
       #endif
       
       #ifdef PNG_hIST_SUPPORTED
      -PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_uint_16p *hist));
      -PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_const_uint_16p hist));
      +PNG_EXPORT(141, png_uint_32, png_get_hIST,
      +   (png_const_structrp png_ptr, png_inforp info_ptr, png_uint_16p *hist));
      +PNG_EXPORT(142, void, png_set_hIST,
      +   (png_const_structrp png_ptr, png_inforp info_ptr, png_const_uint_16p hist));
       #endif
       
      -PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height,
      -    int *bit_depth, int *color_type, int *interlace_method,
      -    int *compression_method, int *filter_method));
      +PNG_EXPORT(143, png_uint_32, png_get_IHDR,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    png_uint_32 *width, png_uint_32 *height,
      +    int *bit_depth, int *color_type,
      +    int *interlace_method, int *compression_method, int *filter_method));
       
      -PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth,
      -    int color_type, int interlace_method, int compression_method,
      -    int filter_method));
      +PNG_EXPORT(144, void, png_set_IHDR,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_uint_32 width, png_uint_32 height,
      +    int bit_depth, int color_type,
      +    int interlace_method, int compression_method, int filter_method));
       
       #ifdef PNG_mDCV_SUPPORTED
      -PNG_FP_EXPORT(256, png_uint_32, png_get_mDCV, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr,
      +PNG_FP_EXPORT(256, png_uint_32, png_get_mDCV,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
           /* The chromaticities of the mastering display.  As cHRM, but independent of
            * the encoding endpoints in cHRM, or cICP, or iCCP.  These values will
            * always be in the range 0 to 1.3107.
            */
      -    double *white_x, double *white_y, double *red_x, double *red_y,
      -    double *green_x, double *green_y, double *blue_x, double *blue_y,
      +    double *white_x, double *white_y,
      +    double *red_x, double *red_y,
      +    double *green_x, double *green_y,
      +    double *blue_x, double *blue_y,
           /* Mastering display luminance in cd/m2 (nits). */
           double *mastering_display_maximum_luminance,
           double *mastering_display_minimum_luminance))
       
       PNG_FIXED_EXPORT(257, png_uint_32, png_get_mDCV_fixed,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
           png_fixed_point *int_white_x, png_fixed_point *int_white_y,
           png_fixed_point *int_red_x, png_fixed_point *int_red_y,
           png_fixed_point *int_green_x, png_fixed_point *int_green_y,
      @@ -2128,19 +2236,21 @@ PNG_FIXED_EXPORT(257, png_uint_32, png_get_mDCV_fixed,
       #endif
       
       #ifdef PNG_mDCV_SUPPORTED
      -PNG_FP_EXPORT(258, void, png_set_mDCV, (png_const_structrp png_ptr,
      -    png_inforp info_ptr,
      +PNG_FP_EXPORT(258, void, png_set_mDCV,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
           /* The chromaticities of the mastering display.  As cHRM, but independent of
            * the encoding endpoints in cHRM, or cICP, or iCCP.
            */
      -    double white_x, double white_y, double red_x, double red_y, double green_x,
      -    double green_y, double blue_x, double blue_y,
      +    double white_x, double white_y,
      +    double red_x, double red_y,
      +    double green_x, double green_y,
      +    double blue_x, double blue_y,
           /* Mastering display luminance in cd/m2 (nits). */
           double mastering_display_maximum_luminance,
           double mastering_display_minimum_luminance))
       
      -PNG_FIXED_EXPORT(259, void, png_set_mDCV_fixed, (png_const_structrp png_ptr,
      -    png_inforp info_ptr,
      +PNG_FIXED_EXPORT(259, void, png_set_mDCV_fixed,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
           /* The admissible range of these values is not the full range of a PNG
            * fixed point value.  Negative values cannot be encoded and the maximum
            * value is about 1.3 */
      @@ -2156,95 +2266,107 @@ PNG_FIXED_EXPORT(259, void, png_set_mDCV_fixed, (png_const_structrp png_ptr,
       #endif
       
       #ifdef PNG_oFFs_SUPPORTED
      -PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr,
      -   png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y,
      -   int *unit_type));
      +PNG_EXPORT(145, png_uint_32, png_get_oFFs,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type));
       #endif
       
       #ifdef PNG_oFFs_SUPPORTED
      -PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y,
      -    int unit_type));
      +PNG_EXPORT(146, void, png_set_oFFs,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_int_32 offset_x, png_int_32 offset_y, int unit_type));
       #endif
       
       #ifdef PNG_pCAL_SUPPORTED
      -PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_charp *purpose, png_int_32 *X0,
      -    png_int_32 *X1, int *type, int *nparams, png_charp *units,
      -    png_charpp *params));
      +PNG_EXPORT(147, png_uint_32, png_get_pCAL,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_charp *purpose, png_int_32 *X0, png_int_32 *X1,
      +    int *type, int *nparams, png_charp *units, png_charpp *params));
       #endif
       
       #ifdef PNG_pCAL_SUPPORTED
      -PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1,
      +PNG_EXPORT(148, void, png_set_pCAL,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_const_charp purpose, png_int_32 X0, png_int_32 X1,
           int type, int nparams, png_const_charp units, png_charpp params));
       #endif
       
       #ifdef PNG_pHYs_SUPPORTED
      -PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y,
      -    int *unit_type));
      +PNG_EXPORT(149, png_uint_32, png_get_pHYs,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type));
       #endif
       
       #ifdef PNG_pHYs_SUPPORTED
      -PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type));
      +PNG_EXPORT(150, void, png_set_pHYs,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_uint_32 res_x, png_uint_32 res_y, int unit_type));
       #endif
       
      -PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr,
      -   png_inforp info_ptr, png_colorp *palette, int *num_palette));
      +PNG_EXPORT(151, png_uint_32, png_get_PLTE,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_colorp *palette, int *num_palette));
       
      -PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr,
      -    png_inforp info_ptr, png_const_colorp palette, int num_palette));
      +PNG_EXPORT(152, void, png_set_PLTE,
      +   (png_structrp png_ptr, png_inforp info_ptr,
      +    png_const_colorp palette, int num_palette));
       
       #ifdef PNG_sBIT_SUPPORTED
      -PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_color_8p *sig_bit));
      +PNG_EXPORT(153, png_uint_32, png_get_sBIT,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_color_8p *sig_bit));
       #endif
       
       #ifdef PNG_sBIT_SUPPORTED
      -PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_const_color_8p sig_bit));
      +PNG_EXPORT(154, void, png_set_sBIT,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_const_color_8p sig_bit));
       #endif
       
       #ifdef PNG_sRGB_SUPPORTED
      -PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr, int *file_srgb_intent));
      +PNG_EXPORT(155, png_uint_32, png_get_sRGB,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    int *file_srgb_intent));
       #endif
       
       #ifdef PNG_sRGB_SUPPORTED
      -PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, int srgb_intent));
      -PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, int srgb_intent));
      +PNG_EXPORT(156, void, png_set_sRGB,
      +   (png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent));
      +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM,
      +   (png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent));
       #endif
       
       #ifdef PNG_iCCP_SUPPORTED
      -PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_charpp name, int *compression_type,
      +PNG_EXPORT(158, png_uint_32, png_get_iCCP,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_charpp name, int *compression_type,
           png_bytepp profile, png_uint_32 *proflen));
       #endif
       
       #ifdef PNG_iCCP_SUPPORTED
      -PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_const_charp name, int compression_type,
      +PNG_EXPORT(159, void, png_set_iCCP,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_const_charp name, int compression_type,
           png_const_bytep profile, png_uint_32 proflen));
       #endif
       
       #ifdef PNG_sPLT_SUPPORTED
      -PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_sPLT_tpp entries));
      +PNG_EXPORT(160, int, png_get_sPLT,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_sPLT_tpp entries));
       #endif
       
       #ifdef PNG_sPLT_SUPPORTED
      -PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_const_sPLT_tp entries, int nentries));
      +PNG_EXPORT(161, void, png_set_sPLT,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_const_sPLT_tp entries, int nentries));
       #endif
       
       #ifdef PNG_TEXT_SUPPORTED
       /* png_get_text also returns the number of text chunks in *num_text */
      -PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_textp *text_ptr, int *num_text));
      +PNG_EXPORT(162, int, png_get_text,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_textp *text_ptr, int *num_text));
       #endif
       
       /* Note while png_set_text() will accept a structure whose text,
      @@ -2255,35 +2377,41 @@ PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr,
        */
       
       #ifdef PNG_TEXT_SUPPORTED
      -PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_const_textp text_ptr, int num_text));
      +PNG_EXPORT(163, void, png_set_text,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_const_textp text_ptr, int num_text));
       #endif
       
       #ifdef PNG_tIME_SUPPORTED
      -PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_timep *mod_time));
      +PNG_EXPORT(164, png_uint_32, png_get_tIME,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_timep *mod_time));
       #endif
       
       #ifdef PNG_tIME_SUPPORTED
      -PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_const_timep mod_time));
      +PNG_EXPORT(165, void, png_set_tIME,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_const_timep mod_time));
       #endif
       
       #ifdef PNG_tRNS_SUPPORTED
      -PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans,
      +PNG_EXPORT(166, png_uint_32, png_get_tRNS,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_bytep *trans_alpha, int *num_trans,
           png_color_16p *trans_color));
       #endif
       
       #ifdef PNG_tRNS_SUPPORTED
      -PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr,
      -    png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans,
      +PNG_EXPORT(167, void, png_set_tRNS,
      +   (png_structrp png_ptr, png_inforp info_ptr,
      +    png_const_bytep trans_alpha, int num_trans,
           png_const_color_16p trans_color));
       #endif
       
       #ifdef PNG_sCAL_SUPPORTED
      -PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr, int *unit, double *width, double *height))
      +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    int *unit, double *width, double *height))
       #if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \
          defined(PNG_FLOATING_POINT_SUPPORTED)
       /* NOTE: this API is currently implemented using floating point arithmetic,
      @@ -2292,21 +2420,22 @@ PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr,
        * is highly recommended that png_get_sCAL_s be used instead.
        */
       PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit,
      -    png_fixed_point *width, png_fixed_point *height))
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    int *unit, png_fixed_point *width, png_fixed_point *height))
       #endif
       PNG_EXPORT(169, png_uint_32, png_get_sCAL_s,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit,
      -    png_charpp swidth, png_charpp sheight));
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    int *unit, png_charpp swidth, png_charpp sheight));
       
      -PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, int unit, double width, double height))
      -PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr,
      -   png_inforp info_ptr, int unit, png_fixed_point width,
      -   png_fixed_point height))
      -PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, int unit,
      -    png_const_charp swidth, png_const_charp sheight));
      +PNG_FP_EXPORT(170, void, png_set_sCAL,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    int unit, double width, double height))
      +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    int unit, png_fixed_point width, png_fixed_point height))
      +PNG_EXPORT(171, void, png_set_sCAL_s,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    int unit, png_const_charp swidth, png_const_charp sheight));
       #endif /* sCAL */
       
       #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
      @@ -2409,7 +2538,8 @@ PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr,
        *    be processed by libpng.
        */
       #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
      -PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr,
      +PNG_EXPORT(172, void, png_set_keep_unknown_chunks,
      +   (png_structrp png_ptr,
           int keep, png_const_bytep chunk_list, int num_chunks));
       #endif /* HANDLE_AS_UNKNOWN */
       
      @@ -2417,14 +2547,14 @@ PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr,
        * the result is therefore true (non-zero) if special handling is required,
        * false for the default handling.
        */
      -PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr,
      -    png_const_bytep chunk_name));
      +PNG_EXPORT(173, int, png_handle_as_unknown,
      +   (png_const_structrp png_ptr, png_const_bytep chunk_name));
       #endif /* SET_UNKNOWN_CHUNKS */
       
       #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
      -PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_const_unknown_chunkp unknowns,
      -    int num_unknowns));
      +PNG_EXPORT(174, void, png_set_unknown_chunks,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_const_unknown_chunkp unknowns, int num_unknowns));
          /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added
           * unknowns to the location currently stored in the png_struct.  This is
           * invariably the wrong value on write.  To fix this call the following API
      @@ -2435,43 +2565,47 @@ PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr,
           */
       
       PNG_EXPORT(175, void, png_set_unknown_chunk_location,
      -    (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location));
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    int chunk, int location));
       
      -PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_unknown_chunkpp entries));
      +PNG_EXPORT(176, int, png_get_unknown_chunks,
      +   (png_const_structrp png_ptr, png_inforp info_ptr,
      +    png_unknown_chunkpp entries));
       #endif
       
       /* Png_free_data() will turn off the "valid" flag for anything it frees.
        * If you need to turn it off for a chunk that your application has freed,
        * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK);
        */
      -PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr,
      -    png_inforp info_ptr, int mask));
      +PNG_EXPORT(177, void, png_set_invalid,
      +   (png_const_structrp png_ptr, png_inforp info_ptr, int mask));
       
       #ifdef PNG_INFO_IMAGE_SUPPORTED
       /* The "params" pointer is currently not used and is for future expansion. */
       #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
      -PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr,
      +PNG_EXPORT(178, void, png_read_png,
      +   (png_structrp png_ptr, png_inforp info_ptr,
           int transforms, png_voidp params));
       #endif
       #ifdef PNG_WRITE_SUPPORTED
      -PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr,
      +PNG_EXPORT(179, void, png_write_png,
      +   (png_structrp png_ptr, png_inforp info_ptr,
           int transforms, png_voidp params));
       #endif
       #endif
       
       PNG_EXPORT(180, png_const_charp, png_get_copyright,
      -    (png_const_structrp png_ptr));
      +   (png_const_structrp png_ptr));
       PNG_EXPORT(181, png_const_charp, png_get_header_ver,
      -    (png_const_structrp png_ptr));
      +   (png_const_structrp png_ptr));
       PNG_EXPORT(182, png_const_charp, png_get_header_version,
      -    (png_const_structrp png_ptr));
      +   (png_const_structrp png_ptr));
       PNG_EXPORT(183, png_const_charp, png_get_libpng_ver,
      -    (png_const_structrp png_ptr));
      +   (png_const_structrp png_ptr));
       
       #ifdef PNG_MNG_FEATURES_SUPPORTED
      -PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr,
      -    png_uint_32 mng_features_permitted));
      +PNG_EXPORT(184, png_uint_32, png_permit_mng_features,
      +   (png_structrp png_ptr, png_uint_32 mng_features_permitted));
       #endif
       
       /* For use in png_set_keep_unknown, added to version 1.2.6 */
      @@ -2485,71 +2619,74 @@ PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr,
        * messages before passing them to the error or warning handler.
        */
       #ifdef PNG_ERROR_NUMBERS_SUPPORTED
      -PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr,
      -    png_uint_32 strip_mode));
      +PNG_EXPORT(185, void, png_set_strip_error_numbers,
      +   (png_structrp png_ptr, png_uint_32 strip_mode));
       #endif
       
       /* Added in libpng-1.2.6 */
       #ifdef PNG_SET_USER_LIMITS_SUPPORTED
      -PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr,
      +PNG_EXPORT(186, void, png_set_user_limits,
      +   (png_structrp png_ptr,
           png_uint_32 user_width_max, png_uint_32 user_height_max));
       PNG_EXPORT(187, png_uint_32, png_get_user_width_max,
      -    (png_const_structrp png_ptr));
      +   (png_const_structrp png_ptr));
       PNG_EXPORT(188, png_uint_32, png_get_user_height_max,
      -    (png_const_structrp png_ptr));
      +   (png_const_structrp png_ptr));
       /* Added in libpng-1.4.0 */
      -PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr,
      -    png_uint_32 user_chunk_cache_max));
      +PNG_EXPORT(189, void, png_set_chunk_cache_max,
      +   (png_structrp png_ptr, png_uint_32 user_chunk_cache_max));
       PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max,
      -    (png_const_structrp png_ptr));
      +   (png_const_structrp png_ptr));
       /* Added in libpng-1.4.1 */
      -PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr,
      -    png_alloc_size_t user_chunk_cache_max));
      +PNG_EXPORT(191, void, png_set_chunk_malloc_max,
      +   (png_structrp png_ptr, png_alloc_size_t user_chunk_cache_max));
       PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max,
      -    (png_const_structrp png_ptr));
      +   (png_const_structrp png_ptr));
       #endif
       
       #if defined(PNG_INCH_CONVERSIONS_SUPPORTED)
       PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr));
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr));
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr));
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr));
       
       PNG_FP_EXPORT(196, float, png_get_x_offset_inches,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr))
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr))
       #ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */
       PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr))
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr))
       #endif
       
      -PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr))
      +PNG_FP_EXPORT(197, float, png_get_y_offset_inches,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr))
       #ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */
       PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed,
      -    (png_const_structrp png_ptr, png_const_inforp info_ptr))
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr))
       #endif
       
       #  ifdef PNG_pHYs_SUPPORTED
      -PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr,
      -    png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y,
      -    int *unit_type));
      +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi,
      +   (png_const_structrp png_ptr, png_const_inforp info_ptr,
      +    png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type));
       #  endif /* pHYs */
       #endif  /* INCH_CONVERSIONS */
       
       /* Added in libpng-1.4.0 */
       #ifdef PNG_IO_STATE_SUPPORTED
      -PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr));
      +PNG_EXPORT(199, png_uint_32, png_get_io_state,
      +   (png_const_structrp png_ptr));
       
       /* Removed from libpng 1.6; use png_get_io_chunk_type. */
      -PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr),
      -    PNG_DEPRECATED)
      +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name,
      +   (png_structrp png_ptr),
      +   PNG_DEPRECATED)
       
       PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type,
      -    (png_const_structrp png_ptr));
      +   (png_const_structrp png_ptr));
       
       /* The flags returned by png_get_io_state() are the following: */
       #  define PNG_IO_NONE        0x0000   /* no I/O at this moment */
      @@ -2674,21 +2811,26 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type,
       #endif /* READ_COMPOSITE_NODIV */
       
       #ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED
      -PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf));
      -PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf));
      -PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf));
      +PNG_EXPORT(201, png_uint_32, png_get_uint_32,
      +   (png_const_bytep buf));
      +PNG_EXPORT(202, png_uint_16, png_get_uint_16,
      +   (png_const_bytep buf));
      +PNG_EXPORT(203, png_int_32, png_get_int_32,
      +   (png_const_bytep buf));
       #endif
       
      -PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr,
      -    png_const_bytep buf));
      +PNG_EXPORT(204, png_uint_32, png_get_uint_31,
      +   (png_const_structrp png_ptr, png_const_bytep buf));
       /* No png_get_int_16 -- may be added if there's a real need for it. */
       
       /* Place a 32-bit number into a buffer in PNG byte order (big-endian). */
       #ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
      -PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i));
      +PNG_EXPORT(205, void, png_save_uint_32,
      +   (png_bytep buf, png_uint_32 i));
       #endif
       #ifdef PNG_SAVE_INT_32_SUPPORTED
      -PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i));
      +PNG_EXPORT(206, void, png_save_int_32,
      +   (png_bytep buf, png_int_32 i));
       #endif
       
       /* Place a 16-bit number into a buffer in PNG byte order.
      @@ -2696,7 +2838,8 @@ PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i));
        * just to avoid potential problems on pre-ANSI C compilers.
        */
       #ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED
      -PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i));
      +PNG_EXPORT(207, void, png_save_uint_16,
      +   (png_bytep buf, unsigned int i));
       /* No png_save_int_16 -- may be added if there's a real need for it. */
       #endif
       
      @@ -2743,10 +2886,10 @@ PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i));
       
       #ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
       PNG_EXPORT(242, void, png_set_check_for_invalid_index,
      -    (png_structrp png_ptr, int allowed));
      +   (png_structrp png_ptr, int allowed));
       #  ifdef PNG_GET_PALETTE_MAX_SUPPORTED
      -PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr,
      -    png_const_infop info_ptr));
      +PNG_EXPORT(243, int, png_get_palette_max,
      +   (png_const_structp png_ptr, png_const_infop info_ptr));
       #  endif
       #endif /* CHECK_FOR_INVALID_INDEX */
       
      @@ -3110,24 +3253,25 @@ typedef struct
        * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.)
        */
       #ifdef PNG_STDIO_SUPPORTED
      -PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image,
      -   const char *file_name));
      +PNG_EXPORT(234, int, png_image_begin_read_from_file,
      +   (png_imagep image, const char *file_name));
          /* The named file is opened for read and the image header is filled in
           * from the PNG header in the file.
           */
       
      -PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image,
      -   FILE *file));
      +PNG_EXPORT(235, int, png_image_begin_read_from_stdio,
      +   (png_imagep image, FILE *file));
          /* The PNG header is read from the stdio FILE object. */
       #endif /* STDIO */
       
      -PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image,
      -   png_const_voidp memory, size_t size));
      +PNG_EXPORT(236, int, png_image_begin_read_from_memory,
      +   (png_imagep image, png_const_voidp memory, size_t size));
          /* The PNG header is read from the given memory buffer. */
       
      -PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image,
      -   png_const_colorp background, void *buffer, png_int_32 row_stride,
      -   void *colormap));
      +PNG_EXPORT(237, int, png_image_finish_read,
      +   (png_imagep image,
      +    png_const_colorp background, void *buffer, png_int_32 row_stride,
      +    void *colormap));
          /* Finish reading the image into the supplied buffer and clean up the
           * png_image structure.
           *
      @@ -3160,7 +3304,8 @@ PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image,
           * written to the colormap; this may be less than the original value.
           */
       
      -PNG_EXPORT(238, void, png_image_free, (png_imagep image));
      +PNG_EXPORT(238, void, png_image_free,
      +   (png_imagep image));
          /* Free any data allocated by libpng in image->opaque, setting the pointer to
           * NULL.  May be called at any time after the structure is initialized.
           */
      @@ -3184,14 +3329,16 @@ PNG_EXPORT(238, void, png_image_free, (png_imagep image));
        * colormap_entries: set to the number of entries in the color-map (0 to 256)
        */
       #ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
      -PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image,
      -   const char *file, int convert_to_8bit, const void *buffer,
      -   png_int_32 row_stride, const void *colormap));
      +PNG_EXPORT(239, int, png_image_write_to_file,
      +   (png_imagep image,
      +    const char *file, int convert_to_8bit, const void *buffer,
      +    png_int_32 row_stride, const void *colormap));
          /* Write the image to the named file. */
       
      -PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file,
      -   int convert_to_8_bit, const void *buffer, png_int_32 row_stride,
      -   const void *colormap));
      +PNG_EXPORT(240, int, png_image_write_to_stdio,
      +   (png_imagep image,
      +    FILE *file, int convert_to_8_bit, const void *buffer,
      +    png_int_32 row_stride, const void *colormap));
          /* Write the image to the given FILE object. */
       #endif /* SIMPLIFIED_WRITE_STDIO */
       
      @@ -3216,9 +3363,11 @@ PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file,
        * notices) you need to use one of the other APIs.
        */
       
      -PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory,
      -   png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit,
      -   const void *buffer, png_int_32 row_stride, const void *colormap));
      +PNG_EXPORT(245, int, png_image_write_to_memory,
      +   (png_imagep image,
      +    void *memory, png_alloc_size_t * PNG_RESTRICT memory_bytes,
      +    int convert_to_8_bit,
      +    const void *buffer, png_int_32 row_stride, const void *colormap));
          /* Write the image to the given memory buffer.  The function both writes the
           * whole PNG data stream to *memory and updates *memory_bytes with the count
           * of bytes written.
      @@ -3394,7 +3543,7 @@ PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option,
        * one to use is one more than this.)
        */
       #ifdef PNG_EXPORT_LAST_ORDINAL
      -  PNG_EXPORT_LAST_ORDINAL(259);
      +   PNG_EXPORT_LAST_ORDINAL(259);
       #endif
       
       #ifdef __cplusplus
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
      index 4bc5f7bb468..959c604edbc 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngconf.h
      @@ -29,9 +29,9 @@
        * However, the following notice accompanied the original version of this
        * file and, per its terms, should not be removed:
        *
      - * libpng version 1.6.51
      + * libpng version 1.6.54
        *
      - * Copyright (c) 2018-2025 Cosmin Truta
      + * Copyright (c) 2018-2026 Cosmin Truta
        * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson
        * Copyright (c) 1996-1997 Andreas Dilger
        * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c
      index 44c86ebfef9..324d1951a52 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngerror.c
      @@ -78,7 +78,8 @@ png_error,(png_const_structrp png_ptr, png_const_charp error_message),
       }
       #else
       PNG_FUNCTION(void,PNGAPI
      -png_err,(png_const_structrp png_ptr),PNG_NORETURN)
      +png_err,(png_const_structrp png_ptr),
      +    PNG_NORETURN)
       {
          /* Prior to 1.5.2 the error_fn received a NULL pointer, expressed
           * erroneously as '\0', instead of the empty string "".  This was
      @@ -405,8 +406,8 @@ static const char png_digit[16] = {
       };
       
       static void /* PRIVATE */
      -png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp
      -    error_message)
      +png_format_buffer(png_const_structrp png_ptr, png_charp buffer,
      +    png_const_charp error_message)
       {
          png_uint_32 chunk_name = png_ptr->chunk_name;
          int iout = 0, ishift = 24;
      @@ -485,8 +486,8 @@ png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message)
       #ifdef PNG_READ_SUPPORTED
       #ifdef PNG_BENIGN_ERRORS_SUPPORTED
       void PNGAPI
      -png_chunk_benign_error(png_const_structrp png_ptr, png_const_charp
      -    error_message)
      +png_chunk_benign_error(png_const_structrp png_ptr,
      +    png_const_charp error_message)
       {
          if ((png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) != 0)
             png_chunk_warning(png_ptr, error_message);
      @@ -543,7 +544,8 @@ png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error)
       #ifdef PNG_ERROR_TEXT_SUPPORTED
       #ifdef PNG_FLOATING_POINT_SUPPORTED
       PNG_FUNCTION(void,
      -png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN)
      +png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),
      +    PNG_NORETURN)
       {
       #  define fixed_message "fixed point overflow in "
       #  define fixed_message_ln ((sizeof fixed_message)-1)
      @@ -696,7 +698,8 @@ png_default_error,(png_const_structrp png_ptr, png_const_charp error_message),
       }
       
       PNG_FUNCTION(void,PNGAPI
      -png_longjmp,(png_const_structrp png_ptr, int val),PNG_NORETURN)
      +png_longjmp,(png_const_structrp png_ptr, int val),
      +    PNG_NORETURN)
       {
       #ifdef PNG_SETJMP_SUPPORTED
          if (png_ptr != NULL && png_ptr->longjmp_fn != NULL &&
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c
      index ed2e7f886f5..a5bdcd1b524 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngget.c
      @@ -151,8 +151,8 @@ png_get_compression_type(png_const_structrp png_ptr, png_const_inforp info_ptr)
       }
       
       png_uint_32 PNGAPI
      -png_get_x_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
      -   info_ptr)
      +png_get_x_pixels_per_meter(png_const_structrp png_ptr,
      +    png_const_inforp info_ptr)
       {
       #ifdef PNG_pHYs_SUPPORTED
          png_debug(1, "in png_get_x_pixels_per_meter");
      @@ -172,8 +172,8 @@ png_get_x_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
       }
       
       png_uint_32 PNGAPI
      -png_get_y_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp
      -    info_ptr)
      +png_get_y_pixels_per_meter(png_const_structrp png_ptr,
      +    png_const_inforp info_ptr)
       {
       #ifdef PNG_pHYs_SUPPORTED
          png_debug(1, "in png_get_y_pixels_per_meter");
      @@ -215,8 +215,8 @@ png_get_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp info_ptr)
       
       #ifdef PNG_FLOATING_POINT_SUPPORTED
       float PNGAPI
      -png_get_pixel_aspect_ratio(png_const_structrp png_ptr, png_const_inforp
      -   info_ptr)
      +png_get_pixel_aspect_ratio(png_const_structrp png_ptr,
      +    png_const_inforp info_ptr)
       {
       #ifdef PNG_READ_pHYs_SUPPORTED
          png_debug(1, "in png_get_pixel_aspect_ratio");
      @@ -766,7 +766,6 @@ png_get_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
          }
       
          return 0;
      -
       }
       #endif
       
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
      index 4cfae474751..b413b510acf 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pnglibconf.h
      @@ -31,9 +31,9 @@
        * However, the following notice accompanied the original version of this
        * file and, per its terms, should not be removed:
        */
      -/* libpng version 1.6.51 */
      +/* libpng version 1.6.54 */
       
      -/* Copyright (c) 2018-2025 Cosmin Truta */
      +/* Copyright (c) 2018-2026 Cosmin Truta */
       /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
       
       /* This code is released under the libpng license. */
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngmem.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngmem.c
      index 12b71bcbc02..8ec703616ec 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngmem.c
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngmem.c
      @@ -75,7 +75,8 @@ png_destroy_png_struct(png_structrp png_ptr)
        * have the ability to do that.
        */
       PNG_FUNCTION(png_voidp,PNGAPI
      -png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
      +png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),
      +    PNG_ALLOCATED)
       {
          png_voidp ret;
       
      @@ -147,7 +148,8 @@ png_malloc_array_checked(png_const_structrp png_ptr, int nelements,
       
       PNG_FUNCTION(png_voidp /* PRIVATE */,
       png_malloc_array,(png_const_structrp png_ptr, int nelements,
      -    size_t element_size),PNG_ALLOCATED)
      +    size_t element_size),
      +    PNG_ALLOCATED)
       {
          if (nelements <= 0 || element_size == 0)
             png_error(png_ptr, "internal error: array alloc");
      @@ -157,7 +159,8 @@ png_malloc_array,(png_const_structrp png_ptr, int nelements,
       
       PNG_FUNCTION(png_voidp /* PRIVATE */,
       png_realloc_array,(png_const_structrp png_ptr, png_const_voidp old_array,
      -    int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED)
      +    int old_elements, int add_elements, size_t element_size),
      +    PNG_ALLOCATED)
       {
          /* These are internal errors: */
          if (add_elements <= 0 || element_size == 0 || old_elements < 0 ||
      @@ -196,7 +199,8 @@ png_realloc_array,(png_const_structrp png_ptr, png_const_voidp old_array,
        * function png_malloc_default is also provided.
        */
       PNG_FUNCTION(png_voidp,PNGAPI
      -png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED)
      +png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),
      +    PNG_ALLOCATED)
       {
          png_voidp ret;
       
      @@ -270,7 +274,8 @@ png_free(png_const_structrp png_ptr, png_voidp ptr)
       }
       
       PNG_FUNCTION(void,PNGAPI
      -png_free_default,(png_const_structrp png_ptr, png_voidp ptr),PNG_DEPRECATED)
      +png_free_default,(png_const_structrp png_ptr, png_voidp ptr),
      +    PNG_DEPRECATED)
       {
          if (png_ptr == NULL || ptr == NULL)
             return;
      @@ -284,8 +289,8 @@ png_free_default,(png_const_structrp png_ptr, png_voidp ptr),PNG_DEPRECATED)
        * of allocating and freeing memory.
        */
       void PNGAPI
      -png_set_mem_fn(png_structrp png_ptr, png_voidp mem_ptr, png_malloc_ptr
      -  malloc_fn, png_free_ptr free_fn)
      +png_set_mem_fn(png_structrp png_ptr, png_voidp mem_ptr,
      +    png_malloc_ptr malloc_fn, png_free_ptr free_fn)
       {
          if (png_ptr != NULL)
          {
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h b/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h
      index dcd005efb34..ee91f58d4ba 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngpriv.h
      @@ -1104,15 +1104,17 @@ extern "C" {
        */
       /* Zlib support */
       #define PNG_UNEXPECTED_ZLIB_RETURN (-7)
      -PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret),
      +PNG_INTERNAL_FUNCTION(void, png_zstream_error,
      +   (png_structrp png_ptr, int ret),
          PNG_EMPTY);
          /* Used by the zlib handling functions to ensure that z_stream::msg is always
           * set before they return.
           */
       
       #ifdef PNG_WRITE_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr,
      -   png_compression_bufferp *list),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_free_buffer_list,
      +   (png_structrp png_ptr, png_compression_bufferp *list),
      +   PNG_EMPTY);
          /* Free the buffer list used by the compressed write code. */
       #endif
       
      @@ -1124,22 +1126,25 @@ PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr,
          defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \
          (defined(PNG_sCAL_SUPPORTED) && \
          defined(PNG_FLOATING_ARITHMETIC_SUPPORTED))
      -PNG_INTERNAL_FUNCTION(png_fixed_point,png_fixed,(png_const_structrp png_ptr,
      -   double fp, png_const_charp text),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(png_fixed_point, png_fixed,
      +   (png_const_structrp png_ptr, double fp, png_const_charp text),
      +   PNG_EMPTY);
       #endif
       
       #if defined(PNG_FLOATING_POINT_SUPPORTED) && \
          !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \
          (defined(PNG_cLLI_SUPPORTED) || defined(PNG_mDCV_SUPPORTED))
      -PNG_INTERNAL_FUNCTION(png_uint_32,png_fixed_ITU,(png_const_structrp png_ptr,
      -   double fp, png_const_charp text),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(png_uint_32, png_fixed_ITU,
      +   (png_const_structrp png_ptr, double fp, png_const_charp text),
      +   PNG_EMPTY);
       #endif
       
       /* Check the user version string for compatibility, returns false if the version
        * numbers aren't compatible.
        */
      -PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr,
      -   png_const_charp user_png_ver),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(int, png_user_version_check,
      +   (png_structrp png_ptr, png_const_charp user_png_ver),
      +   PNG_EMPTY);
       
       #ifdef PNG_READ_SUPPORTED /* should only be used on read */
       /* Security: read limits on the largest allocations while reading a PNG.  This
      @@ -1164,24 +1169,28 @@ PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr,
        * does, however, call the application provided allocator and that could call
        * png_error (although that would be a bug in the application implementation.)
        */
      -PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_base,(png_const_structrp png_ptr,
      -   png_alloc_size_t size),PNG_ALLOCATED);
      +PNG_INTERNAL_FUNCTION(png_voidp, png_malloc_base,
      +   (png_const_structrp png_ptr, png_alloc_size_t size),
      +   PNG_ALLOCATED);
       
       #if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\
          defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED)
       /* Internal array allocator, outputs no error or warning messages on failure,
        * just returns NULL.
        */
      -PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_array,(png_const_structrp png_ptr,
      -   int nelements, size_t element_size),PNG_ALLOCATED);
      +PNG_INTERNAL_FUNCTION(png_voidp, png_malloc_array,
      +   (png_const_structrp png_ptr, int nelements, size_t element_size),
      +   PNG_ALLOCATED);
       
       /* The same but an existing array is extended by add_elements.  This function
        * also memsets the new elements to 0 and copies the old elements.  The old
        * array is not freed or altered.
        */
      -PNG_INTERNAL_FUNCTION(png_voidp,png_realloc_array,(png_const_structrp png_ptr,
      -   png_const_voidp array, int old_elements, int add_elements,
      -   size_t element_size),PNG_ALLOCATED);
      +PNG_INTERNAL_FUNCTION(png_voidp, png_realloc_array,
      +   (png_const_structrp png_ptr,
      +    png_const_voidp array, int old_elements, int add_elements,
      +    size_t element_size),
      +   PNG_ALLOCATED);
       #endif /* text, sPLT or unknown chunks */
       
       /* Magic to create a struct when there is no struct to call the user supplied
      @@ -1190,84 +1199,106 @@ PNG_INTERNAL_FUNCTION(png_voidp,png_realloc_array,(png_const_structrp png_ptr,
        * restriction so libpng has to assume that the 'free' handler, at least, might
        * call png_error.
        */
      -PNG_INTERNAL_FUNCTION(png_structp,png_create_png_struct,
      -   (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn,
      -    png_error_ptr warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn,
      -    png_free_ptr free_fn),PNG_ALLOCATED);
      +PNG_INTERNAL_FUNCTION(png_structp, png_create_png_struct,
      +   (png_const_charp user_png_ver,
      +    png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn,
      +    png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn),
      +   PNG_ALLOCATED);
       
       /* Free memory from internal libpng struct */
      -PNG_INTERNAL_FUNCTION(void,png_destroy_png_struct,(png_structrp png_ptr),
      +PNG_INTERNAL_FUNCTION(void, png_destroy_png_struct,
      +   (png_structrp png_ptr),
          PNG_EMPTY);
       
       /* Free an allocated jmp_buf (always succeeds) */
      -PNG_INTERNAL_FUNCTION(void,png_free_jmpbuf,(png_structrp png_ptr),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_free_jmpbuf,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
       
       /* Function to allocate memory for zlib.  PNGAPI is disallowed. */
      -PNG_INTERNAL_FUNCTION(voidpf,png_zalloc,(voidpf png_ptr, uInt items, uInt size),
      +PNG_INTERNAL_FUNCTION(voidpf, png_zalloc,
      +   (voidpf png_ptr, uInt items, uInt size),
          PNG_ALLOCATED);
       
       /* Function to free memory for zlib.  PNGAPI is disallowed. */
      -PNG_INTERNAL_FUNCTION(void,png_zfree,(voidpf png_ptr, voidpf ptr),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_zfree,
      +   (voidpf png_ptr, voidpf ptr),
      +   PNG_EMPTY);
       
       /* Next four functions are used internally as callbacks.  PNGCBAPI is required
        * but not PNG_EXPORT.  PNGAPI added at libpng version 1.2.3, changed to
        * PNGCBAPI at 1.5.0
        */
       
      -PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr,
      -    png_bytep data, size_t length),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void PNGCBAPI, png_default_read_data,
      +   (png_structp png_ptr, png_bytep data, size_t length),
      +   PNG_EMPTY);
       
       #ifdef PNG_PROGRESSIVE_READ_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr,
      -    png_bytep buffer, size_t length),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void PNGCBAPI, png_push_fill_buffer,
      +   (png_structp png_ptr, png_bytep buffer, size_t length),
      +   PNG_EMPTY);
       #endif
       
      -PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr,
      -    png_bytep data, size_t length),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void PNGCBAPI, png_default_write_data,
      +   (png_structp png_ptr, png_bytep data, size_t length),
      +   PNG_EMPTY);
       
       #ifdef PNG_WRITE_FLUSH_SUPPORTED
       #  ifdef PNG_STDIO_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_flush,(png_structp png_ptr),
      +PNG_INTERNAL_FUNCTION(void PNGCBAPI, png_default_flush,
      +   (png_structp png_ptr),
          PNG_EMPTY);
       #  endif
       #endif
       
       /* Reset the CRC variable */
      -PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_reset_crc,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
       
       /* Write the "data" buffer to whatever output you are using */
      -PNG_INTERNAL_FUNCTION(void,png_write_data,(png_structrp png_ptr,
      -    png_const_bytep data, size_t length),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_data,
      +   (png_structrp png_ptr, png_const_bytep data, size_t length),
      +   PNG_EMPTY);
       
       /* Read and check the PNG file signature */
      -PNG_INTERNAL_FUNCTION(void,png_read_sig,(png_structrp png_ptr,
      -   png_inforp info_ptr),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_sig,
      +   (png_structrp png_ptr, png_inforp info_ptr),
      +   PNG_EMPTY);
       
       /* Read the chunk header (length + type name) */
      -PNG_INTERNAL_FUNCTION(png_uint_32,png_read_chunk_header,(png_structrp png_ptr),
      +PNG_INTERNAL_FUNCTION(png_uint_32, png_read_chunk_header,
      +   (png_structrp png_ptr),
          PNG_EMPTY);
       
       /* Read data from whatever input you are using into the "data" buffer */
      -PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_bytep data,
      -    size_t length),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_data,
      +   (png_structrp png_ptr, png_bytep data, size_t length),
      +   PNG_EMPTY);
       
       /* Read bytes into buf, and update png_ptr->crc */
      -PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf,
      -    png_uint_32 length),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_crc_read,
      +   (png_structrp png_ptr, png_bytep buf, png_uint_32 length),
      +   PNG_EMPTY);
       
       /* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */
      -PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr,
      -   png_uint_32 skip),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(int, png_crc_finish,
      +   (png_structrp png_ptr, png_uint_32 skip),
      +   PNG_EMPTY);
       
       /* Calculate the CRC over a section of data.  Note that we are only
        * passing a maximum of 64K on systems that have this as a memory limit,
        * since this is the maximum buffer size we can specify.
        */
      -PNG_INTERNAL_FUNCTION(void,png_calculate_crc,(png_structrp png_ptr,
      -   png_const_bytep ptr, size_t length),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_calculate_crc,
      +   (png_structrp png_ptr, png_const_bytep ptr, size_t length),
      +   PNG_EMPTY);
       
       #ifdef PNG_WRITE_FLUSH_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_flush,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
       #endif
       
       /* Write various chunks */
      @@ -1275,68 +1306,86 @@ PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY);
       /* Write the IHDR chunk, and update the png_struct with the necessary
        * information.
        */
      -PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr,
      -   png_uint_32 width, png_uint_32 height, int bit_depth, int color_type,
      -   int compression_method, int filter_method, int interlace_method),PNG_EMPTY);
      -
      -PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr,
      -   png_const_colorp palette, png_uint_32 num_pal),PNG_EMPTY);
      -
      -PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr,
      -   png_const_bytep row_data, png_alloc_size_t row_data_length, int flush),
      +PNG_INTERNAL_FUNCTION(void, png_write_IHDR,
      +   (png_structrp png_ptr,
      +    png_uint_32 width, png_uint_32 height, int bit_depth, int color_type,
      +    int compression_method, int filter_method, int interlace_method),
          PNG_EMPTY);
       
      -PNG_INTERNAL_FUNCTION(void,png_write_IEND,(png_structrp png_ptr),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_PLTE,
      +   (png_structrp png_ptr,
      +    png_const_colorp palette, png_uint_32 num_pal),
      +   PNG_EMPTY);
      +
      +PNG_INTERNAL_FUNCTION(void, png_compress_IDAT,
      +   (png_structrp png_ptr,
      +    png_const_bytep row_data, png_alloc_size_t row_data_length, int flush),
      +   PNG_EMPTY);
      +
      +PNG_INTERNAL_FUNCTION(void, png_write_IEND,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
       
       #ifdef PNG_WRITE_gAMA_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_gAMA_fixed,(png_structrp png_ptr,
      -    png_fixed_point file_gamma),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_gAMA_fixed,
      +   (png_structrp png_ptr, png_fixed_point file_gamma),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_sBIT_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_sBIT,(png_structrp png_ptr,
      -    png_const_color_8p sbit, int color_type),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_sBIT,
      +   (png_structrp png_ptr, png_const_color_8p sbit, int color_type),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_cHRM_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_ptr,
      -    const png_xy *xy), PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_cHRM_fixed,
      +   (png_structrp png_ptr, const png_xy *xy),
      +   PNG_EMPTY);
          /* The xy value must have been previously validated */
       #endif
       
       #ifdef PNG_WRITE_cICP_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_cICP,(png_structrp png_ptr,
      +PNG_INTERNAL_FUNCTION(void, png_write_cICP,
      +   (png_structrp png_ptr,
           png_byte colour_primaries, png_byte transfer_function,
      -    png_byte matrix_coefficients, png_byte video_full_range_flag), PNG_EMPTY);
      +    png_byte matrix_coefficients, png_byte video_full_range_flag),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_cLLI_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_cLLI_fixed,(png_structrp png_ptr,
      -   png_uint_32 maxCLL, png_uint_32 maxFALL), PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_cLLI_fixed,
      +   (png_structrp png_ptr, png_uint_32 maxCLL, png_uint_32 maxFALL),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_mDCV_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_mDCV_fixed,(png_structrp png_ptr,
      -   png_uint_16 red_x, png_uint_16 red_y,
      -   png_uint_16 green_x, png_uint_16 green_y,
      -   png_uint_16 blue_x, png_uint_16 blue_y,
      -   png_uint_16 white_x, png_uint_16 white_y,
      -   png_uint_32 maxDL, png_uint_32 minDL), PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_mDCV_fixed,
      +   (png_structrp png_ptr,
      +    png_uint_16 red_x, png_uint_16 red_y,
      +    png_uint_16 green_x, png_uint_16 green_y,
      +    png_uint_16 blue_x, png_uint_16 blue_y,
      +    png_uint_16 white_x, png_uint_16 white_y,
      +    png_uint_32 maxDL, png_uint_32 minDL),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_sRGB_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr,
      -    int intent),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_sRGB,
      +   (png_structrp png_ptr, int intent),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_eXIf_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_eXIf,(png_structrp png_ptr,
      -    png_bytep exif, int num_exif),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_eXIf,
      +   (png_structrp png_ptr, png_bytep exif, int num_exif),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_iCCP_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr,
      -   png_const_charp name, png_const_bytep profile, png_uint_32 proflen),
      +PNG_INTERNAL_FUNCTION(void, png_write_iCCP,
      +   (png_structrp png_ptr,
      +    png_const_charp name, png_const_bytep profile, png_uint_32 proflen),
          PNG_EMPTY);
          /* Writes a previously 'set' profile.  The profile argument is **not**
           * compressed.
      @@ -1344,82 +1393,106 @@ PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr,
       #endif
       
       #ifdef PNG_WRITE_sPLT_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_sPLT,(png_structrp png_ptr,
      -    png_const_sPLT_tp palette),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_sPLT,
      +   (png_structrp png_ptr, png_const_sPLT_tp palette),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_tRNS_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_tRNS,(png_structrp png_ptr,
      +PNG_INTERNAL_FUNCTION(void, png_write_tRNS,
      +   (png_structrp png_ptr,
           png_const_bytep trans, png_const_color_16p values, int number,
      -    int color_type),PNG_EMPTY);
      +    int color_type),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_bKGD_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_bKGD,(png_structrp png_ptr,
      -    png_const_color_16p values, int color_type),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_bKGD,
      +   (png_structrp png_ptr, png_const_color_16p values, int color_type),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_hIST_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_hIST,(png_structrp png_ptr,
      -    png_const_uint_16p hist, int num_hist),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_hIST,
      +   (png_structrp png_ptr, png_const_uint_16p hist, int num_hist),
      +   PNG_EMPTY);
       #endif
       
       /* Chunks that have keywords */
       #ifdef PNG_WRITE_tEXt_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr,
      -   png_const_charp key, png_const_charp text, size_t text_len),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_tEXt,
      +   (png_structrp png_ptr,
      +    png_const_charp key, png_const_charp text, size_t text_len),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_zTXt_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr, png_const_charp
      -    key, png_const_charp text, int compression),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_zTXt,
      +   (png_structrp png_ptr,
      +    png_const_charp key, png_const_charp text, int compression),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_iTXt_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_iTXt,(png_structrp png_ptr,
      +PNG_INTERNAL_FUNCTION(void, png_write_iTXt,
      +   (png_structrp png_ptr,
           int compression, png_const_charp key, png_const_charp lang,
      -    png_const_charp lang_key, png_const_charp text),PNG_EMPTY);
      +    png_const_charp lang_key, png_const_charp text),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_TEXT_SUPPORTED  /* Added at version 1.0.14 and 1.2.4 */
      -PNG_INTERNAL_FUNCTION(int,png_set_text_2,(png_const_structrp png_ptr,
      -    png_inforp info_ptr, png_const_textp text_ptr, int num_text),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(int, png_set_text_2,
      +   (png_const_structrp png_ptr,
      +    png_inforp info_ptr, png_const_textp text_ptr, int num_text),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_oFFs_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_oFFs,(png_structrp png_ptr,
      -    png_int_32 x_offset, png_int_32 y_offset, int unit_type),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_oFFs,
      +   (png_structrp png_ptr,
      +    png_int_32 x_offset, png_int_32 y_offset, int unit_type),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_pCAL_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_pCAL,(png_structrp png_ptr,
      -    png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams,
      -    png_const_charp units, png_charpp params),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_pCAL,
      +   (png_structrp png_ptr,
      +    png_charp purpose, png_int_32 X0, png_int_32 X1,
      +    int type, int nparams, png_const_charp units, png_charpp params),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_pHYs_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_pHYs,(png_structrp png_ptr,
      +PNG_INTERNAL_FUNCTION(void, png_write_pHYs,
      +   (png_structrp png_ptr,
           png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit,
      -    int unit_type),PNG_EMPTY);
      +    int unit_type),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_tIME_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_tIME,(png_structrp png_ptr,
      -    png_const_timep mod_time),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_tIME,
      +   (png_structrp png_ptr, png_const_timep mod_time),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_WRITE_sCAL_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr,
      -    int unit, png_const_charp width, png_const_charp height),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_sCAL_s,
      +   (png_structrp png_ptr,
      +    int unit, png_const_charp width, png_const_charp height),
      +   PNG_EMPTY);
       #endif
       
       /* Called when finished processing a row of data */
      -PNG_INTERNAL_FUNCTION(void,png_write_finish_row,(png_structrp png_ptr),
      -    PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_finish_row,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
       
       /* Internal use only.   Called before first row of data */
      -PNG_INTERNAL_FUNCTION(void,png_write_start_row,(png_structrp png_ptr),
      -    PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_start_row,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
       
       /* Combine a row of data, dealing with alpha, etc. if requested.  'row' is an
        * array of png_ptr->width pixels.  If the image is not interlaced or this
      @@ -1447,8 +1520,9 @@ PNG_INTERNAL_FUNCTION(void,png_write_start_row,(png_structrp png_ptr),
       #ifndef PNG_USE_COMPILE_TIME_MASKS
       #  define PNG_USE_COMPILE_TIME_MASKS 1
       #endif
      -PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr,
      -    png_bytep row, int display),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_combine_row,
      +   (png_const_structrp png_ptr, png_bytep row, int display),
      +   PNG_EMPTY);
       
       #ifdef PNG_READ_INTERLACING_SUPPORTED
       /* Expand an interlaced row: the 'row_info' describes the pass data that has
      @@ -1457,170 +1531,230 @@ PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr,
        * the pixels are *replicated* to the intervening space.  This is essential for
        * the correct operation of png_combine_row, above.
        */
      -PNG_INTERNAL_FUNCTION(void,png_do_read_interlace,(png_row_infop row_info,
      -    png_bytep row, int pass, png_uint_32 transformations),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_do_read_interlace,
      +   (png_row_infop row_info,
      +    png_bytep row, int pass, png_uint_32 transformations),
      +   PNG_EMPTY);
       #endif
       
       /* GRR TO DO (2.0 or whenever):  simplify other internal calling interfaces */
       
       #ifdef PNG_WRITE_INTERLACING_SUPPORTED
       /* Grab pixels out of a row for an interlaced pass */
      -PNG_INTERNAL_FUNCTION(void,png_do_write_interlace,(png_row_infop row_info,
      -    png_bytep row, int pass),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_do_write_interlace,
      +   (png_row_infop row_info, png_bytep row, int pass),
      +   PNG_EMPTY);
       #endif
       
       /* Unfilter a row: check the filter value before calling this, there is no point
        * calling it for PNG_FILTER_VALUE_NONE.
        */
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row,(png_structrp pp, png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row, int filter),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row,
      +   (png_structrp pp, png_row_infop row_info,
      +    png_bytep row, png_const_bytep prev_row, int filter),
      +   PNG_EMPTY);
       
       #if PNG_ARM_NEON_OPT > 0
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon,(png_row_infop row_info,
      -    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_neon,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_neon,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_up_neon,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_neon,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_neon,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_neon,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_neon,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_neon,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_neon,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
       #endif
       
       #if PNG_MIPS_MSA_IMPLEMENTATION == 1
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_msa,(png_row_infop row_info,
      -    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_msa,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_msa,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_msa,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_msa,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_msa,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_msa,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_up_msa,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_msa,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_msa,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_msa,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_msa,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_msa,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_msa,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
       #endif
       
       #if PNG_MIPS_MMI_IMPLEMENTATION > 0
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_mmi,(png_row_infop row_info,
      -    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_mmi,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_mmi,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_mmi,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_mmi,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_mmi,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_mmi,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_up_mmi,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_mmi,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_mmi,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_mmi,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_mmi,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_mmi,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_mmi,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
       #endif
       
       #if PNG_POWERPC_VSX_OPT > 0
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_vsx,(png_row_infop row_info,
      -    png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_vsx,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_vsx,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_vsx,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_vsx,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_vsx,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_vsx,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_up_vsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_vsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_vsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_vsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_vsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_vsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_vsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
       #endif
       
       #if PNG_INTEL_SSE_IMPLEMENTATION > 0
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_sse2,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_sse2,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_sse2,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_sse2,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_sse2,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_sse2,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_sse2,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_sse2,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_sse2,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_sse2,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_sse2,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_sse2,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
       #endif
       
       #if PNG_LOONGARCH_LSX_IMPLEMENTATION == 1
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_lsx,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_lsx,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_lsx,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_lsx,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_lsx,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_lsx,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_lsx,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_up_lsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_lsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_lsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_lsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_lsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_lsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_lsx,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
       #endif
       
       #if PNG_RISCV_RVV_IMPLEMENTATION == 1
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_rvv,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_rvv,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_rvv,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_rvv,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_rvv,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_rvv,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_rvv,(png_row_infop
      -    row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_up_rvv,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub3_rvv,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_sub4_rvv,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg3_rvv,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_avg4_rvv,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth3_rvv,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_filter_row_paeth4_rvv,
      +   (png_row_infop row_info, png_bytep row, png_const_bytep prev_row),
      +   PNG_EMPTY);
       #endif
       
       /* Choose the best filter to use and filter the row data */
      -PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr,
      -    png_row_infop row_info),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_write_find_filter,
      +   (png_structrp png_ptr, png_row_infop row_info),
      +   PNG_EMPTY);
       
       #ifdef PNG_SEQUENTIAL_READ_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr,
      -   png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_IDAT_data,
      +   (png_structrp png_ptr, png_bytep output, png_alloc_size_t avail_out),
      +   PNG_EMPTY);
          /* Read 'avail_out' bytes of data from the IDAT stream.  If the output buffer
           * is NULL the function checks, instead, for the end of the stream.  In this
           * case a benign error will be issued if the stream end is not found or if
           * extra data has to be consumed.
           */
      -PNG_INTERNAL_FUNCTION(void,png_read_finish_IDAT,(png_structrp png_ptr),
      +PNG_INTERNAL_FUNCTION(void, png_read_finish_IDAT,
      +   (png_structrp png_ptr),
          PNG_EMPTY);
          /* This cleans up when the IDAT LZ stream does not end when the last image
           * byte is read; there is still some pending input.
           */
       
      -PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr),
      +PNG_INTERNAL_FUNCTION(void, png_read_finish_row,
      +   (png_structrp png_ptr),
          PNG_EMPTY);
          /* Finish a row while reading, dealing with interlacing passes, etc. */
       #endif /* SEQUENTIAL_READ */
       
       /* Initialize the row buffers, etc. */
      -PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_start_row,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
       
       #if ZLIB_VERNUM >= 0x1240
      -PNG_INTERNAL_FUNCTION(int,png_zlib_inflate,(png_structrp png_ptr, int flush),
      -      PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(int, png_zlib_inflate,
      +   (png_structrp png_ptr, int flush),
      +   PNG_EMPTY);
       #  define PNG_INFLATE(pp, flush) png_zlib_inflate(pp, flush)
       #else /* Zlib < 1.2.4 */
       #  define PNG_INFLATE(pp, flush) inflate(&(pp)->zstream, flush)
      @@ -1628,38 +1762,44 @@ PNG_INTERNAL_FUNCTION(int,png_zlib_inflate,(png_structrp png_ptr, int flush),
       
       #ifdef PNG_READ_TRANSFORMS_SUPPORTED
       /* Optional call to update the users info structure */
      -PNG_INTERNAL_FUNCTION(void,png_read_transform_info,(png_structrp png_ptr,
      -    png_inforp info_ptr),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_transform_info,
      +   (png_structrp png_ptr, png_inforp info_ptr),
      +   PNG_EMPTY);
       #endif
       
       /* Shared transform functions, defined in pngtran.c */
       #if defined(PNG_WRITE_FILLER_SUPPORTED) || \
           defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
      -PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,(png_row_infop row_info,
      -    png_bytep row, int at_start),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_do_strip_channel,
      +   (png_row_infop row_info, png_bytep row, int at_start),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_16BIT_SUPPORTED
       #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
      -PNG_INTERNAL_FUNCTION(void,png_do_swap,(png_row_infop row_info,
      -    png_bytep row),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_do_swap,
      +   (png_row_infop row_info, png_bytep row),
      +   PNG_EMPTY);
       #endif
       #endif
       
       #if defined(PNG_READ_PACKSWAP_SUPPORTED) || \
           defined(PNG_WRITE_PACKSWAP_SUPPORTED)
      -PNG_INTERNAL_FUNCTION(void,png_do_packswap,(png_row_infop row_info,
      -    png_bytep row),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_do_packswap,
      +   (png_row_infop row_info, png_bytep row),
      +   PNG_EMPTY);
       #endif
       
       #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
      -PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_row_infop row_info,
      -    png_bytep row),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_do_invert,
      +   (png_row_infop row_info, png_bytep row),
      +   PNG_EMPTY);
       #endif
       
       #if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
      -PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info,
      -    png_bytep row),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_do_bgr,
      +   (png_row_infop row_info, png_bytep row),
      +   PNG_EMPTY);
       #endif
       
       /* The following decodes the appropriate chunks, and does error correction,
      @@ -1680,25 +1820,27 @@ typedef enum
          handled_ok          /* known, supported and handled without error */
       } png_handle_result_code;
       
      -PNG_INTERNAL_FUNCTION(png_handle_result_code,png_handle_unknown,
      -    (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length, int keep),
      -    PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(png_handle_result_code, png_handle_unknown,
      +   (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length, int keep),
      +   PNG_EMPTY);
          /* This is the function that gets called for unknown chunks.  The 'keep'
           * argument is either non-zero for a known chunk that has been set to be
           * handled as unknown or zero for an unknown chunk.  By default the function
           * just skips the chunk or errors out if it is critical.
           */
       
      -PNG_INTERNAL_FUNCTION(png_handle_result_code,png_handle_chunk,
      -    (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(png_handle_result_code, png_handle_chunk,
      +   (png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length),
      +   PNG_EMPTY);
          /* This handles the current chunk png_ptr->chunk_name with unread
           * data[length] and returns one of the above result codes.
           */
       
       #if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) ||\
           defined(PNG_HANDLE_AS_UNKNOWN_SUPPORTED)
      -PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling,
      -    (png_const_structrp png_ptr, png_uint_32 chunk_name),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(int, png_chunk_unknown_handling,
      +   (png_const_structrp png_ptr, png_uint_32 chunk_name),
      +   PNG_EMPTY);
          /* Exactly as the API png_handle_as_unknown() except that the argument is a
           * 32-bit chunk name, not a string.
           */
      @@ -1706,93 +1848,122 @@ PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling,
       
       /* Handle the transformations for reading and writing */
       #ifdef PNG_READ_TRANSFORMS_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_do_read_transformations,(png_structrp png_ptr,
      -   png_row_infop row_info),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_do_read_transformations,
      +   (png_structrp png_ptr, png_row_infop row_info),
      +   PNG_EMPTY);
       #endif
       #ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_do_write_transformations,(png_structrp png_ptr,
      -   png_row_infop row_info),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_do_write_transformations,
      +   (png_structrp png_ptr, png_row_infop row_info),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_READ_TRANSFORMS_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_init_read_transformations,(png_structrp png_ptr),
      -    PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_init_read_transformations,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
       #endif
       
       #ifdef PNG_PROGRESSIVE_READ_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_push_read_chunk,(png_structrp png_ptr,
      -    png_inforp info_ptr),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_push_read_sig,(png_structrp png_ptr,
      -    png_inforp info_ptr),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_push_check_crc,(png_structrp png_ptr),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_push_save_buffer,(png_structrp png_ptr),
      -    PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_push_restore_buffer,(png_structrp png_ptr,
      -    png_bytep buffer, size_t buffer_length),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_push_read_IDAT,(png_structrp png_ptr),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr,
      -    png_bytep buffer, size_t buffer_length),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr),
      -    PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr,
      -   png_inforp info_ptr),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr,
      -   png_inforp info_ptr),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_push_have_row,(png_structrp png_ptr,
      -    png_bytep row),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_push_read_end,(png_structrp png_ptr,
      -    png_inforp info_ptr),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr,
      -    png_inforp info_ptr),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr),
      -    PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_push_read_chunk,
      +   (png_structrp png_ptr, png_inforp info_ptr),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_push_read_sig,
      +   (png_structrp png_ptr, png_inforp info_ptr),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_push_check_crc,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_push_save_buffer,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_push_restore_buffer,
      +   (png_structrp png_ptr, png_bytep buffer, size_t buffer_length),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_push_read_IDAT,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_process_IDAT_data,
      +   (png_structrp png_ptr, png_bytep buffer, size_t buffer_length),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_push_process_row,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_push_have_info,
      +   (png_structrp png_ptr, png_inforp info_ptr),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_push_have_end,
      +   (png_structrp png_ptr, png_inforp info_ptr),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_push_have_row,
      +   (png_structrp png_ptr, png_bytep row),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_push_read_end,
      +   (png_structrp png_ptr, png_inforp info_ptr),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_process_some_data,
      +   (png_structrp png_ptr, png_inforp info_ptr),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_read_push_finish_row,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
       #endif /* PROGRESSIVE_READ */
       
       #ifdef PNG_iCCP_SUPPORTED
       /* Routines for checking parts of an ICC profile. */
       #ifdef PNG_READ_iCCP_SUPPORTED
      -PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr,
      -   png_const_charp name, png_uint_32 profile_length), PNG_EMPTY);
      -#endif /* READ_iCCP */
      -PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr,
      -   png_const_charp name, png_uint_32 profile_length,
      -   png_const_bytep profile /* first 132 bytes only */, int color_type),
      +PNG_INTERNAL_FUNCTION(int, png_icc_check_length,
      +   (png_const_structrp png_ptr,
      +    png_const_charp name, png_uint_32 profile_length),
      +   PNG_EMPTY);
      +#endif /* READ_iCCP */
      +PNG_INTERNAL_FUNCTION(int, png_icc_check_header,
      +   (png_const_structrp png_ptr,
      +    png_const_charp name, png_uint_32 profile_length,
      +    png_const_bytep profile /* first 132 bytes only */, int color_type),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(int, png_icc_check_tag_table,
      +   (png_const_structrp png_ptr,
      +    png_const_charp name, png_uint_32 profile_length,
      +    png_const_bytep profile /* header plus whole tag table */),
          PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr,
      -   png_const_charp name, png_uint_32 profile_length,
      -   png_const_bytep profile /* header plus whole tag table */), PNG_EMPTY);
       #endif /* iCCP */
       
       #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_set_rgb_coefficients, (png_structrp png_ptr),
      +PNG_INTERNAL_FUNCTION(void, png_set_rgb_coefficients,
      +   (png_structrp png_ptr),
          PNG_EMPTY);
          /* Set the rgb_to_gray coefficients from the cHRM Y values (if unset) */
       #endif /* READ_RGB_TO_GRAY */
       
       /* Added at libpng version 1.4.0 */
      -PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr,
      -    png_uint_32 width, png_uint_32 height, int bit_depth,
      -    int color_type, int interlace_type, int compression_type,
      -    int filter_type),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_check_IHDR,
      +   (png_const_structrp png_ptr,
      +    png_uint_32 width, png_uint_32 height, int bit_depth, int color_type,
      +    int interlace_type, int compression_type, int filter_type),
      +   PNG_EMPTY);
       
       /* Added at libpng version 1.5.10 */
       #if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \
           defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
      -PNG_INTERNAL_FUNCTION(void,png_do_check_palette_indexes,
      -   (png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_do_check_palette_indexes,
      +   (png_structrp png_ptr, png_row_infop row_info),
      +   PNG_EMPTY);
       #endif
       
       #if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
      -PNG_INTERNAL_FUNCTION(void,png_fixed_error,(png_const_structrp png_ptr,
      -   png_const_charp name),PNG_NORETURN);
      +PNG_INTERNAL_FUNCTION(void, png_fixed_error,
      +   (png_const_structrp png_ptr, png_const_charp name),
      +   PNG_NORETURN);
       #endif
       
       /* Puts 'string' into 'buffer' at buffer[pos], taking care never to overwrite
        * the end.  Always leaves the buffer nul terminated.  Never errors out (and
        * there is no error code.)
        */
      -PNG_INTERNAL_FUNCTION(size_t,png_safecat,(png_charp buffer, size_t bufsize,
      -   size_t pos, png_const_charp string),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(size_t, png_safecat,
      +   (png_charp buffer, size_t bufsize, size_t pos, png_const_charp string),
      +   PNG_EMPTY);
       
       /* Various internal functions to handle formatted warning messages, currently
        * only implemented for warnings.
      @@ -1803,8 +1974,9 @@ PNG_INTERNAL_FUNCTION(size_t,png_safecat,(png_charp buffer, size_t bufsize,
        * Returns the pointer to the start of the formatted string.  This utility only
        * does unsigned values.
        */
      -PNG_INTERNAL_FUNCTION(png_charp,png_format_number,(png_const_charp start,
      -   png_charp end, int format, png_alloc_size_t number),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(png_charp, png_format_number,
      +   (png_const_charp start, png_charp end, int format, png_alloc_size_t number),
      +   PNG_EMPTY);
       
       /* Convenience macro that takes an array: */
       #define PNG_FORMAT_NUMBER(buffer,format,number) \
      @@ -1836,23 +2008,26 @@ PNG_INTERNAL_FUNCTION(png_charp,png_format_number,(png_const_charp start,
       typedef char png_warning_parameters[PNG_WARNING_PARAMETER_COUNT][
          PNG_WARNING_PARAMETER_SIZE];
       
      -PNG_INTERNAL_FUNCTION(void,png_warning_parameter,(png_warning_parameters p,
      -   int number, png_const_charp string),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_warning_parameter,
      +   (png_warning_parameters p, int number, png_const_charp string),
      +   PNG_EMPTY);
          /* Parameters are limited in size to PNG_WARNING_PARAMETER_SIZE characters,
           * including the trailing '\0'.
           */
      -PNG_INTERNAL_FUNCTION(void,png_warning_parameter_unsigned,
      +PNG_INTERNAL_FUNCTION(void, png_warning_parameter_unsigned,
          (png_warning_parameters p, int number, int format, png_alloc_size_t value),
          PNG_EMPTY);
          /* Use png_alloc_size_t because it is an unsigned type as big as any we
           * need to output.  Use the following for a signed value.
           */
      -PNG_INTERNAL_FUNCTION(void,png_warning_parameter_signed,
      +PNG_INTERNAL_FUNCTION(void, png_warning_parameter_signed,
          (png_warning_parameters p, int number, int format, png_int_32 value),
          PNG_EMPTY);
       
      -PNG_INTERNAL_FUNCTION(void,png_formatted_warning,(png_const_structrp png_ptr,
      -   png_warning_parameters p, png_const_charp message),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_formatted_warning,
      +   (png_const_structrp png_ptr,
      +    png_warning_parameters p, png_const_charp message),
      +   PNG_EMPTY);
          /* 'message' follows the X/Open approach of using @1, @2 to insert
           * parameters previously supplied using the above functions.  Errors in
           * specifying the parameters will simply result in garbage substitutions.
      @@ -1874,14 +2049,16 @@ PNG_INTERNAL_FUNCTION(void,png_formatted_warning,(png_const_structrp png_ptr,
        * If benign errors aren't supported they end up as the corresponding base call
        * (png_warning or png_error.)
        */
      -PNG_INTERNAL_FUNCTION(void,png_app_warning,(png_const_structrp png_ptr,
      -   png_const_charp message),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_app_warning,
      +   (png_const_structrp png_ptr, png_const_charp message),
      +   PNG_EMPTY);
          /* The application provided invalid parameters to an API function or called
           * an API function at the wrong time, libpng can completely recover.
           */
       
      -PNG_INTERNAL_FUNCTION(void,png_app_error,(png_const_structrp png_ptr,
      -   png_const_charp message),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_app_error,
      +   (png_const_structrp png_ptr, png_const_charp message),
      +   PNG_EMPTY);
          /* As above but libpng will ignore the call, or attempt some other partial
           * recovery from the error.
           */
      @@ -1890,8 +2067,9 @@ PNG_INTERNAL_FUNCTION(void,png_app_error,(png_const_structrp png_ptr,
       #  define png_app_error(pp,s) png_error(pp,s)
       #endif
       
      -PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr,
      -   png_const_charp message, int error),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_chunk_report,
      +   (png_const_structrp png_ptr, png_const_charp message, int error),
      +   PNG_EMPTY);
          /* Report a recoverable issue in chunk data.  On read this is used to report
           * a problem found while reading a particular chunk and the
           * png_chunk_benign_error or png_chunk_warning function is used as
      @@ -1917,14 +2095,17 @@ PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr,
       #define PNG_sCAL_MAX_DIGITS (PNG_sCAL_PRECISION+1/*.*/+1/*E*/+10/*exponent*/)
       
       #ifdef PNG_FLOATING_POINT_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_ascii_from_fp,(png_const_structrp png_ptr,
      -   png_charp ascii, size_t size, double fp, unsigned int precision),
      +PNG_INTERNAL_FUNCTION(void, png_ascii_from_fp,
      +   (png_const_structrp png_ptr,
      +    png_charp ascii, size_t size, double fp, unsigned int precision),
          PNG_EMPTY);
       #endif /* FLOATING_POINT */
       
       #ifdef PNG_FIXED_POINT_SUPPORTED
      -PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr,
      -   png_charp ascii, size_t size, png_fixed_point fp),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_ascii_from_fixed,
      +   (png_const_structrp png_ptr,
      +    png_charp ascii, size_t size, png_fixed_point fp),
      +   PNG_EMPTY);
       #endif /* FIXED_POINT */
       #endif /* sCAL */
       
      @@ -2016,8 +2197,9 @@ PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr,
        * that omits the last character (i.e. set the size to the index of
        * the problem character.)  This has not been tested within libpng.
        */
      -PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string,
      -   size_t size, int *statep, size_t *whereami),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(int, png_check_fp_number,
      +   (png_const_charp string, size_t size, int *statep, size_t *whereami),
      +   PNG_EMPTY);
       
       /* This is the same but it checks a complete string and returns true
        * only if it just contains a floating point number.  As of 1.5.4 this
      @@ -2025,8 +2207,9 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string,
        * it was valid (otherwise it returns 0.)  This can be used for testing
        * for negative or zero values using the sticky flag.
        */
      -PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string,
      -   size_t size),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(int, png_check_fp_string,
      +   (png_const_charp string, size_t size),
      +   PNG_EMPTY);
       #endif /* pCAL || sCAL */
       
       #if defined(PNG_READ_GAMMA_SUPPORTED) ||\
      @@ -2039,14 +2222,17 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string,
        * for overflow, true (1) if no overflow, in which case *res
        * holds the result.
        */
      -PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a,
      -   png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(int, png_muldiv,
      +   (png_fixed_point_p res, png_fixed_point a,
      +    png_int_32 multiplied_by, png_int_32 divided_by),
      +   PNG_EMPTY);
       
       /* Calculate a reciprocal - used for gamma values.  This returns
        * 0 if the argument is 0 in order to maintain an undefined value;
        * there are no warnings.
        */
      -PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a),
      +PNG_INTERNAL_FUNCTION(png_fixed_point, png_reciprocal,
      +   (png_fixed_point a),
          PNG_EMPTY);
       #endif
       
      @@ -2055,11 +2241,13 @@ PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a),
        * values.  Accuracy is suitable for gamma calculations but this is
        * not exact - use png_muldiv for that.  Only required at present on read.
        */
      -PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal2,(png_fixed_point a,
      -   png_fixed_point b),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(png_fixed_point, png_reciprocal2,
      +   (png_fixed_point a, png_fixed_point b),
      +   PNG_EMPTY);
       
       /* Return true if the gamma value is significantly different from 1.0 */
      -PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value),
      +PNG_INTERNAL_FUNCTION(int, png_gamma_significant,
      +   (png_fixed_point gamma_value),
          PNG_EMPTY);
       
       /* PNGv3: 'resolve' the file gamma according to the new PNGv3 rules for colour
      @@ -2070,8 +2258,9 @@ PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value),
        * transforms.  For this reason a gamma specified by png_set_gamma always takes
        * precedence.
        */
      -PNG_INTERNAL_FUNCTION(png_fixed_point,png_resolve_file_gamma,
      -   (png_const_structrp png_ptr),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(png_fixed_point, png_resolve_file_gamma,
      +   (png_const_structrp png_ptr),
      +   PNG_EMPTY);
       
       /* Internal fixed point gamma correction.  These APIs are called as
        * required to convert single values - they don't need to be fast,
      @@ -2080,37 +2269,45 @@ PNG_INTERNAL_FUNCTION(png_fixed_point,png_resolve_file_gamma,
        * While the input is an 'unsigned' value it must actually be the
        * correct bit value - 0..255 or 0..65535 as required.
        */
      -PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_correct,(png_structrp png_ptr,
      -   unsigned int value, png_fixed_point gamma_value),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_16bit_correct,(unsigned int value,
      -   png_fixed_point gamma_value),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(png_byte,png_gamma_8bit_correct,(unsigned int value,
      -   png_fixed_point gamma_value),PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr),
      +PNG_INTERNAL_FUNCTION(png_uint_16, png_gamma_correct,
      +   (png_structrp png_ptr, unsigned int value, png_fixed_point gamma_value),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(png_uint_16, png_gamma_16bit_correct,
      +   (unsigned int value, png_fixed_point gamma_value),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(png_byte, png_gamma_8bit_correct,
      +   (unsigned int value, png_fixed_point gamma_value),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_destroy_gamma_table,
      +   (png_structrp png_ptr),
      +   PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_build_gamma_table,
      +   (png_structrp png_ptr, int bit_depth),
          PNG_EMPTY);
      -PNG_INTERNAL_FUNCTION(void,png_build_gamma_table,(png_structrp png_ptr,
      -   int bit_depth),PNG_EMPTY);
       #endif /* READ_GAMMA */
       
       #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
       /* Set the RGB coefficients if not already set by png_set_rgb_to_gray */
      -PNG_INTERNAL_FUNCTION(void,png_set_rgb_coefficients,(png_structrp png_ptr),
      +PNG_INTERNAL_FUNCTION(void, png_set_rgb_coefficients,
      +   (png_structrp png_ptr),
          PNG_EMPTY);
       #endif
       
       #if defined(PNG_cHRM_SUPPORTED) || defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
      -PNG_INTERNAL_FUNCTION(int,png_XYZ_from_xy,(png_XYZ *XYZ, const png_xy *xy),
      +PNG_INTERNAL_FUNCTION(int, png_XYZ_from_xy,
      +   (png_XYZ *XYZ, const png_xy *xy),
          PNG_EMPTY);
       #endif /* cHRM || READ_RGB_TO_GRAY */
       
       #ifdef PNG_COLORSPACE_SUPPORTED
      -PNG_INTERNAL_FUNCTION(int,png_xy_from_XYZ,(png_xy *xy, const png_XYZ *XYZ),
      +PNG_INTERNAL_FUNCTION(int, png_xy_from_XYZ,
      +   (png_xy *xy, const png_XYZ *XYZ),
          PNG_EMPTY);
       #endif
       
       /* SIMPLIFIED READ/WRITE SUPPORT */
       #if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\
      -   defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
      +    defined(PNG_SIMPLIFIED_WRITE_SUPPORTED)
       /* The internal structure that png_image::opaque points to. */
       typedef struct png_control
       {
      @@ -2138,28 +2335,34 @@ typedef struct png_control
        * errors that might occur.  Returns true on success, false on failure (either
        * of the function or as a result of a png_error.)
        */
      -PNG_INTERNAL_CALLBACK(void,png_safe_error,(png_structp png_ptr,
      -   png_const_charp error_message),PNG_NORETURN);
      +PNG_INTERNAL_CALLBACK(void, png_safe_error,
      +   (png_structp png_ptr, png_const_charp error_message),
      +   PNG_NORETURN);
       
       #ifdef PNG_WARNINGS_SUPPORTED
      -PNG_INTERNAL_CALLBACK(void,png_safe_warning,(png_structp png_ptr,
      -   png_const_charp warning_message),PNG_EMPTY);
      +PNG_INTERNAL_CALLBACK(void, png_safe_warning,
      +   (png_structp png_ptr, png_const_charp warning_message),
      +   PNG_EMPTY);
       #else
       #  define png_safe_warning 0/*dummy argument*/
       #endif
       
      -PNG_INTERNAL_FUNCTION(int,png_safe_execute,(png_imagep image,
      -   int (*function)(png_voidp), png_voidp arg),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(int, png_safe_execute,
      +   (png_imagep image, int (*function)(png_voidp), png_voidp arg),
      +   PNG_EMPTY);
       
       /* Utility to log an error; this also cleans up the png_image; the function
        * always returns 0 (false).
        */
      -PNG_INTERNAL_FUNCTION(int,png_image_error,(png_imagep image,
      -   png_const_charp error_message),PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(int, png_image_error,
      +   (png_imagep image, png_const_charp error_message),
      +   PNG_EMPTY);
       
       #ifndef PNG_SIMPLIFIED_READ_SUPPORTED
       /* png_image_free is used by the write code but not exported */
      -PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, png_image_free,
      +   (png_imagep image),
      +   PNG_EMPTY);
       #endif /* !SIMPLIFIED_READ */
       
       #endif /* SIMPLIFIED READ/WRITE */
      @@ -2170,8 +2373,9 @@ PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY);
        * the generic code is used.
        */
       #ifdef PNG_FILTER_OPTIMIZATIONS
      -PNG_INTERNAL_FUNCTION(void, PNG_FILTER_OPTIMIZATIONS, (png_structp png_ptr,
      -   unsigned int bpp), PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(void, PNG_FILTER_OPTIMIZATIONS,
      +   (png_structp png_ptr, unsigned int bpp),
      +   PNG_EMPTY);
          /* Just declare the optimization that will be used */
       #else
          /* List *all* the possible optimizations here - this branch is required if
      @@ -2180,37 +2384,44 @@ PNG_INTERNAL_FUNCTION(void, PNG_FILTER_OPTIMIZATIONS, (png_structp png_ptr,
           */
       #  if PNG_ARM_NEON_OPT > 0
       PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_neon,
      -   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
      +   (png_structp png_ptr, unsigned int bpp),
      +   PNG_EMPTY);
       #endif
       
       #if PNG_MIPS_MSA_IMPLEMENTATION == 1
       PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_mips,
      -   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
      +   (png_structp png_ptr, unsigned int bpp),
      +   PNG_EMPTY);
       #endif
       
       #  if PNG_MIPS_MMI_IMPLEMENTATION > 0
       PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_mips,
      -   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
      +   (png_structp png_ptr, unsigned int bpp),
      +   PNG_EMPTY);
       #  endif
       
       #  if PNG_INTEL_SSE_IMPLEMENTATION > 0
       PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_sse2,
      -   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
      +   (png_structp png_ptr, unsigned int bpp),
      +   PNG_EMPTY);
       #  endif
       #endif
       
       #if PNG_LOONGARCH_LSX_OPT > 0
       PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_lsx,
      -    (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
      +   (png_structp png_ptr, unsigned int bpp),
      +   PNG_EMPTY);
       #endif
       
       #  if PNG_RISCV_RVV_IMPLEMENTATION == 1
       PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_rvv,
      -   (png_structp png_ptr, unsigned int bpp), PNG_EMPTY);
      +   (png_structp png_ptr, unsigned int bpp),
      +   PNG_EMPTY);
       #endif
       
      -PNG_INTERNAL_FUNCTION(png_uint_32, png_check_keyword, (png_structrp png_ptr,
      -   png_const_charp key, png_bytep new_key), PNG_EMPTY);
      +PNG_INTERNAL_FUNCTION(png_uint_32, png_check_keyword,
      +   (png_structrp png_ptr, png_const_charp key, png_bytep new_key),
      +   PNG_EMPTY);
       
       #if PNG_ARM_NEON_IMPLEMENTATION == 1
       PNG_INTERNAL_FUNCTION(void,
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
      index b53668a09ce..79fd9ad6a82 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngread.c
      @@ -29,7 +29,7 @@
        * However, the following notice accompanied the original version of this
        * file and, per its terms, should not be removed:
        *
      - * Copyright (c) 2018-2025 Cosmin Truta
      + * Copyright (c) 2018-2026 Cosmin Truta
        * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
        * Copyright (c) 1996-1997 Andreas Dilger
        * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
      @@ -52,7 +52,8 @@
       /* Create a PNG structure for reading, and allocate any memory needed. */
       PNG_FUNCTION(png_structp,PNGAPI
       png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
      -    png_error_ptr error_fn, png_error_ptr warn_fn),PNG_ALLOCATED)
      +    png_error_ptr error_fn, png_error_ptr warn_fn),
      +    PNG_ALLOCATED)
       {
       #ifndef PNG_USER_MEM_SUPPORTED
          png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
      @@ -68,7 +69,8 @@ png_create_read_struct,(png_const_charp user_png_ver, png_voidp error_ptr,
       PNG_FUNCTION(png_structp,PNGAPI
       png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
           png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
      -    png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED)
      +    png_malloc_ptr malloc_fn, png_free_ptr free_fn),
      +    PNG_ALLOCATED)
       {
          png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr,
              error_fn, warn_fn, mem_ptr, malloc_fn, free_fn);
      @@ -548,7 +550,6 @@ png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row)
       
          if (png_ptr->read_row_fn != NULL)
             (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
      -
       }
       #endif /* SEQUENTIAL_READ */
       
      @@ -896,7 +897,7 @@ png_set_read_status_fn(png_structrp png_ptr, png_read_status_ptr read_row_fn)
       #ifdef PNG_INFO_IMAGE_SUPPORTED
       void PNGAPI
       png_read_png(png_structrp png_ptr, png_inforp info_ptr,
      -    int transforms, voidp params)
      +    int transforms, png_voidp params)
       {
          png_debug(1, "in png_read_png");
       
      @@ -1133,19 +1134,20 @@ png_read_png(png_structrp png_ptr, png_inforp info_ptr,
       
       typedef struct
       {
      -   /* Arguments: */
      +   /* Arguments */
          png_imagep image;
      -   png_voidp  buffer;
      +   png_voidp buffer;
          png_int_32 row_stride;
      -   png_voidp  colormap;
      +   png_voidp colormap;
          png_const_colorp background;
      -   /* Local variables: */
      -   png_voidp       local_row;
      -   png_voidp       first_row;
      -   ptrdiff_t       row_bytes;           /* step between rows */
      -   int             file_encoding;       /* E_ values above */
      -   png_fixed_point gamma_to_linear;     /* For P_FILE, reciprocal of gamma */
      -   int             colormap_processing; /* PNG_CMAP_ values above */
      +
      +   /* Instance variables */
      +   png_voidp local_row;
      +   png_voidp first_row;
      +   ptrdiff_t row_step;              /* step between rows */
      +   int file_encoding;               /* E_ values above */
      +   png_fixed_point gamma_to_linear; /* For P_FILE, reciprocal of gamma */
      +   int colormap_processing;         /* PNG_CMAP_ values above */
       } png_image_read_control;
       
       /* Do all the *safe* initialization - 'safe' means that png_error won't be
      @@ -2866,17 +2868,17 @@ png_image_read_and_map(png_voidp argument)
          }
       
          {
      -      png_uint_32  height = image->height;
      -      png_uint_32  width = image->width;
      -      int          proc = display->colormap_processing;
      -      png_bytep    first_row = png_voidcast(png_bytep, display->first_row);
      -      ptrdiff_t    step_row = display->row_bytes;
      +      png_uint_32 height = image->height;
      +      png_uint_32 width = image->width;
      +      int proc = display->colormap_processing;
      +      png_bytep first_row = png_voidcast(png_bytep, display->first_row);
      +      ptrdiff_t row_step = display->row_step;
             int pass;
       
             for (pass = 0; pass < passes; ++pass)
             {
      -         unsigned int     startx, stepx, stepy;
      -         png_uint_32      y;
      +         unsigned int startx, stepx, stepy;
      +         png_uint_32 y;
       
                if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
                {
      @@ -2900,7 +2902,7 @@ png_image_read_and_map(png_voidp argument)
                for (; ylocal_row);
      -            png_bytep outrow = first_row + y * step_row;
      +            png_bytep outrow = first_row + y * row_step;
                   png_const_bytep end_row = outrow + width;
       
                   /* Read read the libpng data into the temporary buffer. */
      @@ -3109,20 +3111,20 @@ png_image_read_colormapped(png_voidp argument)
           */
          {
             png_voidp first_row = display->buffer;
      -      ptrdiff_t row_bytes = display->row_stride;
      +      ptrdiff_t row_step = display->row_stride;
       
      -      /* The following expression is designed to work correctly whether it gives
      -       * a signed or an unsigned result.
      +      /* The following adjustment is to ensure that calculations are correct,
      +       * regardless whether row_step is positive or negative.
              */
      -      if (row_bytes < 0)
      +      if (row_step < 0)
             {
                char *ptr = png_voidcast(char*, first_row);
      -         ptr += (image->height-1) * (-row_bytes);
      +         ptr += (image->height-1) * (-row_step);
                first_row = png_voidcast(png_voidp, ptr);
             }
       
             display->first_row = first_row;
      -      display->row_bytes = row_bytes;
      +      display->row_step = row_step;
          }
       
          if (passes == 0)
      @@ -3140,17 +3142,17 @@ png_image_read_colormapped(png_voidp argument)
       
          else
          {
      -      png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes;
      +      ptrdiff_t row_step = display->row_step;
       
             while (--passes >= 0)
             {
      -         png_uint_32      y = image->height;
      -         png_bytep        row = png_voidcast(png_bytep, display->first_row);
      +         png_uint_32 y = image->height;
      +         png_bytep row = png_voidcast(png_bytep, display->first_row);
       
                for (; y > 0; --y)
                {
                   png_read_row(png_ptr, row, NULL);
      -            row += row_bytes;
      +            row += row_step;
                }
             }
       
      @@ -3166,9 +3168,11 @@ png_image_read_direct_scaled(png_voidp argument)
              argument);
          png_imagep image = display->image;
          png_structrp png_ptr = image->opaque->png_ptr;
      +   png_inforp info_ptr = image->opaque->info_ptr;
          png_bytep local_row = png_voidcast(png_bytep, display->local_row);
          png_bytep first_row = png_voidcast(png_bytep, display->first_row);
      -   ptrdiff_t row_bytes = display->row_bytes;
      +   ptrdiff_t row_step = display->row_step;
      +   size_t row_bytes = png_get_rowbytes(png_ptr, info_ptr);
          int passes;
       
          /* Handle interlacing. */
      @@ -3197,9 +3201,14 @@ png_image_read_direct_scaled(png_voidp argument)
                /* Read into local_row (gets transformed 8-bit data). */
                png_read_row(png_ptr, local_row, NULL);
       
      -         /* Copy from local_row to user buffer. */
      -         memcpy(output_row, local_row, (size_t)row_bytes);
      -         output_row += row_bytes;
      +         /* Copy from local_row to user buffer.
      +          * Use row_bytes (i.e. the actual size in bytes of the row data) for
      +          * copying into output_row. Use row_step for advancing output_row,
      +          * to respect the caller's stride for padding or negative (bottom-up)
      +          * layouts.
      +          */
      +         memcpy(output_row, local_row, row_bytes);
      +         output_row += row_step;
             }
          }
       
      @@ -3231,17 +3240,18 @@ png_image_read_composite(png_voidp argument)
          }
       
          {
      -      png_uint_32  height = image->height;
      -      png_uint_32  width = image->width;
      -      ptrdiff_t    step_row = display->row_bytes;
      +      png_uint_32 height = image->height;
      +      png_uint_32 width = image->width;
      +      ptrdiff_t row_step = display->row_step;
             unsigned int channels =
                 (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1;
      +      int optimize_alpha = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0;
             int pass;
       
             for (pass = 0; pass < passes; ++pass)
             {
      -         unsigned int     startx, stepx, stepy;
      -         png_uint_32      y;
      +         unsigned int startx, stepx, stepy;
      +         png_uint_32 y;
       
                if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
                {
      @@ -3273,7 +3283,7 @@ png_image_read_composite(png_voidp argument)
                   png_read_row(png_ptr, inrow, NULL);
       
                   outrow = png_voidcast(png_bytep, display->first_row);
      -            outrow += y * step_row;
      +            outrow += y * row_step;
                   end_row = outrow + width * channels;
       
                   /* Now do the composition on each pixel in this row. */
      @@ -3292,20 +3302,44 @@ png_image_read_composite(png_voidp argument)
       
                            if (alpha < 255) /* else just use component */
                            {
      -                        /* This is PNG_OPTIMIZED_ALPHA, the component value
      -                         * is a linear 8-bit value.  Combine this with the
      -                         * current outrow[c] value which is sRGB encoded.
      -                         * Arithmetic here is 16-bits to preserve the output
      -                         * values correctly.
      -                         */
      -                        component *= 257*255; /* =65535 */
      -                        component += (255-alpha)*png_sRGB_table[outrow[c]];
      +                        if (optimize_alpha != 0)
      +                        {
      +                           /* This is PNG_OPTIMIZED_ALPHA, the component value
      +                            * is a linear 8-bit value.  Combine this with the
      +                            * current outrow[c] value which is sRGB encoded.
      +                            * Arithmetic here is 16-bits to preserve the output
      +                            * values correctly.
      +                            */
      +                           component *= 257*255; /* =65535 */
      +                           component += (255-alpha)*png_sRGB_table[outrow[c]];
       
      -                        /* So 'component' is scaled by 255*65535 and is
      -                         * therefore appropriate for the sRGB to linear
      -                         * conversion table.
      -                         */
      -                        component = PNG_sRGB_FROM_LINEAR(component);
      +                           /* Clamp to the valid range to defend against
      +                            * unforeseen cases where the data might be sRGB
      +                            * instead of linear premultiplied.
      +                            * (Belt-and-suspenders for CVE-2025-66293.)
      +                            */
      +                           if (component > 255*65535)
      +                              component = 255*65535;
      +
      +                           /* So 'component' is scaled by 255*65535 and is
      +                            * therefore appropriate for the sRGB-to-linear
      +                            * conversion table.
      +                            */
      +                           component = PNG_sRGB_FROM_LINEAR(component);
      +                        }
      +                        else
      +                        {
      +                           /* Compositing was already done on the palette
      +                            * entries.  The data is sRGB premultiplied on black.
      +                            * Composite with the background in sRGB space.
      +                            * This is not gamma-correct, but matches what was
      +                            * done to the palette.
      +                            */
      +                           png_uint_32 background = outrow[c];
      +                           component += ((255-alpha) * background + 127) / 255;
      +                           if (component > 255)
      +                              component = 255;
      +                        }
                            }
       
                            outrow[c] = (png_byte)component;
      @@ -3394,12 +3428,12 @@ png_image_read_background(png_voidp argument)
                 */
                {
                   png_bytep first_row = png_voidcast(png_bytep, display->first_row);
      -            ptrdiff_t step_row = display->row_bytes;
      +            ptrdiff_t row_step = display->row_step;
       
                   for (pass = 0; pass < passes; ++pass)
                   {
      -               unsigned int     startx, stepx, stepy;
      -               png_uint_32      y;
      +               unsigned int startx, stepx, stepy;
      +               png_uint_32 y;
       
                      if (png_ptr->interlaced == PNG_INTERLACE_ADAM7)
                      {
      @@ -3426,7 +3460,7 @@ png_image_read_background(png_voidp argument)
                         {
                            png_bytep inrow = png_voidcast(png_bytep,
                                display->local_row);
      -                     png_bytep outrow = first_row + y * step_row;
      +                     png_bytep outrow = first_row + y * row_step;
                            png_const_bytep end_row = outrow + width;
       
                            /* Read the row, which is packed: */
      @@ -3471,7 +3505,7 @@ png_image_read_background(png_voidp argument)
                         {
                            png_bytep inrow = png_voidcast(png_bytep,
                                display->local_row);
      -                     png_bytep outrow = first_row + y * step_row;
      +                     png_bytep outrow = first_row + y * row_step;
                            png_const_bytep end_row = outrow + width;
       
                            /* Read the row, which is packed: */
      @@ -3517,9 +3551,9 @@ png_image_read_background(png_voidp argument)
                   png_uint_16p first_row = png_voidcast(png_uint_16p,
                       display->first_row);
                   /* The division by two is safe because the caller passed in a
      -             * stride which was multiplied by 2 (below) to get row_bytes.
      +             * stride which was multiplied by 2 (below) to get row_step.
                    */
      -            ptrdiff_t    step_row = display->row_bytes / 2;
      +            ptrdiff_t row_step = display->row_step / 2;
                   unsigned int preserve_alpha = (image->format &
                       PNG_FORMAT_FLAG_ALPHA) != 0;
                   unsigned int outchannels = 1U+preserve_alpha;
      @@ -3533,8 +3567,8 @@ png_image_read_background(png_voidp argument)
       
                   for (pass = 0; pass < passes; ++pass)
                   {
      -               unsigned int     startx, stepx, stepy;
      -               png_uint_32      y;
      +               unsigned int startx, stepx, stepy;
      +               png_uint_32 y;
       
                      /* The 'x' start and step are adjusted to output components here.
                       */
      @@ -3561,7 +3595,7 @@ png_image_read_background(png_voidp argument)
                      for (; ybuffer;
      -      ptrdiff_t row_bytes = display->row_stride;
      +      ptrdiff_t row_step = display->row_stride;
       
             if (linear != 0)
      -         row_bytes *= 2;
      +         row_step *= 2;
       
      -      /* The following expression is designed to work correctly whether it gives
      -       * a signed or an unsigned result.
      +      /* The following adjustment is to ensure that calculations are correct,
      +       * regardless whether row_step is positive or negative.
              */
      -      if (row_bytes < 0)
      +      if (row_step < 0)
             {
                char *ptr = png_voidcast(char*, first_row);
      -         ptr += (image->height-1) * (-row_bytes);
      +         ptr += (image->height - 1) * (-row_step);
                first_row = png_voidcast(png_voidp, ptr);
             }
       
             display->first_row = first_row;
      -      display->row_bytes = row_bytes;
      +      display->row_step = row_step;
          }
       
          if (do_local_compose != 0)
      @@ -4063,17 +4097,17 @@ png_image_read_direct(png_voidp argument)
       
          else
          {
      -      png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes;
      +      ptrdiff_t row_step = display->row_step;
       
             while (--passes >= 0)
             {
      -         png_uint_32      y = image->height;
      -         png_bytep        row = png_voidcast(png_bytep, display->first_row);
      +         png_uint_32 y = image->height;
      +         png_bytep row = png_voidcast(png_bytep, display->first_row);
       
                for (; y > 0; --y)
                {
                   png_read_row(png_ptr, row, NULL);
      -            row += row_bytes;
      +            row += row_step;
                }
             }
       
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
      index a19615f49fe..7680fe64828 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrtran.c
      @@ -1149,8 +1149,8 @@ png_set_rgb_to_gray(png_structrp png_ptr, int error_action, double red,
       #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
           defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
       void PNGAPI
      -png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
      -    read_user_transform_fn)
      +png_set_read_user_transform_fn(png_structrp png_ptr,
      +    png_user_transform_ptr read_user_transform_fn)
       {
          png_debug(1, "in png_set_read_user_transform_fn");
       
      @@ -1872,6 +1872,7 @@ png_init_read_transformations(png_structrp png_ptr)
                    * transformations elsewhere.
                    */
                   png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA);
      +            png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA;
                } /* color_type == PNG_COLOR_TYPE_PALETTE */
       
                /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
      index 07d53cb2c76..01bb0c8bedc 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngrutil.c
      @@ -2415,7 +2415,7 @@ png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       static png_handle_result_code /* PRIVATE */
       png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       {
      -   png_text  text_info;
      +   png_text text_info;
          png_bytep buffer;
          png_charp key;
          png_charp text;
      @@ -2488,8 +2488,8 @@ static png_handle_result_code /* PRIVATE */
       png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
       {
          png_const_charp errmsg = NULL;
      -   png_bytep       buffer;
      -   png_uint_32     keyword_length;
      +   png_bytep buffer;
      +   png_uint_32 keyword_length;
       
          png_debug(1, "in png_handle_zTXt");
       
      diff --git a/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c b/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c
      index 2350057e70e..b9f6cb5d437 100644
      --- a/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c
      +++ b/src/java.desktop/share/native/libsplashscreen/libpng/pngtrans.c
      @@ -831,8 +831,8 @@ png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
           defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
       #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
       void PNGAPI
      -png_set_user_transform_info(png_structrp png_ptr, png_voidp
      -   user_transform_ptr, int user_transform_depth, int user_transform_channels)
      +png_set_user_transform_info(png_structrp png_ptr, png_voidp user_transform_ptr,
      +    int user_transform_depth, int user_transform_channels)
       {
          png_debug(1, "in png_set_user_transform_info");
       
      
      From 599ed0bb5fd62e26c71651bc02f198cd27636cfb Mon Sep 17 00:00:00 2001
      From: SendaoYan 
      Date: Wed, 21 Jan 2026 03:39:02 +0000
      Subject: [PATCH 180/204] 8375485: Tests in vmTestbase/nsk are failing due to
       missing class unloading after 8373945
      
      Reviewed-by: lmesnik, cjplummer
      ---
       .../jvmti/scenarios/events/EM02/em02t003.java   | 17 ++++++++++++++++-
       .../vmTestbase/nsk/share/ClassUnloader.java     | 17 ++++++++++++++++-
       2 files changed, 32 insertions(+), 2 deletions(-)
      
      diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003.java
      index d4cb4a8569a..a23732dc554 100644
      --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003.java
      +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t003.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2004, 2018, 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
      @@ -105,6 +105,21 @@ public class em02t003 extends DebugeeClass {
                       return Consts.TEST_FAILED;
                   }
       
      +            while (thrd.isAlive()) {
      +                logger.display("Thread state: " + thrd.getState()
      +                    + " - waiting for completion.");
      +                try {
      +                    // small delay to avoid produce huge amount of output
      +                    Thread.sleep(100);
      +                } catch (InterruptedException e) {
      +                }
      +            }
      +            try {
      +                // give some time wait thread to exit completely
      +                Thread.sleep(2000);
      +            } catch (InterruptedException e) {
      +            }
      +
                   logger.display("MethodCompiling:: Provoke unloading compiled method - "
                                       + "\n\ttrying to unload class...");
                   thrd = null;
      diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java b/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java
      index 94b086f5757..5c7d2b8d640 100644
      --- a/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java
      +++ b/test/hotspot/jtreg/vmTestbase/nsk/share/ClassUnloader.java
      @@ -239,6 +239,9 @@ public class ClassUnloader {
            *
            * @see WhiteBox.getWhiteBox().fullGC()
            */
      +
      +    public static final int MAX_UNLOAD_ATTEMPS = 10;
      +
           public boolean unloadClass() {
       
               // free references to class and class loader to be able for collecting by GC
      @@ -247,14 +250,26 @@ public class ClassUnloader {
       
               // force class unloading by triggering full GC
               WhiteBox.getWhiteBox().fullGC();
      +        int count = 0;
      +        while (count++ < MAX_UNLOAD_ATTEMPS && !isClassLoaderReclaimed()) {
      +            System.out.println("ClassUnloader: waiting for class loader reclaiming... " + count);
      +            WhiteBox.getWhiteBox().fullGC();
      +            try {
      +                // small delay to give more changes to process objects
      +                // inside VM like jvmti deferred queue
      +                Thread.sleep(100);
      +            } catch (InterruptedException e) {
      +            }
      +        }
       
               // force GC to unload marked class loader and its classes
               if (isClassLoaderReclaimed()) {
      -            Runtime.getRuntime().gc();
      +            System.out.println("ClassUnloader: class loader has been reclaimed.");
                   return true;
               }
       
               // class loader has not been reclaimed
      +        System.out.println("ClassUnloader: class loader is still reachable.");
               return false;
           }
       }
      
      From a448f0b9f46de35ef26994e8540b9ae242372e8d Mon Sep 17 00:00:00 2001
      From: SendaoYan 
      Date: Wed, 21 Jan 2026 03:39:26 +0000
      Subject: [PATCH 181/204] 8375668: Compiler warning
       implicit-const-int-float-conversion by clang23
      
      Reviewed-by: dholmes, cnorrbin
      ---
       src/hotspot/os/linux/cgroupSubsystem_linux.hpp | 2 +-
       1 file changed, 1 insertion(+), 1 deletion(-)
      
      diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp
      index 8aafbf49c63..d083a9985c2 100644
      --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp
      +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp
      @@ -188,7 +188,7 @@ class CachedMetric : public CHeapObj{
           volatile jlong _next_check_counter;
         public:
           CachedMetric() {
      -      _metric = value_unlimited;
      +      _metric = static_cast(value_unlimited);
             _next_check_counter = min_jlong;
           }
           bool should_check_metric() {
      
      From 34d6e5e07b8ee43ee7f913dd47fa7c897f52e6c0 Mon Sep 17 00:00:00 2001
      From: Kim Barrett 
      Date: Wed, 21 Jan 2026 05:56:19 +0000
      Subject: [PATCH 182/204] 8375737: Fix -Wzero-as-null-pointer-constant warnings
       in arm32 code
      
      Reviewed-by: dholmes
      ---
       src/hotspot/cpu/arm/frame_arm.cpp         | 6 +++---
       src/hotspot/cpu/arm/nativeInst_arm_32.cpp | 6 +++---
       src/hotspot/cpu/arm/nativeInst_arm_32.hpp | 4 ++--
       3 files changed, 8 insertions(+), 8 deletions(-)
      
      diff --git a/src/hotspot/cpu/arm/frame_arm.cpp b/src/hotspot/cpu/arm/frame_arm.cpp
      index 7a23296a3d4..f791fae7bd7 100644
      --- a/src/hotspot/cpu/arm/frame_arm.cpp
      +++ b/src/hotspot/cpu/arm/frame_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
      @@ -356,10 +356,10 @@ frame frame::sender_for_interpreter_frame(RegisterMap* map) const {
       bool frame::is_interpreted_frame_valid(JavaThread* thread) const {
         assert(is_interpreted_frame(), "Not an interpreted frame");
         // These are reasonable sanity checks
      -  if (fp() == 0 || (intptr_t(fp()) & (wordSize-1)) != 0) {
      +  if (fp() == nullptr || (intptr_t(fp()) & (wordSize-1)) != 0) {
           return false;
         }
      -  if (sp() == 0 || (intptr_t(sp()) & (wordSize-1)) != 0) {
      +  if (sp() == nullptr || (intptr_t(sp()) & (wordSize-1)) != 0) {
           return false;
         }
         if (fp() + interpreter_frame_initial_sp_offset < sp()) {
      diff --git a/src/hotspot/cpu/arm/nativeInst_arm_32.cpp b/src/hotspot/cpu/arm/nativeInst_arm_32.cpp
      index 232294b246a..df780ac31a6 100644
      --- a/src/hotspot/cpu/arm/nativeInst_arm_32.cpp
      +++ b/src/hotspot/cpu/arm/nativeInst_arm_32.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
      @@ -172,7 +172,7 @@ void NativeMovConstReg::set_data(intptr_t x, address pc) {
       
           address addr = oop_addr != nullptr ? (address)oop_addr : (address)metadata_addr;
       
      -    if(pc == 0) {
      +    if (pc == nullptr) {
             offset = addr - instruction_address() - 8;
           } else {
             offset = addr - pc - 8;
      @@ -228,7 +228,7 @@ void NativeMovConstReg::set_data(intptr_t x, address pc) {
       
       void NativeMovConstReg::set_pc_relative_offset(address addr, address pc) {
         int offset;
      -  if (pc == 0) {
      +  if (pc == nullptr) {
           offset = addr - instruction_address() - 8;
         } else {
           offset = addr - pc - 8;
      diff --git a/src/hotspot/cpu/arm/nativeInst_arm_32.hpp b/src/hotspot/cpu/arm/nativeInst_arm_32.hpp
      index 82385bf0244..2b52db89285 100644
      --- a/src/hotspot/cpu/arm/nativeInst_arm_32.hpp
      +++ b/src/hotspot/cpu/arm/nativeInst_arm_32.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
      @@ -371,7 +371,7 @@ class NativeMovConstReg: public NativeInstruction {
        public:
       
         intptr_t data() const;
      -  void set_data(intptr_t x, address pc = 0);
      +  void set_data(intptr_t x, address pc = nullptr);
         bool is_pc_relative() {
           return !is_movw();
         }
      
      From b5727d27622e1e321733f8d0e606b366984104be Mon Sep 17 00:00:00 2001
      From: Kim Barrett 
      Date: Wed, 21 Jan 2026 06:04:09 +0000
      Subject: [PATCH 183/204] 8375738: Fix -Wzero-as-null-pointer-constant warnings
       in MacOSX/bsd code
      
      Reviewed-by: erikj, dholmes
      ---
       make/hotspot/lib/CompileGtest.gmk           | 5 +++--
       src/hotspot/os/bsd/memMapPrinter_macosx.cpp | 2 +-
       src/hotspot/os/bsd/os_bsd.cpp               | 4 ++--
       3 files changed, 6 insertions(+), 5 deletions(-)
      
      diff --git a/make/hotspot/lib/CompileGtest.gmk b/make/hotspot/lib/CompileGtest.gmk
      index 60912992134..327014b1e9d 100644
      --- a/make/hotspot/lib/CompileGtest.gmk
      +++ b/make/hotspot/lib/CompileGtest.gmk
      @@ -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
      @@ -61,7 +61,8 @@ $(eval $(call SetupJdkLibrary, BUILD_GTEST_LIBGTEST, \
           INCLUDE_FILES := gtest-all.cc gmock-all.cc, \
           DISABLED_WARNINGS_gcc := format-nonliteral maybe-uninitialized undef \
               unused-result zero-as-null-pointer-constant, \
      -    DISABLED_WARNINGS_clang := format-nonliteral undef unused-result, \
      +    DISABLED_WARNINGS_clang := format-nonliteral undef unused-result \
      +        zero-as-null-pointer-constant, \
           DISABLED_WARNINGS_microsoft := 4530, \
           DEFAULT_CFLAGS := false, \
           CFLAGS := $(JVM_CFLAGS) \
      diff --git a/src/hotspot/os/bsd/memMapPrinter_macosx.cpp b/src/hotspot/os/bsd/memMapPrinter_macosx.cpp
      index 6fd08d63e85..30e258c9d2c 100644
      --- a/src/hotspot/os/bsd/memMapPrinter_macosx.cpp
      +++ b/src/hotspot/os/bsd/memMapPrinter_macosx.cpp
      @@ -132,7 +132,7 @@ public:
         static const char* tagToStr(uint32_t user_tag) {
           switch (user_tag) {
             case 0:
      -        return 0;
      +        return nullptr;
             X1(MALLOC, malloc);
             X1(MALLOC_SMALL, malloc_small);
             X1(MALLOC_LARGE, malloc_large);
      diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp
      index 667810bd6ca..0e21c2d1785 100644
      --- a/src/hotspot/os/bsd/os_bsd.cpp
      +++ b/src/hotspot/os/bsd/os_bsd.cpp
      @@ -628,7 +628,7 @@ static void *thread_native_entry(Thread *thread) {
         log_info(os, thread)("Thread finished (tid: %zu, pthread id: %zu).",
           os::current_thread_id(), (uintx) pthread_self());
       
      -  return 0;
      +  return nullptr;
       }
       
       bool os::create_thread(Thread* thread, ThreadType thr_type,
      @@ -1420,7 +1420,7 @@ int os::get_loaded_modules_info(os::LoadedModulesCallbackFunc callback, void *pa
       #elif defined(__APPLE__)
         for (uint32_t i = 1; i < _dyld_image_count(); i++) {
           // Value for top_address is returned as 0 since we don't have any information about module size
      -    if (callback(_dyld_get_image_name(i), (address)_dyld_get_image_header(i), (address)0, param)) {
      +    if (callback(_dyld_get_image_name(i), (address)_dyld_get_image_header(i), nullptr, param)) {
             return 1;
           }
         }
      
      From 560a92a6327221c90596bcd17a87722e4910472a Mon Sep 17 00:00:00 2001
      From: Jie Fu 
      Date: Wed, 21 Jan 2026 06:33:54 +0000
      Subject: [PATCH 184/204] 8375787: compiler/vectorapi/TestCastShapeBadOpc.java
       fails with release VMs
      
      Reviewed-by: syan, lmesnik, fyang, epeter
      ---
       test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java | 2 ++
       1 file changed, 2 insertions(+)
      
      diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java b/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java
      index 4c20c84bc50..743c243cb58 100644
      --- a/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java
      +++ b/test/hotspot/jtreg/compiler/vectorapi/TestCastShapeBadOpc.java
      @@ -30,6 +30,7 @@
        * @run main/othervm
        *      -XX:+IgnoreUnrecognizedVMOptions
        *      -XX:-TieredCompilation -Xbatch
      + *      -XX:+UnlockDiagnosticVMOptions
        *      -XX:StressSeed=1462975402
        *      -XX:+StressIncrementalInlining
        *      -XX:CompileCommand=compileonly,${test.main.class}::test2
      @@ -44,6 +45,7 @@
        * @run main/othervm
        *      -XX:+IgnoreUnrecognizedVMOptions
        *      -XX:-TieredCompilation -Xbatch
      + *      -XX:+UnlockDiagnosticVMOptions
        *      -XX:+StressIncrementalInlining
        *      -XX:CompileCommand=compileonly,${test.main.class}::test2
        *      ${test.main.class}
      
      From 4f87fb53ee5c6071fa57dfe9452eca9fe7b460ee Mon Sep 17 00:00:00 2001
      From: Thomas Schatzl 
      Date: Wed, 21 Jan 2026 09:01:00 +0000
      Subject: [PATCH 185/204] 8375622: G1: Convert G1CodeRootSet to use Atomic
      
      Reviewed-by: shade, sjohanss
      ---
       src/hotspot/share/gc/g1/g1CodeRootSet.cpp | 14 +++++++-------
       1 file changed, 7 insertions(+), 7 deletions(-)
      
      diff --git a/src/hotspot/share/gc/g1/g1CodeRootSet.cpp b/src/hotspot/share/gc/g1/g1CodeRootSet.cpp
      index 60ad3a2af32..ca4487876b9 100644
      --- a/src/hotspot/share/gc/g1/g1CodeRootSet.cpp
      +++ b/src/hotspot/share/gc/g1/g1CodeRootSet.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
      @@ -28,7 +28,7 @@
       #include "gc/g1/g1HeapRegion.hpp"
       #include "memory/allocation.hpp"
       #include "oops/oop.inline.hpp"
      -#include "runtime/atomicAccess.hpp"
      +#include "runtime/atomic.hpp"
       #include "utilities/concurrentHashTable.inline.hpp"
       #include "utilities/concurrentHashTableTasks.inline.hpp"
       
      @@ -60,7 +60,7 @@ class G1CodeRootSetHashTable : public CHeapObj {
         HashTable _table;
         HashTableScanTask _table_scanner;
       
      -  size_t volatile _num_entries;
      +  Atomic _num_entries;
       
         bool is_empty() const { return number_of_entries() == 0; }
       
      @@ -120,7 +120,7 @@ public:
           bool grow_hint = false;
           bool inserted = _table.insert(Thread::current(), lookup, method, &grow_hint);
           if (inserted) {
      -      AtomicAccess::inc(&_num_entries);
      +      _num_entries.add_then_fetch(1u);
           }
           if (grow_hint) {
             _table.grow(Thread::current());
      @@ -131,7 +131,7 @@ public:
           HashTableLookUp lookup(method);
           bool removed = _table.remove(Thread::current(), lookup);
           if (removed) {
      -      AtomicAccess::dec(&_num_entries);
      +      _num_entries.sub_then_fetch(1u);
           }
           return removed;
         }
      @@ -182,7 +182,7 @@ public:
           guarantee(succeeded, "unable to clean table");
       
           if (num_deleted != 0) {
      -      size_t current_size = AtomicAccess::sub(&_num_entries, num_deleted);
      +      size_t current_size = _num_entries.sub_then_fetch(num_deleted);
             shrink_to_match(current_size);
           }
         }
      @@ -226,7 +226,7 @@ public:
       
         size_t mem_size() { return sizeof(*this) + _table.get_mem_size(Thread::current()); }
       
      -  size_t number_of_entries() const { return AtomicAccess::load(&_num_entries); }
      +  size_t number_of_entries() const { return _num_entries.load_relaxed(); }
       };
       
       uintx G1CodeRootSetHashTable::HashTableLookUp::get_hash() const {
      
      From b1340305c8f5ea53b45b8bd3bd2ebe8f74864d40 Mon Sep 17 00:00:00 2001
      From: Ivan Walulya 
      Date: Wed, 21 Jan 2026 09:51:01 +0000
      Subject: [PATCH 186/204] 8238686: G1 may waste lots of space or fail to
       uncommit when observing MinHeapFreeRatio during sizing after full gc
      
      Reviewed-by: tschatzl, sjohanss
      ---
       src/hotspot/share/gc/g1/g1Arguments.cpp | 13 ++++++++++++-
       1 file changed, 12 insertions(+), 1 deletion(-)
      
      diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp
      index 58e76cdd43a..ffb06a7d822 100644
      --- a/src/hotspot/share/gc/g1/g1Arguments.cpp
      +++ b/src/hotspot/share/gc/g1/g1Arguments.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) 2017, Red Hat, Inc. and/or its affiliates.
        * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        *
      @@ -209,6 +209,17 @@ void G1Arguments::initialize() {
           FLAG_SET_DEFAULT(GCTimeRatio, 24);
         }
       
      +  // Do not interfere with GC-Pressure driven heap resizing unless the user
      +  // explicitly sets otherwise. G1 heap sizing should be free to grow or shrink
      +  // the heap based on GC pressure, rather than being forced to satisfy
      +  // MinHeapFreeRatio or MaxHeapFreeRatio defaults that the user did not set.
      +  if (FLAG_IS_DEFAULT(MinHeapFreeRatio)) {
      +    FLAG_SET_DEFAULT(MinHeapFreeRatio, 0);
      +  }
      +  if (FLAG_IS_DEFAULT(MaxHeapFreeRatio)) {
      +    FLAG_SET_DEFAULT(MaxHeapFreeRatio, 100);
      +  }
      +
         // Below, we might need to calculate the pause time interval based on
         // the pause target. When we do so we are going to give G1 maximum
         // flexibility and allow it to do pauses when it needs to. So, we'll
      
      From 5c7c2f093b83a017970d9d05c258b4c0910bfc2c Mon Sep 17 00:00:00 2001
      From: Francesco Andreuzzi 
      Date: Wed, 21 Jan 2026 10:42:05 +0000
      Subject: [PATCH 187/204] 8375717: Outdated link in jdk.jfr.internal.JVM
       javadoc
      
      Reviewed-by: egahlin
      ---
       src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java | 12 ++++++------
       1 file changed, 6 insertions(+), 6 deletions(-)
      
      diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java
      index 841221c57d9..24c92e81a4c 100644
      --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java
      +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java
      @@ -71,7 +71,7 @@ public final class JVM {
           /**
            * Begin recording events
            *
      -     * Requires that JFR has been started with {@link #createNativeJFR()}
      +     * Requires that JFR has been started with {@link JVMSupport#createJFR()}
            */
           public static native void beginRecording();
       
      @@ -83,7 +83,7 @@ public final class JVM {
           /**
            * End recording events, which includes flushing data in thread buffers
            *
      -     * Requires that JFR has been started with {@link #createNativeJFR()}
      +     * Requires that JFR has been started with {@link JVMSupport#createJFR()}
            *
            */
           public static native void endRecording();
      @@ -144,7 +144,7 @@ public final class JVM {
           /**
            * Return unique identifier for stack trace.
            *
      -     * Requires that JFR has been started with {@link #createNativeJFR()}
      +     * Requires that JFR has been started with {@link JVMSupport#createJFR()}
            *
            * @param skipCount number of frames to skip, or 0 if no frames should be
            *                  skipped
      @@ -295,7 +295,7 @@ public final class JVM {
           /**
            * Sets the file where data should be written.
            *
      -     * Requires that JFR has been started with {@link #createNativeJFR()}
      +     * Requires that JFR has been started with {@link JVMSupport#createJFR()}
            *
            * 
            * Recording  Previous  Current  Action
      @@ -380,7 +380,7 @@ public final class JVM {
            * chunk, data should be written after GMT offset and size of metadata event
            * should be adjusted
            *
      -     * Requires that JFR has been started with {@link #createNativeJFR()}
      +     * Requires that JFR has been started with {@link JVMSupport#createJFR()}
            *
            * @param bytes binary representation of metadata descriptor
            */
      @@ -409,7 +409,7 @@ public final class JVM {
           /**
            * Destroys native part of JFR. If already destroy, call is ignored.
            *
      -     * Requires that JFR has been started with {@link #createNativeJFR()}
      +     * Requires that JFR has been started with {@link JVMSupport#createJFR()}
            *
            * @return if an instance was actually destroyed.
            *
      
      From 983ae96f60c935aa52f482d21ae6a0d947679541 Mon Sep 17 00:00:00 2001
      From: Jatin Bhateja 
      Date: Wed, 21 Jan 2026 11:20:18 +0000
      Subject: [PATCH 188/204] 8375498: [VectorAPI] Dump primary vector IR details
       with -XX:+TraceNewVectors
      
      Reviewed-by: epeter
      ---
       src/hotspot/share/opto/vectorIntrinsics.cpp | 81 ++++++++++++---------
       1 file changed, 45 insertions(+), 36 deletions(-)
      
      diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp
      index 6dcf4615b10..65d54e076b6 100644
      --- a/src/hotspot/share/opto/vectorIntrinsics.cpp
      +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp
      @@ -74,6 +74,11 @@ static bool is_vector_mask(ciKlass* klass) {
         return klass->is_subclass_of(ciEnv::current()->vector_VectorMask_klass());
       }
       
      +static Node* trace_vector(Node* operation) {
      +  VectorNode::trace_new_vector(operation, "VectorAPI");
      +  return operation;
      +}
      +
       bool LibraryCallKit::arch_supports_vector_rotate(int opc, int num_elem, BasicType elem_bt,
                                                        VectorMaskUseType mask_use_type, bool has_scalar_args) {
         bool is_supported = true;
      @@ -458,7 +463,7 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) {
           }
           default: fatal("unsupported arity: %d", n);
         }
      -
      +  trace_vector(operation);
         if (is_masked_op && mask != nullptr) {
           if (use_predicate) {
             operation->add_req(mask);
      @@ -466,7 +471,7 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) {
           } else {
             operation->add_flag(Node::Flag_is_predicated_using_blend);
             operation = gvn().transform(operation);
      -      operation = new VectorBlendNode(opd1, operation, mask);
      +      operation = trace_vector(new VectorBlendNode(opd1, operation, mask));
           }
         }
         operation = gvn().transform(operation);
      @@ -627,7 +632,7 @@ bool LibraryCallKit::inline_vector_mask_operation() {
           mask_vec = gvn().transform(VectorStoreMaskNode::make(gvn(), mask_vec, elem_bt, num_elem));
         }
         const Type* maskoper_ty = mopc == Op_VectorMaskToLong ? (const Type*)TypeLong::LONG : (const Type*)TypeInt::INT;
      -  Node* maskoper = gvn().transform(VectorMaskOpNode::make(mask_vec, maskoper_ty, mopc));
      +  Node* maskoper = gvn().transform(trace_vector(VectorMaskOpNode::make(mask_vec, maskoper_ty, mopc)));
         if (mopc != Op_VectorMaskToLong) {
           maskoper = ConvI2L(maskoper);
         }
      @@ -710,10 +715,10 @@ bool LibraryCallKit::inline_vector_frombits_coerced() {
         if (opc == Op_VectorLongToMask) {
           const TypeVect* vt = TypeVect::makemask(elem_bt, num_elem);
           if (Matcher::mask_op_prefers_predicate(opc, vt)) {
      -      broadcast = gvn().transform(new VectorLongToMaskNode(elem, vt));
      +      broadcast = gvn().transform(trace_vector(new VectorLongToMaskNode(elem, vt)));
           } else {
             const TypeVect* mvt = TypeVect::make(T_BOOLEAN, num_elem);
      -      broadcast = gvn().transform(new VectorLongToMaskNode(elem, mvt));
      +      broadcast = gvn().transform(trace_vector(new VectorLongToMaskNode(elem, mvt)));
             broadcast = gvn().transform(new VectorLoadMaskNode(broadcast, vt));
           }
         } else {
      @@ -741,7 +746,7 @@ bool LibraryCallKit::inline_vector_frombits_coerced() {
             }
             default: fatal("%s", type2name(elem_bt));
           }
      -    broadcast = VectorNode::scalar2vector(elem, num_elem, elem_bt, is_mask);
      +    broadcast = trace_vector(VectorNode::scalar2vector(elem, num_elem, elem_bt, is_mask));
           broadcast = gvn().transform(broadcast);
         }
       
      @@ -927,22 +932,22 @@ bool LibraryCallKit::inline_vector_mem_operation(bool is_store) {
           if (is_mask) {
             val = gvn().transform(VectorStoreMaskNode::make(gvn(), val, elem_bt, num_elem));
           }
      -    Node* vstore = gvn().transform(StoreVectorNode::make(0, control(), memory(addr), addr, addr_type, val, store_num_elem));
      +    Node* vstore = gvn().transform(trace_vector(StoreVectorNode::make(0, control(), memory(addr), addr, addr_type, val, store_num_elem)));
           set_memory(vstore, addr_type);
         } else {
           // When using byte array, we need to load as byte then reinterpret the value. Otherwise, do a simple vector load.
           Node* vload = nullptr;
           if (mismatched_ms) {
      -      vload = gvn().transform(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, mem_num_elem, mem_elem_bt));
      +      vload = gvn().transform(trace_vector(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, mem_num_elem, mem_elem_bt)));
             const TypeVect* to_vect_type = TypeVect::make(elem_bt, num_elem);
             vload = gvn().transform(new VectorReinterpretNode(vload, vload->bottom_type()->is_vect(), to_vect_type));
           } else {
             // Special handle for masks
             if (is_mask) {
      -        vload = gvn().transform(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, num_elem, T_BOOLEAN));
      +        vload = gvn().transform(trace_vector(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, num_elem, T_BOOLEAN)));
               vload = gvn().transform(new VectorLoadMaskNode(vload, TypeVect::makemask(elem_bt, num_elem)));
             } else {
      -        vload = gvn().transform(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, num_elem, elem_bt));
      +        vload = gvn().transform(trace_vector(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, num_elem, elem_bt)));
             }
           }
           Node* box = box_vector(vload, vbox_type, elem_bt, num_elem);
      @@ -1140,7 +1145,7 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) {
             const TypeVect* to_mask_type = TypeVect::makemask(mem_elem_bt, mem_num_elem);
             mask = gvn().transform(new VectorReinterpretNode(mask, from_mask_type, to_mask_type));
           }
      -    Node* vstore = gvn().transform(new StoreVectorMaskedNode(control(), memory(addr), addr, val, addr_type, mask));
      +    Node* vstore = gvn().transform(trace_vector(new StoreVectorMaskedNode(control(), memory(addr), addr, val, addr_type, mask)));
           set_memory(vstore, addr_type);
         } else {
           Node* vload = nullptr;
      @@ -1155,13 +1160,13 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) {
           if (supports_predicate) {
             // Generate masked load vector node if predicate feature is supported.
             const TypeVect* vt = TypeVect::make(mem_elem_bt, mem_num_elem);
      -      vload = gvn().transform(new LoadVectorMaskedNode(control(), memory(addr), addr, addr_type, vt, mask));
      +      vload = gvn().transform(trace_vector(new LoadVectorMaskedNode(control(), memory(addr), addr, addr_type, vt, mask)));
           } else {
             // Use the vector blend to implement the masked load vector. The biased elements are zeros.
             Node* zero = gvn().transform(gvn().zerocon(mem_elem_bt));
             zero = gvn().transform(VectorNode::scalar2vector(zero, mem_num_elem, mem_elem_bt));
      -      vload = gvn().transform(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, mem_num_elem, mem_elem_bt));
      -      vload = gvn().transform(new VectorBlendNode(zero, vload, mask));
      +      vload = gvn().transform(trace_vector(LoadVectorNode::make(0, control(), memory(addr), addr, addr_type, mem_num_elem, mem_elem_bt)));
      +      vload = gvn().transform(trace_vector(new VectorBlendNode(zero, vload, mask)));
           }
       
           if (mismatched_ms) {
      @@ -1365,17 +1370,17 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) {
       
           Node* vstore = nullptr;
           if (mask != nullptr) {
      -      vstore = gvn().transform(new StoreVectorScatterMaskedNode(control(), memory(addr), addr, addr_type, val, indexes, mask));
      +      vstore = gvn().transform(trace_vector(new StoreVectorScatterMaskedNode(control(), memory(addr), addr, addr_type, val, indexes, mask)));
           } else {
      -      vstore = gvn().transform(new StoreVectorScatterNode(control(), memory(addr), addr, addr_type, val, indexes));
      +      vstore = gvn().transform(trace_vector(new StoreVectorScatterNode(control(), memory(addr), addr, addr_type, val, indexes)));
           }
           set_memory(vstore, addr_type);
         } else {
           Node* vload = nullptr;
           if (mask != nullptr) {
      -      vload = gvn().transform(new LoadVectorGatherMaskedNode(control(), memory(addr), addr, addr_type, vector_type, indexes, mask));
      +      vload = gvn().transform(trace_vector(new LoadVectorGatherMaskedNode(control(), memory(addr), addr, addr_type, vector_type, indexes, mask)));
           } else {
      -      vload = gvn().transform(new LoadVectorGatherNode(control(), memory(addr), addr, addr_type, vector_type, indexes));
      +      vload = gvn().transform(trace_vector(new LoadVectorGatherNode(control(), memory(addr), addr, addr_type, vector_type, indexes)));
           }
           Node* box = box_vector(vload, vbox_type, elem_bt, num_elem);
           set_result(box);
      @@ -1493,7 +1498,7 @@ bool LibraryCallKit::inline_vector_reduction() {
       
         // Make an unordered Reduction node. This affects only AddReductionVF/VD and MulReductionVF/VD,
         // as these operations are allowed to be associative (not requiring strict order) in VectorAPI.
      -  value = ReductionNode::make(opc, nullptr, init, value, elem_bt, /* requires_strict_order */ false);
      +  value = trace_vector(ReductionNode::make(opc, nullptr, init, value, elem_bt, /* requires_strict_order */ false));
       
         if (mask != nullptr && use_predicate) {
           value->add_req(mask);
      @@ -1585,7 +1590,7 @@ bool LibraryCallKit::inline_vector_test() {
           return false; // operand unboxing failed
         }
       
      -  Node* cmp = gvn().transform(new VectorTestNode(opd1, opd2, booltest));
      +  Node* cmp = gvn().transform(trace_vector(new VectorTestNode(opd1, opd2, booltest)));
         BoolTest::mask test = Matcher::vectortest_mask(booltest == BoolTest::overflow,
                                                        opd1->bottom_type()->isa_vectmask(), num_elem);
         Node* bol = gvn().transform(new BoolNode(cmp, test));
      @@ -1653,7 +1658,7 @@ bool LibraryCallKit::inline_vector_blend() {
           return false; // operand unboxing failed
         }
       
      -  Node* blend = gvn().transform(new VectorBlendNode(v1, v2, mask));
      +  Node* blend = gvn().transform(trace_vector(new VectorBlendNode(v1, v2, mask)));
       
         Node* box = box_vector(blend, vbox_type, elem_bt, num_elem);
         set_result(box);
      @@ -1748,6 +1753,7 @@ bool LibraryCallKit::inline_vector_compare() {
       
         const TypeVect* vmask_type = TypeVect::makemask(mask_bt, num_elem);
         Node* operation = new VectorMaskCmpNode(pred, v1, v2, pred_node, vmask_type);
      +  trace_vector(operation);
       
         if (is_masked_op) {
           if (use_predicate) {
      @@ -1887,6 +1893,7 @@ bool LibraryCallKit::inline_vector_rearrange() {
         }
       
         Node* rearrange = new VectorRearrangeNode(v1, shuffle);
      +  trace_vector(rearrange);
         if (is_masked_op) {
           if (use_predicate) {
             rearrange->add_req(mask);
      @@ -2034,6 +2041,7 @@ bool LibraryCallKit::inline_vector_select_from() {
       
         // and finally rearrange
         Node* rearrange = new VectorRearrangeNode(v2, shuffle);
      +  trace_vector(rearrange);
         if (is_masked_op) {
           if (use_predicate) {
             // masked rearrange is supported so use that directly
      @@ -2193,6 +2201,7 @@ bool LibraryCallKit::inline_vector_broadcast_int() {
         }
       
         Node* operation = VectorNode::make(opc, opd1, opd2, num_elem, elem_bt);
      +  trace_vector(operation);
         if (is_masked_op && mask != nullptr) {
           if (use_predicate) {
             operation->add_req(mask);
      @@ -2370,7 +2379,7 @@ bool LibraryCallKit::inline_vector_convert() {
               return false;
             }
       
      -      op = gvn().transform(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_for_cast));
      +      op = gvn().transform(trace_vector(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_for_cast)));
             // Now ensure that the destination gets properly resized to needed size.
             op = gvn().transform(new VectorReinterpretNode(op, op->bottom_type()->is_vect(), dst_type));
           } else if (num_elem_from > num_elem_to) {
      @@ -2391,7 +2400,7 @@ bool LibraryCallKit::inline_vector_convert() {
       
             const TypeVect* resize_type = TypeVect::make(elem_bt_from, num_elem_for_resize);
             op = gvn().transform(new VectorReinterpretNode(op, src_type, resize_type));
      -      op = gvn().transform(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_to));
      +      op = gvn().transform(trace_vector(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_to)));
           } else { // num_elem_from == num_elem_to
             if (is_mask) {
               // Make sure that cast for vector mask is implemented to particular type/size combination.
      @@ -2400,16 +2409,16 @@ bool LibraryCallKit::inline_vector_convert() {
                                 num_elem_to, type2name(elem_bt_to), is_mask);
                 return false;
               }
      -        op = gvn().transform(new VectorMaskCastNode(op, dst_type));
      +        op = gvn().transform(trace_vector(new VectorMaskCastNode(op, dst_type)));
             } else {
               // Since input and output number of elements match, and since we know this vector size is
               // supported, simply do a cast with no resize needed.
      -        op = gvn().transform(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_to));
      +        op = gvn().transform(trace_vector(VectorCastNode::make(cast_vopc, op, elem_bt_to, num_elem_to)));
             }
           }
         } else if (!Type::equals(src_type, dst_type)) {
           assert(!is_cast, "must be reinterpret");
      -    op = gvn().transform(new VectorReinterpretNode(op, src_type, dst_type));
      +    op = gvn().transform(trace_vector(new VectorReinterpretNode(op, src_type, dst_type)));
         }
       
         const TypeInstPtr* vbox_type_to = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass_to);
      @@ -2494,7 +2503,7 @@ bool LibraryCallKit::inline_vector_insert() {
           default: fatal("%s", type2name(elem_bt)); break;
         }
       
      -  Node* operation = gvn().transform(VectorInsertNode::make(opd, insert_val, idx->get_con(), gvn()));
      +  Node* operation = gvn().transform(trace_vector(VectorInsertNode::make(opd, insert_val, idx->get_con(), gvn())));
       
         Node* vbox = box_vector(operation, vbox_type, elem_bt, num_elem);
         set_result(vbox);
      @@ -2552,7 +2561,7 @@ bool LibraryCallKit::inline_vector_extract() {
             if (opd == nullptr) {
               return false;
             }
      -      opd = gvn().transform(VectorStoreMaskNode::make(gvn(), opd, elem_bt, num_elem));
      +      opd = gvn().transform(trace_vector(VectorStoreMaskNode::make(gvn(), opd, elem_bt, num_elem)));
             opd = gvn().transform(new ExtractUBNode(opd, pos));
             opd = gvn().transform(new ConvI2LNode(opd));
           } else if (arch_supports_vector(Op_VectorMaskToLong, num_elem, elem_bt, VecMaskUseLoad)) {
      @@ -2562,7 +2571,7 @@ bool LibraryCallKit::inline_vector_extract() {
             }
             // VectorMaskToLongNode requires the input is either a mask or a vector with BOOLEAN type.
             if (!Matcher::mask_op_prefers_predicate(Op_VectorMaskToLong, opd->bottom_type()->is_vect())) {
      -        opd = gvn().transform(VectorStoreMaskNode::make(gvn(), opd, elem_bt, num_elem));
      +        opd = gvn().transform(trace_vector(VectorStoreMaskNode::make(gvn(), opd, elem_bt, num_elem)));
             }
             // ((toLong() >>> pos) & 1L
             opd = gvn().transform(new VectorMaskToLongNode(opd, TypeLong::LONG));
      @@ -2680,8 +2689,8 @@ static Node* LowerSelectFromTwoVectorOperation(PhaseGVN& phase, Node* index_vec,
         vmask_type = TypeVect::makemask(elem_bt, num_elem);
         mask = phase.transform(new VectorMaskCastNode(mask, vmask_type));
       
      -  Node* p1 = phase.transform(new VectorRearrangeNode(src1, wrapped_index_vec));
      -  Node* p2 = phase.transform(new VectorRearrangeNode(src2, wrapped_index_vec));
      +  Node* p1 = phase.transform(trace_vector(new VectorRearrangeNode(src1, wrapped_index_vec)));
      +  Node* p2 = phase.transform(trace_vector(new VectorRearrangeNode(src2, wrapped_index_vec)));
       
         return new VectorBlendNode(p2, p1, mask);
       }
      @@ -2799,7 +2808,7 @@ bool LibraryCallKit::inline_vector_select_from_two_vectors() {
           Node* wrap_mask = gvn().makecon(TypeInteger::make(indexRangeMask, indexRangeMask, Type::WidenMin, index_elem_bt != T_LONG ? T_INT : index_elem_bt));
           Node* wrap_mask_vec = gvn().transform(VectorNode::scalar2vector(wrap_mask, num_elem, index_elem_bt, false));
           opd1 = gvn().transform(VectorNode::make(Op_AndV, opd1, wrap_mask_vec, opd1->bottom_type()->is_vect()));
      -    operation = gvn().transform(VectorNode::make(Op_SelectFromTwoVector, opd1, opd2, opd3, vt));
      +    operation = gvn().transform(trace_vector(VectorNode::make(Op_SelectFromTwoVector, opd1, opd2, opd3, vt)));
         }
       
         // Wrap it up in VectorBox to keep object type information.
      @@ -2884,7 +2893,7 @@ bool LibraryCallKit::inline_vector_compress_expand() {
         }
       
         const TypeVect* vt = TypeVect::make(elem_bt, num_elem, opc == Op_CompressM);
      -  Node* operation = gvn().transform(VectorNode::make(opc, opd1, mask, vt));
      +  Node* operation = gvn().transform(trace_vector(VectorNode::make(opc, opd1, mask, vt)));
       
         // Wrap it up in VectorBox to keep object type information.
         const TypeInstPtr* box_type = opc == Op_CompressM ? mbox_type : vbox_type;
      @@ -3017,12 +3026,12 @@ bool LibraryCallKit::inline_index_vector() {
             default: fatal("%s", type2name(elem_bt));
           }
           scale = gvn().transform(VectorNode::scalar2vector(scale, num_elem, elem_bt));
      -    index = gvn().transform(VectorNode::make(vmul_op, index, scale, vt));
      +    index = gvn().transform(trace_vector(VectorNode::make(vmul_op, index, scale, vt)));
         }
       
         // Add "opd" if addition is needed.
         if (needs_add) {
      -    index = gvn().transform(VectorNode::make(vadd_op, opd, index, vt));
      +    index = gvn().transform(trace_vector(VectorNode::make(vadd_op, opd, index, vt)));
         }
         Node* vbox = box_vector(index, vbox_type, elem_bt, num_elem);
         set_result(vbox);
      @@ -3139,7 +3148,7 @@ bool LibraryCallKit::inline_index_partially_in_upper_range() {
           // Compute the vector mask with "mask = iota < indexLimit".
           ConINode* pred_node = (ConINode*)gvn().makecon(TypeInt::make(BoolTest::lt));
           const TypeVect* vmask_type = TypeVect::makemask(elem_bt, num_elem);
      -    mask = gvn().transform(new VectorMaskCmpNode(BoolTest::lt, iota, indexLimit, pred_node, vmask_type));
      +    mask = gvn().transform(trace_vector(new VectorMaskCmpNode(BoolTest::lt, iota, indexLimit, pred_node, vmask_type)));
         }
         Node* vbox = box_vector(mask, box_type, elem_bt, num_elem);
         set_result(vbox);
      
      From 4c9103f7b6c91b0f237859516ef72bb9ee27157e Mon Sep 17 00:00:00 2001
      From: Matthias Baesken 
      Date: Wed, 21 Jan 2026 14:14:33 +0000
      Subject: [PATCH 189/204] 8374998: Failing os::write - remove bad file
      
      Reviewed-by: mdoerr, lucy
      ---
       src/hotspot/os/posix/perfMemory_posix.cpp | 5 +++++
       1 file changed, 5 insertions(+)
      
      diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp
      index 08a19270943..ce9c2a4f031 100644
      --- a/src/hotspot/os/posix/perfMemory_posix.cpp
      +++ b/src/hotspot/os/posix/perfMemory_posix.cpp
      @@ -112,6 +112,10 @@ static void save_memory_to_file(char* addr, size_t size) {
           result = ::close(fd);
           if (result == OS_ERR) {
             warning("Could not close %s: %s\n", destfile, os::strerror(errno));
      +    } else {
      +      if (!successful_write) {
      +        remove(destfile);
      +      }
           }
         }
         FREE_C_HEAP_ARRAY(char, destfile);
      @@ -949,6 +953,7 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size
               warning("Insufficient space for shared memory file: %s/%s\n", dirname, filename);
             }
             result = OS_ERR;
      +      remove(filename);
             break;
           }
         }
      
      From 3033e6f421d0f6e0aea1d976a806d7abca7c6360 Mon Sep 17 00:00:00 2001
      From: Kim Barrett 
      Date: Wed, 21 Jan 2026 14:55:26 +0000
      Subject: [PATCH 190/204] 8375544: JfrSet::clear should not use memset
      
      Reviewed-by: mgronlun
      ---
       src/hotspot/share/jfr/utilities/jfrSet.hpp | 10 +++++++++-
       1 file changed, 9 insertions(+), 1 deletion(-)
      
      diff --git a/src/hotspot/share/jfr/utilities/jfrSet.hpp b/src/hotspot/share/jfr/utilities/jfrSet.hpp
      index 3d394d10d8b..c443434800a 100644
      --- a/src/hotspot/share/jfr/utilities/jfrSet.hpp
      +++ b/src/hotspot/share/jfr/utilities/jfrSet.hpp
      @@ -26,6 +26,7 @@
       #define SHARE_JFR_UTILITIES_JFRSET_HPP
       
       #include "cppstdlib/new.hpp"
      +#include "cppstdlib/type_traits.hpp"
       #include "jfr/utilities/jfrTypes.hpp"
       #include "memory/allocation.hpp"
       
      @@ -110,7 +111,14 @@ class JfrSetStorage : public AnyObj {
         }
       
         void clear() {
      -    memset(_table, 0, _table_size * sizeof(K));
      +    for (unsigned i = 0; i < _table_size; ++i) {
      +      if constexpr (std::is_copy_assignable_v) {
      +        _table[i] = K{};
      +      } else {
      +        _table[i].~K();
      +        ::new (&_table[i]) K{};
      +      }
      +    }
         }
       };
       
      
      From 17086d31196827432477391fd2921a82868eaa05 Mon Sep 17 00:00:00 2001
      From: Maurizio Cimadamore 
      Date: Wed, 21 Jan 2026 16:14:35 +0000
      Subject: [PATCH 191/204] 8375646: Some parser flags seem unused
      
      Reviewed-by: jlahoda, vromero
      ---
       .../sun/tools/javac/parser/JavacParser.java   | 114 ++++++++----------
       1 file changed, 51 insertions(+), 63 deletions(-)
      
      diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
      index d3539b53541..d658275d4dc 100644
      --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
      +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
      @@ -271,16 +271,11 @@ public class JavacParser implements Parser {
           /** When terms are parsed, the mode determines which is expected:
            *     mode = EXPR        : an expression
            *     mode = TYPE        : a type
      -     *     mode = NOPARAMS    : no parameters allowed for type
      -     *     mode = TYPEARG     : type argument
      -     *     mode |= NOLAMBDA   : lambdas are not allowed
      +     *     mode = NOLAMBDA    : lambdas are not allowed
            */
           protected static final int EXPR          = 1 << 0;
           protected static final int TYPE          = 1 << 1;
      -    protected static final int NOPARAMS      = 1 << 2;
      -    protected static final int TYPEARG       = 1 << 3;
      -    protected static final int DIAMOND       = 1 << 4;
      -    protected static final int NOLAMBDA      = 1 << 5;
      +    protected static final int NOLAMBDA      = 1 << 2;
       
           protected void setMode(int mode) {
               this.mode = mode;
      @@ -1439,12 +1434,6 @@ public class JavacParser implements Parser {
               int startMode = mode;
               List typeArgs = typeArgumentsOpt(EXPR);
               switch (token.kind) {
      -        case QUES:
      -            if (isMode(TYPE) && isMode(TYPEARG) && !isMode(NOPARAMS)) {
      -                selectTypeMode();
      -                return typeArgument();
      -            } else
      -                return illegal();
               case PLUSPLUS: case SUBSUB: case BANG: case TILDE: case PLUS: case SUB:
                   if (typeArgs == null && isMode(EXPR)) {
                       TokenKind tk = token.kind;
      @@ -1522,7 +1511,7 @@ public class JavacParser implements Parser {
                   if (isMode(EXPR)) {
                       selectExprMode();
                       nextToken();
      -                if (token.kind == LT) typeArgs = typeArguments(false);
      +                if (token.kind == LT) typeArgs = typeArguments();
                       t = creator(pos, typeArgs);
                       typeArgs = null;
                   } else return illegal();
      @@ -1625,7 +1614,6 @@ public class JavacParser implements Parser {
                                   return illegal();
                               }
                               int prevmode = mode;
      -                        setMode(mode & ~NOPARAMS);
                               typeArgs = typeArgumentsOpt(EXPR);
                               setMode(prevmode);
                               if (isMode(EXPR)) {
      @@ -1653,7 +1641,7 @@ public class JavacParser implements Parser {
                                       selectExprMode();
                                       int pos1 = token.pos;
                                       nextToken();
      -                                if (token.kind == LT) typeArgs = typeArguments(false);
      +                                if (token.kind == LT) typeArgs = typeArguments();
                                       t = innerCreator(pos1, typeArgs, t);
                                       typeArgs = null;
                                       break loop;
      @@ -1704,7 +1692,7 @@ public class JavacParser implements Parser {
                                       nextToken();
                                       selectTypeMode();
                                       t = toP(F.at(token.pos).Select(t, ident()));
      -                                t = typeArgumentsOpt(t);
      +                                t = typeApplyOpt(t);
                                   }
                                   t = bracketsOpt(t);
                                   if (token.kind != COLCOL) {
      @@ -1721,7 +1709,7 @@ public class JavacParser implements Parser {
                       }
                   }
                   if (typeArgs != null) illegal();
      -            t = typeArgumentsOpt(t);
      +            t = typeApplyOpt(t);
                   break;
               case BYTE: case SHORT: case CHAR: case INT: case LONG: case FLOAT:
               case DOUBLE: case BOOLEAN:
      @@ -1880,7 +1868,7 @@ public class JavacParser implements Parser {
                           selectExprMode();
                           int pos2 = token.pos;
                           nextToken();
      -                    if (token.kind == LT) typeArgs = typeArguments(false);
      +                    if (token.kind == LT) typeArgs = typeArguments();
                           t = innerCreator(pos2, typeArgs, t);
                           typeArgs = null;
                       } else {
      @@ -1900,7 +1888,7 @@ public class JavacParser implements Parser {
                           if (tyannos != null && tyannos.nonEmpty()) {
                               t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
                           }
      -                    t = argumentsOpt(typeArgs, typeArgumentsOpt(t));
      +                    t = argumentsOpt(typeArgs, typeApplyOpt(t));
                           typeArgs = null;
                       }
                   } else if (isMode(EXPR) && token.kind == COLCOL) {
      @@ -2302,7 +2290,7 @@ public class JavacParser implements Parser {
               } else {
                   int pos = token.pos;
                   accept(DOT);
      -            typeArgs = (token.kind == LT) ? typeArguments(false) : null;
      +            typeArgs = (token.kind == LT) ? typeArguments() : null;
                   t = toP(F.at(pos).Select(t, ident()));
                   t = argumentsOpt(typeArgs, t);
               }
      @@ -2373,12 +2361,11 @@ public class JavacParser implements Parser {
       
           /**  TypeArgumentsOpt = [ TypeArguments ]
            */
      -    JCExpression typeArgumentsOpt(JCExpression t) {
      +    JCExpression typeApplyOpt(JCExpression t) {
               if (token.kind == LT &&
      -            isMode(TYPE) &&
      -            !isMode(NOPARAMS)) {
      +            isMode(TYPE)) {
                   selectTypeMode();
      -            return typeArguments(t, false);
      +            return typeApply(t);
               } else {
                   return t;
               }
      @@ -2389,12 +2376,11 @@ public class JavacParser implements Parser {
       
           List typeArgumentsOpt(int useMode) {
               if (token.kind == LT) {
      -            if (!isMode(useMode) ||
      -                isMode(NOPARAMS)) {
      +            if (!isMode(useMode)) {
                       illegal();
                   }
                   setMode(useMode);
      -            return typeArguments(false);
      +            return typeArguments();
               }
               return null;
           }
      @@ -2404,35 +2390,29 @@ public class JavacParser implements Parser {
            *  TypeArguments  = "<" TypeArgument {"," TypeArgument} ">"
            *  }
            */
      -    List typeArguments(boolean diamondAllowed) {
      +    List typeArguments() {
               if (token.kind == LT) {
                   nextToken();
      -            if (token.kind == GT && diamondAllowed) {
      -                setMode(mode | DIAMOND);
      +            ListBuffer args = new ListBuffer<>();
      +            args.append(!isMode(EXPR) ? typeArgument() : parseType());
      +            while (token.kind == COMMA) {
                       nextToken();
      -                return List.nil();
      -            } else {
      -                ListBuffer args = new ListBuffer<>();
                       args.append(!isMode(EXPR) ? typeArgument() : parseType());
      -                while (token.kind == COMMA) {
      -                    nextToken();
      -                    args.append(!isMode(EXPR) ? typeArgument() : parseType());
      -                }
      -                switch (token.kind) {
      -
      -                case GTGTGTEQ: case GTGTEQ: case GTEQ:
      -                case GTGTGT: case GTGT:
      -                    token = S.split();
      -                    break;
      -                case GT:
      -                    nextToken();
      -                    break;
      -                default:
      -                    args.append(syntaxError(token.pos, Errors.Expected2(GT, COMMA)));
      -                    break;
      -                }
      -                return args.toList();
                   }
      +            switch (token.kind) {
      +
      +            case GTGTGTEQ: case GTGTEQ: case GTEQ:
      +            case GTGTGT: case GTGT:
      +                token = S.split();
      +                break;
      +            case GT:
      +                nextToken();
      +                break;
      +            default:
      +                args.append(syntaxError(token.pos, Errors.Expected2(GT, COMMA)));
      +                break;
      +            }
      +            return args.toList();
               } else {
                   return List.of(syntaxError(token.pos, Errors.Expected(LT)));
               }
      @@ -2480,12 +2460,23 @@ public class JavacParser implements Parser {
               return result;
           }
       
      -    JCTypeApply typeArguments(JCExpression t, boolean diamondAllowed) {
      +    JCTypeApply typeApply(JCExpression t) {
               int pos = token.pos;
      -        List args = typeArguments(diamondAllowed);
      +        List args = typeArguments();
               return toP(F.at(pos).TypeApply(t, args));
           }
       
      +    JCTypeApply typeApplyOrDiamond(JCExpression t) {
      +        if (peekToken(GT)) {
      +            int pos = token.pos;
      +            accept(LT);
      +            accept(GT);
      +            return toP(F.at(pos).TypeApply(t, List.nil()));
      +        } else {
      +            return typeApply(t);
      +        }
      +    }
      +
           /**
            * BracketsOpt = { [Annotations] "[" "]" }*
            *
      @@ -2585,7 +2576,7 @@ public class JavacParser implements Parser {
               selectExprMode();
               List typeArgs = null;
               if (token.kind == LT) {
      -            typeArgs = typeArguments(false);
      +            typeArgs = typeArguments();
               }
               Name refName;
               ReferenceMode refMode;
      @@ -2622,15 +2613,13 @@ public class JavacParser implements Parser {
       
               int prevmode = mode;
               selectTypeMode();
      -        boolean diamondFound = false;
               int lastTypeargsPos = -1;
               if (token.kind == LT) {
                   lastTypeargsPos = token.pos;
      -            t = typeArguments(t, true);
      -            diamondFound = isMode(DIAMOND);
      +            t = typeApplyOrDiamond(t);
               }
               while (token.kind == DOT) {
      -            if (diamondFound) {
      +            if (TreeInfo.isDiamond(t)) {
                       //cannot select after a diamond
                       illegal();
                   }
      @@ -2645,8 +2634,7 @@ public class JavacParser implements Parser {
       
                   if (token.kind == LT) {
                       lastTypeargsPos = token.pos;
      -                t = typeArguments(t, true);
      -                diamondFound = isMode(DIAMOND);
      +                t = typeApplyOrDiamond(t);
                   }
               }
               setMode(prevmode);
      @@ -2657,7 +2645,7 @@ public class JavacParser implements Parser {
                   }
       
                   JCExpression e = arrayCreatorRest(newpos, t);
      -            if (diamondFound) {
      +            if (TreeInfo.isDiamond(t)) {
                       reportSyntaxError(lastTypeargsPos, Errors.CannotCreateArrayWithDiamond);
                       return toP(F.at(newpos).Erroneous(List.of(e)));
                   }
      @@ -2702,7 +2690,7 @@ public class JavacParser implements Parser {
       
               if (token.kind == LT) {
                   int prevmode = mode;
      -            t = typeArguments(t, true);
      +            t = typeApplyOrDiamond(t);
                   setMode(prevmode);
               }
               return classCreatorRest(newpos, encl, typeArgs, t);
      
      From a0ac5b34a742cf18d86f3ac77110bcaa00192169 Mon Sep 17 00:00:00 2001
      From: Damon Nguyen 
      Date: Wed, 21 Jan 2026 18:47:39 +0000
      Subject: [PATCH 192/204] 8375775: JDK 26 RDP2 L10n resource files update
      
      Reviewed-by: naoto, jlu, liach
      ---
       .../com/sun/tools/javac/resources/compiler_zh_CN.properties     | 2 +-
       .../classes/com/sun/tools/javac/resources/javac_ja.properties   | 2 +-
       .../com/sun/tools/javac/resources/javac_zh_CN.properties        | 2 +-
       .../classes/jdk/tools/jlink/resources/plugins_de.properties     | 2 +-
       .../classes/jdk/tools/jlink/resources/plugins_ja.properties     | 2 +-
       .../classes/jdk/tools/jlink/resources/plugins_zh_CN.properties  | 2 +-
       6 files changed, 6 insertions(+), 6 deletions(-)
      
      diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties
      index 861f371632d..900557a29da 100644
      --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties
      +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler_zh_CN.properties
      @@ -292,7 +292,7 @@ compiler.err.annotation.decl.not.allowed.here=此处不允许批注接口声明
       compiler.err.cant.inherit.from.final=无法从最终{0}进行继承
       
       # 0: symbol or name
      -compiler.err.cant.ref.before.ctor.called=对 {0} 的引用只能在显式调用构造器后显示
      +compiler.err.cant.ref.before.ctor.called=对 {0} 的引用只能在显式调用构造器后出现
       
       # 0: symbol or name
       compiler.err.cant.assign.initialized.before.ctor.called=对初始化字段 ''{0}'' 的分配只能在显式调用构造器后显示
      diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties
      index 0ea1796a8e6..3ae7ab1690e 100644
      --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties
      +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_ja.properties
      @@ -60,7 +60,7 @@ javac.opt.target=指定されたJava SEリリースに適したクラス・フ
       javac.opt.release=指定されたJava SEリリースに対してコンパイルします。サポートされているリリース: \n    {0}
       javac.opt.source=指定されたJava SEリリースとソースの互換性を保持します。サポートされているリリース: \n    {0}
       javac.opt.Werror=警告が発生した場合にコンパイルを終了します
      -javac.opt.arg.Werror=(,)*
      +javac.opt.arg.Werror=<キー>(,<キー>)*
       javac.opt.Werror.custom=コンパイルを終了する警告のlintカテゴリを\nコンマで区切って指定します。\n指定したカテゴリを除外するには、キーの前に''-''を指定します。\nサポートされているキーを表示するには--help-lintを使用します。
       javac.opt.A=注釈プロセッサに渡されるオプション
       javac.opt.implicit=暗黙的に参照されるファイルについてクラス・ファイルを生成するかどうかを指定する
      diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties
      index 0cbfac7e778..447d0d26239 100644
      --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties
      +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties
      @@ -60,7 +60,7 @@ javac.opt.target=生成适合指定的 Java SE 发行版的类文件。支持的
       javac.opt.release=为指定的 Java SE 发行版编译。支持的发行版:{0}
       javac.opt.source=提供与指定的 Java SE 发行版的源兼容性。支持的发行版:{0}
       javac.opt.Werror=出现任何警告时终止编译
      -javac.opt.arg.Werror=(,)*
      +javac.opt.arg.Werror=<键>(,<键>)*
       javac.opt.Werror.custom=指定出现警告时应终止编译的 lint 类别,\n以逗号分隔。\n在关键字前面加上 ''-'' 可排除指定的类别。\n使用 --help-lint 可查看支持的关键字。
       javac.opt.A=传递给批注处理程序的选项
       javac.opt.implicit=指定是否为隐式引用文件生成类文件
      diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_de.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_de.properties
      index 80d1ba6e05f..c313d6a348d 100644
      --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_de.properties
      +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_de.properties
      @@ -170,7 +170,7 @@ plugin.opt.resources-last-sorter=\      --resources-last-sorter     Das le
       
       plugin.opt.disable-plugin=\      --disable-plugin      Deaktiviert das angegebene Plug-in
       
      -plugin.opt.compress=\      --compress              Komprimiert alle Ressourcen im Ausgabeimage:\n                                        Zulässige Werte:\n                                        zip-'{0-9}', wobei "zip-0" für keine Komprimierung\n                                        und "zip-9" für die beste Komprimierung steht.\n                                        Standardwert ist "zip-6."\n                                        Veraltete Werte, die in einem zukünftigen Release entfernt werden:\n                                        0:  Keine Komprimierung. Verwenden Sie stattdessen "zip-0".\n                                        1:  Gemeinsame Verwendung konstanter Zeichenfolgen\n                                        2:  ZIP. Verwenden Sie stattdessen "zip-6".
      +plugin.opt.compress=\      --compress              Komprimiert alle Ressourcen im Ausgabeimage:\n                                        Zulässige Werte:\n                                        zip-'{0-9}', wobei "zip-0" für keine Komprimierung\n                                        und "zip-9" für die beste Komprimierung steht.\n                                        Standardwert ist "zip-6".\n                                        Veraltete Werte, die in einem zukünftigen Release entfernt werden:\n                                        0:  Keine Komprimierung. Verwenden Sie stattdessen "zip-0".\n                                        1:  Gemeinsame Verwendung konstanter Zeichenfolgen\n                                        2:  ZIP. Verwenden Sie stattdessen "zip-6".
       
       plugin.opt.strip-debug=\  -G, --strip-debug                     Entfernt Debuginformationen
       
      diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties
      index 6ed0a486132..a5dc70061f6 100644
      --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties
      +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_ja.properties
      @@ -170,7 +170,7 @@ plugin.opt.resources-last-sorter=\      --resources-last-sorter     最後
       
       plugin.opt.disable-plugin=\      --disable-plugin      指定したプラグインを無効にします
       
      -plugin.opt.compress=\      --compress              出力イメージ内のすべてのリソースを圧縮します:\n                                        使用可能な値は\n                                        zip-'{0-9}'です。zip-0では圧縮は行われず、\n                                        zip-9では最適な圧縮が行われます。\n                                        デフォルトはzip-6です。\n                                        今後のリリースで削除される非推奨の値:\n                                        0:  圧縮なし。かわりにzip-0を使用。\n                                        1:  定数文字列の共有\n                                        2:  ZIP。かわりにzip-6を使用。
      +plugin.opt.compress=\      --compress <圧縮>             出力イメージ内のすべてのリソースを圧縮します:\n                                        使用可能な値は\n                                        zip-'{0-9}'です。zip-0では圧縮は行われず、\n                                        zip-9では最適な圧縮が行われます。\n                                        デフォルトはzip-6です。\n                                        今後のリリースで削除される非推奨の値:\n                                        0:  圧縮なし。かわりにzip-0を使用。\n                                        1:  定数文字列の共有\n                                        2:  ZIP。かわりにzip-6を使用。
       
       plugin.opt.strip-debug=\  -G, --strip-debug                     デバッグ情報を削除します
       
      diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_zh_CN.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_zh_CN.properties
      index a0480a31fc3..1de1ef372b6 100644
      --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_zh_CN.properties
      +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins_zh_CN.properties
      @@ -170,7 +170,7 @@ plugin.opt.resources-last-sorter=\      --resources-last-sorter     允许
       
       plugin.opt.disable-plugin=\      --disable-plugin      禁用所提及的插件
       
      -plugin.opt.compress=\      --compress              在输出映像中压缩所有资源:\n                                        接受的值包括:\n                                        zip-'{0-9}',其中 zip-0 表示无压缩,\n                                        zip-9 表示最佳压缩。\n                                        默认值为 zip-6。\n                                        要在未来发行版中删除的已过时值:\n                                        0:无压缩。改为使用 zip-0。\n                                        1:常量字符串共享\n                                        2:ZIP。改为使用 zip-6。
      +plugin.opt.compress=\      --compress <压缩>             在输出映像中压缩所有资源:\n                                        接受的值包括:\n                                        zip-'{0-9}',其中 zip-0 表示无压缩,\n                                        zip-9 表示最佳压缩。\n                                        默认值为 zip-6。\n                                        要在未来发行版中删除的已过时值:\n                                        0:无压缩。改为使用 zip-0。\n                                        1:常量字符串共享\n                                        2:ZIP。改为使用 zip-6。
       
       plugin.opt.strip-debug=\  -G, --strip-debug                     去除调试信息
       
      
      From 3d919ad43a041eb60ce51e78831c77fd3b109aee Mon Sep 17 00:00:00 2001
      From: Serguei Spitsyn 
      Date: Thu, 22 Jan 2026 01:53:42 +0000
      Subject: [PATCH 193/204] 8373366: HandshakeState should disallow suspend ops
       for disabler threads 8375362: Deadlock with unmount of suspended virtual
       thread interrupting another virtual thread
      
      Reviewed-by: lmesnik, pchilanomate
      ---
       src/hotspot/share/runtime/handshake.cpp       |   6 +-
       src/hotspot/share/runtime/javaThread.cpp      |  12 +-
       src/hotspot/share/runtime/javaThread.hpp      |   8 +-
       .../share/runtime/mountUnmountDisabler.cpp    |  15 +-
       .../share/runtime/suspendResumeManager.cpp    |   3 +-
       .../ThreadStateTest2/ThreadStateTest2.java    | 144 ++++++++++++++++++
       .../ThreadStateTest2/libThreadStateTest2.cpp  | 118 ++++++++++++++
       7 files changed, 286 insertions(+), 20 deletions(-)
       create mode 100644 test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/ThreadStateTest2.java
       create mode 100644 test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/libThreadStateTest2.cpp
      
      diff --git a/src/hotspot/share/runtime/handshake.cpp b/src/hotspot/share/runtime/handshake.cpp
      index 89b02717a7a..b54068d65d6 100644
      --- a/src/hotspot/share/runtime/handshake.cpp
      +++ b/src/hotspot/share/runtime/handshake.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
      @@ -521,8 +521,8 @@ HandshakeOperation* HandshakeState::get_op_for_self(bool allow_suspend, bool che
         assert(_lock.owned_by_self(), "Lock must be held");
         assert(allow_suspend || !check_async_exception, "invalid case");
       #if INCLUDE_JVMTI
      -  if (allow_suspend && _handshakee->is_disable_suspend()) {
      -    // filter out suspend operations while JavaThread is in disable_suspend mode
      +  if (allow_suspend && (_handshakee->is_disable_suspend() || _handshakee->is_vthread_transition_disabler())) {
      +    // filter out suspend operations while JavaThread can not be suspended
           allow_suspend = false;
         }
       #endif
      diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp
      index 4ee9a9dfd79..e73347f35d8 100644
      --- a/src/hotspot/share/runtime/javaThread.cpp
      +++ b/src/hotspot/share/runtime/javaThread.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) 2021, Azul Systems, Inc. All rights reserved.
        * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        *
      @@ -499,7 +499,7 @@ JavaThread::JavaThread(MemTag mem_tag) :
         _suspend_resume_manager(this, &_handshake._lock),
       
         _is_in_vthread_transition(false),
      -  DEBUG_ONLY(_is_vthread_transition_disabler(false) COMMA)
      +  JVMTI_ONLY(_is_vthread_transition_disabler(false) COMMA)
         DEBUG_ONLY(_is_disabler_at_start(false) COMMA)
       
         _popframe_preserved_args(nullptr),
      @@ -1165,11 +1165,13 @@ void JavaThread::set_is_in_vthread_transition(bool val) {
         AtomicAccess::store(&_is_in_vthread_transition, val);
       }
       
      -#ifdef ASSERT
      +#if INCLUDE_JVMTI
       void JavaThread::set_is_vthread_transition_disabler(bool val) {
         _is_vthread_transition_disabler = val;
       }
      +#endif
       
      +#ifdef ASSERT
       void JavaThread::set_is_disabler_at_start(bool val) {
         _is_disabler_at_start = val;
       }
      @@ -1183,7 +1185,9 @@ void JavaThread::set_is_disabler_at_start(bool val) {
       //
       bool JavaThread::java_suspend(bool register_vthread_SR) {
         // Suspending a vthread transition disabler can cause deadlocks.
      -  assert(!is_vthread_transition_disabler(), "no suspend allowed for vthread transition disablers");
      +  // The HandshakeState::has_operation does not allow such suspends.
      +  // But the suspender thread is an exclusive transition disablers, so there can't be other disabers here.
      +  JVMTI_ONLY(assert(!is_vthread_transition_disabler(), "suspender thread is an exclusive transition disabler");)
       
         guarantee(Thread::is_JavaThread_protected(/* target */ this),
                   "target JavaThread is not protected in calling context.");
      diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp
      index d4c12887e10..1aae37c0697 100644
      --- a/src/hotspot/share/runtime/javaThread.hpp
      +++ b/src/hotspot/share/runtime/javaThread.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.
        * Copyright (c) 2021, Azul Systems, Inc. All rights reserved.
        * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
        *
      @@ -734,14 +734,14 @@ public:
       
       private:
         bool _is_in_vthread_transition;                    // thread is in virtual thread mount state transition
      -  DEBUG_ONLY(bool _is_vthread_transition_disabler;)  // thread currently disabled vthread transitions
      +  JVMTI_ONLY(bool _is_vthread_transition_disabler;)  // thread currently disabled vthread transitions
         DEBUG_ONLY(bool _is_disabler_at_start;)            // thread at process of disabling vthread transitions
       public:
         bool is_in_vthread_transition() const;
         void set_is_in_vthread_transition(bool val);
      +  JVMTI_ONLY(bool is_vthread_transition_disabler() const { return _is_vthread_transition_disabler; })
      +  JVMTI_ONLY(void set_is_vthread_transition_disabler(bool val);)
       #ifdef ASSERT
      -  bool is_vthread_transition_disabler() const       { return _is_vthread_transition_disabler; }
      -  void set_is_vthread_transition_disabler(bool val);
         bool is_disabler_at_start() const                 { return _is_disabler_at_start; }
         void set_is_disabler_at_start(bool val);
       #endif
      diff --git a/src/hotspot/share/runtime/mountUnmountDisabler.cpp b/src/hotspot/share/runtime/mountUnmountDisabler.cpp
      index 8635eeb2dcc..65a82d6c563 100644
      --- a/src/hotspot/share/runtime/mountUnmountDisabler.cpp
      +++ b/src/hotspot/share/runtime/mountUnmountDisabler.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
      @@ -129,7 +129,8 @@ bool MountUnmountDisabler::is_start_transition_disabled(JavaThread* thread, oop
         int base_disable_count = notify_jvmti_events() ? 1 : 0;
         return java_lang_Thread::vthread_transition_disable_count(vthread) > 0
                || global_vthread_transition_disable_count() > base_disable_count
      -         JVMTI_ONLY(|| (JvmtiVTSuspender::is_vthread_suspended(java_lang_Thread::thread_id(vthread)) || thread->is_suspended()));
      +         JVMTI_ONLY(|| (!thread->is_vthread_transition_disabler() &&
      +                        (JvmtiVTSuspender::is_vthread_suspended(java_lang_Thread::thread_id(vthread)) || thread->is_suspended())));
       }
       
       void MountUnmountDisabler::start_transition(JavaThread* current, oop vthread, bool is_mount, bool is_thread_end) {
      @@ -294,7 +295,7 @@ MountUnmountDisabler::disable_transition_for_one() {
         // carrierThread to float up.
         // This pairs with the release barrier in end_transition().
         OrderAccess::acquire();
      -  DEBUG_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(true);)
      +  JVMTI_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(true);)
       }
       
       // disable transitions for all virtual threads
      @@ -335,7 +336,7 @@ MountUnmountDisabler::disable_transition_for_all() {
         // carrierThread to float up.
         // This pairs with the release barrier in end_transition().
         OrderAccess::acquire();
      -  DEBUG_ONLY(thread->set_is_vthread_transition_disabler(true);)
      +  JVMTI_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(true);)
         DEBUG_ONLY(thread->set_is_disabler_at_start(false);)
       }
       
      @@ -358,14 +359,12 @@ MountUnmountDisabler::enable_transition_for_one() {
         if (java_lang_Thread::vthread_transition_disable_count(_vthread()) == 0) {
           ml.notify_all();
         }
      -  DEBUG_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(false);)
      +  JVMTI_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(false);)
       }
       
       // enable transitions for all virtual threads
       void
       MountUnmountDisabler::enable_transition_for_all() {
      -  JavaThread* thread = JavaThread::current();
      -
         // End of the critical section. If some target was unmounted, we need a
         // release barrier before decrementing _global_vthread_transition_disable_count
         // to make sure any memory operations executed by the disabler are visible to
      @@ -384,7 +383,7 @@ MountUnmountDisabler::enable_transition_for_all() {
         if (global_vthread_transition_disable_count() == base_disable_count || _is_exclusive) {
           ml.notify_all();
         }
      -  DEBUG_ONLY(thread->set_is_vthread_transition_disabler(false);)
      +  JVMTI_ONLY(JavaThread::current()->set_is_vthread_transition_disabler(false);)
       }
       
       int MountUnmountDisabler::global_vthread_transition_disable_count() {
      diff --git a/src/hotspot/share/runtime/suspendResumeManager.cpp b/src/hotspot/share/runtime/suspendResumeManager.cpp
      index 067579b6386..3408d763e57 100644
      --- a/src/hotspot/share/runtime/suspendResumeManager.cpp
      +++ b/src/hotspot/share/runtime/suspendResumeManager.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
      @@ -130,6 +130,7 @@ void SuspendResumeManager::do_owner_suspend() {
         assert(_state_lock->owned_by_self(), "Lock must be held");
         assert(!_target->has_last_Java_frame() || _target->frame_anchor()->walkable(), "should have walkable stack");
         assert(_target->thread_state() == _thread_blocked, "Caller should have transitioned to _thread_blocked");
      +  JVMTI_ONLY(assert(!_target->is_vthread_transition_disabler(), "attempt to suspend a vthread transition disabler");)
       
         while (is_suspended()) {
           log_trace(thread, suspend)("JavaThread:" INTPTR_FORMAT " suspended", p2i(_target));
      diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/ThreadStateTest2.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/ThreadStateTest2.java
      new file mode 100644
      index 00000000000..ce3f2a5fe40
      --- /dev/null
      +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/ThreadStateTest2.java
      @@ -0,0 +1,144 @@
      +/*
      + * 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 8373366
      + * @summary HandshakeState should disallow suspend ops for disabler threads
      + * @requires vm.continuations
      + * @requires vm.jvmti
      + * @requires vm.compMode != "Xcomp"
      + * @modules java.base/java.lang:+open
      + * @library /test/lib
      + * @run main/othervm/native -agentlib:ThreadStateTest2 ThreadStateTest2
      + */
      +
      +import java.util.concurrent.atomic.AtomicBoolean;
      +import java.util.concurrent.Executors;
      +import java.util.concurrent.ExecutorService;
      +import java.util.concurrent.ThreadFactory;
      +import jdk.test.lib.thread.VThreadScheduler;
      +
      +/* Testing scenario:
      + * Several threads are involved:
      + *  - VT-0: a virtual thread which is interrupt-friendly and constantly interrupted with JVMTI InterruptThread
      + *  - VT-1: a virtual thread which state is constantly checked with JVMTI GetThreadState
      + *  - VT-2: a virtual thread: in a loop calls JVMTI InterruptThread(VT-0) and GetThreadState(VT-1)
      + *  - main: a platform thread: in a loop invokes native method testSuspendResume which suspends and resumes VT-2
      + * The JVMTI functions above install a MountUnmountDisabler for target virtual thread (VT-0 or VT-1).
      + * The goal is to catch VT-2 in an attempt to self-suspend while in a context of MountUnmountDisabler.
      + * This would mean there is a suspend point while VT-2 is in a context of MountUnmountDisabler.
      + * The InterruptThread implementation does a Java upcall to j.l.Thread::interrupt().
      + * The JavaCallWrapper constructor has such a suspend point.
      + */
      +public class ThreadStateTest2 {
      +    private static native void setMonitorContendedMode(boolean enable);
      +    private static native void testSuspendResume(Thread vthread);
      +    private static native void testInterruptThread(Thread vthread);
      +    private static native int testGetThreadState(Thread vthread);
      +
      +    static Thread vthread0;
      +    static Thread vthread1;
      +    static Thread vthread2;
      +    static AtomicBoolean vt2Started = new AtomicBoolean();
      +    static AtomicBoolean vt2Finished = new AtomicBoolean();
      +
      +    static void log(String msg) { System.out.println(msg); }
      +
      +    // Should handle interruptions from vthread2.
      +    final Runnable FOO_0 = () -> {
      +        log("VT-0 started");
      +        while (!vt2Finished.get()) {
      +            try {
      +                Thread.sleep(10);
      +            } catch (InterruptedException ie) {
      +                // ignore
      +            }
      +        }
      +        log("VT-0 finished");
      +    };
      +
      +    // A target for vthread2 to check state with JVMTI GetThreadState.
      +    final Runnable FOO_1 = () -> {
      +        log("VT-1 started");
      +        while (!vt2Finished.get()) {
      +            Thread.yield();
      +        }
      +        log("VT-1 finished");
      +    };
      +
      +    // In a loop execute JVMTI functions on threads vthread0 and vthread1:
      +    // InterruptThread(vthread0) and GetThreadState(vthread1).
      +    final Runnable FOO_2 = () -> {
      +        log("VT-2 started");
      +        vt2Started.set(true);
      +        for (int i = 0; i < 40; i++) {
      +            testInterruptThread(vthread0);
      +            int state = testGetThreadState(vthread1);
      +            if (state == 2) {
      +                break;
      +            }
      +            Thread.yield();
      +        }
      +        vt2Finished.set(true);
      +        log("VT-2 finished");
      +    };
      +
      +    private void runTest() throws Exception {
      +        // Force creation of JvmtiThreadState on vthread start.
      +        setMonitorContendedMode(true);
      +
      +        ExecutorService scheduler = Executors.newFixedThreadPool(2);
      +        ThreadFactory factory = VThreadScheduler.virtualThreadBuilder(scheduler).factory();
      +
      +        vthread0 = factory.newThread(FOO_0);
      +        vthread1 = factory.newThread(FOO_1);
      +        vthread2 = factory.newThread(FOO_2);
      +        vthread0.setName("VT-0");
      +        vthread1.setName("VT-1");
      +        vthread2.setName("VT-2");
      +        vthread0.start();
      +        vthread1.start();
      +        vthread2.start();
      +
      +        // Give some time for vthreads to start.
      +        while (!vt2Started.get()) {
      +            Thread.sleep(1);
      +        }
      +        while (!vt2Finished.get() /* && tryCount-- > 0 */) {
      +            testSuspendResume(vthread2);
      +        }
      +        vthread0.join();
      +        vthread1.join();
      +        vthread2.join();
      +
      +        // Let all carriers go away.
      +        scheduler.shutdown();
      +        Thread.sleep(20);
      +    }
      +
      +    public static void main(String[] args) throws Exception {
      +        ThreadStateTest2 obj = new ThreadStateTest2();
      +        obj.runTest();
      +    }
      +}
      diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/libThreadStateTest2.cpp b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/libThreadStateTest2.cpp
      new file mode 100644
      index 00000000000..8464e8ebc57
      --- /dev/null
      +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/ThreadStateTest2/libThreadStateTest2.cpp
      @@ -0,0 +1,118 @@
      +/*
      + * 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 
      +#include 
      +#include 
      +#include 
      +#include "jvmti_common.hpp"
      +
      +// set by Agent_OnLoad
      +static jvmtiEnv* jvmti = nullptr;
      +static jrawMonitorID agent_event_lock = nullptr;
      +
      +extern "C" {
      +
      +static void JNICALL
      +MonitorContended(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread,
      +                 jobject object) {
      +}
      +
      +JNIEXPORT void JNICALL
      +Java_ThreadStateTest2_testSuspendResume(JNIEnv* jni, jclass klass, jthread thread) {
      +  jvmtiError err;
      +  RawMonitorLocker event_locker(jvmti, jni, agent_event_lock);
      +
      +  LOG("\nMAIN: testSuspendResume: before suspend\n");
      +  err = jvmti->SuspendThread(thread);
      +  if (err == JVMTI_ERROR_THREAD_NOT_ALIVE) {
      +    return;
      +  }
      +  check_jvmti_status(jni, err, "testSuspendResume error in JVMTI SuspendThread");
      +  LOG("\nMAIN: testSuspendResume:  after suspend\n");
      +
      +  event_locker.wait(1);
      +
      +  LOG("MAIN: testSuspendResume: before resume\n");
      +  err = jvmti->ResumeThread(thread);
      +  check_jvmti_status(jni, err, "testSuspendResume error in JVMTI ResumeThread");
      +}
      +
      +JNIEXPORT void JNICALL
      +Java_ThreadStateTest2_setMonitorContendedMode(JNIEnv* jni, jclass klass, jboolean enable) {
      +  set_event_notification_mode(jvmti, jni, enable ? JVMTI_ENABLE : JVMTI_DISABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTER, nullptr);
      +}
      +
      +JNIEXPORT void JNICALL
      +Java_ThreadStateTest2_testInterruptThread(JNIEnv* jni, jclass klass, jthread vthread) {
      +  char* tname = get_thread_name(jvmti, jni, vthread);
      +  LOG("VT-2: testInterruptThread: %s\n", tname);
      +
      +  jvmtiError err = jvmti->InterruptThread(vthread);
      +  check_jvmti_status(jni, err, "testInterruptThread error in JVMTI InterruptThread");
      +}
      +
      +JNIEXPORT jint JNICALL
      +Java_ThreadStateTest2_testGetThreadState(JNIEnv* jni, jclass klass, jthread vthread) {
      +  jint  state = get_thread_state(jvmti, jni, vthread);
      +  char* tname = get_thread_name(jvmti, jni, vthread);
      +
      +  LOG("VT-2: testGetThreadState: %s state: %x\n", tname, state);
      +  return state;
      +}
      +
      +JNIEXPORT jint JNICALL
      +Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
      +  jvmtiEventCallbacks callbacks;
      +  jvmtiCapabilities caps;
      +  jvmtiError err;
      +
      +  printf("Agent_OnLoad: started\n");
      +  if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
      +    LOG("Agent_OnLoad: error in GetEnv");
      +    return JNI_ERR;
      +  }
      +
      +  memset(&caps, 0, sizeof(caps));
      +  caps.can_suspend = 1;
      +  caps.can_signal_thread = 1;
      +  caps.can_support_virtual_threads = 1;
      +  caps.can_generate_monitor_events = 1;
      +
      +  err = jvmti->AddCapabilities(&caps);
      +  if (err != JVMTI_ERROR_NONE) {
      +    LOG("Agent_OnLoad: error in JVMTI AddCapabilities: %d\n", err);
      +  }
      +  memset(&callbacks, 0, sizeof(callbacks));
      +  callbacks.MonitorContendedEnter = &MonitorContended;
      +  err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
      +  if (err != JVMTI_ERROR_NONE) {
      +    LOG("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err);
      +  }
      +  agent_event_lock = create_raw_monitor(jvmti, "agent_event_lock");
      +  printf("Agent_OnLoad: finished\n");
      +
      +  return 0;
      +}
      +
      +} // extern "C"
      
      From 38a8309b3f2544fa13448f5217e4227f0e2fe171 Mon Sep 17 00:00:00 2001
      From: Ivan Walulya 
      Date: Thu, 22 Jan 2026 05:38:32 +0000
      Subject: [PATCH 194/204] 8341630: G1: Adopt PartialArrayState to consolidate
       marking stack in concurrent marking
      
      Co-authored-by: Stefan Johansson 
      Reviewed-by: tschatzl, sjohanss
      ---
       src/hotspot/share/gc/g1/g1ConcurrentMark.cpp  | 107 ++++++++++++++++--
       src/hotspot/share/gc/g1/g1ConcurrentMark.hpp  |  81 +++++++------
       .../share/gc/g1/g1ConcurrentMark.inline.hpp   |  49 ++++----
       .../g1/g1ConcurrentMarkObjArrayProcessor.cpp  |  80 -------------
       .../g1/g1ConcurrentMarkObjArrayProcessor.hpp  |  59 ----------
       ...ConcurrentMarkObjArrayProcessor.inline.hpp |  38 -------
       src/hotspot/share/gc/shared/taskqueue.hpp     |   6 +-
       7 files changed, 167 insertions(+), 253 deletions(-)
       delete mode 100644 src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.cpp
       delete mode 100644 src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.hpp
       delete mode 100644 src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp
      
      diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp
      index 1077939f953..52591f7ce5f 100644
      --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp
      +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp
      @@ -51,6 +51,9 @@
       #include "gc/shared/gcTimer.hpp"
       #include "gc/shared/gcTraceTime.inline.hpp"
       #include "gc/shared/gcVMOperations.hpp"
      +#include "gc/shared/partialArraySplitter.inline.hpp"
      +#include "gc/shared/partialArrayState.hpp"
      +#include "gc/shared/partialArrayTaskStats.hpp"
       #include "gc/shared/referencePolicy.hpp"
       #include "gc/shared/suspendibleThreadSet.hpp"
       #include "gc/shared/taskqueue.inline.hpp"
      @@ -75,6 +78,7 @@
       #include "runtime/prefetch.inline.hpp"
       #include "runtime/threads.hpp"
       #include "utilities/align.hpp"
      +#include "utilities/checkedCast.hpp"
       #include "utilities/formatBuffer.hpp"
       #include "utilities/growableArray.hpp"
       #include "utilities/powerOfTwo.hpp"
      @@ -98,7 +102,7 @@ bool G1CMBitMapClosure::do_addr(HeapWord* const addr) {
         // We move that task's local finger along.
         _task->move_finger_to(addr);
       
      -  _task->scan_task_entry(G1TaskQueueEntry::from_oop(cast_to_oop(addr)));
      +  _task->process_entry(G1TaskQueueEntry(cast_to_oop(addr)), false /* stolen */);
         // we only partially drain the local queue and global stack
         _task->drain_local_queue(true);
         _task->drain_global_stack(true);
      @@ -489,6 +493,7 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h,
       
         _task_queues(new G1CMTaskQueueSet(_max_num_tasks)),
         _terminator(_max_num_tasks, _task_queues),
      +  _partial_array_state_manager(new PartialArrayStateManager(_max_num_tasks)),
       
         _first_overflow_barrier_sync(),
         _second_overflow_barrier_sync(),
      @@ -555,6 +560,10 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h,
         reset_at_marking_complete();
       }
       
      +PartialArrayStateManager* G1ConcurrentMark::partial_array_state_manager() const {
      +  return _partial_array_state_manager;
      +}
      +
       void G1ConcurrentMark::reset() {
         _has_aborted = false;
       
      @@ -649,7 +658,26 @@ void G1ConcurrentMark::set_concurrency_and_phase(uint active_tasks, bool concurr
         }
       }
       
      +#if TASKQUEUE_STATS
      +void G1ConcurrentMark::print_and_reset_taskqueue_stats() {
      +
      +  _task_queues->print_and_reset_taskqueue_stats("G1ConcurrentMark Oop Queue");
      +
      +  auto get_pa_stats = [&](uint i) {
      +    return _tasks[i]->partial_array_task_stats();
      +  };
      +
      +  PartialArrayTaskStats::log_set(_max_num_tasks, get_pa_stats,
      +                                 "G1ConcurrentMark Partial Array Task Stats");
      +
      +  for (uint i = 0; i < _max_num_tasks; ++i) {
      +    get_pa_stats(i)->reset();
      +  }
      +}
      +#endif
      +
       void G1ConcurrentMark::reset_at_marking_complete() {
      +  TASKQUEUE_STATS_ONLY(print_and_reset_taskqueue_stats());
         // We set the global marking state to some default values when we're
         // not doing marking.
         reset_marking_for_restart();
      @@ -803,11 +831,25 @@ void G1ConcurrentMark::cleanup_for_next_mark() {
       
         clear_bitmap(_concurrent_workers, true);
       
      +  reset_partial_array_state_manager();
      +
         // Repeat the asserts from above.
         guarantee(cm_thread()->in_progress(), "invariant");
         guarantee(!_g1h->collector_state()->mark_or_rebuild_in_progress(), "invariant");
       }
       
      +void G1ConcurrentMark::reset_partial_array_state_manager() {
      +  for (uint i = 0; i < _max_num_tasks; ++i) {
      +    _tasks[i]->unregister_partial_array_splitter();
      +  }
      +
      +  partial_array_state_manager()->reset();
      +
      +  for (uint i = 0; i < _max_num_tasks; ++i) {
      +    _tasks[i]->register_partial_array_splitter();
      +  }
      +}
      +
       void G1ConcurrentMark::clear_bitmap(WorkerThreads* workers) {
         assert_at_safepoint_on_vm_thread();
         // To avoid fragmentation the full collection requesting to clear the bitmap
      @@ -1788,17 +1830,18 @@ public:
         { }
       
         void operator()(G1TaskQueueEntry task_entry) const {
      -    if (task_entry.is_array_slice()) {
      -      guarantee(_g1h->is_in_reserved(task_entry.slice()), "Slice " PTR_FORMAT " must be in heap.", p2i(task_entry.slice()));
      +    if (task_entry.is_partial_array_state()) {
      +      oop obj = task_entry.to_partial_array_state()->source();
      +      guarantee(_g1h->is_in_reserved(obj), "Partial Array " PTR_FORMAT " must be in heap.", p2i(obj));
             return;
           }
      -    guarantee(oopDesc::is_oop(task_entry.obj()),
      +    guarantee(oopDesc::is_oop(task_entry.to_oop()),
                     "Non-oop " PTR_FORMAT ", phase: %s, info: %d",
      -              p2i(task_entry.obj()), _phase, _info);
      -    G1HeapRegion* r = _g1h->heap_region_containing(task_entry.obj());
      +              p2i(task_entry.to_oop()), _phase, _info);
      +    G1HeapRegion* r = _g1h->heap_region_containing(task_entry.to_oop());
           guarantee(!(r->in_collection_set() || r->has_index_in_opt_cset()),
                     "obj " PTR_FORMAT " from %s (%d) in region %u in (optional) collection set",
      -              p2i(task_entry.obj()), _phase, _info, r->hrm_index());
      +              p2i(task_entry.to_oop()), _phase, _info, r->hrm_index());
         }
       };
       
      @@ -2054,6 +2097,17 @@ void G1CMTask::reset(G1CMBitMap* mark_bitmap) {
         _mark_stats_cache.reset();
       }
       
      +void G1CMTask::register_partial_array_splitter() {
      +
      +  ::new (&_partial_array_splitter) PartialArraySplitter(_cm->partial_array_state_manager(),
      +                                                        _cm->max_num_tasks(),
      +                                                        ObjArrayMarkingStride);
      +}
      +
      +void G1CMTask::unregister_partial_array_splitter() {
      +  _partial_array_splitter.~PartialArraySplitter();
      +}
      +
       bool G1CMTask::should_exit_termination() {
         if (!regular_clock_call()) {
           return true;
      @@ -2184,7 +2238,7 @@ bool G1CMTask::get_entries_from_global_stack() {
           if (task_entry.is_null()) {
             break;
           }
      -    assert(task_entry.is_array_slice() || oopDesc::is_oop(task_entry.obj()), "Element " PTR_FORMAT " must be an array slice or oop", p2i(task_entry.obj()));
      +    assert(task_entry.is_partial_array_state() || oopDesc::is_oop(task_entry.to_oop()), "Element " PTR_FORMAT " must be an array slice or oop", p2i(task_entry.to_oop()));
           bool success = _task_queue->push(task_entry);
           // We only call this when the local queue is empty or under a
           // given target limit. So, we do not expect this push to fail.
      @@ -2215,7 +2269,7 @@ void G1CMTask::drain_local_queue(bool partially) {
           G1TaskQueueEntry entry;
           bool ret = _task_queue->pop_local(entry);
           while (ret) {
      -      scan_task_entry(entry);
      +      process_entry(entry, false /* stolen */);
             if (_task_queue->size() <= target_size || has_aborted()) {
               ret = false;
             } else {
      @@ -2225,6 +2279,37 @@ void G1CMTask::drain_local_queue(bool partially) {
         }
       }
       
      +size_t G1CMTask::start_partial_array_processing(oop obj) {
      +  assert(should_be_sliced(obj), "Must be an array object %d and large %zu", obj->is_objArray(), obj->size());
      +
      +  objArrayOop obj_array = objArrayOop(obj);
      +  size_t array_length = obj_array->length();
      +
      +  size_t initial_chunk_size = _partial_array_splitter.start(_task_queue, obj_array, nullptr, array_length);
      +
      +  // Mark objArray klass metadata
      +  if (_cm_oop_closure->do_metadata()) {
      +    _cm_oop_closure->do_klass(obj_array->klass());
      +  }
      +
      +  process_array_chunk(obj_array, 0, initial_chunk_size);
      +
      +  // Include object header size
      +  return objArrayOopDesc::object_size(checked_cast(initial_chunk_size));
      +}
      +
      +size_t G1CMTask::process_partial_array(const G1TaskQueueEntry& task, bool stolen) {
      +  PartialArrayState* state = task.to_partial_array_state();
      +  // Access state before release by claim().
      +  objArrayOop obj = objArrayOop(state->source());
      +
      +  PartialArraySplitter::Claim claim =
      +    _partial_array_splitter.claim(state, _task_queue, stolen);
      +
      +  process_array_chunk(obj, claim._start, claim._end);
      +  return heap_word_size((claim._end - claim._start) * heapOopSize);
      +}
      +
       void G1CMTask::drain_global_stack(bool partially) {
         if (has_aborted()) {
           return;
      @@ -2429,7 +2514,7 @@ void G1CMTask::attempt_stealing() {
         while (!has_aborted()) {
           G1TaskQueueEntry entry;
           if (_cm->try_stealing(_worker_id, entry)) {
      -      scan_task_entry(entry);
      +      process_entry(entry, true /* stolen */);
       
             // And since we're towards the end, let's totally drain the
             // local queue and global stack.
      @@ -2758,12 +2843,12 @@ G1CMTask::G1CMTask(uint worker_id,
                          G1ConcurrentMark* cm,
                          G1CMTaskQueue* task_queue,
                          G1RegionMarkStats* mark_stats) :
      -  _objArray_processor(this),
         _worker_id(worker_id),
         _g1h(G1CollectedHeap::heap()),
         _cm(cm),
         _mark_bitmap(nullptr),
         _task_queue(task_queue),
      +  _partial_array_splitter(_cm->partial_array_state_manager(), _cm->max_num_tasks(), ObjArrayMarkingStride),
         _mark_stats_cache(mark_stats, G1RegionMarkStatsCache::RegionMarkStatsCacheSize),
         _calls(0),
         _time_target_ms(0.0),
      diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp
      index 7ea9151c6f1..52a1b133439 100644
      --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp
      +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp
      @@ -26,11 +26,13 @@
       #define SHARE_GC_G1_G1CONCURRENTMARK_HPP
       
       #include "gc/g1/g1ConcurrentMarkBitMap.hpp"
      -#include "gc/g1/g1ConcurrentMarkObjArrayProcessor.hpp"
       #include "gc/g1/g1HeapRegionSet.hpp"
       #include "gc/g1/g1HeapVerifier.hpp"
       #include "gc/g1/g1RegionMarkStatsCache.hpp"
       #include "gc/shared/gcCause.hpp"
      +#include "gc/shared/partialArraySplitter.hpp"
      +#include "gc/shared/partialArrayState.hpp"
      +#include "gc/shared/partialArrayTaskStats.hpp"
       #include "gc/shared/taskqueue.hpp"
       #include "gc/shared/taskTerminator.hpp"
       #include "gc/shared/verifyOption.hpp"
      @@ -54,41 +56,7 @@ class G1RegionToSpaceMapper;
       class G1SurvivorRegions;
       class ThreadClosure;
       
      -// This is a container class for either an oop or a continuation address for
      -// mark stack entries. Both are pushed onto the mark stack.
      -class G1TaskQueueEntry {
      -private:
      -  void* _holder;
      -
      -  static const uintptr_t ArraySliceBit = 1;
      -
      -  G1TaskQueueEntry(oop obj) : _holder(obj) {
      -    assert(_holder != nullptr, "Not allowed to set null task queue element");
      -  }
      -  G1TaskQueueEntry(HeapWord* addr) : _holder((void*)((uintptr_t)addr | ArraySliceBit)) { }
      -public:
      -
      -  G1TaskQueueEntry() : _holder(nullptr) { }
      -  // Trivially copyable, for use in GenericTaskQueue.
      -
      -  static G1TaskQueueEntry from_slice(HeapWord* what) { return G1TaskQueueEntry(what); }
      -  static G1TaskQueueEntry from_oop(oop obj) { return G1TaskQueueEntry(obj); }
      -
      -  oop obj() const {
      -    assert(!is_array_slice(), "Trying to read array slice " PTR_FORMAT " as oop", p2i(_holder));
      -    return cast_to_oop(_holder);
      -  }
      -
      -  HeapWord* slice() const {
      -    assert(is_array_slice(), "Trying to read oop " PTR_FORMAT " as array slice", p2i(_holder));
      -    return (HeapWord*)((uintptr_t)_holder & ~ArraySliceBit);
      -  }
      -
      -  bool is_oop() const { return !is_array_slice(); }
      -  bool is_array_slice() const { return ((uintptr_t)_holder & ArraySliceBit) != 0; }
      -  bool is_null() const { return _holder == nullptr; }
      -};
      -
      +typedef ScannerTask G1TaskQueueEntry;
       typedef GenericTaskQueue G1CMTaskQueue;
       typedef GenericTaskQueueSet G1CMTaskQueueSet;
       
      @@ -412,6 +380,8 @@ class G1ConcurrentMark : public CHeapObj {
         G1CMTaskQueueSet*       _task_queues; // Task queue set
         TaskTerminator          _terminator;  // For termination
       
      +  PartialArrayStateManager* _partial_array_state_manager;
      +
         // Two sync barriers that are used to synchronize tasks when an
         // overflow occurs. The algorithm is the following. All tasks enter
         // the first one to ensure that they have all stopped manipulating
      @@ -489,6 +459,8 @@ class G1ConcurrentMark : public CHeapObj {
         // Prints all gathered CM-related statistics
         void print_stats();
       
      +  void print_and_reset_taskqueue_stats();
      +
         HeapWord*           finger()       { return _finger;   }
         bool                concurrent()   { return _concurrent; }
         uint                active_tasks() { return _num_active_tasks; }
      @@ -583,6 +555,8 @@ public:
       
         uint worker_id_offset() const { return _worker_id_offset; }
       
      +  uint max_num_tasks() const {return _max_num_tasks; }
      +
         // Clear statistics gathered during the concurrent cycle for the given region after
         // it has been reclaimed.
         void clear_statistics(G1HeapRegion* r);
      @@ -632,6 +606,8 @@ public:
         // Calculates the number of concurrent GC threads to be used in the marking phase.
         uint calc_active_marking_workers();
       
      +  PartialArrayStateManager* partial_array_state_manager() const;
      +
         // Resets the global marking data structures, as well as the
         // task local ones; should be called during concurrent start.
         void reset();
      @@ -643,6 +619,10 @@ public:
         // to be called concurrently to the mutator. It will yield to safepoint requests.
         void cleanup_for_next_mark();
       
      +  // Recycle the memory that has been requested by allocators associated with
      +  // this manager.
      +  void reset_partial_array_state_manager();
      +
         // Clear the next marking bitmap during safepoint.
         void clear_bitmap(WorkerThreads* workers);
       
      @@ -733,14 +713,13 @@ private:
           refs_reached_period           = 1024,
         };
       
      -  G1CMObjArrayProcessor       _objArray_processor;
      -
         uint                        _worker_id;
         G1CollectedHeap*            _g1h;
         G1ConcurrentMark*           _cm;
         G1CMBitMap*                 _mark_bitmap;
         // the task queue of this task
         G1CMTaskQueue*              _task_queue;
      +  PartialArraySplitter        _partial_array_splitter;
       
         G1RegionMarkStatsCache      _mark_stats_cache;
         // Number of calls to this task
      @@ -851,13 +830,24 @@ private:
         // mark bitmap scan, and so needs to be pushed onto the mark stack.
         bool is_below_finger(oop obj, HeapWord* global_finger) const;
       
      -  template void process_grey_task_entry(G1TaskQueueEntry task_entry);
      +  template void process_grey_task_entry(G1TaskQueueEntry task_entry, bool stolen);
      +
      +  static bool should_be_sliced(oop obj);
      +  // Start processing the given objArrayOop by first pushing its continuations and
      +  // then scanning the first chunk including the header.
      +  size_t start_partial_array_processing(oop obj);
      +  // Process the given continuation. Returns the number of words scanned.
      +  size_t process_partial_array(const G1TaskQueueEntry& task, bool stolen);
      +  // Apply the closure to the given range of elements in the objArray.
      +  inline void process_array_chunk(objArrayOop obj, size_t start, size_t end);
       public:
      -  // Apply the closure on the given area of the objArray. Return the number of words
      -  // scanned.
      -  inline size_t scan_objArray(objArrayOop obj, MemRegion mr);
         // Resets the task; should be called right at the beginning of a marking phase.
         void reset(G1CMBitMap* mark_bitmap);
      +  // Register/unregister Partial Array Splitter Allocator with the PartialArrayStateManager.
      +  // This allows us to discard memory arenas used for partial object array states at the end
      +  // of a concurrent mark cycle.
      +  void register_partial_array_splitter();
      +  void unregister_partial_array_splitter();
         // Clears all the fields that correspond to a claimed region.
         void clear_region_fields();
       
      @@ -913,7 +903,7 @@ public:
         inline bool deal_with_reference(T* p);
       
         // Scans an object and visits its children.
      -  inline void scan_task_entry(G1TaskQueueEntry task_entry);
      +  inline void process_entry(G1TaskQueueEntry task_entry, bool stolen);
       
         // Pushes an object on the local queue.
         inline void push(G1TaskQueueEntry task_entry);
      @@ -958,6 +948,11 @@ public:
         Pair flush_mark_stats_cache();
         // Prints statistics associated with this task
         void print_stats();
      +#if TASKQUEUE_STATS
      +  PartialArrayTaskStats* partial_array_task_stats() {
      +    return _partial_array_splitter.stats();
      +  }
      +#endif
       };
       
       // Class that's used to to print out per-region liveness
      diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp
      index 6f71012ff7c..fe72c68d4eb 100644
      --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp
      +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.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
      @@ -29,7 +29,6 @@
       
       #include "gc/g1/g1CollectedHeap.inline.hpp"
       #include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp"
      -#include "gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp"
       #include "gc/g1/g1HeapRegion.hpp"
       #include "gc/g1/g1HeapRegionRemSet.inline.hpp"
       #include "gc/g1/g1OopClosures.inline.hpp"
      @@ -39,6 +38,7 @@
       #include "gc/shared/suspendibleThreadSet.hpp"
       #include "gc/shared/taskqueue.inline.hpp"
       #include "utilities/bitMap.inline.hpp"
      +#include "utilities/checkedCast.hpp"
       
       inline bool G1CMIsAliveClosure::do_object_b(oop obj) {
         // Check whether the passed in object is null. During discovery the referent
      @@ -107,13 +107,15 @@ inline void G1CMMarkStack::iterate(Fn fn) const {
       #endif
       
       // It scans an object and visits its children.
      -inline void G1CMTask::scan_task_entry(G1TaskQueueEntry task_entry) { process_grey_task_entry(task_entry); }
      +inline void G1CMTask::process_entry(G1TaskQueueEntry task_entry, bool stolen) {
      +  process_grey_task_entry(task_entry, stolen);
      +}
       
       inline void G1CMTask::push(G1TaskQueueEntry task_entry) {
      -  assert(task_entry.is_array_slice() || _g1h->is_in_reserved(task_entry.obj()), "invariant");
      -  assert(task_entry.is_array_slice() || !_g1h->is_on_master_free_list(
      -              _g1h->heap_region_containing(task_entry.obj())), "invariant");
      -  assert(task_entry.is_array_slice() || _mark_bitmap->is_marked(cast_from_oop(task_entry.obj())), "invariant");
      +  assert(task_entry.is_partial_array_state() || _g1h->is_in_reserved(task_entry.to_oop()), "invariant");
      +  assert(task_entry.is_partial_array_state() || !_g1h->is_on_master_free_list(
      +              _g1h->heap_region_containing(task_entry.to_oop())), "invariant");
      +  assert(task_entry.is_partial_array_state() || _mark_bitmap->is_marked(cast_from_oop(task_entry.to_oop())), "invariant");
       
         if (!_task_queue->push(task_entry)) {
           // The local task queue looks full. We need to push some entries
      @@ -159,29 +161,34 @@ inline bool G1CMTask::is_below_finger(oop obj, HeapWord* global_finger) const {
       }
       
       template
      -inline void G1CMTask::process_grey_task_entry(G1TaskQueueEntry task_entry) {
      -  assert(scan || (task_entry.is_oop() && task_entry.obj()->is_typeArray()), "Skipping scan of grey non-typeArray");
      -  assert(task_entry.is_array_slice() || _mark_bitmap->is_marked(cast_from_oop(task_entry.obj())),
      +inline void G1CMTask::process_grey_task_entry(G1TaskQueueEntry task_entry, bool stolen) {
      +  assert(scan || (!task_entry.is_partial_array_state() && task_entry.to_oop()->is_typeArray()), "Skipping scan of grey non-typeArray");
      +  assert(task_entry.is_partial_array_state() || _mark_bitmap->is_marked(cast_from_oop(task_entry.to_oop())),
                "Any stolen object should be a slice or marked");
       
         if (scan) {
      -    if (task_entry.is_array_slice()) {
      -      _words_scanned += _objArray_processor.process_slice(task_entry.slice());
      +    if (task_entry.is_partial_array_state()) {
      +      _words_scanned += process_partial_array(task_entry, stolen);
           } else {
      -      oop obj = task_entry.obj();
      -      if (G1CMObjArrayProcessor::should_be_sliced(obj)) {
      -        _words_scanned += _objArray_processor.process_obj(obj);
      +      oop obj = task_entry.to_oop();
      +      if (should_be_sliced(obj)) {
      +        _words_scanned += start_partial_array_processing(obj);
             } else {
      -        _words_scanned += obj->oop_iterate_size(_cm_oop_closure);;
      +        _words_scanned += obj->oop_iterate_size(_cm_oop_closure);
             }
           }
         }
         check_limits();
       }
       
      -inline size_t G1CMTask::scan_objArray(objArrayOop obj, MemRegion mr) {
      -  obj->oop_iterate(_cm_oop_closure, mr);
      -  return mr.word_size();
      +inline bool G1CMTask::should_be_sliced(oop obj) {
      +  return obj->is_objArray() && ((objArrayOop)obj)->length() >= (int)ObjArrayMarkingStride;
      +}
      +
      +inline void G1CMTask::process_array_chunk(objArrayOop obj, size_t start, size_t end) {
      +  obj->oop_iterate_elements_range(_cm_oop_closure,
      +                                  checked_cast(start),
      +                                  checked_cast(end));
       }
       
       inline void G1ConcurrentMark::update_top_at_mark_start(G1HeapRegion* r) {
      @@ -265,7 +272,7 @@ inline bool G1CMTask::make_reference_grey(oop obj) {
         // be pushed on the stack. So, some duplicate work, but no
         // correctness problems.
         if (is_below_finger(obj, global_finger)) {
      -    G1TaskQueueEntry entry = G1TaskQueueEntry::from_oop(obj);
      +    G1TaskQueueEntry entry(obj);
           if (obj->is_typeArray()) {
             // Immediately process arrays of primitive types, rather
             // than pushing on the mark stack.  This keeps us from
      @@ -277,7 +284,7 @@ inline bool G1CMTask::make_reference_grey(oop obj) {
             // by only doing a bookkeeping update and avoiding the
             // actual scan of the object - a typeArray contains no
             // references, and the metadata is built-in.
      -      process_grey_task_entry(entry);
      +      process_grey_task_entry(entry, false /* stolen */);
           } else {
             push(entry);
           }
      diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.cpp
      deleted file mode 100644
      index 7f62e5527d5..00000000000
      --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.cpp
      +++ /dev/null
      @@ -1,80 +0,0 @@
      -/*
      - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
      - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      - *
      - * This code is free software; you can redistribute it and/or modify it
      - * 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/g1ConcurrentMark.inline.hpp"
      -#include "gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp"
      -#include "gc/g1/g1HeapRegion.inline.hpp"
      -#include "gc/shared/gc_globals.hpp"
      -#include "memory/memRegion.hpp"
      -#include "utilities/globalDefinitions.hpp"
      -
      -void G1CMObjArrayProcessor::push_array_slice(HeapWord* what) {
      -  _task->push(G1TaskQueueEntry::from_slice(what));
      -}
      -
      -size_t G1CMObjArrayProcessor::process_array_slice(objArrayOop obj, HeapWord* start_from, size_t remaining) {
      -  size_t words_to_scan = MIN2(remaining, (size_t)ObjArrayMarkingStride);
      -
      -  if (remaining > ObjArrayMarkingStride) {
      -    push_array_slice(start_from + ObjArrayMarkingStride);
      -  }
      -
      -  // Then process current area.
      -  MemRegion mr(start_from, words_to_scan);
      -  return _task->scan_objArray(obj, mr);
      -}
      -
      -size_t G1CMObjArrayProcessor::process_obj(oop obj) {
      -  assert(should_be_sliced(obj), "Must be an array object %d and large %zu", obj->is_objArray(), obj->size());
      -
      -  return process_array_slice(objArrayOop(obj), cast_from_oop(obj), objArrayOop(obj)->size());
      -}
      -
      -size_t G1CMObjArrayProcessor::process_slice(HeapWord* slice) {
      -
      -  // Find the start address of the objArrayOop.
      -  // Shortcut the BOT access if the given address is from a humongous object. The BOT
      -  // slide is fast enough for "smaller" objects in non-humongous regions, but is slower
      -  // than directly using heap region table.
      -  G1CollectedHeap* g1h = G1CollectedHeap::heap();
      -  G1HeapRegion* r = g1h->heap_region_containing(slice);
      -
      -  HeapWord* const start_address = r->is_humongous() ?
      -                                  r->humongous_start_region()->bottom() :
      -                                  r->block_start(slice);
      -
      -  assert(cast_to_oop(start_address)->is_objArray(), "Address " PTR_FORMAT " does not refer to an object array ", p2i(start_address));
      -  assert(start_address < slice,
      -         "Object start address " PTR_FORMAT " must be smaller than decoded address " PTR_FORMAT,
      -         p2i(start_address),
      -         p2i(slice));
      -
      -  objArrayOop objArray = objArrayOop(cast_to_oop(start_address));
      -
      -  size_t already_scanned = pointer_delta(slice, start_address);
      -  size_t remaining = objArray->size() - already_scanned;
      -
      -  return process_array_slice(objArray, slice, remaining);
      -}
      diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.hpp
      deleted file mode 100644
      index c2737dbbda6..00000000000
      --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.hpp
      +++ /dev/null
      @@ -1,59 +0,0 @@
      -/*
      - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
      - * 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_G1CONCURRENTMARKOBJARRAYPROCESSOR_HPP
      -#define SHARE_GC_G1_G1CONCURRENTMARKOBJARRAYPROCESSOR_HPP
      -
      -#include "oops/oopsHierarchy.hpp"
      -
      -class G1CMTask;
      -
      -// Helper class to mark through large objArrays during marking in an efficient way.
      -// Instead of pushing large object arrays, we push continuations onto the
      -// mark stack. These continuations are identified by having their LSB set.
      -// This allows incremental processing of large objects.
      -class G1CMObjArrayProcessor {
      -private:
      -  // Reference to the task for doing the actual work.
      -  G1CMTask* _task;
      -
      -  // Push the continuation at the given address onto the mark stack.
      -  void push_array_slice(HeapWord* addr);
      -
      -  // Process (apply the closure) on the given continuation of the given objArray.
      -  size_t process_array_slice(objArrayOop const obj, HeapWord* start_from, size_t remaining);
      -public:
      -  static bool should_be_sliced(oop obj);
      -
      -  G1CMObjArrayProcessor(G1CMTask* task) : _task(task) {
      -  }
      -
      -  // Process the given continuation. Returns the number of words scanned.
      -  size_t process_slice(HeapWord* slice);
      -  // Start processing the given objArrayOop by scanning the header and pushing its
      -  // continuation.
      -  size_t process_obj(oop obj);
      -};
      -
      -#endif // SHARE_GC_G1_G1CONCURRENTMARKOBJARRAYPROCESSOR_HPP
      diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp
      deleted file mode 100644
      index f6d47acdc01..00000000000
      --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkObjArrayProcessor.inline.hpp
      +++ /dev/null
      @@ -1,38 +0,0 @@
      -/*
      - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
      - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      - *
      - * This code is free software; you can redistribute it and/or modify it
      - * 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_G1CONCURRENTMARKOBJARRAYPROCESSOR_INLINE_HPP
      -#define SHARE_GC_G1_G1CONCURRENTMARKOBJARRAYPROCESSOR_INLINE_HPP
      -
      -#include "gc/g1/g1ConcurrentMarkObjArrayProcessor.hpp"
      -
      -#include "gc/shared/gc_globals.hpp"
      -#include "oops/oop.inline.hpp"
      -#include "oops/oopsHierarchy.hpp"
      -
      -inline bool G1CMObjArrayProcessor::should_be_sliced(oop obj) {
      -  return obj->is_objArray() && ((objArrayOop)obj)->size() >= 2 * ObjArrayMarkingStride;
      -}
      -
      -#endif // SHARE_GC_G1_G1CONCURRENTMARKOBJARRAYPROCESSOR_INLINE_HPP
      diff --git a/src/hotspot/share/gc/shared/taskqueue.hpp b/src/hotspot/share/gc/shared/taskqueue.hpp
      index 4334773a4e9..5c2fe4e5178 100644
      --- a/src/hotspot/share/gc/shared/taskqueue.hpp
      +++ b/src/hotspot/share/gc/shared/taskqueue.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
      @@ -641,6 +641,10 @@ public:
           return (raw_value() & PartialArrayTag) != 0;
         }
       
      +  bool is_null() const {
      +    return _p == nullptr;
      +  }
      +
         oop* to_oop_ptr() const {
           return static_cast(decode(OopTag));
         }
      
      From 0f4d775085109981fbf00623d38da22655d04675 Mon Sep 17 00:00:00 2001
      From: Tobias Hartmann 
      Date: Thu, 22 Jan 2026 06:56:51 +0000
      Subject: [PATCH 195/204] 8375534: Debug method 'pp' should support compressed
       oops
      
      Reviewed-by: vlivanov, phubner
      ---
       src/hotspot/share/utilities/debug.cpp | 6 ++----
       1 file changed, 2 insertions(+), 4 deletions(-)
      
      diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp
      index 0e1ca1efb98..504b923237e 100644
      --- a/src/hotspot/share/utilities/debug.cpp
      +++ b/src/hotspot/share/utilities/debug.cpp
      @@ -429,10 +429,8 @@ extern "C" DEBUGEXPORT void pp(void* p) {
           tty->print_cr("null");
           return;
         }
      -  if (Universe::heap()->is_in(p)) {
      -    oop obj = cast_to_oop(p);
      -    obj->print();
      -  } else {
      +
      +  if (!Universe::heap()->print_location(tty, p)) {
           // Ask NMT about this pointer.
           // GDB note: We will be using SafeFetch to access the supposed malloc header. If the address is
           // not readable, this will generate a signal. That signal will trip up the debugger: gdb will
      
      From f3381f0ffe2207e1765558f6f49e5a0280a3f920 Mon Sep 17 00:00:00 2001
      From: Thomas Schatzl 
      Date: Thu, 22 Jan 2026 08:29:05 +0000
      Subject: [PATCH 196/204] 8375314: Parallel: Crash iterating over unloaded
       classes for ObjectCountAfterGC event
      
      Reviewed-by: rkennke, sjohanss, iwalulya
      ---
       src/hotspot/share/gc/g1/g1CollectedHeap.hpp   |   1 -
       .../share/gc/g1/g1CollectedHeap.inline.hpp    |   8 +-
       .../share/gc/parallel/psParallelCompact.cpp   |  23 ++--
       .../share/gc/parallel/psParallelCompact.hpp   |   1 +
       src/hotspot/share/gc/shared/collectedHeap.hpp |   4 +-
       .../share/gc/shared/collectedHeap.inline.hpp  |   9 +-
       .../gc/parallel/TestObjectCountAfterGC.java   | 104 ++++++++++++++++++
       7 files changed, 134 insertions(+), 16 deletions(-)
       create mode 100644 test/hotspot/jtreg/gc/parallel/TestObjectCountAfterGC.java
      
      diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
      index a0104d04f4f..8009df1fa6a 100644
      --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
      +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
      @@ -1268,7 +1268,6 @@ public:
       
         bool is_marked(oop obj) const;
       
      -  inline static bool is_obj_filler(const oop obj);
         // Determine if an object is dead, given the object and also
         // the region to which the object belongs.
         inline bool is_obj_dead(const oop obj, const G1HeapRegion* hr) const;
      diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp
      index 577450b3be9..958b171444e 100644
      --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp
      +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp
      @@ -38,6 +38,7 @@
       #include "gc/g1/g1Policy.hpp"
       #include "gc/g1/g1RegionPinCache.inline.hpp"
       #include "gc/g1/g1RemSet.hpp"
      +#include "gc/shared/collectedHeap.inline.hpp"
       #include "gc/shared/markBitMap.inline.hpp"
       #include "gc/shared/taskqueue.inline.hpp"
       #include "oops/stackChunkOop.hpp"
      @@ -229,16 +230,11 @@ inline bool G1CollectedHeap::requires_barriers(stackChunkOop obj) const {
         return !heap_region_containing(obj)->is_young(); // is_in_young does an unnecessary null check
       }
       
      -inline bool G1CollectedHeap::is_obj_filler(const oop obj) {
      -  Klass* k = obj->klass_without_asserts();
      -  return k == Universe::fillerArrayKlass() || k == vmClasses::FillerObject_klass();
      -}
      -
       inline bool G1CollectedHeap::is_obj_dead(const oop obj, const G1HeapRegion* hr) const {
         assert(!hr->is_free(), "looking up obj " PTR_FORMAT " in Free region %u", p2i(obj), hr->hrm_index());
         if (hr->is_in_parsable_area(obj)) {
           // This object is in the parsable part of the heap, live unless scrubbed.
      -    return is_obj_filler(obj);
      +    return is_filler_object(obj);
         } else {
           // From Remark until a region has been concurrently scrubbed, parts of the
           // region is not guaranteed to be parsable. Use the bitmap for liveness.
      diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp
      index b1b07b4bc5c..bab72296d4c 100644
      --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp
      +++ b/src/hotspot/share/gc/parallel/psParallelCompact.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
      @@ -44,6 +44,7 @@
       #include "gc/parallel/psStringDedup.hpp"
       #include "gc/parallel/psYoungGen.hpp"
       #include "gc/shared/classUnloadingContext.hpp"
      +#include "gc/shared/collectedHeap.inline.hpp"
       #include "gc/shared/fullGCForwarding.inline.hpp"
       #include "gc/shared/gcCause.hpp"
       #include "gc/shared/gcHeapSummary.hpp"
      @@ -932,6 +933,17 @@ void PSParallelCompact::summary_phase(bool should_do_max_compaction)
         }
       }
       
      +void PSParallelCompact::report_object_count_after_gc() {
      +  GCTraceTime(Debug, gc, phases) tm("Report Object Count", &_gc_timer);
      +  // The heap is compacted, all objects are iterable. However there may be
      +  // filler objects in the heap which we should ignore.
      +  class SkipFillerObjectClosure : public BoolObjectClosure {
      +  public:
      +    bool do_object_b(oop obj) override { return !CollectedHeap::is_filler_object(obj); }
      +  } cl;
      +  _gc_tracer.report_object_count_after_gc(&cl, &ParallelScavengeHeap::heap()->workers());
      +}
      +
       bool PSParallelCompact::invoke(bool clear_all_soft_refs, bool should_do_max_compaction) {
         assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint");
         assert(Thread::current() == (Thread*)VMThread::vm_thread(),
      @@ -1027,6 +1039,8 @@ bool PSParallelCompact::invoke(bool clear_all_soft_refs, bool should_do_max_comp
       
           heap->print_heap_change(pre_gc_values);
       
      +    report_object_count_after_gc();
      +
           // Track memory usage and detect low memory
           MemoryService::track_memory_usage();
           heap->update_counters();
      @@ -1274,10 +1288,6 @@ void PSParallelCompact::marking_phase(ParallelOldTracer *gc_tracer) {
           }
         }
       
      -  {
      -    GCTraceTime(Debug, gc, phases) tm("Report Object Count", &_gc_timer);
      -    _gc_tracer.report_object_count_after_gc(is_alive_closure(), &ParallelScavengeHeap::heap()->workers());
      -  }
       #if TASKQUEUE_STATS
         ParCompactionManager::print_and_reset_taskqueue_stats();
       #endif
      @@ -1835,8 +1845,7 @@ void PSParallelCompact::verify_filler_in_dense_prefix() {
             oop obj = cast_to_oop(cur_addr);
             oopDesc::verify(obj);
             if (!mark_bitmap()->is_marked(cur_addr)) {
      -        Klass* k = cast_to_oop(cur_addr)->klass();
      -        assert(k == Universe::fillerArrayKlass() || k == vmClasses::FillerObject_klass(), "inv");
      +        assert(CollectedHeap::is_filler_object(cast_to_oop(cur_addr)), "inv");
             }
             cur_addr += obj->size();
           }
      diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.hpp b/src/hotspot/share/gc/parallel/psParallelCompact.hpp
      index 2297d720b35..4ac9395d727 100644
      --- a/src/hotspot/share/gc/parallel/psParallelCompact.hpp
      +++ b/src/hotspot/share/gc/parallel/psParallelCompact.hpp
      @@ -749,6 +749,7 @@ private:
         // Move objects to new locations.
         static void compact();
       
      +  static void report_object_count_after_gc();
         // Add available regions to the stack and draining tasks to the task queue.
         static void prepare_region_draining_tasks(uint parallel_gc_threads);
       
      diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp
      index 6f335b1cdf4..363ccf321b2 100644
      --- a/src/hotspot/share/gc/shared/collectedHeap.hpp
      +++ b/src/hotspot/share/gc/shared/collectedHeap.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
      @@ -309,6 +309,8 @@ protected:
           fill_with_object(start, pointer_delta(end, start), zap);
         }
       
      +  inline static bool is_filler_object(oop obj);
      +
         virtual void fill_with_dummy_object(HeapWord* start, HeapWord* end, bool zap);
         static size_t min_dummy_object_size() {
           return oopDesc::header_size();
      diff --git a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp b/src/hotspot/share/gc/shared/collectedHeap.inline.hpp
      index c9d84f54449..194c1fe0bf2 100644
      --- a/src/hotspot/share/gc/shared/collectedHeap.inline.hpp
      +++ b/src/hotspot/share/gc/shared/collectedHeap.inline.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
      @@ -27,7 +27,9 @@
       
       #include "gc/shared/collectedHeap.hpp"
       
      +#include "classfile/vmClasses.hpp"
       #include "gc/shared/memAllocator.hpp"
      +#include "memory/universe.hpp"
       #include "oops/oop.inline.hpp"
       #include "utilities/align.hpp"
       
      @@ -50,4 +52,9 @@ inline void CollectedHeap::add_vmthread_cpu_time(jlong time) {
         _vmthread_cpu_time += time;
       }
       
      +inline bool CollectedHeap::is_filler_object(oop obj) {
      +  Klass* k = obj->klass_without_asserts();
      +  return k == Universe::fillerArrayKlass() || k == vmClasses::FillerObject_klass();
      +}
      +
       #endif // SHARE_GC_SHARED_COLLECTEDHEAP_INLINE_HPP
      diff --git a/test/hotspot/jtreg/gc/parallel/TestObjectCountAfterGC.java b/test/hotspot/jtreg/gc/parallel/TestObjectCountAfterGC.java
      new file mode 100644
      index 00000000000..e61e7518938
      --- /dev/null
      +++ b/test/hotspot/jtreg/gc/parallel/TestObjectCountAfterGC.java
      @@ -0,0 +1,104 @@
      +/*
      + * 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 gc.parallel;
      +
      +/*
      + * @test TestObjectCountAfterGC
      + * @bug 8375314
      + * @summary Verifies that the HeapInspection VM operation for the ObjectCountAfterGC JFR event does not crash the VM.
      + *          Creates a set of custom classes that are about to be unloaded to cause metaspace to uncommit pages. When
      + *          the execution of the heap inspection iterates over the heap, it will come across these unloaded classes
      + *          referencing uncommitted memory, crashing.
      + * @requires vm.gc.Parallel
      + * @requires vm.opt.final.ClassUnloading
      + * @library /test/lib
      + * @library /testlibrary/asm
      + * @modules java.base/jdk.internal.misc
      + * @run main/othervm -XX:+UseParallelGC -Xlog:gc=debug,metaspace=info -XX:StartFlightRecording:gc=all,duration=1s,filename=myrecording.jfr
      + *                   gc.parallel.TestObjectCountAfterGC
      + */
      +
      +import java.lang.ref.Reference;
      +
      +import org.objectweb.asm.ClassWriter;
      +import org.objectweb.asm.MethodVisitor;
      +import org.objectweb.asm.Opcodes;
      +
      +public class TestObjectCountAfterGC {
      +
      +    static final String className = "ClassToLoadUnload";
      +
      +    public static void main(String args[]) throws Exception {
      +        final int KEEPALIVE_LENGTH = 100;
      +
      +        Object[] keepalive = new Object[KEEPALIVE_LENGTH];
      +
      +        for (int i = 1; i < 1000; i++) {
      +            ClassLoader cl = new MyClassLoader();
      +            Object o = null;
      +            // Create some random kept alive objects so that the
      +            // compaction regions are not totally empty and the
      +            // heap inspection VM operation needs to iterate them.
      +            keepalive[(i / KEEPALIVE_LENGTH) % KEEPALIVE_LENGTH] = new int[100];
      +            o = cl.loadClass(className + i).newInstance();
      +
      +            cl = null;
      +            o = null;
      +        }
      +
      +        // There is heap inspection VM operation for the ObjectCountAfterGC event
      +        // when JFR stops recording.
      +
      +        Reference.reachabilityFence(keepalive);
      +    }
      +}
      +
      +class MyClassLoader extends ClassLoader {
      +
      +    // Create a class of the given name with a default constructor.
      +    public byte[] createClass(String name) {
      +        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
      +        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, name, null, "java/lang/Object", null);
      +        // Add default constructor that just calls the super class constructor.
      +        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null);
      +        mv.visitCode();
      +        mv.visitVarInsn(Opcodes.ALOAD, 0);
      +        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false);
      +        mv.visitInsn(Opcodes.RETURN);
      +        mv.visitMaxs(0, 0);
      +        mv.visitEnd();
      +        return cw.toByteArray();
      +    }
      +
      +    // If the given name starts with "TestObjectCountAfterGC" create a new class on the fly,
      +    // delegate otherwise.
      +    public Class loadClass(String name) throws ClassNotFoundException {
      +        if (!name.startsWith(TestObjectCountAfterGC.className)) {
      +            return super.loadClass(name);
      +        }
      +        byte[] cls = createClass(name);
      +        return defineClass(name, cls, 0, cls.length, null);
      +    }
      +  }
      +
      
      From e50bf1f2a4702ef48cf16cc4f45d034a652bf358 Mon Sep 17 00:00:00 2001
      From: Thomas Schatzl 
      Date: Thu, 22 Jan 2026 08:29:27 +0000
      Subject: [PATCH 197/204] 8375616: G1: Convert G1BatchedTask to use Atomic
      
      Reviewed-by: sjohanss, kbarrett
      ---
       src/hotspot/share/gc/g1/g1BatchedTask.cpp | 9 ++++-----
       src/hotspot/share/gc/g1/g1BatchedTask.hpp | 5 +++--
       2 files changed, 7 insertions(+), 7 deletions(-)
      
      diff --git a/src/hotspot/share/gc/g1/g1BatchedTask.cpp b/src/hotspot/share/gc/g1/g1BatchedTask.cpp
      index 57558301541..1f082153476 100644
      --- a/src/hotspot/share/gc/g1/g1BatchedTask.cpp
      +++ b/src/hotspot/share/gc/g1/g1BatchedTask.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
      @@ -26,7 +26,6 @@
       #include "gc/g1/g1BatchedTask.hpp"
       #include "gc/g1/g1CollectedHeap.inline.hpp"
       #include "gc/g1/g1GCParPhaseTimesTracker.hpp"
      -#include "runtime/atomicAccess.hpp"
       #include "utilities/growableArray.hpp"
       
       void G1AbstractSubTask::record_work_item(uint worker_id, uint index, size_t count) {
      @@ -40,7 +39,7 @@ const char* G1AbstractSubTask::name() const {
       }
       
       bool G1BatchedTask::try_claim_serial_task(int& task) {
      -  task = AtomicAccess::fetch_then_add(&_num_serial_tasks_done, 1);
      +  task = _num_serial_tasks_done.fetch_then_add(1);
         return task < _serial_tasks.length();
       }
       
      @@ -96,8 +95,8 @@ void G1BatchedTask::work(uint worker_id) {
       }
       
       G1BatchedTask::~G1BatchedTask() {
      -  assert(AtomicAccess::load(&_num_serial_tasks_done) >= _serial_tasks.length(),
      -         "Only %d tasks of %d claimed", AtomicAccess::load(&_num_serial_tasks_done), _serial_tasks.length());
      +  assert(_num_serial_tasks_done.load_relaxed() >= _serial_tasks.length(),
      +         "Only %d tasks of %d claimed", _num_serial_tasks_done.load_relaxed(), _serial_tasks.length());
       
         for (G1AbstractSubTask* task : _parallel_tasks) {
           delete task;
      diff --git a/src/hotspot/share/gc/g1/g1BatchedTask.hpp b/src/hotspot/share/gc/g1/g1BatchedTask.hpp
      index 020fda634e4..a6d2ef923c0 100644
      --- a/src/hotspot/share/gc/g1/g1BatchedTask.hpp
      +++ b/src/hotspot/share/gc/g1/g1BatchedTask.hpp
      @@ -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
      @@ -28,6 +28,7 @@
       #include "gc/g1/g1GCPhaseTimes.hpp"
       #include "gc/shared/workerThread.hpp"
       #include "memory/allocation.hpp"
      +#include "runtime/atomic.hpp"
       
       template 
       class GrowableArrayCHeap;
      @@ -120,7 +121,7 @@ public:
       // 5) ~T()
       //
       class G1BatchedTask : public WorkerTask {
      -  volatile int _num_serial_tasks_done;
      +  Atomic _num_serial_tasks_done;
         G1GCPhaseTimes* _phase_times;
       
         bool try_claim_serial_task(int& task);
      
      From 92236ead1dea813cf456855f0aa6b73c16e9dc70 Mon Sep 17 00:00:00 2001
      From: Quan Anh Mai 
      Date: Thu, 22 Jan 2026 08:32:01 +0000
      Subject: [PATCH 198/204] 8375618: Incorrect assert in CastLLNode::Ideal
      
      Reviewed-by: chagedorn, dlong
      ---
       src/hotspot/share/opto/castnode.cpp           |  6 +-
       src/hotspot/share/opto/type.cpp               | 14 +++++
       src/hotspot/share/opto/type.hpp               | 10 +++
       .../jtreg/compiler/igvn/CastLLBits.java       | 63 +++++++++++++++++++
       4 files changed, 90 insertions(+), 3 deletions(-)
       create mode 100644 test/hotspot/jtreg/compiler/igvn/CastLLBits.java
      
      diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp
      index 998b6a79903..2ebbdd7cdb3 100644
      --- a/src/hotspot/share/opto/castnode.cpp
      +++ b/src/hotspot/share/opto/castnode.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
      @@ -392,8 +392,8 @@ Node* CastLLNode::Ideal(PhaseGVN* phase, bool can_reshape) {
           if (t != Type::TOP && t_in != Type::TOP) {
             const TypeLong* tl = t->is_long();
             const TypeLong* t_in_l = t_in->is_long();
      -      assert(tl->_lo >= t_in_l->_lo && tl->_hi <= t_in_l->_hi, "CastLL type should be narrower than or equal to the type of its input");
      -      assert((tl != t_in_l) == (tl->_lo > t_in_l->_lo || tl->_hi < t_in_l->_hi), "if type differs then this nodes's type must be narrower");
      +      assert(t_in_l->contains(tl), "CastLL type should be narrower than or equal to the type of its input");
      +      assert((tl != t_in_l) == t_in_l->strictly_contains(tl), "if type differs then this nodes's type must be narrower");
             if (tl != t_in_l) {
               const TypeInt* ti = TypeInt::make(checked_cast(tl->_lo), checked_cast(tl->_hi), tl->_widen);
               Node* castii = phase->transform(new CastIINode(in(0), in1->in(1), ti));
      diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp
      index d3271e79f2f..c637737eef9 100644
      --- a/src/hotspot/share/opto/type.cpp
      +++ b/src/hotspot/share/opto/type.cpp
      @@ -1816,6 +1816,13 @@ bool TypeInt::contains(const TypeInt* t) const {
         return TypeIntHelper::int_type_is_subset(this, t);
       }
       
      +#ifdef ASSERT
      +bool TypeInt::strictly_contains(const TypeInt* t) const {
      +  assert(!_is_dual && !t->_is_dual, "dual types should only be used for join calculation");
      +  return TypeIntHelper::int_type_is_subset(this, t) && !TypeIntHelper::int_type_is_equal(this, t);
      +}
      +#endif // ASSERT
      +
       const Type* TypeInt::xmeet(const Type* t) const {
         return TypeIntHelper::int_type_xmeet(this, t);
       }
      @@ -1944,6 +1951,13 @@ bool TypeLong::contains(const TypeLong* t) const {
         return TypeIntHelper::int_type_is_subset(this, t);
       }
       
      +#ifdef ASSERT
      +bool TypeLong::strictly_contains(const TypeLong* t) const {
      +  assert(!_is_dual && !t->_is_dual, "dual types should only be used for join calculation");
      +  return TypeIntHelper::int_type_is_subset(this, t) && !TypeIntHelper::int_type_is_equal(this, t);
      +}
      +#endif // ASSERT
      +
       const Type* TypeLong::xmeet(const Type* t) const {
         return TypeIntHelper::int_type_xmeet(this, t);
       }
      diff --git a/src/hotspot/share/opto/type.hpp b/src/hotspot/share/opto/type.hpp
      index 7c7ff035a54..135f37f7267 100644
      --- a/src/hotspot/share/opto/type.hpp
      +++ b/src/hotspot/share/opto/type.hpp
      @@ -812,6 +812,11 @@ public:
         bool contains(jint i) const;
         bool contains(const TypeInt* t) const;
       
      +#ifdef ASSERT
      +  // Check whether t is a proper subset (i.e. a subset that is not equal to the superset) of this
      +  bool strictly_contains(const TypeInt* t) const;
      +#endif // ASSERT
      +
         virtual bool is_finite() const;  // Has a finite value
       
         virtual const Type* xmeet(const Type* t) const;
      @@ -897,6 +902,11 @@ public:
         bool contains(jlong i) const;
         bool contains(const TypeLong* t) const;
       
      +#ifdef ASSERT
      +  // Check whether t is a proper subset (i.e. a subset that is not equal to the superset) of this
      +  bool strictly_contains(const TypeLong* t) const;
      +#endif // ASSERT
      +
         // Check for positive 32-bit value.
         int is_positive_int() const { return _lo >= 0 && _hi <= (jlong)max_jint; }
       
      diff --git a/test/hotspot/jtreg/compiler/igvn/CastLLBits.java b/test/hotspot/jtreg/compiler/igvn/CastLLBits.java
      new file mode 100644
      index 00000000000..a212fd1d3b6
      --- /dev/null
      +++ b/test/hotspot/jtreg/compiler/igvn/CastLLBits.java
      @@ -0,0 +1,63 @@
      +/*
      + * 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.igvn;
      +
      +/**
      + * @test
      + * @bug 8375618
      + * @summary A CastLLNode may change only a bit of its input, which triggers the incorrect assertion
      + *          that the signed range must changes
      + * @run main/othervm -XX:-TieredCompilation -Xbatch ${test.main.class}
      + * @run main ${test.main.class}
      + */
      +public class CastLLBits {
      +    static long instanceCount;
      +
      +    public static void main(String[] args) {
      +        for (int i = 0; i < 2000; i++) {
      +            test();
      +        }
      +    }
      +
      +    static void test() {
      +        int i, i1 = 6, i9, i10, i11, i12;
      +        boolean b = false;
      +        for (i = 25; i > 5; i--) {
      +            i9 = 1;
      +            do {
      +                i1 += 0;
      +                for (i10 = 1; i10 < 3; ++i10) {
      +                    instanceCount = i9;
      +                }
      +                i12 = 3;
      +                while (--i12 > 0) {
      +                    i11 = (int) instanceCount;
      +                    i1 = i11;
      +                    if (b) {};
      +                    instanceCount &= 21;
      +                }
      +                i9++;
      +            } while (i9 < 9);
      +        }
      +    }
      +}
      
      From 63be87d7f38a83c5fcdf59b54c6d63e0f0ca34d6 Mon Sep 17 00:00:00 2001
      From: Thomas Schatzl 
      Date: Thu, 22 Jan 2026 08:35:03 +0000
      Subject: [PATCH 199/204] 8375977: G1: Convert JVMCICleaningTask to use
       Atomic
      
      Reviewed-by: kbarrett
      ---
       src/hotspot/share/gc/g1/g1ParallelCleaning.cpp | 7 +++----
       src/hotspot/share/gc/g1/g1ParallelCleaning.hpp | 7 +++++--
       2 files changed, 8 insertions(+), 6 deletions(-)
      
      diff --git a/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp b/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp
      index 8d5e2a3239c..e3eabff5a50 100644
      --- a/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp
      +++ b/src/hotspot/share/gc/g1/g1ParallelCleaning.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
      @@ -24,7 +24,6 @@
       
       
       #include "gc/g1/g1ParallelCleaning.hpp"
      -#include "runtime/atomicAccess.hpp"
       #if INCLUDE_JVMCI
       #include "jvmci/jvmci.hpp"
       #endif
      @@ -35,11 +34,11 @@ JVMCICleaningTask::JVMCICleaningTask() :
       }
       
       bool JVMCICleaningTask::claim_cleaning_task() {
      -  if (AtomicAccess::load(&_cleaning_claimed)) {
      +  if (_cleaning_claimed.load_relaxed()) {
           return false;
         }
       
      -  return !AtomicAccess::cmpxchg(&_cleaning_claimed, false, true);
      +  return _cleaning_claimed.compare_set(false, true);
       }
       
       void JVMCICleaningTask::work(bool unloading_occurred) {
      diff --git a/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp b/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp
      index d8725cb110d..815b0883e16 100644
      --- a/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp
      +++ b/src/hotspot/share/gc/g1/g1ParallelCleaning.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,10 +26,13 @@
       #define SHARE_GC_G1_G1PARALLELCLEANING_HPP
       
       #include "gc/shared/parallelCleaning.hpp"
      +#if INCLUDE_JVMCI
      +#include "runtime/atomic.hpp"
      +#endif
       
       #if INCLUDE_JVMCI
       class JVMCICleaningTask : public StackObj {
      -  volatile bool _cleaning_claimed;
      +  Atomic _cleaning_claimed;
       
       public:
         JVMCICleaningTask();
      
      From 03038d802cc43b7694f554978ac9de8edca8a954 Mon Sep 17 00:00:00 2001
      From: Thomas Schatzl 
      Date: Thu, 22 Jan 2026 08:35:32 +0000
      Subject: [PATCH 200/204] 8375978: G1: Convert G1Policy to use Atomic
      
      Reviewed-by: kbarrett
      ---
       src/hotspot/share/gc/g1/g1Policy.cpp |  4 ++--
       src/hotspot/share/gc/g1/g1Policy.hpp | 13 +++++--------
       2 files changed, 7 insertions(+), 10 deletions(-)
      
      diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp
      index 2847a25c5b4..8818b477aae 100644
      --- a/src/hotspot/share/gc/g1/g1Policy.cpp
      +++ b/src/hotspot/share/gc/g1/g1Policy.cpp
      @@ -203,8 +203,8 @@ void G1Policy::update_young_length_bounds(size_t pending_cards, size_t card_rs_l
         // allocation.
         // That is "fine" - at most this will schedule a GC (hopefully only a little) too
         // early or too late.
      -  AtomicAccess::store(&_young_list_desired_length, new_young_list_desired_length);
      -  AtomicAccess::store(&_young_list_target_length, new_young_list_target_length);
      +  _young_list_desired_length.store_relaxed(new_young_list_desired_length);
      +  _young_list_target_length.store_relaxed(new_young_list_target_length);
       }
       
       // Calculates desired young gen length. It is calculated from:
      diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp
      index cf12a7a8027..9513c79869e 100644
      --- a/src/hotspot/share/gc/g1/g1Policy.hpp
      +++ b/src/hotspot/share/gc/g1/g1Policy.hpp
      @@ -35,7 +35,7 @@
       #include "gc/g1/g1RemSetTrackingPolicy.hpp"
       #include "gc/g1/g1YoungGenSizer.hpp"
       #include "gc/shared/gcCause.hpp"
      -#include "runtime/atomicAccess.hpp"
      +#include "runtime/atomic.hpp"
       #include "utilities/pair.hpp"
       #include "utilities/ticks.hpp"
       
      @@ -81,12 +81,9 @@ class G1Policy: public CHeapObj {
       
         // Desired young gen length without taking actually available free regions into
         // account.
      -  volatile uint _young_list_desired_length;
      +  Atomic _young_list_desired_length;
         // Actual target length given available free memory.
      -  volatile uint _young_list_target_length;
      -  // The max number of regions we can extend the eden by while the GC
      -  // locker is active. This should be >= _young_list_target_length;
      -  volatile uint _young_list_max_length;
      +  Atomic _young_list_target_length;
       
         // The survivor rate groups below must be initialized after the predictor because they
         // indirectly use it through the "this" object passed to their constructor.
      @@ -362,8 +359,8 @@ public:
         // This must be called at the very beginning of an evacuation pause.
         void decide_on_concurrent_start_pause();
       
      -  uint young_list_desired_length() const { return AtomicAccess::load(&_young_list_desired_length); }
      -  uint young_list_target_length() const { return AtomicAccess::load(&_young_list_target_length); }
      +  uint young_list_desired_length() const { return _young_list_desired_length.load_relaxed(); }
      +  uint young_list_target_length() const { return _young_list_target_length.load_relaxed(); }
       
         bool should_allocate_mutator_region() const;
         bool should_expand_on_mutator_allocation() const;
      
      From 6165daf03c8582cca8e5b075560aa978b90f677c Mon Sep 17 00:00:00 2001
      From: Matthias Baesken 
      Date: Thu, 22 Jan 2026 08:50:11 +0000
      Subject: [PATCH 201/204] 8375458: Check legal folder of JDK image for unwanted
       files
      
      Reviewed-by: erikj
      ---
       test/jdk/build/CheckFiles.java | 59 +++++++++++++++++++++++++++++++++-
       1 file changed, 58 insertions(+), 1 deletion(-)
      
      diff --git a/test/jdk/build/CheckFiles.java b/test/jdk/build/CheckFiles.java
      index eb903c0a224..5a915e881f0 100644
      --- a/test/jdk/build/CheckFiles.java
      +++ b/test/jdk/build/CheckFiles.java
      @@ -23,6 +23,7 @@
        */
       
       import java.io.IOException;
      +import java.nio.file.DirectoryStream;
       import java.nio.file.FileVisitResult;
       import java.nio.file.Files;
       import java.nio.file.Path;
      @@ -36,7 +37,7 @@ import jdk.test.lib.Platform;
       
       /*
        * @test
      - * @summary Check for unwanted file (types/extensions) in the jdk image
      + * @summary Check for unwanted files (types/extensions) in the jdk image
        * @library /test/lib
        * @requires !vm.debug
        * @run main CheckFiles
      @@ -47,6 +48,20 @@ public class CheckFiles {
           // JTREG=JAVA_OPTIONS=-Djdk.test.build.CheckFiles.dir=/path/to/dir
           public static final String DIR_PROPERTY = "jdk.test.build.CheckFiles.dir";
       
      +    private static boolean isGpl(Path myFile) {
      +        if (myFile == null || !Files.exists(myFile)) {
      +            return false;
      +        }
      +
      +        try {
      +            String firstLine = Files.readAllLines(myFile).stream()
      +                                    .findFirst().orElse("");
      +            return firstLine.contains("The GNU General Public License (GPL)");
      +        } catch (IOException e) {
      +            return false;
      +        }
      +    }
      +
           public static void main(String[] args) throws Exception {
               String jdkPathString = System.getProperty("test.jdk");
               Path jdkHome = Paths.get(jdkPathString);
      @@ -148,6 +163,48 @@ public class CheckFiles {
                       throw new Error("jmods dir scan failed");
                   }
               }
      +
      +        Path legalDir = mainDirToScan.resolve("legal");
      +        Path javabaseLicenseFile = mainDirToScan.resolve("legal/java.base/LICENSE");
      +        if (isGpl(javabaseLicenseFile)) { // for now check only legal dir of GPL based images; other ones might have other content
      +            System.out.println("GPL info found in java.base LICENSE file");
      +            ArrayList allowedEndingsLegalDir = new ArrayList<>();
      +            allowedEndingsLegalDir.add(".md");
      +            allowedEndingsLegalDir.add("ADDITIONAL_LICENSE_INFO");
      +            allowedEndingsLegalDir.add("ASSEMBLY_EXCEPTION");
      +            allowedEndingsLegalDir.add("LICENSE");
      +
      +            ArrayList requiredFilesInLegalSubdirs = new ArrayList<>();
      +            requiredFilesInLegalSubdirs.add("LICENSE");
      +            requiredFilesInLegalSubdirs.add("ADDITIONAL_LICENSE_INFO");
      +            requiredFilesInLegalSubdirs.add("ASSEMBLY_EXCEPTION");
      +
      +            System.out.println("Legal directory to scan:" + legalDir);
      +            try (DirectoryStream stream = Files.newDirectoryStream(legalDir)) {
      +                for (Path subfolder : stream) {
      +                    if (Files.isDirectory(subfolder)) {
      +                        System.out.println("Checking legal dir subfolder for required files: " + subfolder.getFileName());
      +
      +                        for (String fileName : requiredFilesInLegalSubdirs) {
      +                            Path filePath = subfolder.resolve(fileName);
      +                            if (Files.exists(filePath)) {
      +                                System.out.println("  Found " + fileName);
      +                            } else {
      +                                System.out.println("  Missing " + fileName);
      +                                throw new Error("legal dir scan for required files failed");
      +                            }
      +                        }
      +                    }
      +                }
      +            }
      +
      +            boolean legalDirRes = scanFiles(legalDir, allowedEndingsLegalDir);
      +            if (legalDirRes) {
      +                System.out.println("Legal directory scan successful.");
      +            } else {
      +                throw new Error("Legal dir scan failed");
      +            }
      +        }
           }
       
           private static boolean scanFiles(Path root, ArrayList allowedEndings) throws IOException {
      
      From ddbd4617a6172e3054b2afade4f304f66c79816e Mon Sep 17 00:00:00 2001
      From: Casper Norrbin 
      Date: Thu, 22 Jan 2026 09:45:40 +0000
      Subject: [PATCH 202/204] 8303470: containers/docker/TestMemoryAwareness.java
       failed with "'memory_limit_in_bytes:.*512000 k' missing from stdout/stderr"
      
      Reviewed-by: sgehwolf, dholmes
      ---
       src/hotspot/os/linux/osContainer_linux.cpp | 13 +++++++------
       test/hotspot/jtreg/ProblemList.txt         |  1 -
       2 files changed, 7 insertions(+), 7 deletions(-)
      
      diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp
      index fe1dbc17239..b46263efd99 100644
      --- a/src/hotspot/os/linux/osContainer_linux.cpp
      +++ b/src/hotspot/os/linux/osContainer_linux.cpp
      @@ -304,12 +304,13 @@ void OSContainer::print_container_metric(outputStream* st, const char* metrics,
         constexpr int longest_value = max_length - 11; // Max length - shortest "metric: " string ("cpu_quota: ")
         char value_str[longest_value + 1] = {};
         os::snprintf_checked(value_str, longest_value, metric_fmt::fmt, value);
      -  st->print("%s: %*s", metrics, max_length - static_cast(strlen(metrics)) - 2, value_str); // -2 for the ": "
      -  if (unit[0] != '\0') {
      -    st->print_cr(" %s", unit);
      -  } else {
      -    st->print_cr("");
      -  }
      +
      +  const int pad_width = max_length - static_cast(strlen(metrics)) - 2; // -2 for the ": "
      +  const char* unit_prefix = unit[0] != '\0' ? " " : "";
      +
      +  char line[128] = {};
      +  os::snprintf_checked(line, sizeof(line), "%s: %*s%s%s", metrics, pad_width, value_str, unit_prefix, unit);
      +  st->print_cr("%s", line);
       }
       
       void OSContainer::print_container_helper(outputStream* st, MetricResult& res, const char* metrics) {
      diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt
      index 13e1ea30a34..84520b00056 100644
      --- a/test/hotspot/jtreg/ProblemList.txt
      +++ b/test/hotspot/jtreg/ProblemList.txt
      @@ -113,7 +113,6 @@ runtime/NMT/VirtualAllocCommitMerge.java 8309698 linux-s390x
       applications/jcstress/copy.java 8229852 linux-all
       
       containers/docker/TestJcmd.java 8278102 linux-all
      -containers/docker/TestMemoryAwareness.java 8303470 linux-all
       containers/docker/TestJFREvents.java 8327723 linux-x64
       containers/docker/TestJcmdWithSideCar.java 8341518 linux-x64
       
      
      From e8eb218ca2d05736adc4b0aefa4b17e3062959b8 Mon Sep 17 00:00:00 2001
      From: Liam Miller-Cushon 
      Date: Thu, 22 Jan 2026 10:05:05 +0000
      Subject: [PATCH 203/204] 8374643: Fix reference to implMethodKind in
       LambdaToMethod debug printf statement
      
      Reviewed-by: vromero, liach
      ---
       .../sun/tools/javac/comp/LambdaToMethod.java  | 27 +++++++++-------
       .../tools/javac/resources/compiler.properties | 11 +++++++
       .../examples/LambdaDeserializationStat.java   | 31 +++++++++++++++++++
       .../lambda/SerializableObjectMethods.java     |  2 +-
       .../lambda/SerializableObjectMethods.out      |  4 +++
       5 files changed, 63 insertions(+), 12 deletions(-)
       create mode 100644 test/langtools/tools/javac/diags/examples/LambdaDeserializationStat.java
       create mode 100644 test/langtools/tools/javac/lambda/SerializableObjectMethods.out
      
      diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
      index 22cb796870b..7d0c5192039 100644
      --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
      +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
      @@ -142,6 +142,9 @@ public class LambdaToMethod extends TreeTranslator {
           /** dump statistics about lambda code generation */
           private final boolean dumpLambdaToMethodStats;
       
      +    /** dump statistics about lambda deserialization code generation */
      +    private final boolean dumpLambdaDeserializationStats;
      +
           /** force serializable representation, for stress testing **/
           private final boolean forceSerializable;
       
      @@ -187,6 +190,7 @@ public class LambdaToMethod extends TreeTranslator {
               transTypes = TransTypes.instance(context);
               Options options = Options.instance(context);
               dumpLambdaToMethodStats = options.isSet("debug.dumpLambdaToMethodStats");
      +        dumpLambdaDeserializationStats = options.isSet("debug.dumpLambdaDeserializationStats");
               attr = Attr.instance(context);
               forceSerializable = options.isSet("forceSerializable");
               boolean lineDebugInfo =
      @@ -714,8 +718,9 @@ public class LambdaToMethod extends TreeTranslator {
               String implMethodName = refSym.getQualifiedName().toString();
               String implMethodSignature = typeSig(types.erasure(refSym.type));
       
      +        int implMethodKind = refSym.referenceKind();
               JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType),
      -                make.Literal(refSym.referenceKind()));
      +                make.Literal(implMethodKind));
               ListBuffer serArgs = new ListBuffer<>();
               int i = 0;
               for (Type t : indyType.getParameterTypes()) {
      @@ -743,16 +748,16 @@ public class LambdaToMethod extends TreeTranslator {
                   stmts = new ListBuffer<>();
                   kInfo.deserializeCases.put(implMethodName, stmts);
               }
      -        /* **
      -        System.err.printf("+++++++++++++++++\n");
      -        System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
      -        System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
      -        System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
      -        System.err.printf("*implMethodKind: %d\n", implMethodKind);
      -        System.err.printf("*implClass: '%s'\n", implClass);
      -        System.err.printf("*implMethodName: '%s'\n", implMethodName);
      -        System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
      -        ****/
      +        if (dumpLambdaDeserializationStats) {
      +            log.note(pos, Notes.LambdaDeserializationStat(
      +                    functionalInterfaceClass,
      +                    functionalInterfaceMethodName,
      +                    functionalInterfaceMethodSignature,
      +                    implMethodKind,
      +                    implClass,
      +                    implMethodName,
      +                    implMethodSignature));
      +        }
               stmts.append(stmt);
           }
       
      diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
      index 95a7546e2b3..2ba9122c04a 100644
      --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
      +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
      @@ -1701,6 +1701,17 @@ compiler.note.mref.stat.1=\
           alternate metafactory = {0}\n\
           bridge method = {1}
       
      +# 0: string, 1: string, 2: string, 3: number, 4: string, 5: string, 6: string
      +compiler.note.lambda.deserialization.stat=\
      +    Generating lambda deserialization\n\
      +    functionalInterfaceClass: {0}\n\
      +    functionalInterfaceMethodName: {1}\n\
      +    functionalInterfaceMethodSignature:{2}\n\
      +    implMethodKind: {3}\n\
      +    implClass: {4}\n\
      +    implMethodName: {5}\n\
      +    implMethodSignature: {6}
      +
       compiler.note.note=\
           Note:\u0020
       
      diff --git a/test/langtools/tools/javac/diags/examples/LambdaDeserializationStat.java b/test/langtools/tools/javac/diags/examples/LambdaDeserializationStat.java
      new file mode 100644
      index 00000000000..4ab23ed0b3d
      --- /dev/null
      +++ b/test/langtools/tools/javac/diags/examples/LambdaDeserializationStat.java
      @@ -0,0 +1,31 @@
      +/*
      + * Copyright (c) 2026, Google LLC and/or its affiliates. All rights reserved.
      + * 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.
      + */
      +
      +// key: compiler.note.lambda.deserialization.stat
      +// options: --debug=dumpLambdaDeserializationStats
      +
      +import java.io.Serializable;
      +
      +class LambdaDeserializationStat {
      +    Runnable r = (Runnable & Serializable) () -> {};
      +}
      diff --git a/test/langtools/tools/javac/lambda/SerializableObjectMethods.java b/test/langtools/tools/javac/lambda/SerializableObjectMethods.java
      index 52d4cfc7bf6..b489a22c7b4 100644
      --- a/test/langtools/tools/javac/lambda/SerializableObjectMethods.java
      +++ b/test/langtools/tools/javac/lambda/SerializableObjectMethods.java
      @@ -25,7 +25,7 @@
        * @test
        * @bug 8282080
        * @summary Check that serializable lambdas referring to j.l.Object methods work.
      - * @compile SerializableObjectMethods.java
      + * @compile/ref=SerializableObjectMethods.out -XDrawDiagnostics --debug=dumpLambdaDeserializationStats SerializableObjectMethods.java
        * @run main SerializableObjectMethods
        */
       import java.io.ByteArrayInputStream;
      diff --git a/test/langtools/tools/javac/lambda/SerializableObjectMethods.out b/test/langtools/tools/javac/lambda/SerializableObjectMethods.out
      new file mode 100644
      index 00000000000..d03cc145794
      --- /dev/null
      +++ b/test/langtools/tools/javac/lambda/SerializableObjectMethods.out
      @@ -0,0 +1,4 @@
      +SerializableObjectMethods.java:59:35: compiler.note.lambda.deserialization.stat: SerializableObjectMethods$F, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 5, java/lang/Object, hashCode, ()I
      +SerializableObjectMethods.java:60:35: compiler.note.lambda.deserialization.stat: SerializableObjectMethods$F, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 9, SerializableObjectMethods$I2, hashCode, ()I
      +- compiler.note.unchecked.filename: SerializableObjectMethods.java
      +- compiler.note.unchecked.recompile
      
      From 6e9256cb613c9a3594546a45975a81def2efcf46 Mon Sep 17 00:00:00 2001
      From: Roland Westrelin 
      Date: Thu, 22 Jan 2026 10:37:26 +0000
      Subject: [PATCH 204/204] 8373343: C2: verify AddP base input only set for heap
       addresses
      
      Reviewed-by: dlong, chagedorn, qamai
      ---
       .../share/gc/shared/c2/barrierSetC2.cpp       |  2 +-
       src/hotspot/share/opto/addnode.hpp            |  5 ++-
       src/hotspot/share/opto/callnode.cpp           |  4 +--
       src/hotspot/share/opto/callnode.hpp           |  2 +-
       src/hotspot/share/opto/escape.cpp             |  7 ++--
       src/hotspot/share/opto/graphKit.cpp           |  6 ++--
       src/hotspot/share/opto/library_call.cpp       | 26 +++++++-------
       src/hotspot/share/opto/macro.cpp              | 19 +++++-----
       src/hotspot/share/opto/macro.hpp              | 13 ++++---
       src/hotspot/share/opto/macroArrayCopy.cpp     | 26 ++++++--------
       src/hotspot/share/opto/memnode.cpp            | 35 ++++++++++++++-----
       src/hotspot/share/opto/memnode.hpp            |  4 +++
       src/hotspot/share/opto/parse.hpp              |  2 +-
       src/hotspot/share/opto/parse1.cpp             | 19 +++++-----
       src/hotspot/share/opto/parseHelper.cpp        |  2 +-
       src/hotspot/share/opto/subtypenode.cpp        |  4 +--
       16 files changed, 98 insertions(+), 78 deletions(-)
      
      diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp
      index c4eefee5f65..53577bad1d8 100644
      --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp
      +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.cpp
      @@ -771,7 +771,7 @@ Node* BarrierSetC2::obj_allocate(PhaseMacroExpand* macro, Node* mem, Node* toobi
         //       this will require extensive changes to the loop optimization in order to
         //       prevent a degradation of the optimization.
         //       See comment in memnode.hpp, around line 227 in class LoadPNode.
      -  Node* tlab_end = macro->make_load(toobig_false, mem, tlab_end_adr, 0, TypeRawPtr::BOTTOM, T_ADDRESS);
      +  Node* tlab_end = macro->make_load_raw(toobig_false, mem, tlab_end_adr, 0, TypeRawPtr::BOTTOM, T_ADDRESS);
       
         // Load the TLAB top.
         Node* old_tlab_top = new LoadPNode(toobig_false, mem, tlab_top_adr, TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM, MemNode::unordered);
      diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp
      index 4151ab5d065..6128de00efb 100644
      --- a/src/hotspot/share/opto/addnode.hpp
      +++ b/src/hotspot/share/opto/addnode.hpp
      @@ -226,8 +226,11 @@ public:
                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) {
      +  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);
      diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp
      index fac8596173f..9b3d7b38d15 100644
      --- a/src/hotspot/share/opto/callnode.cpp
      +++ b/src/hotspot/share/opto/callnode.cpp
      @@ -1737,11 +1737,11 @@ void AllocateNode::compute_MemBar_redundancy(ciMethod* initializer)
           _is_allocation_MemBar_redundant = true;
         }
       }
      -Node *AllocateNode::make_ideal_mark(PhaseGVN *phase, Node* obj, Node* control, Node* mem) {
      +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(klass_node, klass_node, phase->MakeConX(in_bytes(Klass::prototype_header_offset()))));
      +    Node* proto_adr = phase->transform(new AddPNode(phase->C->top(), klass_node, phase->MakeConX(in_bytes(Klass::prototype_header_offset()))));
           mark_node = LoadNode::make(*phase, control, mem, proto_adr, TypeRawPtr::BOTTOM, 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/callnode.hpp b/src/hotspot/share/opto/callnode.hpp
      index 0bb064efc43..41d0c9f5aac 100644
      --- a/src/hotspot/share/opto/callnode.hpp
      +++ b/src/hotspot/share/opto/callnode.hpp
      @@ -1101,7 +1101,7 @@ public:
         void compute_MemBar_redundancy(ciMethod* initializer);
         bool is_allocation_MemBar_redundant() { return _is_allocation_MemBar_redundant; }
       
      -  Node* make_ideal_mark(PhaseGVN *phase, Node* obj, Node* control, Node* mem);
      +  Node* make_ideal_mark(PhaseGVN* phase, Node* control, Node* mem);
       
         NOT_PRODUCT(virtual void dump_spec(outputStream* st) const;)
       };
      diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp
      index 792384b1ec1..357c91e8eb5 100644
      --- a/src/hotspot/share/opto/escape.cpp
      +++ b/src/hotspot/share/opto/escape.cpp
      @@ -3772,9 +3772,9 @@ Node* ConnectionGraph::get_addp_base(Node *addp) {
         //       | |
         //       AddP  ( base == address )
         //
      -  // case #6. Constant Pool, ThreadLocal, CastX2P or
      +  // case #6. Constant Pool, ThreadLocal, CastX2P, Klass, OSR buffer buf or
         //          Raw object's field reference:
      -  //      {ConP, ThreadLocal, CastX2P, raw Load}
      +  //      {ConP, ThreadLocal, CastX2P, raw Load, Parm0}
         //  top   |
         //     \  |
         //     AddP  ( base == top )
      @@ -3816,7 +3816,9 @@ Node* ConnectionGraph::get_addp_base(Node *addp) {
             int opcode = uncast_base->Opcode();
             assert(opcode == Op_ConP || opcode == Op_ThreadLocal ||
                    opcode == Op_CastX2P || uncast_base->is_DecodeNarrowPtr() ||
      +             (_igvn->C->is_osr_compilation() && uncast_base->is_Parm() && uncast_base->as_Parm()->_con == TypeFunc::Parms)||
                    (uncast_base->is_Mem() && (uncast_base->bottom_type()->isa_rawptr() != nullptr)) ||
      +             (uncast_base->is_Mem() && (uncast_base->bottom_type()->isa_klassptr() != nullptr)) ||
                    is_captured_store_address(addp), "sanity");
           }
         }
      @@ -4411,7 +4413,6 @@ void ConnectionGraph::split_unique_types(GrowableArray  &alloc_worklist,
         uint new_index_start = (uint) _compile->num_alias_types();
         VectorSet visited;
         ideal_nodes.clear(); // Reset for use with set_map/get_map.
      -  uint unique_old = _compile->unique();
       
         //  Phase 1:  Process possible allocations from alloc_worklist.
         //  Create instance types for the CheckCastPP for allocations where possible.
      diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp
      index bc8ebaf1869..3d127322439 100644
      --- a/src/hotspot/share/opto/graphKit.cpp
      +++ b/src/hotspot/share/opto/graphKit.cpp
      @@ -2743,7 +2743,7 @@ 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(superklass, superklass, gvn.MakeConX(in_bytes(Klass::super_check_offset_offset()))));
      +  Node *p1 = gvn.transform(new AddPNode(C->top(), 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));
         int cacheoff_con = in_bytes(Klass::secondary_super_cache_offset());
      @@ -2761,7 +2761,7 @@ 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(subklass,subklass,chk_off_X));
      +  Node* p2 = gvn.transform(new AddPNode(C->top(), 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.
      @@ -3598,7 +3598,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(klass_node, klass_node, in_bytes(Klass::layout_helper_offset()));
      +  Node* lhp = basic_plus_adr(top(), 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/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
      index 2c2a7c020b8..e481833c816 100644
      --- a/src/hotspot/share/opto/library_call.cpp
      +++ b/src/hotspot/share/opto/library_call.cpp
      @@ -3000,7 +3000,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(kls, in_bytes(InstanceKlass::init_state_offset()));
      +    Node* insp = basic_plus_adr(top(), 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);
      @@ -3048,7 +3048,7 @@ bool LibraryCallKit::inline_native_vthread_start_transition(address funcAddr, co
         IdealKit ideal(this);
       
         Node* thread = ideal.thread();
      -  Node* jt_addr = basic_plus_adr(thread, in_bytes(JavaThread::is_in_vthread_transition_offset()));
      +  Node* jt_addr = basic_plus_adr(top(), 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);
      @@ -3089,7 +3089,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(thread, in_bytes(JavaThread::is_in_vthread_transition_offset()));
      +    Node* jt_addr = basic_plus_adr(top(), 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);
      @@ -3115,7 +3115,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* addr = basic_plus_adr(thread, in_bytes(JavaThread::is_disable_suspend_offset()));
      +    Node* addr = basic_plus_adr(top(), thread, in_bytes(JavaThread::is_disable_suspend_offset()));
           const TypePtr *addr_type = _gvn.type(addr)->isa_ptr();
       
           sync_kit(ideal);
      @@ -3689,7 +3689,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(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_OFFSET_JFR));
      +  Node* vthread_offset = basic_plus_adr(top(), 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));
      @@ -3714,7 +3714,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(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_ID_OFFSET_JFR));
      +  Node* thread_id_offset = basic_plus_adr(top(), 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 .
      @@ -3736,7 +3736,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) {
         Node* epoch = _gvn.transform(new AndINode(epoch_raw, _gvn.transform(epoch_mask)));
       
         // Store the vthread epoch to the jfr thread local.
      -  Node* vthread_epoch_offset = basic_plus_adr(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EPOCH_OFFSET_JFR));
      +  Node* vthread_epoch_offset = basic_plus_adr(top(), 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);
      @@ -3759,7 +3759,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(jt, in_bytes(THREAD_LOCAL_OFFSET_JFR + VTHREAD_EXCLUDED_OFFSET_JFR));
      +  Node* thread_local_excluded_offset = basic_plus_adr(top(), 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
      @@ -3814,7 +3814,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(thread, in_bytes(JavaThread::monitor_owner_id_offset()));
      +  Node* monitor_owner_id_offset = basic_plus_adr(top(), 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);)
      @@ -3956,7 +3956,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(klass, in_bytes(Klass::java_mirror_offset()));
      +  Node* p = basic_plus_adr(top(), 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);
      @@ -3996,7 +3996,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(kls, in_bytes(offset));
      +  Node* modp = basic_plus_adr(top(), kls, in_bytes(offset));
         Node* mods = make_load(nullptr, modp, type, bt, MemNode::unordered);
         Node* mask = intcon(modifier_mask);
         Node* bits = intcon(modifier_bits);
      @@ -4130,7 +4130,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(kls, in_bytes(Klass::super_offset()));
      +    p = basic_plus_adr(top(), 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);
      @@ -4668,7 +4668,7 @@ Node* LibraryCallKit::generate_virtual_guard(Node* obj_klass,
         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(obj_klass, entry_offset);
      +  Node* entry_addr  = basic_plus_adr(top(), 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).
      diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp
      index 4df03714376..56262d226fc 100644
      --- a/src/hotspot/share/opto/macro.cpp
      +++ b/src/hotspot/share/opto/macro.cpp
      @@ -1198,8 +1198,8 @@ bool PhaseMacroExpand::eliminate_boxing_node(CallStaticJavaNode *boxing) {
       }
       
       
      -Node* PhaseMacroExpand::make_load(Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt) {
      -  Node* adr = basic_plus_adr(base, offset);
      +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);
         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);
      @@ -1207,8 +1207,8 @@ Node* PhaseMacroExpand::make_load(Node* ctl, Node* mem, Node* base, int offset,
       }
       
       
      -Node* PhaseMacroExpand::make_store(Node* ctl, Node* mem, Node* base, int offset, Node* value, BasicType bt) {
      -  Node* adr = basic_plus_adr(base, offset);
      +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);
         mem = StoreNode::make(_igvn, ctl, mem, adr, nullptr, value, bt, MemNode::unordered);
         transform_later(mem);
         return mem;
      @@ -1753,20 +1753,20 @@ PhaseMacroExpand::initialize_object(AllocateNode* alloc,
                                           Node* size_in_bytes) {
         InitializeNode* init = alloc->initialization();
         // Store the klass & mark bits
      -  Node* mark_node = alloc->make_ideal_mark(&_igvn, object, control, rawmem);
      +  Node* mark_node = alloc->make_ideal_mark(&_igvn, control, rawmem);
         if (!mark_node->is_Con()) {
           transform_later(mark_node);
         }
      -  rawmem = make_store(control, rawmem, object, oopDesc::mark_offset_in_bytes(), mark_node, TypeX_X->basic_type());
      +  rawmem = make_store_raw(control, rawmem, object, oopDesc::mark_offset_in_bytes(), mark_node, TypeX_X->basic_type());
       
         if (!UseCompactObjectHeaders) {
      -    rawmem = make_store(control, rawmem, object, oopDesc::klass_offset_in_bytes(), klass_node, T_METADATA);
      +    rawmem = make_store_raw(control, rawmem, object, oopDesc::klass_offset_in_bytes(), klass_node, T_METADATA);
         }
         int header_size = alloc->minimum_header_size();  // conservatively small
       
         // Array length
         if (length != nullptr) {         // Arrays need length field
      -    rawmem = make_store(control, rawmem, object, arrayOopDesc::length_offset_in_bytes(), length, T_INT);
      +    rawmem = make_store_raw(control, rawmem, object, arrayOopDesc::length_offset_in_bytes(), length, T_INT);
           // conservatively small header size:
           header_size = arrayOopDesc::base_offset_in_bytes(T_BYTE);
           if (_igvn.type(klass_node)->isa_aryklassptr()) {   // we know the exact header size in most cases:
      @@ -1792,6 +1792,7 @@ PhaseMacroExpand::initialize_object(AllocateNode* alloc,
           if (!(UseTLAB && ZeroTLAB)) {
             rawmem = ClearArrayNode::clear_memory(control, rawmem, object,
                                                   header_size, size_in_bytes,
      +                                            true,
                                                   &_igvn);
           }
         } else {
      @@ -1946,7 +1947,7 @@ 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( old_eden_top, new_eden_top,
      +        prefetch_adr = new AddPNode( top(), new_eden_top,
                                                   _igvn.MakeConX(distance) );
               transform_later(prefetch_adr);
               prefetch = new PrefetchAllocationNode( i_o, prefetch_adr );
      diff --git a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp
      index c899bed567c..0f8d8fb5172 100644
      --- a/src/hotspot/share/opto/macro.hpp
      +++ b/src/hotspot/share/opto/macro.hpp
      @@ -58,10 +58,10 @@ public:
           _igvn.register_new_node_with_optimizer(n);
           return n;
         }
      -  Node* make_load( Node* ctl, Node* mem, Node* base, int offset,
      -                   const Type* value_type, BasicType bt);
      -  Node* make_store(Node* ctl, Node* mem, Node* base, int offset,
      -                   Node* value, BasicType bt);
      +  Node* make_load_raw(Node* ctl, Node* mem, Node* base, int offset,
      +                      const Type* value_type, BasicType bt);
      +  Node* make_store_raw(Node* ctl, Node* mem, Node* base, int offset,
      +                       Node* value, BasicType bt);
       
         Node* make_leaf_call(Node* ctrl, Node* mem,
                              const TypeFunc* call_type, address call_addr,
      @@ -144,11 +144,10 @@ private:
                                   Node* slice_idx,
                                   Node* slice_len,
                                   Node* dest_size);
      -  bool generate_block_arraycopy(Node** ctrl, MergeMemNode** mem, Node* io,
      +  bool generate_block_arraycopy(Node** ctrl, MergeMemNode** mem,
                                       const TypePtr* adr_type,
                                       BasicType basic_elem_type,
      -                                AllocateNode* alloc,
      -                                Node* src,  Node* src_offset,
      +                                Node* src, Node* src_offset,
                                       Node* dest, Node* dest_offset,
                                       Node* dest_size, bool dest_uninitialized);
         MergeMemNode* generate_slow_arraycopy(ArrayCopyNode *ac,
      diff --git a/src/hotspot/share/opto/macroArrayCopy.cpp b/src/hotspot/share/opto/macroArrayCopy.cpp
      index fb4a1842a36..0719ffc45a5 100644
      --- a/src/hotspot/share/opto/macroArrayCopy.cpp
      +++ b/src/hotspot/share/opto/macroArrayCopy.cpp
      @@ -384,7 +384,6 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode*
           transform_later(slow_region);
         }
       
      -  Node* original_dest = dest;
         bool  dest_needs_zeroing   = false;
         bool  acopy_to_uninitialized = false;
       
      @@ -424,7 +423,6 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode*
           // No zeroing elimination needed here.
           alloc                  = nullptr;
           acopy_to_uninitialized = false;
      -    //original_dest        = dest;
           //dest_needs_zeroing   = false;
         }
       
      @@ -557,10 +555,9 @@ Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode*
               MergeMemNode* local_mem = MergeMemNode::make(mem);
               transform_later(local_mem);
       
      -        didit = generate_block_arraycopy(&local_ctrl, &local_mem, local_io,
      -                                         adr_type, basic_elem_type, alloc,
      -                                         src, src_offset, dest, dest_offset,
      -                                         dest_size, acopy_to_uninitialized);
      +        didit = generate_block_arraycopy(&local_ctrl, &local_mem, adr_type,
      +                                         basic_elem_type, src, src_offset,
      +                                         dest, dest_offset, dest_size, acopy_to_uninitialized);
               if (didit) {
                 // Present the results of the block-copying fast call.
                 result_region->init_req(bcopy_path, local_ctrl);
      @@ -641,7 +638,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(dest_klass, ek_offset);
      +        Node* p1 = basic_plus_adr(top(), 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,
      @@ -918,12 +915,12 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem,
         if (start_con >= 0 && end_con >= 0) {
           // Constant start and end.  Simple.
           mem = ClearArrayNode::clear_memory(ctrl, mem, dest,
      -                                       start_con, end_con, &_igvn);
      +                                       start_con, end_con, false, &_igvn);
         } else if (start_con >= 0 && dest_size != top()) {
           // Constant start, pre-rounded end after the tail of the array.
           Node* end = dest_size;
           mem = ClearArrayNode::clear_memory(ctrl, mem, dest,
      -                                       start_con, end, &_igvn);
      +                                       start_con, end, false, &_igvn);
         } else if (start_con >= 0 && slice_len != top()) {
           // Constant start, non-constant end.  End needs rounding up.
           // End offset = round_up(abase + ((slice_idx_con + slice_len) << scale), 8)
      @@ -936,7 +933,7 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem,
           end = transform_later(new AddXNode(end, MakeConX(end_base)) );
           end = transform_later(new AndXNode(end, MakeConX(~end_round)) );
           mem = ClearArrayNode::clear_memory(ctrl, mem, dest,
      -                                       start_con, end, &_igvn);
      +                                       start_con, end, false, &_igvn);
         } else if (start_con < 0 && dest_size != top()) {
           // Non-constant start, pre-rounded end after the tail of the array.
           // This is almost certainly a "round-to-end" operation.
      @@ -970,7 +967,7 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem,
           }
           Node* end = dest_size; // pre-rounded
           mem = ClearArrayNode::clear_memory(ctrl, mem, dest,
      -                                       start, end, &_igvn);
      +                                       start, end, false, &_igvn);
         } else {
           // Non-constant start, unrounded non-constant end.
           // (Nobody zeroes a random midsection of an array using this routine.)
      @@ -981,11 +978,10 @@ void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem,
         merge_mem->set_memory_at(alias_idx, mem);
       }
       
      -bool PhaseMacroExpand::generate_block_arraycopy(Node** ctrl, MergeMemNode** mem, Node* io,
      +bool PhaseMacroExpand::generate_block_arraycopy(Node** ctrl, MergeMemNode** mem,
                                                       const TypePtr* adr_type,
                                                       BasicType basic_elem_type,
      -                                                AllocateNode* alloc,
      -                                                Node* src,  Node* src_offset,
      +                                                Node* src, Node* src_offset,
                                                       Node* dest, Node* dest_offset,
                                                       Node* dest_size, bool dest_uninitialized) {
         // See if there is an advantage from block transfer.
      @@ -1133,7 +1129,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(dest_elem_klass, sco_offset);
      +  Node* p3 = basic_plus_adr(top(), 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 0d4fb6791a4..7f152eddd65 100644
      --- a/src/hotspot/share/opto/memnode.cpp
      +++ b/src/hotspot/share/opto/memnode.cpp
      @@ -2563,7 +2563,13 @@ Node* LoadNode::klass_identity_common(PhaseGVN* phase) {
                  ) {
                 int mirror_field = in_bytes(Klass::java_mirror_offset());
                 if (tkls->offset() == mirror_field) {
      -            return adr2->in(AddPNode::Base);
      +#ifdef ASSERT
      +            const TypeKlassPtr* tkls2 = phase->type(adr2->in(AddPNode::Address))->is_klassptr();
      +            assert(tkls2->offset() == 0, "not a load of java_mirror");
      +#endif
      +            assert(adr2->in(AddPNode::Base)->is_top(), "not an off heap load");
      +            assert(adr2->in(AddPNode::Offset)->find_intptr_t_con(-1) == in_bytes(Klass::java_mirror_offset()), "incorrect offset");
      +            return adr2->in(AddPNode::Address);
                 }
               }
             }
      @@ -4112,18 +4118,27 @@ bool ClearArrayNode::step_through(Node** np, uint instance_id, PhaseValues* phas
         return true;
       }
       
      +Node* ClearArrayNode::make_address(Node* dest, Node* offset, bool raw_base, PhaseGVN* phase) {
      +  Node* base = dest;
      +  if (raw_base) {
      +    // 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));
      +}
      +
       //----------------------------clear_memory-------------------------------------
       // Generate code to initialize object storage to zero.
       Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
                                          intptr_t start_offset,
                                          Node* end_offset,
      +                                   bool raw_base,
                                          PhaseGVN* phase) {
         intptr_t offset = start_offset;
       
         int unit = BytesPerLong;
         if ((offset % unit) != 0) {
      -    Node* adr = new AddPNode(dest, dest, phase->MakeConX(offset));
      -    adr = phase->transform(adr);
      +    Node* adr = make_address(dest, phase->MakeConX(offset), raw_base, phase);
           const TypePtr* atp = TypeRawPtr::BOTTOM;
           mem = StoreNode::make(*phase, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT, MemNode::unordered);
           mem = phase->transform(mem);
      @@ -4132,12 +4147,13 @@ Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
         assert((offset % unit) == 0, "");
       
         // Initialize the remaining stuff, if any, with a ClearArray.
      -  return clear_memory(ctl, mem, dest, phase->MakeConX(offset), end_offset, phase);
      +  return clear_memory(ctl, mem, dest, phase->MakeConX(offset), end_offset, raw_base, phase);
       }
       
       Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
                                          Node* start_offset,
                                          Node* end_offset,
      +                                   bool raw_base,
                                          PhaseGVN* phase) {
         if (start_offset == end_offset) {
           // nothing to do
      @@ -4157,7 +4173,7 @@ Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
       
         // Bulk clear double-words
         Node* zsize = phase->transform(new SubXNode(zend, zbase) );
      -  Node* adr = phase->transform(new AddPNode(dest, dest, start_offset) );
      +  Node* adr = make_address(dest, start_offset, raw_base, phase);
         mem = new ClearArrayNode(ctl, mem, zsize, adr, false);
         return phase->transform(mem);
       }
      @@ -4165,6 +4181,7 @@ Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
       Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
                                          intptr_t start_offset,
                                          intptr_t end_offset,
      +                                   bool raw_base,
                                          PhaseGVN* phase) {
         if (start_offset == end_offset) {
           // nothing to do
      @@ -4178,11 +4195,10 @@ Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
         }
         if (done_offset > start_offset) {
           mem = clear_memory(ctl, mem, dest,
      -                       start_offset, phase->MakeConX(done_offset), phase);
      +                       start_offset, phase->MakeConX(done_offset), raw_base, phase);
         }
         if (done_offset < end_offset) { // emit the final 32-bit store
      -    Node* adr = new AddPNode(dest, dest, phase->MakeConX(done_offset));
      -    adr = phase->transform(adr);
      +    Node* adr = make_address(dest, phase->MakeConX(done_offset), raw_base, phase);
           const TypePtr* atp = TypeRawPtr::BOTTOM;
           mem = StoreNode::make(*phase, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT, MemNode::unordered);
           mem = phase->transform(mem);
      @@ -5389,6 +5405,7 @@ Node* InitializeNode::complete_stores(Node* rawctl, Node* rawmem, Node* rawptr,
               zeroes_done = align_down(zeroes_done, BytesPerInt);
               rawmem = ClearArrayNode::clear_memory(rawctl, rawmem, rawptr,
                                                     zeroes_done, zeroes_needed,
      +                                              true,
                                                     phase);
               zeroes_done = zeroes_needed;
               if (zsize > InitArrayShortSize && ++big_init_gaps > 2)
      @@ -5447,7 +5464,7 @@ Node* InitializeNode::complete_stores(Node* rawctl, Node* rawmem, Node* rawptr,
           }
           if (zeroes_done < size_limit) {
             rawmem = ClearArrayNode::clear_memory(rawctl, rawmem, rawptr,
      -                                            zeroes_done, size_in_bytes, phase);
      +                                            zeroes_done, size_in_bytes, true, phase);
           }
         }
       
      diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp
      index e84556528e6..249df51beb9 100644
      --- a/src/hotspot/share/opto/memnode.hpp
      +++ b/src/hotspot/share/opto/memnode.hpp
      @@ -1072,6 +1072,7 @@ public:
       class ClearArrayNode: public Node {
       private:
         bool _is_large;
      +  static Node* make_address(Node* dest, Node* offset, bool raw_base, PhaseGVN* phase);
       public:
         ClearArrayNode( Node *ctrl, Node *arymem, Node *word_cnt, Node *base, bool is_large)
           : Node(ctrl,arymem,word_cnt,base), _is_large(is_large) {
      @@ -1099,14 +1100,17 @@ public:
         static Node* clear_memory(Node* control, Node* mem, Node* dest,
                                   intptr_t start_offset,
                                   intptr_t end_offset,
      +                            bool raw_base,
                                   PhaseGVN* phase);
         static Node* clear_memory(Node* control, Node* mem, Node* dest,
                                   intptr_t start_offset,
                                   Node* end_offset,
      +                            bool raw_base,
                                   PhaseGVN* phase);
         static Node* clear_memory(Node* control, Node* mem, Node* dest,
                                   Node* start_offset,
                                   Node* end_offset,
      +                            bool raw_base,
                                   PhaseGVN* phase);
         // Return allocation input memory edge if it is different instance
         // or itself if it is the one we are looking for.
      diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp
      index 19b302a8924..397a7796f88 100644
      --- a/src/hotspot/share/opto/parse.hpp
      +++ b/src/hotspot/share/opto/parse.hpp
      @@ -442,7 +442,7 @@ class Parse : public GraphKit {
         SafePointNode* create_entry_map();
       
         // OSR helpers
      -  Node *fetch_interpreter_state(int index, BasicType bt, Node *local_addrs, Node *local_addrs_base);
      +  Node *fetch_interpreter_state(int index, BasicType bt, Node* local_addrs);
         Node* check_interpreter_type(Node* l, const Type* type, SafePointNode* &bad_type_exit);
         void  load_interpreter_state(Node* osr_buf);
       
      diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp
      index 6122f7e7bfc..2f699650037 100644
      --- a/src/hotspot/share/opto/parse1.cpp
      +++ b/src/hotspot/share/opto/parse1.cpp
      @@ -103,10 +103,9 @@ void Parse::print_statistics() {
       // on stack replacement.
       Node *Parse::fetch_interpreter_state(int index,
                                            BasicType bt,
      -                                     Node *local_addrs,
      -                                     Node *local_addrs_base) {
      +                                     Node* local_addrs) {
         Node *mem = memory(Compile::AliasIdxRaw);
      -  Node *adr = basic_plus_adr( local_addrs_base, local_addrs, -index*wordSize );
      +  Node *adr = basic_plus_adr(top(), local_addrs, -index*wordSize);
         Node *ctl = control();
       
         // Very similar to LoadNode::make, except we handle un-aligned longs and
      @@ -121,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(local_addrs_base, local_addrs, -(index+1)*wordSize);
      +    adr = basic_plus_adr(top(), 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)
      @@ -220,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(osr_buf, osr_buf, (max_locals+mcnt*2-1)*wordSize);
      +  Node *monitors_addr = basic_plus_adr(top(), 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());
      @@ -241,9 +240,9 @@ void Parse::load_interpreter_state(Node* osr_buf) {
           // Displaced headers and locked objects are interleaved in the
           // temp OSR buffer.  We only copy the locked objects out here.
           // Fetch the locked object from the OSR temp buffer and copy to our fastlock node.
      -    Node *lock_object = fetch_interpreter_state(index*2, T_OBJECT, monitors_addr, osr_buf);
      +    Node *lock_object = fetch_interpreter_state(index*2, T_OBJECT, monitors_addr);
           // Try and copy the displaced header to the BoxNode
      -    Node *displaced_hdr = fetch_interpreter_state((index*2) + 1, T_ADDRESS, monitors_addr, osr_buf);
      +    Node *displaced_hdr = fetch_interpreter_state((index*2) + 1, T_ADDRESS, monitors_addr);
       
       
           store_to_memory(control(), box, displaced_hdr, T_ADDRESS, MemNode::unordered);
      @@ -271,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(osr_buf, osr_buf, (max_locals-1)*wordSize);
      +  Node *locals_addr = basic_plus_adr(top(), 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());
      @@ -318,7 +317,7 @@ void Parse::load_interpreter_state(Node* osr_buf) {
             // really for T_OBJECT types so correct it.
             bt = T_OBJECT;
           }
      -    Node *value = fetch_interpreter_state(index, bt, locals_addr, osr_buf);
      +    Node *value = fetch_interpreter_state(index, bt, locals_addr);
           set_local(index, value);
         }
       
      @@ -2128,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(klass, klass, in_bytes(Klass::misc_flags_offset()));
      +  Node* access_flags_addr = basic_plus_adr(top(), 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)));
      diff --git a/src/hotspot/share/opto/parseHelper.cpp b/src/hotspot/share/opto/parseHelper.cpp
      index 1a1cea05454..2e8fca68a8d 100644
      --- a/src/hotspot/share/opto/parseHelper.cpp
      +++ b/src/hotspot/share/opto/parseHelper.cpp
      @@ -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(array_klass, array_klass, element_klass_offset);
      +  Node* p2 = basic_plus_adr(top(), 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 b58194b87d2..8e4c7d829a7 100644
      --- a/src/hotspot/share/opto/subtypenode.cpp
      +++ b/src/hotspot/share/opto/subtypenode.cpp
      @@ -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(superklass, superklass, phase->MakeConX(in_bytes(Klass::super_check_offset_offset()))));
      +        Node* p1 = phase->transform(new AddPNode(C->top(), 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(subklass, subklass, chk_off_X));
      +          Node* p2 = phase->transform(new AddPNode(C->top(), 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);