From 5c6947f736568413d53d5a00de2e865f86e637c4 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Wed, 7 Jan 2026 06:30:14 +0000 Subject: [PATCH 001/113] 8373429: gc/g1/TestCodeCacheUnloadDuringConcCycle fails on various platforms Reviewed-by: mbaesken, mdoerr --- .../jtreg/gc/g1/TestCodeCacheUnloadDuringConcCycle.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcCycle.java b/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcCycle.java index af746ad206d..e36ccace9dc 100644 --- a/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcCycle.java +++ b/test/hotspot/jtreg/gc/g1/TestCodeCacheUnloadDuringConcCycle.java @@ -53,9 +53,9 @@ import java.util.regex.Pattern; import jdk.test.lib.Asserts; +import jdk.test.lib.Platform; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; -import static jdk.test.lib.Asserts.*; import jdk.test.whitebox.WhiteBox; public class TestCodeCacheUnloadDuringConcCycle { @@ -70,9 +70,10 @@ private static OutputAnalyzer runTest(String concPhase) throws Exception { "-Xbootclasspath/a:.", "-Xlog:gc=trace,codecache", "-XX:+WhiteBoxAPI", - "-XX:ReservedCodeCacheSize=8M", + "-XX:ReservedCodeCacheSize=" + (Platform.is32bit() ? "4M" : "8M"), "-XX:StartAggressiveSweepingAt=50", "-XX:CompileCommand=compileonly,gc.g1.SomeClass::*", + "-XX:CompileCommand=compileonly,gc.g1.Foo*::*", TestCodeCacheUnloadDuringConcCycleRunner.class.getName(), concPhase); return output; @@ -140,7 +141,7 @@ private static void triggerCodeCacheGC() throws Exception { System.out.println("Compiled " + i + " classes"); } i++; - } while (i < 1000); + } while (i < 200); System.out.println("Compilation done, compiled " + i + " classes"); } catch (Throwable t) { } From c1c0ac877033c3edb0c2681c2c5f825be8adcfb3 Mon Sep 17 00:00:00 2001 From: Damon Fenacci Date: Wed, 7 Jan 2026 07:29:00 +0000 Subject: [PATCH 002/113] 8342772: Assert in LateInlineMHCallGenerator::do_late_inline_check Reviewed-by: vlivanov, chagedorn, thartmann --- src/hotspot/share/opto/callGenerator.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index db03dd9d80c..1465da02ac8 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.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 @@ -418,7 +418,8 @@ bool LateInlineMHCallGenerator::do_late_inline_check(Compile* C, JVMState* jvms) C->inline_printer()->record(cg->method(), call_node()->jvms(), InliningResult::FAILURE, "late method handle call resolution"); } - assert(!cg->is_late_inline() || cg->is_mh_late_inline() || AlwaysIncrementalInline || StressIncrementalInlining, "we're doing late inlining"); + assert(!cg->is_late_inline() || cg->is_mh_late_inline() || cg->is_virtual_late_inline() || + AlwaysIncrementalInline || StressIncrementalInlining, "we're doing late inlining"); _inline_cg = cg; return true; } else { From a01283a5a57723673b1fd3c93434678fdae4102c Mon Sep 17 00:00:00 2001 From: Ana-Maria Mihalceanu Date: Wed, 7 Jan 2026 08:24:31 +0000 Subject: [PATCH 003/113] 8374632: Broken list layout in the man page of jlink Reviewed-by: jpai --- src/jdk.jlink/share/man/jlink.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/jdk.jlink/share/man/jlink.md b/src/jdk.jlink/share/man/jlink.md index 0d16e69c9ef..5c77202434c 100644 --- a/src/jdk.jlink/share/man/jlink.md +++ b/src/jdk.jlink/share/man/jlink.md @@ -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 @@ Developers are responsible for updating their custom runtime images. and zip-9 provides the best compression. Default is zip-6. : Deprecated values to be removed in a future release: + - `0`: No compression. Use zip-0 instead. - `1`: Constant string sharing - `2`: ZIP. Use zip-6 instead. @@ -182,6 +183,7 @@ Description and zip-9 provides the best compression. Default is zip-6. : Deprecated values to be removed in a future release: + - Level 0: No compression. Use zip-0 instead. - Level 1: Constant string sharing - Level 2: ZIP. Use zip-6 instead. From 7e18de137c3b5f08a479af2b64eb22923261900b Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Wed, 7 Jan 2026 09:22:38 +0000 Subject: [PATCH 004/113] 8374210: [BACKOUT] Move input validation checks to Java for java.lang.StringCoding intrinsics Reviewed-by: shade, thartmann --- .../cpu/aarch64/macroAssembler_aarch64.cpp | 12 +- .../cpu/riscv/c2_MacroAssembler_riscv.cpp | 12 +- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 66 +++++------ src/hotspot/share/classfile/vmIntrinsics.hpp | 6 +- src/hotspot/share/opto/c2_globals.hpp | 3 - src/hotspot/share/opto/library_call.cpp | 57 +++------- src/hotspot/share/opto/library_call.hpp | 3 +- .../share/classes/java/lang/String.java | 2 +- .../share/classes/java/lang/StringCoding.java | 93 ++-------------- .../share/classes/java/lang/System.java | 5 +- .../jdk/internal/access/JavaLangAccess.java | 20 ++-- .../share/classes/sun/nio/cs/CESU_8.java | 2 +- .../share/classes/sun/nio/cs/DoubleByte.java | 2 +- .../share/classes/sun/nio/cs/ISO_8859_1.java | 40 ++++--- .../share/classes/sun/nio/cs/SingleByte.java | 2 +- .../share/classes/sun/nio/cs/US_ASCII.java | 2 +- .../share/classes/sun/nio/cs/UTF_8.java | 2 +- .../sun/nio/cs/ext/EUC_JP.java.template | 2 +- .../intrinsics/TestVerifyIntrinsicChecks.java | 105 ------------------ .../intrinsics/string/TestCountPositives.java | 30 +---- .../string/TestEncodeIntrinsics.java | 22 +--- .../intrinsics/string/TestHasNegatives.java | 25 +---- .../patches/java.base/java/lang/Helper.java | 5 - 23 files changed, 106 insertions(+), 412 deletions(-) delete mode 100644 test/hotspot/jtreg/compiler/intrinsics/TestVerifyIntrinsicChecks.java diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 2ccc755be3c..b8a9afc123f 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -6259,14 +6259,10 @@ void MacroAssembler::fill_words(Register base, Register cnt, Register value) // Intrinsic for // -// - sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) -// Encodes char[] to byte[] in ISO-8859-1 -// -// - java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) -// Encodes byte[] (containing UTF-16) to byte[] in ISO-8859-1 -// -// - java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) -// Encodes char[] to byte[] in ASCII +// - sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray +// return the number of characters copied. +// - java/lang/StringUTF16.compress +// return index of non-latin1 character if copy fails, otherwise 'len'. // // This version always returns the number of characters copied, and does not // clobber the 'len' register. A successful copy will complete with the post- diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index dcf20752a21..824ea872935 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -2813,14 +2813,10 @@ void C2_MacroAssembler::char_array_compress_v(Register src, Register dst, Regist // Intrinsic for // -// - sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) -// Encodes char[] to byte[] in ISO-8859-1 -// -// - java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) -// Encodes byte[] (containing UTF-16) to byte[] in ISO-8859-1 -// -// - java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) -// Encodes char[] to byte[] in ASCII +// - sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray +// return the number of characters copied. +// - java/lang/StringUTF16.compress +// return index of non-latin1 character if copy fails, otherwise 'len'. // // This version always returns the number of characters copied. A successful // copy will complete with the post-condition: 'res' == 'len', while an diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 3f18d8c6262..be7deb884ce 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -6251,46 +6251,32 @@ void MacroAssembler::evpbroadcast(BasicType type, XMMRegister dst, Register src, } } -// Encode given char[]/byte[] to byte[] in ISO_8859_1 or ASCII -// -// @IntrinsicCandidate -// int sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0( -// char[] sa, int sp, byte[] da, int dp, int len) { -// int i = 0; -// for (; i < len; i++) { -// char c = sa[sp++]; -// if (c > '\u00FF') -// break; -// da[dp++] = (byte) c; -// } -// return i; -// } -// -// @IntrinsicCandidate -// int java.lang.StringCoding.encodeISOArray0( -// byte[] sa, int sp, byte[] da, int dp, int len) { -// int i = 0; -// for (; i < len; i++) { -// char c = StringUTF16.getChar(sa, sp++); -// if (c > '\u00FF') -// break; -// da[dp++] = (byte) c; -// } -// return i; -// } -// -// @IntrinsicCandidate -// int java.lang.StringCoding.encodeAsciiArray0( -// char[] sa, int sp, byte[] da, int dp, int len) { -// int i = 0; -// for (; i < len; i++) { -// char c = sa[sp++]; -// if (c >= '\u0080') -// break; -// da[dp++] = (byte) c; -// } -// return i; -// } +// encode char[] to byte[] in ISO_8859_1 or ASCII + //@IntrinsicCandidate + //private static int implEncodeISOArray(byte[] sa, int sp, + //byte[] da, int dp, int len) { + // int i = 0; + // for (; i < len; i++) { + // char c = StringUTF16.getChar(sa, sp++); + // if (c > '\u00FF') + // break; + // da[dp++] = (byte)c; + // } + // return i; + //} + // + //@IntrinsicCandidate + //private static int implEncodeAsciiArray(char[] sa, int sp, + // byte[] da, int dp, int len) { + // int i = 0; + // for (; i < len; i++) { + // char c = sa[sp++]; + // if (c >= '\u0080') + // break; + // da[dp++] = (byte)c; + // } + // return i; + //} void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, XMMRegister tmp1Reg, XMMRegister tmp2Reg, XMMRegister tmp3Reg, XMMRegister tmp4Reg, diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 6f9c2326a45..07fa294e8e1 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -415,18 +415,18 @@ class methodHandle; \ do_class(java_lang_StringCoding, "java/lang/StringCoding") \ do_intrinsic(_countPositives, java_lang_StringCoding, countPositives_name, countPositives_signature, F_S) \ - do_name( countPositives_name, "countPositives0") \ + do_name( countPositives_name, "countPositives") \ do_signature(countPositives_signature, "([BII)I") \ \ do_class(sun_nio_cs_iso8859_1_Encoder, "sun/nio/cs/ISO_8859_1$Encoder") \ do_intrinsic(_encodeISOArray, sun_nio_cs_iso8859_1_Encoder, encodeISOArray_name, encodeISOArray_signature, F_S) \ - do_name( encodeISOArray_name, "encodeISOArray0") \ + do_name( encodeISOArray_name, "implEncodeISOArray") \ do_signature(encodeISOArray_signature, "([CI[BII)I") \ \ do_intrinsic(_encodeByteISOArray, java_lang_StringCoding, encodeISOArray_name, indexOfI_signature, F_S) \ \ do_intrinsic(_encodeAsciiArray, java_lang_StringCoding, encodeAsciiArray_name, encodeISOArray_signature, F_S) \ - do_name( encodeAsciiArray_name, "encodeAsciiArray0") \ + do_name( encodeAsciiArray_name, "implEncodeAsciiArray") \ \ do_class(java_math_BigInteger, "java/math/BigInteger") \ do_intrinsic(_multiplyToLen, java_math_BigInteger, multiplyToLen_name, multiplyToLen_signature, F_S) \ diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 7fa3ca638c2..f470049bf7e 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -675,9 +675,6 @@ product(bool, PrintIntrinsics, false, DIAGNOSTIC, \ "prints attempted and successful inlining of intrinsics") \ \ - develop(bool, VerifyIntrinsicChecks, false, \ - "Verify in intrinsic that Java level checks work as expected") \ - \ develop(bool, StressReflectiveCode, false, \ "Use inexact types at allocations, etc., to test reflection") \ \ diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index a057f66a989..2c2a7c020b8 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -940,11 +940,7 @@ inline Node* LibraryCallKit::generate_limit_guard(Node* offset, } // Emit range checks for the given String.value byte array -void LibraryCallKit::generate_string_range_check(Node* array, - Node* offset, - Node* count, - bool char_count, - bool halt_on_oob) { +void LibraryCallKit::generate_string_range_check(Node* array, Node* offset, Node* count, bool char_count) { if (stopped()) { return; // already stopped } @@ -962,17 +958,10 @@ void LibraryCallKit::generate_string_range_check(Node* array, generate_limit_guard(offset, count, load_array_length(array), bailout); if (bailout->req() > 1) { - if (halt_on_oob) { - bailout = _gvn.transform(bailout)->as_Region(); - Node* frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr)); - Node* halt = _gvn.transform(new HaltNode(bailout, frame, "unexpected guard failure in intrinsic")); - C->root()->add_req(halt); - } else { - PreserveJVMState pjvms(this); - set_control(_gvn.transform(bailout)); - uncommon_trap(Deoptimization::Reason_intrinsic, - Deoptimization::Action_maybe_recompile); - } + PreserveJVMState pjvms(this); + set_control(_gvn.transform(bailout)); + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_maybe_recompile); } } @@ -1130,7 +1119,6 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) { //------------------------------inline_countPositives------------------------------ -// int java.lang.StringCoding#countPositives0(byte[] ba, int off, int len) bool LibraryCallKit::inline_countPositives() { if (too_many_traps(Deoptimization::Reason_intrinsic)) { return false; @@ -1142,14 +1130,13 @@ bool LibraryCallKit::inline_countPositives() { Node* offset = argument(1); Node* len = argument(2); - if (VerifyIntrinsicChecks) { - ba = must_be_not_null(ba, true); - generate_string_range_check(ba, offset, len, false, true); - if (stopped()) { - return true; - } - } + ba = must_be_not_null(ba, true); + // Range checks + generate_string_range_check(ba, offset, len, false); + if (stopped()) { + return true; + } Node* ba_start = array_element_address(ba, offset, T_BYTE); Node* result = new CountPositivesNode(control(), memory(TypeAryPtr::BYTES), ba_start, len); set_result(_gvn.transform(result)); @@ -6184,9 +6171,6 @@ CallStaticJavaNode* LibraryCallKit::get_uncommon_trap_from_success_proj(Node* no } //-------------inline_encodeISOArray----------------------------------- -// int sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) -// int java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) -// int java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) // encode char[] to byte[] in ISO_8859_1 or ASCII bool LibraryCallKit::inline_encodeISOArray(bool ascii) { assert(callee()->signature()->size() == 5, "encodeISOArray has 5 parameters"); @@ -6197,14 +6181,8 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) { Node *dst_offset = argument(3); Node *length = argument(4); - // Cast source & target arrays to not-null - if (VerifyIntrinsicChecks) { - src = must_be_not_null(src, true); - dst = must_be_not_null(dst, true); - if (stopped()) { - return true; - } - } + src = must_be_not_null(src, true); + dst = must_be_not_null(dst, true); const TypeAryPtr* src_type = src->Value(&_gvn)->isa_aryptr(); const TypeAryPtr* dst_type = dst->Value(&_gvn)->isa_aryptr(); @@ -6221,15 +6199,6 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) { return false; } - // Check source & target bounds - if (VerifyIntrinsicChecks) { - generate_string_range_check(src, src_offset, length, src_elem == T_BYTE, true); - generate_string_range_check(dst, dst_offset, length, false, true); - if (stopped()) { - return true; - } - } - Node* src_start = array_element_address(src, src_offset, T_CHAR); Node* dst_start = array_element_address(dst, dst_offset, dst_elem); // 'src_start' points to src array + scaled offset diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index bfe29814dec..00ba4c795f1 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -163,8 +163,7 @@ class LibraryCallKit : public GraphKit { Node* array_length, RegionNode* region); void generate_string_range_check(Node* array, Node* offset, - Node* length, bool char_count, - bool halt_on_oob = false); + Node* length, bool char_count); Node* current_thread_helper(Node* &tls_output, ByteSize handle_offset, bool is_immutable); Node* generate_current_thread(Node* &tls_output); diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index d7aef113e15..d3eda052740 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -1111,7 +1111,7 @@ private static byte[] encode8859_1(byte coder, byte[] val, int sp = 0; int sl = len; while (sp < sl) { - int ret = StringCoding.encodeISOArray(val, sp, dst, dp, len); + int ret = StringCoding.implEncodeISOArray(val, sp, dst, dp, len); sp = sp + ret; dp = dp + ret; if (ret != len) { diff --git a/src/java.base/share/classes/java/lang/StringCoding.java b/src/java.base/share/classes/java/lang/StringCoding.java index 545f216b755..c02af28c37d 100644 --- a/src/java.base/share/classes/java/lang/StringCoding.java +++ b/src/java.base/share/classes/java/lang/StringCoding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,11 +26,8 @@ package java.lang; -import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.IntrinsicCandidate; -import java.util.function.BiFunction; - /** * Utility class for string encoding and decoding. */ @@ -41,7 +38,7 @@ private StringCoding() { } /** * Count the number of leading non-zero ascii chars in the range. */ - static int countNonZeroAscii(String s) { + public static int countNonZeroAscii(String s) { byte[] value = s.value(); if (s.isLatin1()) { return countNonZeroAsciiLatin1(value, 0, value.length); @@ -53,7 +50,7 @@ static int countNonZeroAscii(String s) { /** * Count the number of non-zero ascii chars in the range. */ - private static int countNonZeroAsciiLatin1(byte[] ba, int off, int len) { + public static int countNonZeroAsciiLatin1(byte[] ba, int off, int len) { int limit = off + len; for (int i = off; i < limit; i++) { if (ba[i] <= 0) { @@ -66,7 +63,7 @@ private static int countNonZeroAsciiLatin1(byte[] ba, int off, int len) { /** * Count the number of leading non-zero ascii chars in the range. */ - private static int countNonZeroAsciiUTF16(byte[] ba, int off, int strlen) { + public static int countNonZeroAsciiUTF16(byte[] ba, int off, int strlen) { int limit = off + strlen; for (int i = off; i < limit; i++) { char c = StringUTF16.charAt(ba, i); @@ -77,7 +74,7 @@ private static int countNonZeroAsciiUTF16(byte[] ba, int off, int strlen) { return strlen; } - static boolean hasNegatives(byte[] ba, int off, int len) { + public static boolean hasNegatives(byte[] ba, int off, int len) { return countPositives(ba, off, len) != len; } @@ -88,24 +85,9 @@ static boolean hasNegatives(byte[] ba, int off, int len) { * bytes in the range. If there are negative bytes, the implementation must return * a value that is less than or equal to the index of the first negative byte * in the range. - * - * @param ba a byte array - * @param off the index of the first byte to start reading from - * @param len the total number of bytes to read - * @throws NullPointerException if {@code ba} is null - * @throws ArrayIndexOutOfBoundsException if the provided sub-range is - * {@linkplain Preconditions#checkFromIndexSize(int, int, int, BiFunction) out of bounds} */ - static int countPositives(byte[] ba, int off, int len) { - Preconditions.checkFromIndexSize( - off, len, - ba.length, // Implicit null check on `ba` - Preconditions.AIOOBE_FORMATTER); - return countPositives0(ba, off, len); - } - @IntrinsicCandidate - private static int countPositives0(byte[] ba, int off, int len) { + public static int countPositives(byte[] ba, int off, int len) { int limit = off + len; for (int i = off; i < limit; i++) { if (ba[i] < 0) { @@ -115,37 +97,9 @@ private static int countPositives0(byte[] ba, int off, int len) { return len; } - /** - * Encodes as many ISO-8859-1 codepoints as possible from the source byte - * array containing characters encoded in UTF-16, into the destination byte - * array, assuming that the encoding is ISO-8859-1 compatible. - * - * @param sa the source byte array containing characters encoded in UTF-16 - * @param sp the index of the character (not byte!) from the source array to start reading from - * @param da the target byte array - * @param dp the index of the target array to start writing to - * @param len the maximum number of characters (not bytes!) to be encoded - * @return the total number of characters (not bytes!) successfully encoded - * @throws NullPointerException if any of the provided arrays is null - */ - static int encodeISOArray(byte[] sa, int sp, - byte[] da, int dp, int len) { - // This method should tolerate invalid arguments, matching the lenient behavior of the VM intrinsic. - // Hence, using operator expressions instead of `Preconditions`, which throw on failure. - int sl; - if ((sp | dp | len) < 0 || - // Halving the length of `sa` to obtain the number of characters: - sp >= (sl = sa.length >>> 1) || // Implicit null check on `sa` - dp >= da.length) { // Implicit null check on `da` - return 0; - } - int minLen = Math.min(len, Math.min(sl - sp, da.length - dp)); - return encodeISOArray0(sa, sp, da, dp, minLen); - } - @IntrinsicCandidate - private static int encodeISOArray0(byte[] sa, int sp, - byte[] da, int dp, int len) { + public static int implEncodeISOArray(byte[] sa, int sp, + byte[] da, int dp, int len) { int i = 0; for (; i < len; i++) { char c = StringUTF16.getChar(sa, sp++); @@ -156,35 +110,10 @@ private static int encodeISOArray0(byte[] sa, int sp, return i; } - /** - * Encodes as many ASCII codepoints as possible from the source - * character array into the destination byte array, assuming that - * the encoding is ASCII compatible. - * - * @param sa the source character array - * @param sp the index of the source array to start reading from - * @param da the target byte array - * @param dp the index of the target array to start writing to - * @param len the maximum number of characters to be encoded - * @return the total number of characters successfully encoded - * @throws NullPointerException if any of the provided arrays is null - */ - static int encodeAsciiArray(char[] sa, int sp, - byte[] da, int dp, int len) { - // This method should tolerate invalid arguments, matching the lenient behavior of the VM intrinsic. - // Hence, using operator expressions instead of `Preconditions`, which throw on failure. - if ((sp | dp | len) < 0 || - sp >= sa.length || // Implicit null check on `sa` - dp >= da.length) { // Implicit null check on `da` - return 0; - } - int minLen = Math.min(len, Math.min(sa.length - sp, da.length - dp)); - return encodeAsciiArray0(sa, sp, da, dp, minLen); - } - @IntrinsicCandidate - static int encodeAsciiArray0(char[] sa, int sp, - byte[] da, int dp, int len) { + public static int implEncodeAsciiArray(char[] sa, int sp, + byte[] da, int dp, int len) + { int i = 0; for (; i < len; i++) { char c = sa[sp++]; diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 5c2b47afe3d..f3a57c34165 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -55,6 +55,7 @@ import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; import java.util.function.Supplier; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; @@ -2176,8 +2177,8 @@ public int decodeASCII(byte[] src, int srcOff, char[] dst, int dstOff, int len) return String.decodeASCII(src, srcOff, dst, dstOff, len); } - public int encodeASCII(char[] sa, int sp, byte[] da, int dp, int len) { - return StringCoding.encodeAsciiArray(sa, sp, da, dp, len); + public int uncheckedEncodeASCII(char[] src, int srcOff, byte[] dst, int dstOff, int len) { + return StringCoding.implEncodeAsciiArray(src, srcOff, dst, dstOff, len); } public InputStream initialSystemIn() { 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 86e5f317dc7..c5c45ca3553 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -448,19 +448,15 @@ public interface JavaLangAccess { PrintStream initialSystemErr(); /** - * Encodes as many ASCII codepoints as possible from the source - * character array into the destination byte array, assuming that - * the encoding is ASCII compatible. + * Encodes as many ASCII codepoints as possible from the source array into + * the destination byte array, assuming that the encoding is ASCII + * compatible. + *

+ * WARNING: This method does not perform any bound checks. * - * @param sa the source character array - * @param sp the index of the source array to start reading from - * @param da the target byte array - * @param dp the index of the target array to start writing to - * @param len the total number of characters to be encoded - * @return the total number of characters successfully encoded - * @throws NullPointerException if any of the provided arrays is null - */ - int encodeASCII(char[] sa, int sp, byte[] da, int dp, int len); + * @return the number of bytes successfully encoded, or 0 if none + */ + int uncheckedEncodeASCII(char[] src, int srcOff, byte[] dst, int dstOff, int len); /** * Set the cause of Throwable diff --git a/src/java.base/share/classes/sun/nio/cs/CESU_8.java b/src/java.base/share/classes/sun/nio/cs/CESU_8.java index 409b375ec88..11f21f56139 100644 --- a/src/java.base/share/classes/sun/nio/cs/CESU_8.java +++ b/src/java.base/share/classes/sun/nio/cs/CESU_8.java @@ -445,7 +445,7 @@ private CoderResult encodeArrayLoop(CharBuffer src, int dl = dst.arrayOffset() + dst.limit(); // Handle ASCII-only prefix - int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); sp += n; dp += n; diff --git a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java index 0969669a35b..2a4dbdc95ed 100644 --- a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java @@ -600,7 +600,7 @@ protected CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { try { if (isASCIICompatible) { - int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); + int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); sp += n; dp += n; } diff --git a/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java b/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java index ff5970e92a8..39215bfa93d 100644 --- a/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java +++ b/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java @@ -35,6 +35,7 @@ import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.IntrinsicCandidate; public class ISO_8859_1 @@ -141,34 +142,20 @@ public boolean isLegalReplacement(byte[] repl) { private final Surrogate.Parser sgp = new Surrogate.Parser(); - /** - * Encodes as many ISO-8859-1 codepoints as possible from the source - * character array into the destination byte array, assuming that - * the encoding is ISO-8859-1 compatible. - * - * @param sa the source character array - * @param sp the index of the source array to start reading from - * @param da the target byte array - * @param dp the index of the target array to start writing to - * @param len the maximum number of characters to be encoded - * @return the total number of characters successfully encoded - * @throws NullPointerException if any of the provided arrays is null - */ + // Method possible replaced with a compiler intrinsic. private static int encodeISOArray(char[] sa, int sp, byte[] da, int dp, int len) { - // This method should tolerate invalid arguments, matching the lenient behavior of the VM intrinsic. - // Hence, using operator expressions instead of `Preconditions`, which throw on failure. - if ((sp | dp | len) < 0 || - sp >= sa.length || // Implicit null check on `sa` - dp >= da.length) { // Implicit null check on `da` + if (len <= 0) { return 0; } - int minLen = Math.min(len, Math.min(sa.length - sp, da.length - dp)); - return encodeISOArray0(sa, sp, da, dp, minLen); + encodeISOArrayCheck(sa, sp, da, dp, len); + return implEncodeISOArray(sa, sp, da, dp, len); } @IntrinsicCandidate - private static int encodeISOArray0(char[] sa, int sp, byte[] da, int dp, int len) { + private static int implEncodeISOArray(char[] sa, int sp, + byte[] da, int dp, int len) + { int i = 0; for (; i < len; i++) { char c = sa[sp++]; @@ -179,6 +166,17 @@ private static int encodeISOArray0(char[] sa, int sp, byte[] da, int dp, int len return i; } + private static void encodeISOArrayCheck(char[] sa, int sp, + byte[] da, int dp, int len) { + Objects.requireNonNull(sa); + Objects.requireNonNull(da); + Preconditions.checkIndex(sp, sa.length, Preconditions.AIOOBE_FORMATTER); + Preconditions.checkIndex(dp, da.length, Preconditions.AIOOBE_FORMATTER); + + Preconditions.checkIndex(sp + len - 1, sa.length, Preconditions.AIOOBE_FORMATTER); + Preconditions.checkIndex(dp + len - 1, da.length, Preconditions.AIOOBE_FORMATTER); + } + private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { diff --git a/src/java.base/share/classes/sun/nio/cs/SingleByte.java b/src/java.base/share/classes/sun/nio/cs/SingleByte.java index a5bf06cb251..59887b944d3 100644 --- a/src/java.base/share/classes/sun/nio/cs/SingleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/SingleByte.java @@ -217,7 +217,7 @@ private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { int len = Math.min(dl - dp, sl - sp); if (isASCIICompatible) { - int n = JLA.encodeASCII(sa, sp, da, dp, len); + int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, len); sp += n; dp += n; len -= n; diff --git a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java index 3886978d209..bb84ab1bd4b 100644 --- a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java +++ b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java @@ -159,7 +159,7 @@ private CoderResult encodeArrayLoop(CharBuffer src, assert (dp <= dl); dp = (dp <= dl ? dp : dl); - int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); sp += n; dp += n; diff --git a/src/java.base/share/classes/sun/nio/cs/UTF_8.java b/src/java.base/share/classes/sun/nio/cs/UTF_8.java index d2d6d7e485d..54e479f838a 100644 --- a/src/java.base/share/classes/sun/nio/cs/UTF_8.java +++ b/src/java.base/share/classes/sun/nio/cs/UTF_8.java @@ -452,7 +452,7 @@ private CoderResult encodeArrayLoop(CharBuffer src, int dl = dst.arrayOffset() + dst.limit(); // Handle ASCII-only prefix - int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); sp += n; dp += n; diff --git a/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template b/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template index f3d03a9e9c7..4fc0b2796ee 100644 --- a/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template +++ b/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template @@ -309,7 +309,7 @@ public class EUC_JP try { if (enc0201.isASCIICompatible()) { - int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); + int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); sp += n; dp += n; } diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestVerifyIntrinsicChecks.java b/test/hotspot/jtreg/compiler/intrinsics/TestVerifyIntrinsicChecks.java deleted file mode 100644 index c482a73affd..00000000000 --- a/test/hotspot/jtreg/compiler/intrinsics/TestVerifyIntrinsicChecks.java +++ /dev/null @@ -1,105 +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. - */ - -/* - * @test - * @bug 8361842 - * @summary Verify the effectiveness of the `VerifyIntrinsicChecks` VM flag - * through (bypassing `StringCoding::encodeAsciiArray`, and) feeding - * invalid input to an intrinsified `StringCoding::encodeAsciiArray0` - * (note the `0` suffix!). - * @library /compiler/patches - * @library /test/lib - * @build java.base/java.lang.Helper - * @comment `vm.debug == true` is required since `VerifyIntrinsicChecks` is a - * development flag - * @requires vm.debug == true & vm.flavor == "server" & !vm.graal.enabled - * @requires (os.arch != "riscv64" | (os.arch == "riscv64" & vm.cpu.features ~= ".*rvv.*")) - * @run main/othervm compiler.intrinsics.TestVerifyIntrinsicChecks verify - */ - -package compiler.intrinsics; - -import java.lang.Helper; -import java.time.Instant; -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; - -public final class TestVerifyIntrinsicChecks { - - public static void main(String[] args) throws Exception { - switch (args[0]) { - case "verify" -> { - log("Starting JVM in a separate process to verify the crash"); - OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava( - "-Xcomp", - "-XX:-TieredCompilation", - "-XX:CompileCommand=inline,java.lang.StringCoding::encodeAsciiArray0", - "-XX:+VerifyIntrinsicChecks", - "--patch-module", "java.base=%s/java.base".formatted(System.getProperty("test.patch.path")), - "compiler.intrinsics.TestVerifyIntrinsicChecks", - "crash"); - outputAnalyzer.shouldContain("unexpected null in intrinsic"); - outputAnalyzer.shouldNotHaveExitValue(0); - } - case "crash" -> { - log("Triggering the crash"); - warmUpIntrinsicMethod(); - violateIntrinsicMethodContract(); - } - default -> throw new IllegalArgumentException(); - } - } - - private static void warmUpIntrinsicMethod() { - log("Warming up the intrinsic method"); - char[] sa = createAsciiChars(8192); - byte[] sp = new byte[4096]; - for (int i = 0; i < 1_000; i++) { - Helper.StringCodingEncodeAsciiArray0(sa, i, sp, 0, sp.length - i); - } - } - - private static char[] createAsciiChars(int length) { - char[] buffer = new char[length]; - for (int i = 0; i < length; i++) { - buffer[i] = (char) (i % '\u0080'); - } - return buffer; - } - - private static void violateIntrinsicMethodContract() { - log("Violating the intrinsic method contract (sa=null)"); - Helper.StringCodingEncodeAsciiArray0(null, 1, null, 1, 1); - } - - private synchronized static void log(String format, Object... args) { - Object[] extendedArgs = new Object[2 + args.length]; - extendedArgs[0] = Instant.now(); - extendedArgs[1] = Thread.currentThread().getName(); - System.arraycopy(args, 0, extendedArgs, extendedArgs.length - args.length, args.length); - String extendedFormat = "%%s [%%s] %s%%n".formatted(format); - System.out.printf(extendedFormat, extendedArgs); - } - -} diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java index 1c20a49d281..76ef4766159 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -32,7 +32,6 @@ * @build java.base/java.lang.Helper * @run main compiler.intrinsics.string.TestCountPositives */ - /* * @test * @bug 8281146 8318509 @@ -47,38 +46,17 @@ * @run main/othervm/timeout=1200 -XX:UseAVX=3 compiler.intrinsics.string.TestCountPositives * @run main/othervm/timeout=1200 -XX:UseAVX=3 -XX:+UnlockDiagnosticVMOptions -XX:AVX3Threshold=0 compiler.intrinsics.string.TestCountPositives */ - -/* - * @test - * @bug 8281146 - * @summary Verify `StringCoding::countPositives` intrinsic Java wrapper checks - * by enabling the ones in the VM intrinsic using - * `-XX:+VerifyIntrinsicChecks` - * @comment This does not check out-of-range conditions. The - * `-XX:+VerifyIntrinsicChecks` version of this test simply ensures - * that the VM intrinsic will produce no spurious errors. - * @key randomness - * @library /compiler/patches - * @library /test/lib - * @comment `vm.debug == true` is required since `VerifyIntrinsicChecks` is a - * development flag - * @requires vm.debug == true - * @build java.base/java.lang.Helper - * @run main/othervm - * -XX:+VerifyIntrinsicChecks - * compiler.intrinsics.string.TestCountPositives +/** + * This test was derived from compiler.intrinsics.string.TestHasNegatives */ - package compiler.intrinsics.string; import java.lang.Helper; import java.util.Random; +import java.util.stream.IntStream; import jdk.test.lib.Utils; -/** - * This test was derived from {@link TestHasNegatives}. - */ public class TestCountPositives { private static byte[] bytes = new byte[4096 + 32]; diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java index bb343b246ff..38a516e7521 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,26 +31,6 @@ * @run main/othervm/timeout=1200 --add-opens=java.base/sun.nio.cs=ALL-UNNAMED -Xbatch -Xmx256m compiler.intrinsics.string.TestEncodeIntrinsics */ -/* - * @test - * @bug 6896617 8274242 - * @summary Verify `sun.nio.cs.ISO_8859_1.Encoder::encodeISOArray` intrinsic - * Java wrapper checks by enabling the ones in the VM intrinsic using - * `-XX:+VerifyIntrinsicChecks` - * @comment This does not check out-of-range conditions. The - * `-XX:+VerifyIntrinsicChecks` version of this test simply ensures - * that the VM intrinsic will produce no spurious errors. - * @key randomness - * @library /test/lib - * @comment `vm.debug == true` is required since `VerifyIntrinsicChecks` is a - * development flag - * @requires vm.debug == true - * @run main/othervm/timeout=1200 - * -XX:+VerifyIntrinsicChecks - * --add-opens=java.base/sun.nio.cs=ALL-UNNAMED -Xbatch -Xmx256m - * compiler.intrinsics.string.TestEncodeIntrinsics - */ - package compiler.intrinsics.string; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java index a15f6aade2e..6edf2dc2e56 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -31,7 +31,6 @@ * @build java.base/java.lang.Helper * @run main compiler.intrinsics.string.TestHasNegatives */ - /* * @test * @bug 8054307 8318509 @@ -47,31 +46,11 @@ * @run main/othervm/timeout=1200 -XX:UseAVX=3 -XX:+UnlockDiagnosticVMOptions -XX:AVX3Threshold=0 compiler.intrinsics.string.TestHasNegatives */ -/* - * @test - * @bug 8054307 - * @summary Verify `StringCoding::hasNegatives` intrinsic Java wrapper checks - * by enabling the ones in the VM intrinsic using - * `-XX:+VerifyIntrinsicChecks` - * @comment This does not check out-of-range conditions. The - * `-XX:+VerifyIntrinsicChecks` version of this test simply ensures - * that the VM intrinsic will produce no spurious errors. - * @key randomness - * @library /compiler/patches - * @library /test/lib - * @comment `vm.debug == true` is required since `VerifyIntrinsicChecks` is a - * development flag - * @requires vm.debug == true - * @build java.base/java.lang.Helper - * @run main/othervm - * -XX:+VerifyIntrinsicChecks - * compiler.intrinsics.string.TestHasNegatives - */ - package compiler.intrinsics.string; import java.lang.Helper; import java.util.Random; +import java.util.stream.IntStream; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java index 3985ad8ea90..e6c8b68fc6f 100644 --- a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java +++ b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java @@ -39,11 +39,6 @@ public static int StringCodingCountPositives(byte[] ba, int off, int len) { return StringCoding.countPositives(ba, off, len); } - @jdk.internal.vm.annotation.ForceInline - public static int StringCodingEncodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) { - return StringCoding.encodeAsciiArray0(sa, sp, da, dp, len); - } - @jdk.internal.vm.annotation.ForceInline public static byte[] compressByte(byte[] src, int srcOff, int dstSize, int dstOff, int len) { byte[] dst = new byte[dstSize]; From 2074b975c3d08fec2ecd47dab48132be2ec7c3cf Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Wed, 7 Jan 2026 10:06:29 +0000 Subject: [PATCH 005/113] 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 006/113] 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 @@ private void parkOnCarrierThread(boolean timed, long nanos) { * 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 @@ void unpark() { } } + @Override + void unpark() { + unpark(false); + } + /** * Invoked by unblocker thread to unblock this virtual thread. */ @@ -904,11 +917,7 @@ private void unblock() { */ 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 @@ static void test(boolean notify, boolean interrupt, int... timeouts) throws Exce // 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(); - } - } else { - sleepLessThan(timeout); - synchronized (lock) { - lock.notifyAll(); + 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 007/113] 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 @@ static StructuredTaskScope open() { * 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 @@ static StructuredTaskScope open() { * 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 008/113] 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 int testIntRandomLimitsInterpreted(int x, int y) { @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 int testLongRandomLimitsInterpreted(long x, long y) { @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 009/113] 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 static void main(String[] args) throws Exception { 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 void run() { 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 010/113] 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 @@ class VLoopBody : public StackObj { // 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 @@ class VLoopMemorySlices : public StackObj { 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 011/113] 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 static void setup() { @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 final String getRealm() { } 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 @@ private static void configure(HttpURLConnection conn, Authenticator auth) // 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 @@ static void test(String u, 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 @@ static void createServers() throws Exception { 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 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 static void main(String[] args) throws Exception { 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 static void main(String[] args) throws Exception { 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 @@ 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 static void main(String[] args) throws Exception { 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 @@ void readOneRequest(InputStream is) throws IOException { */ void doServerSide() throws Exception { SSLServerSocketFactory sslssf = - new SimpleSSLContext().get().getServerSocketFactory(); + SimpleSSLContext.findSSLContext().getServerSocketFactory(); SSLServerSocket sslServerSocket = (SSLServerSocket) sslssf.createServerSocket( serverPort, 0, @@ -139,7 +139,7 @@ void doClientSide() throws Exception { 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 @@ static HttpServer httpd(String scheme, String principal, String ktab) 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 void testWithServers() throws Exception { 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 @@ private static SSLContext loadSSLContext(Path keyStoreFilePath, String protocol) } } - // 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 012/113] 8373724: Assertion failure in TestSignumVector.java with UseAPX Reviewed-by: sviswanathan --- src/hotspot/cpu/x86/x86.ad | 153 +++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 76 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(const MachNode* mdef) { - return ((mdef->flags() & Node::PD::Flag_ndd_demotable) != 0); +static bool is_ndd_demotable_opr1(const MachNode* mdef) { + return ((mdef->flags() & Node::PD::Flag_ndd_demotable_opr1) != 0); } -static bool is_ndd_demotable_commutative(const MachNode* mdef) { - return ((mdef->flags() & Node::PD::Flag_ndd_demotable_commutative) != 0); +static bool is_ndd_demotable_opr2(const MachNode* mdef) { + return ((mdef->flags() & Node::PD::Flag_ndd_demotable_opr2) != 0); } -static bool is_demotion_candidate(const MachNode* mdef) { - return (is_ndd_demotable(mdef) || is_ndd_demotable_commutative(mdef)); +#ifdef ASSERT +static bool is_ndd_demotable(const MachNode* 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 013/113] 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 014/113] 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 015/113] 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 016/113] 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.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); - } - - /** - * 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; - } + protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); - public static void main(String argv[]) throws Exception { - run(); - } + native static boolean shouldExit(); - 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 @@ public static void run() throws Exception { 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 017/113] 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 018/113] 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 019/113] 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 @@ private String createMarkingLine(int start, int end) { 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 @@ 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 020/113] 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 static double getDoubleRaw(byte[] array, int offset) { * @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 static void setInt(byte[] array, int offset, int value) { * @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 static void setFloat(byte[] array, int offset, float value) { * @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 static void setFloatRaw(byte[] array, int offset, float value) { * @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 static void setLong(byte[] array, int offset, long value) { * @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 static void setDouble(byte[] array, int offset, double value) { * @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 static double getDoubleRaw(byte[] array, int offset) { * @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 static void setInt(byte[] array, int offset, int value) { * @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 static void setFloat(byte[] array, int offset, float value) { * @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 static void setFloatRaw(byte[] array, int offset, float value) { * @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 static void setLong(byte[] array, int offset, long value) { * @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 static void setDouble(byte[] array, int offset, double value) { * @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 021/113] 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 @@ private TemplateToken generateIRRules() { 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 022/113] 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 023/113] 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 024/113] 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 @@ List generateFactoryMethodsAndFields(FactoryKind k, String key, Message 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 @@ List generateFactoryMethodsAndFields(FactoryKind k, String key, Message 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 @@ factoryName, argDecls(types, argNames).stream().collect(Collectors.joining(", ") } } + 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.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 String toString() { /** * 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 enum LintCategory { /** * 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 025/113] 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 @@ class GCLockerTimingDebugLogger : public StackObj { }; 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 @@ class SuspendibleThreadSet : public AllStatic { 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 026/113] 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 @@ static JVMOption createVMOption(String type, String name) { 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 027/113] 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 028/113] 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.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 @@ private static class Space { 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 @@ private static ArrayList paths() throws IOException { 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 @@ private static void compareZeroExist() { } } - 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 @@ private static int testFile(Path dir) { 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 @@ private static int testVolumes() { 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 static void main(String[] args) throws Exception { // 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 029/113] 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 | 197 ++++++++++++++++-- .../jfr/event/tracing/TestConstructors.java | 167 +++++++++++++++ .../event/tracing/TestInstrumentation.java | 4 + 5 files changed, 362 insertions(+), 30 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 @@ public Instrumentation(ClassLoader classLoader, String internalClassName, byte[] } 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 @@ public byte[] generateBytecode() { 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 @@ private ClassHierarchyResolver resolver() { } } - 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 @@ private boolean modifyClassElement(ClassBuilder classBuilder, ClassElement ce) { 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 @@ private boolean modifyMethod(ClassBuilder classBuilder, MethodModel m, Method me 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 @@ /** * 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 @@ * 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); - } + addTracing(builder); + } + builder.with(element); + } + + 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 { - if (modification.tracing()) { - invokeTrace(builder, suffix); - } + invokeTiming(builder, suffix); + } + } else { + if (modification.tracing()) { + invokeTrace(builder, suffix); } } - builder.with(element); } - public static void invokeTiming(CodeBuilder builder, String 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 @@ private static void verifyTracing(List events) throws Exception { 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 @@ private static void verifyTiming(List events) throws Exception { 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 030/113] 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 @@ 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 void test() throws IOException { } } + @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 031/113] 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.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 @@ private enum LoadingMode {OVERRIDE, APPEND} 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 @@ static void loadInclude(String propFile) { } } + 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 @@ * @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 static void main(String[] args) throws Exception { } 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 @@ static void testShowSettings(Executor ex, FilesManager filesMgr) 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 @@ static void testIncludeBasic(Executor ex, FilesManager filesMgr) file2.addAbsoluteInclude(file1); ex.setMasterFile(masterFile); - ex.setExtraFile(extraFile, Executor.ExtraMode.FILE_URI, false); + ex.setExtraFile(extraFile, false); ex.assertSuccess(); } @@ -152,7 +153,7 @@ static void testRepeatedInclude(Executor ex, FilesManager filesMgr) 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 @@ static void testIncludeWithOverrideAll(Executor ex, FilesManager filesMgr) 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 @@ static void specialCharsIncludes(Executor ex, FilesManager filesMgr, 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 @@ static void testUnicodeIncludes3(Executor ex, FilesManager filesMgr) // 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 @@ static void testUnicodeIncludes4(Executor ex, FilesManager filesMgr) // 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 @@ static void testSpaceIncludes3(Executor ex, FilesManager filesMgr) // 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 @@ static void testSpaceIncludes4(Executor ex, FilesManager filesMgr) // 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 @@ static void assertTestSecuritySetPropertyShouldNotInclude() { 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 @@ static void testCannotIncludeCycles(Executor ex, FilesManager filesMgr) 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 @@ static void testMustHaveMasterFileEvenWithExtraFile(Executor ex, // 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 @@ static Include of(PropsFile propsFile) { 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 @@ void addAbsoluteInclude(PropsFile propsFile) { } 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 @@ void assertApplied(OutputAnalyzer oa) { 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 @@ void assertWasOverwritten(OutputAnalyzer oa) { 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 @@ void close() { } } +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 @@ protected void addIncludeDefinition(Include include) { 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 @@ private PropsFile newFile(Path path, PropsFileBuilder builder) 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 @@ PropsFile newMasterFile() throws IOException { 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 @@ void reportCreatedFiles() throws IOException { 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 @@ public void close() throws IOException { } 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 @@ enum ExtraMode { 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 @@ void setMasterFile(PropsFile masterPropsFile) { 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 @@ private void execute(boolean successExpected) throws Exception { 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 032/113] 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 @@ * 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 @@ * * * @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 @@ /** * 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 @@ /** * 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 @@ * 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 @@ * 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 @@ * 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 033/113] 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 @@ void checkDistrust(String variant, X509Certificate[] chain) } 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 034/113] 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 035/113] 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 @@ class DynamicArchiveBuilder : public ArchiveBuilder { 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 @@ class DynamicArchiveBuilder : public ArchiveBuilder { init_header(); gather_source_objs(); - gather_array_klasses(); reserve_buffer(); log_info(cds, dynamic)("Copying %d klasses and %d symbols", @@ -159,7 +158,6 @@ class DynamicArchiveBuilder : public ArchiveBuilder { 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 @@ class DynamicArchiveBuilder : public ArchiveBuilder { write_archive(serialized_data, cl_config); release_header(); - DynamicArchive::post_dump(); - post_dump(); verify_universe("After CDS dynamic dump"); @@ -185,30 +181,6 @@ class DynamicArchiveBuilder : public ArchiveBuilder { 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 @@ class VM_PopulateDynamicDumpSharedSpace: public VM_Heap_Sync_Operation { } }; -// _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 @@ class DynamicArchiveHeader : public FileMapHeader { }; 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 @@ private OutputAnalyzer createAOTCache() throws Exception { // 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 @@ private String getBaseArchiveForDynamicArchive() throws Exception { 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 @@ private String getBaseArchiveForDynamicArchive() throws Exception { } } + 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 SimpleCDSAppTester appCommandLine(String... args) { 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 void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception } 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 036/113] 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 static float clamp(float value, float min, float max) { */ @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 static double fma(double a, double b, double c) { */ @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 037/113] 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.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 void setUp() { //----------------------------------------------------------------------- @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 void setUp() { //----------------------------------------------------------------------- @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 void setUp() { //----------------------------------------------------------------------- @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 void setUp() { //----------------------------------------------------------------------- @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 void setUp() { //----------------------------------------------------------------------- @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 void setUp() { //----------------------------------------------------------------------- @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 void setUp() { //----------------------------------------------------------------------- @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 void setUp() { //----------------------------------------------------------------------- @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 038/113] 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 | 39 +++++-- .../share/classes/sun/awt/AppContext.java | 19 --- .../classes/sun/awt/image/ImageCache.java | 8 +- .../swing/Security/6657138/bug6657138.java | 108 ------------------ 4 files changed, 32 insertions(+), 142 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.border.Border; import javax.swing.plaf.UIResource; -import sun.awt.AppContext; - import sun.lwawt.macosx.CPlatformWindow; import sun.swing.SwingUtilities2; @@ -150,13 +148,34 @@ T get() { protected abstract T create(); } + abstract static class LazySingleton { + T instance; + + final T get() { + if (instance == null) { + instance = getInstance(); + } + return instance; + } + + abstract T getInstance(); + } + abstract static class RecyclableSingleton { - final T get() { - return AppContext.getSoftReferenceValue(this, () -> getInstance()); - } - void reset() { - AppContext.getAppContext().remove(this); + 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 @@ V get(final K key) { 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.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 synchronized PropertyChangeListener[] getPropertyChangeListeners( } 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.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 @@ * 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 039/113] 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 @@ class ShenandoahAdaptiveHeuristics : public ShenandoahHeuristics { 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 @@ class ShenandoahHeuristics : public CHeapObj { 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 @@ class ShenandoahOldHeuristics : public ShenandoahHeuristics { 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 040/113] 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 void run() { }; 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 void run() { }; 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 041/113] 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 @@ * 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 @@ /** * 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 042/113] 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 Object[] testRoundD(byte[] in) { 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 043/113] 8374644: Regression in GZIPInputStream performance after JDK-7036144 Reviewed-by: lancea, alanb --- .../java/util/zip/GZIPInputStream.java | 56 ++++++++++++++++--- 1 file changed, 48 insertions(+), 8 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 GZIPInputStream(InputStream in, int size) throws IOException { 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 void close() throws IOException { /* * 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 void close() throws IOException {} // 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,12 +331,16 @@ private int readUByte(InputStream in) throws IOException { if (b == -1) { throw new EOFException(); } + checkUnexpectedByte(b); + return b; + } + + private void checkUnexpectedByte(final int b) throws IOException { if (b < -1 || b > 255) { - // Report on this.in, not argument in; see read{Header, Trailer}. + // 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); + + ".read() returned value out of range -1..255: " + b); } - return 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 044/113] 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 @@ class CallNode : public SafePointNode { 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 045/113] 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 046/113] 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 void produceImage() throws IOException, ImageFormatException { 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 void produceImage() throws IOException, ImageFormatException { 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 047/113] 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 @@ private 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 static int eatMemory(ExecutionController stresser, GarbageProducer gp, lo * 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 static int eatMemory(ExecutionController stresser,long initialFactor, lon * 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 static int eatMemory(ExecutionController stresser, GarbageProducer gp, lo * 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 048/113] 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 @@ class EpsilonHeap : public CollectedHeap { 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 @@ class EpsilonGenerationCounters : public GenerationCounters { 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 049/113] 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 050/113] 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 @@ private static final class Arguments { // // 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 @@ static List getArgumentList(String inputString) { // 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 @@ private static Collection test_ARGUMENTS() { 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 051/113] 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 @@ 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 void initialize() { 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 052/113] 8374219: Fix issues in jpackage's Executor class Reviewed-by: almatvee --- .../jpackage/internal/DesktopIntegration.java | 3 +- .../jpackage/internal/LibProvidersLookup.java | 35 +- .../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 | 99 +- .../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 | 440 ++-- .../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 | 776 ++----- .../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, 8888 insertions(+), 1907 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.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 @@ 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 @@ static boolean supported() { return (new ToolValidator(TOOL_LDD).validate() == null); } - public LibProvidersLookup() { - } - LibProvidersLookup setPackageLookup(PackageLookup v) { packageLookup = v; return this; @@ -87,23 +83,20 @@ List execute(Path root) throws IOException { } 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(); - - if (ret != 0) { + final var result = Executor.of(TOOL_LDD, path.toString()).saveOutput().execute(); + + 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 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 @@ 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 @@ private static void createDebPackage(Options options, LinuxDebSystemEnvironment 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 @@ private static BuildEnvFromOptions buildEnv() { 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 @@ protected void initLibProvidersLookup(LibProvidersLookup libProvidersLookup) { 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 @@ protected List findErrorsInOutputPackage() throws IOExcepti 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 @@ protected void buildPackage() throws IOException { 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 @@ private void prepareProjectConfig(Map data) throws IOException { } } - 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 @@ private static Stream findProvidingPackages(Path file, Path dpkg) throws 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 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.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 @@ static LinuxApplication createLinuxApplication(Options options) { 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 @@ static LinuxRpmPackage createLinuxRpmPackage(Options options) { 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 @@ static LinuxDebPackage createLinuxDebPackage(Options options) { 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 @@ private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, St 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.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 @@ static String getValue(StandardPackageType pkgType) { } } - 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 { + 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()); + } + } - /* - * 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}"); + return Result.ofErrors(errors); + } - RpmArchReader(String... cmdline) { - this.cmdline = cmdline; - } + /* + * 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}"); - 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); - } + RpmArchReader(String... cmdline) { + this.cmdline = cmdline; + } - private final String[] cmdline; + Result getRpmArch() { + var exec = Executor.of(cmdline).saveOutput(true); + return Result.of(exec::executeExpectSuccess, IOException.class) + .flatMap(LinuxPackageArch::getStdoutFirstLine); } - static final String VALUE = toSupplier(RpmPackageArch::getValue).get(); + private final String[] cmdline; + } - 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"); - } + 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 @@ private LinuxPackage create(Package pkg) { category(), Optional.ofNullable(additionalDependencies), release(), - pkg.asStandardPackageType().map(LinuxPackageArch::getValue).orElseThrow())); + arch.value())); } LinuxPackageBuilder literalName(String v) { @@ -119,6 +119,11 @@ Optional release() { 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 @@ private record Defaults(String menuGroupName) { 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 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 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 @@ 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.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 @@ static Result create() { }); } - static Optional detectNativePackageType() { + static Optional detectNativePackageType() { if (Internal.isDebian()) { return Optional.of(StandardPackageType.LINUX_DEB); } else if (Internal.isRpm()) { @@ -55,13 +55,14 @@ static Optional detectNativePackageType() { } } - 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 @@ static Result mixin(Class type, } } - 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.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 @@ private static CodesignException handleCodesignException(MacApplication app, Cod } 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.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 static Builder build(Supplier> args) { 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 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 MacBundlingEnvironment() { .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 @@ private static BuildEnvFromOptions buildEnv() { .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 static Collection findCertificates(Optional ke 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.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 @@ Path licenseFile() { 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 @@ private void copyDmgContent() throws IOException { } } + 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 @@ private String getInstallDirDisplayName() { } } + 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 @@ private void buildDMG() throws IOException { 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 @@ private void buildDMG() throws IOException { // 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 @@ private void buildDMG() throws IOException { // 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 @@ private void buildDMG() throws IOException { // 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 @@ private void buildDMG() throws IOException { } 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 @@ private void buildDMG() throws IOException { } + 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 @@ static Result create() { // 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.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.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 @@ void build() { 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 @@ private void createComponentPlistFile() throws IOException { 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 @@ private void productbuild() throws IOException { } 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 @@ 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 @@ static void withKeychains(ThrowingConsumer, ? extends Exception> // 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 @@ static void withKeychain(ThrowingConsumer keychai args.addAll(missingKeychains.stream().map(Keychain::asCliArg).toList()); - Executor.of(args.toArray(String[]::new)).executeExpectSuccess(); + Executor.of(args).executeExpectSuccess(); } } @@ -89,7 +94,7 @@ List keychains() { @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 @@ Builder bundler(StandardBundlingOperation op, Consumer bundler) { 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 @@ static Builder build() { 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 @@ public T get() { 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 saveOutput() { + return saveOutput(true); + } + + Executor saveFirstLineOfOutput() { + commandOutputControl.saveFirstLineOfOutput(); + return this; + } + + Executor charset(Charset v) { + commandOutputControl.charset(v); return this; } - Executor setWriteOutputToFile(boolean v) { - writeOutputToFile = v; + Executor storeOutputInFiles(boolean v) { + commandOutputControl.storeOutputInFiles(v); return this; } - Executor setTimeout(long v) { + 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 @@ Executor setQuiet(boolean v) { return this; } - List getOutput() { - return output; + Executor mapper(UnaryOperator v) { + mapper = v; + return this; } - 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)); - } - return this; + Optional> mapper() { + return Optional.ofNullable(mapper); } - int execute() throws IOException { - output = null; + Executor copy() { + return new Executor(this); + } - 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()); + Result execute() throws IOException { + if (mapper != null) { + var mappedExecutor = Objects.requireNonNull(mapper.apply(this)); + if (mappedExecutor != this) { + return mappedExecutor.execute(); } - } 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); } - if (!quietCommand) { - Log.verbose(String.format("Running %s", createLogMessage(pb, true))); - } + var coc = commandOutputControl.copy(); - Process p = pb.start(); + final CommandOutputControl.Executable exec; + if (processBuilder != null) { + exec = coc.createExecutable(copyProcessBuilder()); + } else if (toolProvider != null) { + exec = coc.createExecutable(toolProvider, args.toArray(String[]::new)); + } else { + throw new IllegalStateException("No target to execute"); + } - int code = 0; - if (writeOutputToFile) { - try { - code = waitForProcess(p); - } catch (InterruptedException ex) { - Log.verbose(ex); - throw new RuntimeException(ex); - } + PrintableOutputBuilder printableOutputBuilder; + if (dumpOutput()) { + printableOutputBuilder = new PrintableOutputBuilder(coc); + } else { + printableOutputBuilder = null; } - 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)); + if (timeout == null) { + result = exec.execute(); + } else { + result = exec.execute(timeout.toMillis(), TimeUnit.MILLISECONDS); } - return code; } catch (InterruptedException ex) { - Log.verbose(ex); - throw new RuntimeException(ex); + throw ExceptionBox.toUnchecked(ex); } + + if (dumpOutput()) { + log(result, printableOutputBuilder.create()); + } + + return result; } - private int waitForProcess(Process p) throws InterruptedException { - if (timeout == INFINITE_TIMEOUT) { - return p.waitFor(); - } else { - if (p.waitFor(timeout, TimeUnit.SECONDS)) { - return p.exitValue(); - } else { - Log.verbose(String.format("Command %s timeout after %d seconds", - createLogMessage(pb, false), timeout)); - p.destroy(); - return -1; + 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"); } } - static Executor of(String... cmdline) { - return new Executor().setCommandLine(cmdline); + 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; } - static Executor of(ProcessBuilder pb) { - return new Executor().setProcessBuilder(pb); + private boolean dumpOutput() { + return Log.isVerbose() && !quietCommand; } - 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())); + 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()); } - return sb.toString(); + + result.exitCode().ifPresentOrElse(exitCode -> { + Log.verbose("Returned: " + exitCode + "\n"); + }, () -> { + Log.verbose("Aborted: timed-out" + "\n"); + }); } - public static final int INFINITE_TIMEOUT = -1; + 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); + } + } + + 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); + } + } + + private boolean isBinaryOutput() { + return sinks.length == 2; + } + + private final ByteArrayOutputStream sinks[]; + private final Charset charset; + } - 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 @@ public static void copyFile(Path sourceFile, Path destFile) 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 @@ static void writableOutputDir(Path outdir) { 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.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 @@ 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 @@ import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.List; /** * Log @@ -105,29 +104,6 @@ public void verbose(String msg) { } } - 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 static void verbose(String msg) { 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 @@ ConfigException validate() { 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.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 @@ */ 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 String name() { @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 static void main(String... args) { 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 static int run(PrintWriter out, PrintWriter err, String... args) { 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 @@ private static Collection runIt(Supplier 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 @@ /** * 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 @@ String applyTo(String v) { } } - 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 @@ public boolean test(String t) { 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.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_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 @@ private static void traceWixToolset(WinSystemEnvironment sysEnv) { } } - 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 @@ static Optional lookup(WixTool tool, Optional lookupDir) // 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 @@ private static class CountingBundleVerifier extends TickCounter implements Throw @Override public void accept(JPackageCommand cmd, Executor.Result result) { tick(); - jpackageExitCode = result.exitCode(); + jpackageExitCode = result.getExitCode(); } @Override @@ -371,8 +371,7 @@ public Executor.Result execute(int expectedExitCode) { } 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.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 static Executor of(ToolProvider toolProvider, String... args) { } public Executor() { - outputStreamsControl = new OutputStreamsControl(); - winEnglishOutput = false; } public Executor setExecutable(String v) { @@ -136,62 +127,31 @@ public Executor setWindowsTmpDir(String tmp) { 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 Executor discardStdout() { } public Executor discardStderr(boolean v) { - outputStreamsControl.stderr().discard(v); + commandOutputControl.discardStderr(v); return this; } @@ -208,45 +168,126 @@ public Executor discardStderr() { return discardStderr(true); } - public interface Output { - public List getOutput(); + public Executor binaryOutput(boolean v) { + commandOutputControl.binaryOutput(v); + return this; + } - public default String getFirstLineOfOutput() { - return findFirstLineOfOutput().orElseThrow(); - } + public Executor binaryOutput() { + return binaryOutput(true); + } - public default Optional findFirstLineOfOutput() { - return getOutput().stream().findFirst(); - } + 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; } - public record Result(int exitCode, CommandOutput output, Supplier cmdline) implements Output { + 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 String getFirstLineOfOutput() { + return getOutput().getFirst(); + } + + public List stdout() { + return base.stdout(); + } + + 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 Output stdout() { - return createView(output.stdoutLines()); + public byte[] byteStdout() { + return base.byteStdout(); } - public Output stderr() { - return createView(output.stderrLines()); + public byte[] byteStderr() { + return base.byteStderr(); } - public Result assertExitCodeIs(int expectedExitCode) { - TKit.assertEquals(expectedExitCode, exitCode, String.format( - "Check command %s exited with %d code", - cmdline.get(), expectedExitCode)); + 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 Result assertExitCodeIsZero() { } 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 Result executeWithoutExitCodeCheck() { }).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 Result execute() { } 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 @@ Result getValue() { * 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 Result executeAndRepeatUntilExitCode(int expectedExitCode, int max, int w * @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 List executeWithoutExitCodeCheckAndGetOutput() { 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 @@ private Result runExecutable() throws IOException, InterruptedException { 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 @@ private Result runExecutable() throws IOException, InterruptedException { }); } - 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; - } - - private Result runToolProvider() throws IOException { - final var toolProviderStreamConfig = ToolProviderStreamConfig.create(outputStreamsControl); + sb.append("; ").append(commandOutputControl.description()); - final var exitCode = runToolProvider(toolProviderStreamConfig); - - final var output = combine( - read(outputStreamsControl.stdout(), toolProviderStreamConfig.out()), - read(outputStreamsControl.stderr(), toolProviderStreamConfig.err())); - return createResult(exitCode, output); + return execute(sb, commandOutputControl.createExecutable(toolProvider, args.toArray(String[]::new))); } - 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 Result execute(StringBuilder traceMsg, CommandOutputControl.Executable exec) throws IOException, InterruptedException { + Objects.requireNonNull(traceMsg); - 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); - } + trace("Execute " + traceMsg + "..."); - 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(); - } - } - } + var result = exec.execute(); - 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()); - } - } + trace("Done. Exit code: " + result.getExitCode()); + + return createResult(result); } - 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 String getPrintableCommandLine() { var cmdline = Stream.of(prefixCommandLineArgs(), List.of(exec), args).flatMap( List::stream).toList(); - return String.format(format, printCommandLine(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 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(); + return String.format(format, CommandLineFormat.DEFAULT.apply(cmdline), cmdline.size()); } - 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); - } - } + private record ExecutableAttributes(CommandOutputControl.ExecutableAttributes base, String toStringValue) + implements CommandOutputControl.ExecutableAttributes { - @Override - public void write(byte[] b, int off, int len) throws IOException { - for (final var out : streams) { - out.write(b, off, len); + ExecutableAttributes { + Objects.requireNonNull(base); + if (toStringValue.isBlank()) { + throw new IllegalArgumentException(); } } @Override - public void flush() throws IOException { - forEach(OutputStream::flush); + public String toString() { + return toStringValue; } @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; - } + public List commandLine() { + return base.commandLine(); } - - @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 static void trace(String msg) { + TKit.trace(String.format("exec: %s", msg)); } 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.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 static void useExecutableByDefault() { } /** - * 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 static void useExecutableByDefault() { * @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 @@ && isImagePackageType()) { 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 @@ private static String queryFileMimeType(Path file) { 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.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.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 static void withExplodedDmg(JPackageCommand cmd, 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(); + + final Path mountPoint; - Path mountPoint = null; + 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 static void withExplodedDmg(JPackageCommand cmd, 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 @@ private static VerifyStatus verifyCertificate(ResolvedCertificateRequest resolve "-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 static void assertAdhocSigned(Path path) { 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 String value() { 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 static Optional findCodesignSignOrigin(Path path) { } 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 @@ private static void assertSigned(Path path, boolean sudo) { TKit.trace("Try /usr/bin/codesign again with `sudo`"); assertSigned(path, true); } else { - reportUnexpectedCommandOutcome(exec.getPrintableCommandLine(), result); + reportUnexpectedCommandOutcome(result); } } @@ -264,13 +264,13 @@ public static List getPkgCertificateChain(Path path) { 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 record SignIdentity(String name, CertificateHash fingerprint) { } } - 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 @@ private static int runMsiexecWithRetries(Executor misexec, Optional msiLog 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 @@ private static int runMsiexecWithRetries(Executor misexec, Optional msiLog break; } - return result.exitCode(); + return result.getExitCode(); } static PackageHandlers createMsiPackageHandlers(boolean createMsiLog) { @@ -462,7 +462,7 @@ static String queryRegistryValue(String keyPath, String valueName) { 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 @@ void testDefaultBundlingOperation() { 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 @@ Stream getTestCasesFromErrorTest() throws Exception { 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 java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -45,27 +47,41 @@ public void testForPropertyValues(String expected, String input) { 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 static Collection basic() { .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 static void testEnvVars() { 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 053/113] 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 054/113] 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 @@ static final class InvalidPlistFileException extends Exception { 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.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 @@ static ExternalApplication load(ApplicationLayout appLayout, OperatingSystem os) 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.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 PListReader(Node node) { } } - 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.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 void save(Path appImageDir) throws IOException { 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.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 @@ private void verifyInAppImageFile(JPackageCommand cmd) { } } - 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 @@ private static String launcherDescription( 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 @@ private static void verifyHostArch(JPackageCommand cmd) throws Exception { 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 static void testNoUpgradeTable() throws IOException { 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 055/113] 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 byte[] getNonceValue() { 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 AffinePoint basePointMultiply(byte[] scalar) { 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 @@ private static boolean verifySignedDigestImpl(ECDSAOperations ops, ECPoint publi 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 056/113] 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 057/113] 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 | 50 ++------- .../jtreg/GatherDiagnosticInfoObserver.java | 10 +- .../GatherProcessInfoTimeoutHandler.java | 19 +--- 4 files changed, 122 insertions(+), 63 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 void close() { 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 @@ private HtmlSection(PrintWriter pw, String id, String name, HtmlSection rootSect 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 void link(HtmlSection section, String child, String name) { } 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 SubSection(HtmlSection parent, String name, : 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 void finishedTest(TestResult tr) {
       
           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 @@ private void gatherCoreInfo(Path workDir, String name, Path core, PrintWriter lo
       
           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 @@ protected void runActions(Process process, long pid)
               }
               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 @@ protected void runActions(Process process, long pid)
               }
           }
       
      -    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 058/113] 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 @@ static void sleep(long millis) {
           }
       
           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 @@ private static void checkStates(Thread vt, Thread.State expState) {
               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 059/113] 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 record Provider(Supplier bundlingEnvSupplier) imp
               }
       
               public Provider() {
      -            this(Main::loadBundlingEnvironment);
      +            this(DefaultBundlingEnvironmentLoader.INSTANCE);
               }
       
               @Override
      @@ -104,7 +104,7 @@ public static void main(String... args) {
           }
       
           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 @@ private static String getVersion() {
               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 060/113] 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 @@ class TaskQueueSuper: public CHeapObj {
           _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 @@ class GenericTaskQueue: public TaskQueueSuper {
       
         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 @@ class AtomicImpl::CommonCore {
           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 @@ class AtomicImpl::Atomic {
                                                  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 061/113] 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.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 static final class UnboundIncrementInstruction 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 static boolean validateAndIsWideIinc(int slot, int val) { 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 @@ record Result(boolean shouldFail, int slot) { 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 @@ record Result(boolean shouldFail, int slot) { 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 @@ void testIincConstant() { 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 @@ void testIincConstant() { 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 062/113] 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 063/113] 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 064/113] 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 065/113] 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 066/113] 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 067/113] 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 @@ private static List generatePrimitiveOperations() { 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 @@ private static List generateFloat16Operations() { 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 068/113] 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.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 static void main(String... arg) throws Exception { 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 static void main(String... arg) throws Exception { // 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 @@ private static long filesInRepository() throws IOException { 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 069/113] 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 byte[] getBytes() { 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 String getLoaderNameID(ClassLoader loader) { } @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 @@ MemorySegment reinterpret(long newSize, * 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 @@ MemorySegment reinterpret(long newSize, */ 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 @@ MemorySegment reinterpret(long newSize, * 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 @@ static void copy(Object srcArray, int srcIndex, 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 @@ default MemorySegment allocateFrom(String str) { * 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 @@ default MemorySegment allocateFrom(String str, Charset charset) { 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 @@ default MemorySegment allocateFrom(String str, Charset charset) { 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 @@ StackWalker newStackWalkerInstance(Set options, /** * 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 boolean equals(Object o) { 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 static void copy(Object srcArray, int srcIndex, } } + @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.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 static String read(AbstractMemorySegmentImpl segment, long offset, Charse }; } + @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 static void write(AbstractMemorySegmentImpl segment, long offset, Charset @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 @@ private static void writeByte(AbstractMemorySegmentImpl segment, long offset, Ch @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 @@ private static void writeShort(AbstractMemorySegmentImpl segment, long offset, C @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 static CharsetKind of(Charset charset) { } } - 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.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 void testStrings(String testString) { } } + @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 void testOffset(String testString) { } } + @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 static Object[][] strings() { {""}, {"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 @@ public class ToJavaStringTest { private MemorySegment strSegment; + private int length; @Param({"5", "20", "100", "200", "451"}) int size; @@ -61,19 +65,33 @@ public void setup() { 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 070/113] 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 071/113] 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 @@ class ShenandoahGenerationalControlThread: public ShenandoahController { // 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 @@ class ShenandoahGenerationalHeap : public ShenandoahHeap { 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 072/113] 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.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 void finishedTest(TestResult tr) { 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 void finishedTest(TestResult tr) { } } - 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 073/113] 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 void layoutContainer(Container container) { // 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 074/113] 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 075/113] 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 076/113] 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 @@ class EpsilonHeap : public CollectedHeap { 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 @@ class EpsilonGenerationCounters : public GenerationCounters { 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 077/113] 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 @@ MemorySegment reinterpret(long newSize, * @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 @@ static void copy(Object srcArray, int srcIndex, * @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 @@ default MemorySegment allocateFrom(String str, Charset charset) { * {@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 078/113] 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 void visitNewArray(JCNewArray tree) { 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 void visitNewArray(JCNewArray tree) { 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 @@ > void test(String clazz, AttributedElement m, AttributeM 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 @@ static class Test5b { 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 079/113] 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 080/113] 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 @@ class SCMemProjNode : public ProjNode { 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 @@ class SCMemProjNode : public ProjNode { 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 @@ class LoadStoreNode : public Node { 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 081/113] 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.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.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.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.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 @@ private static MacPackageBuilder createMacPackageBuilder(Options options, Applic 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.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 @@ static PackagingPipeline.Builder build(Optional pkg) { 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 @@ static PackagingPipeline.Builder build(Optional pkg) { .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 @@ public void execute(AppImageBuildEnv env) throws IOException } } + 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 @@ private static void copyAppImage(MacPackage pkg, AppImageLayout srcAppImage, final Optional srcMacBundle; if (pkg.isRuntimeInstaller()) { - srcMacBundle = MacBundle.fromAppImageLayout(srcAppImage); + srcMacBundle = macBundleFromAppImageLayout(srcAppImage); } else { srcMacBundle = Optional.empty(); } @@ -297,7 +320,7 @@ private static void copyAppImage(MacPackage pkg, AppImageLayout srcAppImage, 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 @@ private static void writeApplicationInfoPlist( 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 @@ private static void sign(AppImageBuildEnv env) t } 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 @@ private static void addFaToUTExportedTypeDeclarations(XMLStreamWriter xml, 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 @@ private static UnaryOperator mapPackageTaskContex }; } + 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.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 static MacApplication create(Application app, MacApplicationMixin mixin) } 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 @@ private OptionSpecBuilder(Class valueType) { 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 @@ OptionSpec createArrayOptionSpec() { 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 @@ OptionSpecBuilder mutate(Consumer> mutator) { 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 @@ OptionSpecBuilder converterExceptionFormatString(UnaryOperator mutato OptionSpecBuilder validatorExceptionFactory(OptionValueExceptionFactory v) { validatorBuilder.exceptionFactory(v); + validator = null; return this; } @@ -225,18 +239,27 @@ OptionSpecBuilder converter(Function v) { 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 @@ OptionSpecBuilder withoutConverter() { OptionSpecBuilder withoutValidator() { validatorBuilder.predicate(null).consumer(null); + validator = null; return this; } @@ -423,14 +447,6 @@ private Optional> createConverter() { } } - 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 @@ private OptionValueConverter createArrayConverter() { 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 @@ private T[] toOneElementArray(T v) { 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 @@ private enum MandatoryOption implements OptionScope { .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 boolean test(Path path) { .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.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 @@ private 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 @@ private record DefaultParsedValue(T value, StringToken sourceToken) implement } } } - - @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 @@ static Optional fromPath(Path path) { 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.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 static Path getPathInAppImage(Path appImageDir) { } 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 void save(Path appImageDir) throws IOException { 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 static AppImageFile load(Path appImageDir) { 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 static AppImageFile load(Path appImageDir) { 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 @@ private boolean expectAppImageFile() { 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 @@ private void assertAppImageFile() { 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 @@ private void assertPackageFile() { } 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.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.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.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 static void withExplodedDmg(JPackageCommand cmd, // 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 static void withExplodedDmg(JPackageCommand cmd, "-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 static void withExplodedDmg(JPackageCommand cmd, } 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 static PListReader readPListFromEmbeddedRuntime(Path appImage) { public static PListReader readPList(Path path) { TKit.assertReadableFileExists(path); - return ThrowingSupplier.toSupplier(() -> readPList(Files.readAllLines( - path))).get(); - } - - public static PListReader readPList(List lines) { - return readPList(lines.stream()); + return readPList(toFunction(Files::readAllBytes).apply(path)); } - 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 static boolean appImageSigned(JPackageCommand cmd) { 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 static void writeFaPListFragment(JPackageCommand cmd, XMLStreamWriter xml }).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 @@ private static void copyFaPListFragmentFromPredefinedAppImage(JPackageCommand cm 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.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 static void verifyAppImageSigned( }); // 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 static void assertAdhocSigned(Path path) { } 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 String value() { 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_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 @@ private static Collection platformSpecificProperties() { "Foo", "", "property-x", - "true", "False", "", " Quick brown fox", @@ -546,8 +544,7 @@ private static Collection platformSpecificProperties() { .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 @@ private static Stream testValidXml() { "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.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 @@ static final class TestException extends RuntimeException { } + 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 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 void testBuilderCopy() { } @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.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.andThen(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 @@ private static List test_consumer_negative() { 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.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 static void testEmpty(boolean withIcon) throws IOException { */ @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 static void testBadAppImage() throws IOException { */ @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 @@ private static Path iconPath(String name) { + 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 static Collection testMac() { .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 082/113] 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 | 47 +++++++--- 8 files changed, 112 insertions(+), 171 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.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 Executor setEnvVar(String envVarName, String envVarValue) { 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 @@ Executor storeOutputInFiles() { 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 Result executeWithoutExitCodeCheck() { "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 @@ private Path executablePath() { 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 String getPrintableCommandLine() { 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 @@ private static void trace(String msg) { 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.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 static Path createBundle(JavaAppDesc appDesc, Path outputDir) { 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 AppOutputVerifier addJavaOptions(Collection v) { .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 @@ private Executor getExecutor(String...args) { .saveOutput(saveOutput) .dumpOutput() .setExecutable(executablePath) + .processListener(processListener) .addArguments(List.of(args)); env.forEach((envVarName, envVarValue) -> { @@ -493,6 +496,7 @@ private Executor getExecutor(String...args) { 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.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 static String getExecutableDescription(Path pathToExeFile) { "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 void test2() { 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 void test() throws IOException { 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 void test() throws IOException { 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 @@ 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 void test() throws InterruptedException { 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(); + + Optional childProcess = Optional.empty(); + + try { + // Wait a bit to let the app start + Thread.sleep(Duration.ofSeconds(10)); + + try (var children = mainLauncherProcess.children()) { + childProcess = children.filter(p -> { + return mainLauncherProcess.info().command().equals(p.info().command()); + }).findFirst(); + } - // Find the main app launcher process and kill it - killAppLauncherProcess(cmd, null, 2); + 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)); + // Wait a bit and check if child app launcher process is still running (it must NOT) + Thread.sleep(Duration.ofSeconds(5)); - killAppLauncherProcess(cmd, null, 0); + 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 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 static void test() { 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 @@ 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 @@ void apply(CfgFile cfgFile) { 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 @@ void apply(JPackageCommand cmd, CfgFile origCfgFile) throws InterruptedException // 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)); - - // Find the main app launcher process and kill it - killAppLauncherProcess(cmd, null, expectedNoRestarted ? 1 : 2); + var mainLauncherProcess = f.get(); + + 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 083/113] 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 static void main(String[] args) throws Exception { 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 static void main(String[] args) throws Exception { } 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 084/113] 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 085/113] 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 086/113] 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 | 130 +++++++++++------- .../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, 124 insertions(+), 55 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 void produceImage() throws IOException, ImageFormatException { 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 void produceImage() throws IOException, ImageFormatException { } 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,58 +155,80 @@ public void produceImage() throws IOException, ImageFormatException { 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"); - } + if (!contFlag) { + if (line.contains("[]")) { + contFlag = true; + } else { + continue; + } + } - int startIndex = matcher.start(); - int endIndex = matcher.end(); - String hexByte = line.substring(startIndex, endIndex); + int end = line.indexOf(';'); + if (end >= 0) { + sb.append(line, 0, end + 1); + break; + } else { + sb.append(line).append(System.lineSeparator()); + } + } - 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)); - } + String resultLine = sb.toString(); + int cutOffIndex = resultLine.indexOf('{'); + resultLine = resultLine.substring(cutOffIndex + 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++; - } + 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"); + } - 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++; - } + 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 static void main(String[] args) throws Exception { 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 087/113] 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 088/113] 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 | 54 +++--- .../recorder/repository/jfrEmergencyDump.hpp | 4 +- .../share/jfr/recorder/service/jfrPostBox.cpp | 7 +- .../share/jfr/recorder/service/jfrPostBox.hpp | 25 +-- .../recorder/service/jfrRecorderService.cpp | 180 ++++++++++++++++-- .../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, 274 insertions(+), 73 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"); + + // 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()) { - log_info(jfr, system)("The Watcher thread crashed so no jfr emergency dump will be generated."); + // 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; } - // Ensure a JavaThread is _thread_in_vm when we make this call - JavaThreadInVMAndNative jtivm(thread); + + 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. +// 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 event emit checkpoint events to the same segment. + // 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. - 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); + 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 @@ private static void test(boolean shouldCrash) throws Exception { // 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 089/113] 8373106: JFR suspend/resume deadlock on macOS in pthreads library Reviewed-by: egahlin --- .../periodic/sampling/jfrThreadSampler.cpp | 94 +++++++++---------- 1 file changed, 44 insertions(+), 50 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; - - 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; + { + /* + * 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++; + } } - } - *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 090/113] 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 091/113] 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 | 148 ++++++----- .../helpers/jdk/jpackage/test/Main.java | 55 +++- .../helpers/jdk/jpackage/test/TKit.java | 237 +++++++++--------- .../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, 526 insertions(+), 302 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 @@ Globals executorFactory(ExecutorFactory v) { 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 @@ private void checkMutable() { } 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 void setPrintWriter(PrintWriter out, PrintWriter err) { 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 @@ private String addTimestamp(String msg) { } } - 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 int run(PrintWriter out, PrintWriter err, String... args) { @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 @@ private 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 @@ private static int runWithGlobals( 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 @@ private static int runWithGlobals( } if (VERBOSE.containsIn(options)) { - Log.setVerbose(); + Globals.instance().loggerVerbose(); } final var optionsProcessor = new OptionsProcessor(parsedOptionsBuilder, bundlingEnv); @@ -310,6 +310,10 @@ private static String getVersion() { 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 static Executor of(ToolProvider toolProvider, String... args) { } public Executor() { + commandOutputControl.dumpStdout(TKit.state().out()).dumpStderr(TKit.state().err()); } public Executor setExecutable(String v) { @@ -85,6 +86,10 @@ public Executor setToolProvider(JavaTool v) { 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.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.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 JPackageCommand() { 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 boolean isPackageUnpacked() { } 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 static void useToolProviderByDefault() { } public static void useExecutableByDefault() { - defaultToolProvider.set(Optional.empty()); + TKit.state().setProperty(DefaultToolProviderKey.VALUE, null); } - /** - * 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(); + public JPackageCommand useToolProvider(boolean v) { + verifyMutable(); + if (v) { + toolProviderSource.useDefaultToolProvider(); + } else { + toolProviderSource.useProcess(); + } + return this; } - public JPackageCommand useToolProvider(boolean v) { + public JPackageCommand useToolProvider(ToolProvider v) { verifyMutable(); - withToolProvider = v; + toolProviderSource.useToolProvider(v); return this; } @@ -928,9 +905,7 @@ public JPackageCommand validateOutput(CannedFormattedString... str) { } public boolean isWithToolProvider() { - return Optional.ofNullable(withToolProvider).orElseGet(() -> { - return defaultToolProvider.get().isPresent(); - }); + return toolProviderSource.toolProvider().isPresent(); } public JPackageCommand executePrerequisiteActions() { @@ -938,21 +913,19 @@ public JPackageCommand executePrerequisiteActions() { 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 void run() { 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 void run() { 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.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 static void main(String... args) throws Exception { } 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 static void main(TestBuilder.Builder builder, String... args) throws Exce 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.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.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 void withState(ThrowingRunnable action, Consumer stateBuilderMutator) { + Objects.requireNonNull(action); + Objects.requireNonNull(stateBuilderMutator); - public static State state() { - return STATE.get(); + var stateBuilder = state().buildCopy(); + stateBuilderMutator.accept(stateBuilder); + withState(action, stateBuilder.create()); } - public static void state(State v) { - STATE.set(Objects.requireNonNull(v)); + public static void withNewState(ThrowingRunnable action) { + withState(action, _ -> {}); } - private static void withNewState(ThrowingRunnable action, Consumer stateBuilderMutator) { + public static void withState(ThrowingRunnable action, State state) { Objects.requireNonNull(action); - Objects.requireNonNull(stateBuilderMutator); + Objects.requireNonNull(state); + ScopedValue.where(STATE, state).run(ThrowingRunnable.toRunnable(action)); + } - var oldState = state(); - var builder = oldState.buildCopy(); - stateBuilderMutator.accept(builder); - var newState = builder.create(); - try { - state(newState); - ThrowingRunnable.toRunnable(action).run(); - } finally { - state(oldState); - } + public static State state() { + return STATE.orElse(DEFAULT_STATE); } enum RunTestMode { @@ -178,33 +167,19 @@ static void runTests(List tests, Set modes) { 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 @@ private static String addTimestamp(String msg) { 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 @@ private static void waitForFileCreated(Path fileToWaitFor, Duration timeout) thr 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 static TextStreamVerifier assertTextStream(String what) { 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 @@ private static final class JtregSkippedExceptionClass extends ClassLoader { 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 @@ private State( 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 @@ static Builder build() { 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 @@ Builder initDefaults() { 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 @@ Builder initFrom(State state) { return this; } - Builder logFile(Optional v) { - logFile = v; + Builder currentTest(TestInstance v) { + currentTest = v; return this; } - Builder currentTest(TestInstance v) { - currentTest = v; + Builder out(PrintStream v) { + out = v; return this; } - Builder mainLogStream(PrintStream v) { - mainLogStream = v; + Builder err(PrintStream v) { + err = v; return this; } - Builder stackTraceStream(PrintStream v) { - stackTraceStream = 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 extraLogStream(PrintStream v) { - extraLogStream = v; + 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 @@ State create() { } - 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 @@ Stream getTestCasesFromErrorTest() throws Exception { 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.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 @@ Stream createJPackageTests() throws Exception { 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.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 void test() throws Exception { // 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 void test() throws Exception { 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 void test() throws Exception { 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 @@ private static JPackageCommand init(JPackageCommand cmd) { } - 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); - } - - Workload(ThrowingRunnable runnable, String id) { - this(new ByteArrayOutputStream(), new ByteArrayOutputStream(), runnable, id); + Objects.requireNonNull(testCaseId); + Objects.requireNonNull(outputSink); + Objects.requireNonNull(appJar); } - private String stdoutBufferAsString() { - return new String(stdoutBuffer.toByteArray(), StandardCharsets.UTF_8); + Workload(String testCaseId, Path appJar) { + this(testCaseId, new ByteArrayOutputStream(), appJar); } - 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 void test() throws InterruptedException, ExecutionException { 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 @@ void apply(JPackageCommand cmd, CfgFile origCfgFile) throws InterruptedException // 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 092/113] 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 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; 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 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; 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 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; 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 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; 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 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; 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 @@ using idx_t = ShenandoahSimpleBitMap::idx_t; // 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 093/113] 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 094/113] 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 095/113] 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} | 18 +- .../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 | 104 +++++---- .../tools/jpackage/share/OutputErrorTest.java | 119 +++++++++++ 27 files changed, 637 insertions(+), 287 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 @@ protected void buildPackage() throws IOException { 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 @@ protected void buildPackage() throws IOException { // 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 @@ protected void buildPackage() throws IOException { 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 @@ protected void buildPackage() throws IOException { 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 @@ private static void createDmdPackage(Options options, MacDmgSystemEnvironment sy 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 @@ private static void createPkgPackage(Options options) { 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 @@ private void buildDMG() throws IOException { 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 @@ private void buildDMG() throws IOException { } 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 @@ static PackagingPipeline.Builder build(Optional pkg) { 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.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 @@ static void createApplicationImage(Options options, Application app, PackagingPi 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 @@ static void createApplicationImage(Options options, Application app, PackagingPi .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 @@ static void createNativePackage(Options options, 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 @@ public void createBundle(BundlingOperationDescriptor op, Options cmdline) { 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 @@ private Supplier>> getBundlerSupplier(BundlingOperation }); } - 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 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.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 @@ void execute(StartupParameters startupParameters) { /** * 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 @@ enum PrimaryTaskID implements TaskID { enum PackageTaskID implements TaskID { RUN_POST_IMAGE_USER_SCRIPT, CREATE_CONFIG_FILES, + DELETE_OLD_PACKAGE_FILE, CREATE_PACKAGE_FILE } @@ -183,9 +189,11 @@ interface NoArgTaskAction extends TaskAction { 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 @@ private Builder() { 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 @@ public TaskBuilder addDependents(TaskID ... tasks) { } 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 @@ Builder linkTasks(TaskID tail, TaskID head) { } 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 @@ static Builder configurePackageTasks(Builder builder) { 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 @@ static void runPostAppImageUserScript(PackageBuildEnv e .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 @@ private static Callable createTask(TaskContext context, TaskID id, TaskCon } 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.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 @@ private RuntimeException onOutOfScopeOption(OptionSpec optionSpec) { return error("ERR_NoInstallerEntryPoint", mapFormatArguments(optionSpec)); } else { return error("ERR_InvalidTypeOption", mapFormatArguments( - optionSpec, bundlingOperation.packageTypeValue())); + optionSpec, bundlingOperation.bundleTypeValue())); } } @@ -267,30 +267,31 @@ private static StandardBundlingOperation getBundlingOperation(Options cmdline, 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 @@ private static StandardBundlingOperation getBundlingOperation(Options cmdline, 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 @@ private static StandardBundlingOperation getBundlingOperation(Options cmdline, } } }).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.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 @@ * 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 @@ boolean createBundle() { 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 OperatingSystem os() { 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 boolean isCreateBundle() { @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 @@ static Stream narrow(Stream scope) { 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.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 @@ enum LauncherProperty implements OptionScope { 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 @@ private static OptionValue createAddLauncherOption(String }).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 + * Generic bundle type. E.g.: application image, rpm, msi are all bundle types. */ -public final class AppImagePackageType implements PackageType { - - private AppImagePackageType() { - } +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 @@ /** - * 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 String suffix() { 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 @@ private void runPostMsiScript() throws IOException { 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 @@ private void wrapMsiInExe() throws IOException { 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 @@ private void prepareConfigFiles() throws IOException { 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 JPackageCommand() { prerequisiteActions = new Actions(); verifyActions = new Actions(); excludeStandardAsserts(StandardAssert.MAIN_LAUNCHER_DESCRIPTION); + removeOldOutputBundle = true; } private JPackageCommand(JPackageCommand cmd, boolean immutable) { @@ -91,6 +92,7 @@ private JPackageCommand(JPackageCommand cmd, boolean immutable) { 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 JPackageCommand ignoreDefaultVerbose(boolean v) { 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 @@ private Executor.Result execute(OptionalInt expectedExitCode) { 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 @@ private Map> createSnapshots() { 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 @@ private enum Mode { 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 void processAction(Action action, JPackageCommand cmd, int expectedJPacka } 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.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 void testInitializedOnce(StandardBundlingOperation op) throws IOException // #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 static List testInitializedOnce() { 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 @@ private static final class PackageBuilder { 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.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 void test_TEMP_ROOT_invalid(@TempDir Path workDir) throws IOException { 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 void verifyOptions() throws IOException { 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.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.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 void testHelp() { } @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]; + target.addInitializer(cmd -> { + // Disable the default logic adding `--verbose` option to jpackage command line. + cmd.ignoreDefaultVerbose(true) + .useToolProvider(true) + .saveConsoleOutput(true) + .setFakeRuntime(); + }); - // 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(); + Consumer asserter = result -> { + TKit.assertStringListEquals(List.of(), result.getOutput(), "Check output is empty"); + }; + + 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()); + } - TKit.assertTrue(nonVerboseOutput.size() < verboseOutput[0].size(), - "Check verbose output is longer than regular"); + 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")); + } - expectedVerboseOutputStrings.forEach(str -> { - TKit.assertTextStream(str).label("regular output") - .predicate(String::contains).negate() - .apply(nonVerboseOutput); + 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 096/113] 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 @@ private static ReductionOperation> reductionOperati 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 @@ private static ReductionOperation> reductionOperati 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 @@ private static ReductionOperation> reductionOpera 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 @@ private static ReductionOperation> reductionOpera 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 @@ private static ReductionOperation> reductionOperati 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 @@ private static ReductionOperation> reductionOperati 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 @@ private static ReductionOperation> reductionOpera 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 @@ private static ReductionOperation> reductionOpera 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 @@ static void SUADDAssocByte128VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static byte ANDReduce(byte[] a, int idx) { } 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 @@ static byte ANDReduceAll(byte[] a) { static void ANDReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceByte128VectorTests(IntFunction fa) { 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 @@ static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceByte128VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceByte128VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte ORReduce(byte[] a, int idx) { } 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 @@ static void ORReduceByte128VectorTests(IntFunction fa) { 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 @@ static void ORReduceByte128VectorTests(IntFunction fa) { 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 @@ static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceByte128VectorTestsMasked(IntFunction fa, IntFunction 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 @@ static void ORReduceByte128VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static byte XORReduce(byte[] a, int idx) { } 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 @@ static void XORReduceByte128VectorTests(IntFunction fa) { 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 @@ static void XORReduceByte128VectorTests(IntFunction fa) { 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 @@ static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceByte128VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void XORReduceByte128VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte ADDReduce(byte[] a, int idx) { } 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 @@ static void ADDReduceByte128VectorTests(IntFunction fa) { 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 @@ static void ADDReduceByte128VectorTests(IntFunction fa) { 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 @@ static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceByte128VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void ADDReduceByte128VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte MULReduce(byte[] a, int idx) { } 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 @@ static byte MULReduceAll(byte[] a) { static void MULReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceByte128VectorTests(IntFunction fa) { 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 @@ static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceByte128VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceByte128VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte MINReduce(byte[] a, int idx) { } 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 @@ static byte MINReduceAll(byte[] a) { static void MINReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceByte128VectorTests(IntFunction fa) { 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 @@ static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceByte128VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceByte128VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte MAXReduce(byte[] a, int idx) { } 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 @@ static byte MAXReduceAll(byte[] a) { static void MAXReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceByte128VectorTests(IntFunction fa) { 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 @@ static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceByte128VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceByte128VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte UMINReduce(byte[] a, int idx) { } 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 @@ static byte UMINReduceAll(byte[] a) { static void UMINReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceByte128VectorTests(IntFunction fa) { 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 @@ static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceByte128VectorTestsMasked(IntFunction fa, IntFuncti byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceByte128VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static byte UMAXReduce(byte[] a, int idx) { } 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 @@ static byte UMAXReduceAll(byte[] a) { static void UMAXReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceByte128VectorTests(IntFunction fa) { 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 @@ static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceByte128VectorTestsMasked(IntFunction fa, IntFuncti byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceByte128VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static byte FIRST_NONZEROReduce(byte[] a, int idx) { } 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 @@ static byte FIRST_NONZEROReduceAll(byte[] a) { static void FIRST_NONZEROReduceByte128VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceByte128VectorTests(IntFunction fa) { 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 @@ static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceByte128VectorTestsMasked(IntFunction fa, byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueByte128VectorTests(IntFunction fm) { } 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 @@ static byte SUADDReduce(byte[] a, int idx) { } 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 @@ static void SUADDReduceByte128VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceByte128VectorTests(IntFunction fa) { 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 @@ static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceByte128VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void SUADDAssocByte256VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static byte ANDReduce(byte[] a, int idx) { } 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 @@ static byte ANDReduceAll(byte[] a) { static void ANDReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceByte256VectorTests(IntFunction fa) { 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 @@ static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceByte256VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceByte256VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte ORReduce(byte[] a, int idx) { } 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 @@ static void ORReduceByte256VectorTests(IntFunction fa) { 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 @@ static void ORReduceByte256VectorTests(IntFunction fa) { 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 @@ static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceByte256VectorTestsMasked(IntFunction fa, IntFunction 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 @@ static void ORReduceByte256VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static byte XORReduce(byte[] a, int idx) { } 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 @@ static void XORReduceByte256VectorTests(IntFunction fa) { 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 @@ static void XORReduceByte256VectorTests(IntFunction fa) { 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 @@ static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceByte256VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void XORReduceByte256VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte ADDReduce(byte[] a, int idx) { } 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 @@ static void ADDReduceByte256VectorTests(IntFunction fa) { 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 @@ static void ADDReduceByte256VectorTests(IntFunction fa) { 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 @@ static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceByte256VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void ADDReduceByte256VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte MULReduce(byte[] a, int idx) { } 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 @@ static byte MULReduceAll(byte[] a) { static void MULReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceByte256VectorTests(IntFunction fa) { 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 @@ static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceByte256VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceByte256VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte MINReduce(byte[] a, int idx) { } 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 @@ static byte MINReduceAll(byte[] a) { static void MINReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceByte256VectorTests(IntFunction fa) { 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 @@ static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceByte256VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceByte256VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte MAXReduce(byte[] a, int idx) { } 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 @@ static byte MAXReduceAll(byte[] a) { static void MAXReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceByte256VectorTests(IntFunction fa) { 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 @@ static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceByte256VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceByte256VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte UMINReduce(byte[] a, int idx) { } 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 @@ static byte UMINReduceAll(byte[] a) { static void UMINReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceByte256VectorTests(IntFunction fa) { 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 @@ static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceByte256VectorTestsMasked(IntFunction fa, IntFuncti byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceByte256VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static byte UMAXReduce(byte[] a, int idx) { } 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 @@ static byte UMAXReduceAll(byte[] a) { static void UMAXReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceByte256VectorTests(IntFunction fa) { 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 @@ static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceByte256VectorTestsMasked(IntFunction fa, IntFuncti byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceByte256VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static byte FIRST_NONZEROReduce(byte[] a, int idx) { } 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 @@ static byte FIRST_NONZEROReduceAll(byte[] a) { static void FIRST_NONZEROReduceByte256VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceByte256VectorTests(IntFunction fa) { 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 @@ static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceByte256VectorTestsMasked(IntFunction fa, byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueByte256VectorTests(IntFunction fm) { } 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 @@ static byte SUADDReduce(byte[] a, int idx) { } 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 @@ static void SUADDReduceByte256VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceByte256VectorTests(IntFunction fa) { 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 @@ static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceByte256VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void SUADDAssocByte512VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static byte ANDReduce(byte[] a, int idx) { } 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 @@ static byte ANDReduceAll(byte[] a) { static void ANDReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceByte512VectorTests(IntFunction fa) { 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 @@ static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceByte512VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceByte512VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte ORReduce(byte[] a, int idx) { } 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 @@ static void ORReduceByte512VectorTests(IntFunction fa) { 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 @@ static void ORReduceByte512VectorTests(IntFunction fa) { 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 @@ static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceByte512VectorTestsMasked(IntFunction fa, IntFunction 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 @@ static void ORReduceByte512VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static byte XORReduce(byte[] a, int idx) { } 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 @@ static void XORReduceByte512VectorTests(IntFunction fa) { 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 @@ static void XORReduceByte512VectorTests(IntFunction fa) { 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 @@ static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceByte512VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void XORReduceByte512VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte ADDReduce(byte[] a, int idx) { } 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 @@ static void ADDReduceByte512VectorTests(IntFunction fa) { 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 @@ static void ADDReduceByte512VectorTests(IntFunction fa) { 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 @@ static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceByte512VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void ADDReduceByte512VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte MULReduce(byte[] a, int idx) { } 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 @@ static byte MULReduceAll(byte[] a) { static void MULReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceByte512VectorTests(IntFunction fa) { 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 @@ static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceByte512VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceByte512VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte MINReduce(byte[] a, int idx) { } 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 @@ static byte MINReduceAll(byte[] a) { static void MINReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceByte512VectorTests(IntFunction fa) { 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 @@ static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceByte512VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceByte512VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte MAXReduce(byte[] a, int idx) { } 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 @@ static byte MAXReduceAll(byte[] a) { static void MAXReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceByte512VectorTests(IntFunction fa) { 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 @@ static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceByte512VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceByte512VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte UMINReduce(byte[] a, int idx) { } 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 @@ static byte UMINReduceAll(byte[] a) { static void UMINReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceByte512VectorTests(IntFunction fa) { 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 @@ static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceByte512VectorTestsMasked(IntFunction fa, IntFuncti byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceByte512VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static byte UMAXReduce(byte[] a, int idx) { } 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 @@ static byte UMAXReduceAll(byte[] a) { static void UMAXReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceByte512VectorTests(IntFunction fa) { 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 @@ static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceByte512VectorTestsMasked(IntFunction fa, IntFuncti byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceByte512VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static byte FIRST_NONZEROReduce(byte[] a, int idx) { } 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 @@ static byte FIRST_NONZEROReduceAll(byte[] a) { static void FIRST_NONZEROReduceByte512VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceByte512VectorTests(IntFunction fa) { 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 @@ static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceByte512VectorTestsMasked(IntFunction fa, byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueByte512VectorTests(IntFunction fm) { } 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 @@ static byte SUADDReduce(byte[] a, int idx) { } 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 @@ static void SUADDReduceByte512VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceByte512VectorTests(IntFunction fa) { 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 @@ static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceByte512VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void SUADDAssocByte64VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte ANDReduce(byte[] a, int idx) { } 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 @@ static byte ANDReduceAll(byte[] a) { static void ANDReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceByte64VectorTests(IntFunction fa) { 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 @@ static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceByte64VectorTestsMasked(IntFunction fa, IntFunction byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceByte64VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static byte ORReduce(byte[] a, int idx) { } 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 @@ static void ORReduceByte64VectorTests(IntFunction fa) { 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 @@ static void ORReduceByte64VectorTests(IntFunction fa) { 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 @@ static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceByte64VectorTestsMasked(IntFunction fa, IntFunction< 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 @@ static void ORReduceByte64VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static byte XORReduce(byte[] a, int idx) { } 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 @@ static void XORReduceByte64VectorTests(IntFunction fa) { 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 @@ static void XORReduceByte64VectorTests(IntFunction fa) { 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 @@ static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceByte64VectorTestsMasked(IntFunction fa, IntFunction 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 @@ static void XORReduceByte64VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static byte ADDReduce(byte[] a, int idx) { } 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 @@ static void ADDReduceByte64VectorTests(IntFunction fa) { 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 @@ static void ADDReduceByte64VectorTests(IntFunction fa) { 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 @@ static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceByte64VectorTestsMasked(IntFunction fa, IntFunction 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 @@ static void ADDReduceByte64VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static byte MULReduce(byte[] a, int idx) { } 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 @@ static byte MULReduceAll(byte[] a) { static void MULReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceByte64VectorTests(IntFunction fa) { 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 @@ static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceByte64VectorTestsMasked(IntFunction fa, IntFunction byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceByte64VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static byte MINReduce(byte[] a, int idx) { } 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 @@ static byte MINReduceAll(byte[] a) { static void MINReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceByte64VectorTests(IntFunction fa) { 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 @@ static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceByte64VectorTestsMasked(IntFunction fa, IntFunction byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceByte64VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static byte MAXReduce(byte[] a, int idx) { } 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 @@ static byte MAXReduceAll(byte[] a) { static void MAXReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceByte64VectorTests(IntFunction fa) { 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 @@ static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceByte64VectorTestsMasked(IntFunction fa, IntFunction byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceByte64VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static byte UMINReduce(byte[] a, int idx) { } 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 @@ static byte UMINReduceAll(byte[] a) { static void UMINReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceByte64VectorTests(IntFunction fa) { 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 @@ static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceByte64VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceByte64VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte UMAXReduce(byte[] a, int idx) { } 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 @@ static byte UMAXReduceAll(byte[] a) { static void UMAXReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceByte64VectorTests(IntFunction fa) { 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 @@ static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceByte64VectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceByte64VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte FIRST_NONZEROReduce(byte[] a, int idx) { } 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 @@ static byte FIRST_NONZEROReduceAll(byte[] a) { static void FIRST_NONZEROReduceByte64VectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceByte64VectorTests(IntFunction fa) { 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 @@ static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceByte64VectorTestsMasked(IntFunction fa, I byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueByte64VectorTests(IntFunction fm) { } 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 @@ static byte SUADDReduce(byte[] a, int idx) { } 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 @@ static void SUADDReduceByte64VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceByte64VectorTests(IntFunction fa) { 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 @@ static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceByte64VectorTestsMasked(IntFunction fa, IntFuncti 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 @@ static VectorShape getMaxBit() { 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 @@ static void SUADDAssocByteMaxVectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static byte ANDReduce(byte[] a, int idx) { } 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 @@ static byte ANDReduceAll(byte[] a) { static void ANDReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static byte ANDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte ORReduce(byte[] a, int idx) { } 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 @@ static void ORReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static void ORReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static byte ORReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunction 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 @@ static void ORReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static byte XORReduce(byte[] a, int idx) { } 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 @@ static void XORReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static void XORReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static byte XORReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void XORReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte ADDReduce(byte[] a, int idx) { } 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 @@ static void ADDReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static void ADDReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static byte ADDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void ADDReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte MULReduce(byte[] a, int idx) { } 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 @@ static byte MULReduceAll(byte[] a) { static void MULReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static byte MULReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte MINReduce(byte[] a, int idx) { } 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 @@ static byte MINReduceAll(byte[] a) { static void MINReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static byte MINReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte MAXReduce(byte[] a, int idx) { } 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 @@ static byte MAXReduceAll(byte[] a) { static void MAXReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static byte MAXReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunctio byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static byte UMINReduce(byte[] a, int idx) { } 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 @@ static byte UMINReduceAll(byte[] a) { static void UMINReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static byte UMINReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceByteMaxVectorTestsMasked(IntFunction fa, IntFuncti byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceByteMaxVectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static byte UMAXReduce(byte[] a, int idx) { } 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 @@ static byte UMAXReduceAll(byte[] a) { static void UMAXReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static byte UMAXReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceByteMaxVectorTestsMasked(IntFunction fa, IntFuncti byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = Byte.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Byte.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceByteMaxVectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static byte FIRST_NONZEROReduce(byte[] a, int idx) { } 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 @@ static byte FIRST_NONZEROReduceAll(byte[] a) { static void FIRST_NONZEROReduceByteMaxVectorTests(IntFunction fa) { byte[] a = fa.apply(SPECIES.length()); byte[] r = fr.apply(SPECIES.length()); - byte ra = (byte) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ByteVector av = ByteVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static byte FIRST_NONZEROReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceByteMaxVectorTestsMasked(IntFunction fa, byte[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - byte ra = (byte) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + byte ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (byte) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueByteMaxVectorTests(IntFunction fm) { } 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 @@ static byte SUADDReduce(byte[] a, int idx) { } 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 @@ static void SUADDReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static void SUADDReduceByteMaxVectorTests(IntFunction fa) { 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 @@ static byte SUADDReduceMasked(byte[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceByteMaxVectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void maxDouble128VectorTestsBroadcastSmokeTest(IntFunction fa, } 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 @@ static double ADDReduce(double[] a, int idx) { } 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 @@ static void ADDReduceDouble128VectorTests(IntFunction fa) { 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 @@ static void ADDReduceDouble128VectorTests(IntFunction fa) { 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 @@ static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceDouble128VectorTestsMasked(IntFunction fa, IntFun 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 @@ static void ADDReduceDouble128VectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double MULReduce(double[] a, int idx) { } 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 @@ static double MULReduceAll(double[] a) { static void MULReduceDouble128VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceDouble128VectorTests(IntFunction fa) { 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 @@ static double MULReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceDouble128VectorTestsMasked(IntFunction fa, IntFun double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceDouble128VectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double MINReduce(double[] a, int idx) { } 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 @@ static double MINReduceAll(double[] a) { static void MINReduceDouble128VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceDouble128VectorTests(IntFunction fa) { 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 @@ static double MINReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceDouble128VectorTestsMasked(IntFunction fa, IntFun double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceDouble128VectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double MAXReduce(double[] a, int idx) { } 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 @@ static double MAXReduceAll(double[] a) { static void MAXReduceDouble128VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceDouble128VectorTests(IntFunction fa) { 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 @@ static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceDouble128VectorTestsMasked(IntFunction fa, IntFun double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceDouble128VectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double FIRST_NONZEROReduce(double[] a, int idx) { } 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 @@ static double FIRST_NONZEROReduceAll(double[] a) { static void FIRST_NONZEROReduceDouble128VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceDouble128VectorTests(IntFunction fa) { 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 @@ static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceDouble128VectorTestsMasked(IntFunction double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void maxDouble256VectorTestsBroadcastSmokeTest(IntFunction fa, } 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 @@ static double ADDReduce(double[] a, int idx) { } 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 @@ static void ADDReduceDouble256VectorTests(IntFunction fa) { 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 @@ static void ADDReduceDouble256VectorTests(IntFunction fa) { 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 @@ static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceDouble256VectorTestsMasked(IntFunction fa, IntFun 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 @@ static void ADDReduceDouble256VectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double MULReduce(double[] a, int idx) { } 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 @@ static double MULReduceAll(double[] a) { static void MULReduceDouble256VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceDouble256VectorTests(IntFunction fa) { 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 @@ static double MULReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceDouble256VectorTestsMasked(IntFunction fa, IntFun double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceDouble256VectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double MINReduce(double[] a, int idx) { } 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 @@ static double MINReduceAll(double[] a) { static void MINReduceDouble256VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceDouble256VectorTests(IntFunction fa) { 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 @@ static double MINReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceDouble256VectorTestsMasked(IntFunction fa, IntFun double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceDouble256VectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double MAXReduce(double[] a, int idx) { } 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 @@ static double MAXReduceAll(double[] a) { static void MAXReduceDouble256VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceDouble256VectorTests(IntFunction fa) { 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 @@ static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceDouble256VectorTestsMasked(IntFunction fa, IntFun double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceDouble256VectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double FIRST_NONZEROReduce(double[] a, int idx) { } 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 @@ static double FIRST_NONZEROReduceAll(double[] a) { static void FIRST_NONZEROReduceDouble256VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceDouble256VectorTests(IntFunction fa) { 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 @@ static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceDouble256VectorTestsMasked(IntFunction double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void maxDouble512VectorTestsBroadcastSmokeTest(IntFunction fa, } 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 @@ static double ADDReduce(double[] a, int idx) { } 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 @@ static void ADDReduceDouble512VectorTests(IntFunction fa) { 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 @@ static void ADDReduceDouble512VectorTests(IntFunction fa) { 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 @@ static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceDouble512VectorTestsMasked(IntFunction fa, IntFun 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 @@ static void ADDReduceDouble512VectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double MULReduce(double[] a, int idx) { } 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 @@ static double MULReduceAll(double[] a) { static void MULReduceDouble512VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceDouble512VectorTests(IntFunction fa) { 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 @@ static double MULReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceDouble512VectorTestsMasked(IntFunction fa, IntFun double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceDouble512VectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double MINReduce(double[] a, int idx) { } 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 @@ static double MINReduceAll(double[] a) { static void MINReduceDouble512VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceDouble512VectorTests(IntFunction fa) { 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 @@ static double MINReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceDouble512VectorTestsMasked(IntFunction fa, IntFun double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceDouble512VectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double MAXReduce(double[] a, int idx) { } 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 @@ static double MAXReduceAll(double[] a) { static void MAXReduceDouble512VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceDouble512VectorTests(IntFunction fa) { 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 @@ static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceDouble512VectorTestsMasked(IntFunction fa, IntFun double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceDouble512VectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double FIRST_NONZEROReduce(double[] a, int idx) { } 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 @@ static double FIRST_NONZEROReduceAll(double[] a) { static void FIRST_NONZEROReduceDouble512VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceDouble512VectorTests(IntFunction fa) { 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 @@ static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceDouble512VectorTestsMasked(IntFunction double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void maxDouble64VectorTestsBroadcastSmokeTest(IntFunction fa, I } 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 @@ static double ADDReduce(double[] a, int idx) { } 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 @@ static void ADDReduceDouble64VectorTests(IntFunction fa) { 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 @@ static void ADDReduceDouble64VectorTests(IntFunction fa) { 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 @@ static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc 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 @@ static void ADDReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static double MULReduce(double[] a, int idx) { } 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 @@ static double MULReduceAll(double[] a) { static void MULReduceDouble64VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceDouble64VectorTests(IntFunction fa) { 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 @@ static double MULReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static double MINReduce(double[] a, int idx) { } 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 @@ static double MINReduceAll(double[] a) { static void MINReduceDouble64VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceDouble64VectorTests(IntFunction fa) { 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 @@ static double MINReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static double MAXReduce(double[] a, int idx) { } 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 @@ static double MAXReduceAll(double[] a) { static void MAXReduceDouble64VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceDouble64VectorTests(IntFunction fa) { 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 @@ static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceDouble64VectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static double FIRST_NONZEROReduce(double[] a, int idx) { } 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 @@ static double FIRST_NONZEROReduceAll(double[] a) { static void FIRST_NONZEROReduceDouble64VectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceDouble64VectorTests(IntFunction fa) { 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 @@ static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceDouble64VectorTestsMasked(IntFunction f double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static VectorShape getMaxBit() { 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 @@ static void maxDoubleMaxVectorTestsBroadcastSmokeTest(IntFunction fa, } 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 @@ static double ADDReduce(double[] a, int idx) { } 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 @@ static void ADDReduceDoubleMaxVectorTests(IntFunction fa) { 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 @@ static void ADDReduceDoubleMaxVectorTests(IntFunction fa) { 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 @@ static double ADDReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun 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 @@ static void ADDReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double MULReduce(double[] a, int idx) { } 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 @@ static double MULReduceAll(double[] a) { static void MULReduceDoubleMaxVectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceDoubleMaxVectorTests(IntFunction fa) { 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 @@ static double MULReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double MINReduce(double[] a, int idx) { } 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 @@ static double MINReduceAll(double[] a) { static void MINReduceDoubleMaxVectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceDoubleMaxVectorTests(IntFunction fa) { 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 @@ static double MINReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double MAXReduce(double[] a, int idx) { } 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 @@ static double MAXReduceAll(double[] a) { static void MAXReduceDoubleMaxVectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = Double.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceDoubleMaxVectorTests(IntFunction fa) { 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 @@ static double MAXReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = Double.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Double.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceDoubleMaxVectorTestsMasked(IntFunction fa, IntFun } 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 @@ static double FIRST_NONZEROReduce(double[] a, int idx) { } 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 @@ static double FIRST_NONZEROReduceAll(double[] a) { static void FIRST_NONZEROReduceDoubleMaxVectorTests(IntFunction fa) { double[] a = fa.apply(SPECIES.length()); double[] r = fr.apply(SPECIES.length()); - double ra = (double) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - DoubleVector av = DoubleVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceDoubleMaxVectorTests(IntFunction fa) { 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 @@ static double FIRST_NONZEROReduceMasked(double[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceDoubleMaxVectorTestsMasked(IntFunction double[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - double ra = (double) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + double ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (double) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void maxFloat128VectorTestsBroadcastSmokeTest(IntFunction fa, In } 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 @@ static float ADDReduce(float[] a, int idx) { } 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 @@ static void ADDReduceFloat128VectorTests(IntFunction fa) { 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 @@ static void ADDReduceFloat128VectorTests(IntFunction fa) { 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 @@ static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void ADDReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float MULReduce(float[] a, int idx) { } 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 @@ static float MULReduceAll(float[] a) { static void MULReduceFloat128VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceFloat128VectorTests(IntFunction fa) { 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 @@ static float MULReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float MINReduce(float[] a, int idx) { } 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 @@ static float MINReduceAll(float[] a) { static void MINReduceFloat128VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceFloat128VectorTests(IntFunction fa) { 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 @@ static float MINReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float MAXReduce(float[] a, int idx) { } 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 @@ static float MAXReduceAll(float[] a) { static void MAXReduceFloat128VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceFloat128VectorTests(IntFunction fa) { 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 @@ static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceFloat128VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float FIRST_NONZEROReduce(float[] a, int idx) { } 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 @@ static float FIRST_NONZEROReduceAll(float[] a) { static void FIRST_NONZEROReduceFloat128VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceFloat128VectorTests(IntFunction fa) { 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 @@ static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceFloat128VectorTestsMasked(IntFunction fa float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void maxFloat256VectorTestsBroadcastSmokeTest(IntFunction fa, In } 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 @@ static float ADDReduce(float[] a, int idx) { } 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 @@ static void ADDReduceFloat256VectorTests(IntFunction fa) { 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 @@ static void ADDReduceFloat256VectorTests(IntFunction fa) { 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 @@ static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void ADDReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float MULReduce(float[] a, int idx) { } 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 @@ static float MULReduceAll(float[] a) { static void MULReduceFloat256VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceFloat256VectorTests(IntFunction fa) { 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 @@ static float MULReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float MINReduce(float[] a, int idx) { } 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 @@ static float MINReduceAll(float[] a) { static void MINReduceFloat256VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceFloat256VectorTests(IntFunction fa) { 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 @@ static float MINReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float MAXReduce(float[] a, int idx) { } 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 @@ static float MAXReduceAll(float[] a) { static void MAXReduceFloat256VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceFloat256VectorTests(IntFunction fa) { 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 @@ static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceFloat256VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float FIRST_NONZEROReduce(float[] a, int idx) { } 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 @@ static float FIRST_NONZEROReduceAll(float[] a) { static void FIRST_NONZEROReduceFloat256VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceFloat256VectorTests(IntFunction fa) { 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 @@ static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceFloat256VectorTestsMasked(IntFunction fa float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void maxFloat512VectorTestsBroadcastSmokeTest(IntFunction fa, In } 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 @@ static float ADDReduce(float[] a, int idx) { } 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 @@ static void ADDReduceFloat512VectorTests(IntFunction fa) { 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 @@ static void ADDReduceFloat512VectorTests(IntFunction fa) { 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 @@ static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void ADDReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float MULReduce(float[] a, int idx) { } 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 @@ static float MULReduceAll(float[] a) { static void MULReduceFloat512VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceFloat512VectorTests(IntFunction fa) { 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 @@ static float MULReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float MINReduce(float[] a, int idx) { } 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 @@ static float MINReduceAll(float[] a) { static void MINReduceFloat512VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceFloat512VectorTests(IntFunction fa) { 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 @@ static float MINReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float MAXReduce(float[] a, int idx) { } 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 @@ static float MAXReduceAll(float[] a) { static void MAXReduceFloat512VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceFloat512VectorTests(IntFunction fa) { 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 @@ static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceFloat512VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float FIRST_NONZEROReduce(float[] a, int idx) { } 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 @@ static float FIRST_NONZEROReduceAll(float[] a) { static void FIRST_NONZEROReduceFloat512VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceFloat512VectorTests(IntFunction fa) { 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 @@ static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceFloat512VectorTestsMasked(IntFunction fa float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void maxFloat64VectorTestsBroadcastSmokeTest(IntFunction fa, Int } 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 @@ static float ADDReduce(float[] a, int idx) { } 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 @@ static void ADDReduceFloat64VectorTests(IntFunction fa) { 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 @@ static void ADDReduceFloat64VectorTests(IntFunction fa) { 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 @@ static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti 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 @@ static void ADDReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static float MULReduce(float[] a, int idx) { } 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 @@ static float MULReduceAll(float[] a) { static void MULReduceFloat64VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceFloat64VectorTests(IntFunction fa) { 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 @@ static float MULReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static float MINReduce(float[] a, int idx) { } 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 @@ static float MINReduceAll(float[] a) { static void MINReduceFloat64VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceFloat64VectorTests(IntFunction fa) { 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 @@ static float MINReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static float MAXReduce(float[] a, int idx) { } 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 @@ static float MAXReduceAll(float[] a) { static void MAXReduceFloat64VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceFloat64VectorTests(IntFunction fa) { 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 @@ static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceFloat64VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static float FIRST_NONZEROReduce(float[] a, int idx) { } 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 @@ static float FIRST_NONZEROReduceAll(float[] a) { static void FIRST_NONZEROReduceFloat64VectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceFloat64VectorTests(IntFunction fa) { 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 @@ static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceFloat64VectorTestsMasked(IntFunction fa, float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static VectorShape getMaxBit() { 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 @@ static void maxFloatMaxVectorTestsBroadcastSmokeTest(IntFunction fa, In } 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 @@ static float ADDReduce(float[] a, int idx) { } 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 @@ static void ADDReduceFloatMaxVectorTests(IntFunction fa) { 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 @@ static void ADDReduceFloatMaxVectorTests(IntFunction fa) { 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 @@ static float ADDReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void ADDReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float MULReduce(float[] a, int idx) { } 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 @@ static float MULReduceAll(float[] a) { static void MULReduceFloatMaxVectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceFloatMaxVectorTests(IntFunction fa) { 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 @@ static float MULReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float MINReduce(float[] a, int idx) { } 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 @@ static float MINReduceAll(float[] a) { static void MINReduceFloatMaxVectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceFloatMaxVectorTests(IntFunction fa) { 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 @@ static float MINReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.POSITIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.POSITIVE_INFINITY; + ra = MIN_IDENTITY; 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 @@ static void MINReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float MAXReduce(float[] a, int idx) { } 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 @@ static float MAXReduceAll(float[] a) { static void MAXReduceFloatMaxVectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = Float.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceFloatMaxVectorTests(IntFunction fa) { 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 @@ static float MAXReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = Float.NEGATIVE_INFINITY; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Float.NEGATIVE_INFINITY; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceFloatMaxVectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static float FIRST_NONZEROReduce(float[] a, int idx) { } 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 @@ static float FIRST_NONZEROReduceAll(float[] a) { static void FIRST_NONZEROReduceFloatMaxVectorTests(IntFunction fa) { float[] a = fa.apply(SPECIES.length()); float[] r = fr.apply(SPECIES.length()); - float ra = (float) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - FloatVector av = FloatVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceFloatMaxVectorTests(IntFunction fa) { 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 @@ static float FIRST_NONZEROReduceMasked(float[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceFloatMaxVectorTestsMasked(IntFunction fa float[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - float ra = (float) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + float ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (float) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void SUADDAssocInt128VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static int ANDReduce(int[] a, int idx) { } 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 @@ static int ANDReduceAll(int[] a) { static void ANDReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceInt128VectorTests(IntFunction fa) { 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 @@ static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceInt128VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceInt128VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int ORReduce(int[] a, int idx) { } 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 @@ static void ORReduceInt128VectorTests(IntFunction fa) { 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 @@ static void ORReduceInt128VectorTests(IntFunction fa) { 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 @@ static int ORReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceInt128VectorTestsMasked(IntFunction fa, IntFunction fa, IntFunction fa) { 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 @@ static void XORReduceInt128VectorTests(IntFunction fa) { 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 @@ static int XORReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceInt128VectorTestsMasked(IntFunction fa, IntFunction< 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 @@ static void XORReduceInt128VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int ADDReduce(int[] a, int idx) { } 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 @@ static void ADDReduceInt128VectorTests(IntFunction fa) { 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 @@ static void ADDReduceInt128VectorTests(IntFunction fa) { 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 @@ static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceInt128VectorTestsMasked(IntFunction fa, IntFunction< 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 @@ static void ADDReduceInt128VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int MULReduce(int[] a, int idx) { } 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 @@ static int MULReduceAll(int[] a) { static void MULReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceInt128VectorTests(IntFunction fa) { 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 @@ static int MULReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceInt128VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceInt128VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int MINReduce(int[] a, int idx) { } 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 @@ static int MINReduceAll(int[] a) { static void MINReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceInt128VectorTests(IntFunction fa) { 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 @@ static int MINReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceInt128VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceInt128VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int MAXReduce(int[] a, int idx) { } 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 @@ static int MAXReduceAll(int[] a) { static void MAXReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceInt128VectorTests(IntFunction fa) { 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 @@ static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceInt128VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceInt128VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int UMINReduce(int[] a, int idx) { } 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 @@ static int UMINReduceAll(int[] a) { static void UMINReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceInt128VectorTests(IntFunction fa) { 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 @@ static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceInt128VectorTestsMasked(IntFunction fa, IntFunction int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceInt128VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static int UMAXReduce(int[] a, int idx) { } 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 @@ static int UMAXReduceAll(int[] a) { static void UMAXReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceInt128VectorTests(IntFunction fa) { 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 @@ static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceInt128VectorTestsMasked(IntFunction fa, IntFunction int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceInt128VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static int FIRST_NONZEROReduce(int[] a, int idx) { } 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 @@ static int FIRST_NONZEROReduceAll(int[] a) { static void FIRST_NONZEROReduceInt128VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceInt128VectorTests(IntFunction fa) { 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 @@ static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceInt128VectorTestsMasked(IntFunction fa, In int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueInt128VectorTests(IntFunction fm) { } 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 @@ static int SUADDReduce(int[] a, int idx) { } 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 @@ static void SUADDReduceInt128VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceInt128VectorTests(IntFunction fa) { 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 @@ static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceInt128VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void SUADDAssocInt256VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static int ANDReduce(int[] a, int idx) { } 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 @@ static int ANDReduceAll(int[] a) { static void ANDReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceInt256VectorTests(IntFunction fa) { 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 @@ static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceInt256VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceInt256VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int ORReduce(int[] a, int idx) { } 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 @@ static void ORReduceInt256VectorTests(IntFunction fa) { 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 @@ static void ORReduceInt256VectorTests(IntFunction fa) { 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 @@ static int ORReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceInt256VectorTestsMasked(IntFunction fa, IntFunction fa, IntFunction fa) { 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 @@ static void XORReduceInt256VectorTests(IntFunction fa) { 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 @@ static int XORReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceInt256VectorTestsMasked(IntFunction fa, IntFunction< 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 @@ static void XORReduceInt256VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int ADDReduce(int[] a, int idx) { } 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 @@ static void ADDReduceInt256VectorTests(IntFunction fa) { 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 @@ static void ADDReduceInt256VectorTests(IntFunction fa) { 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 @@ static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceInt256VectorTestsMasked(IntFunction fa, IntFunction< 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 @@ static void ADDReduceInt256VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int MULReduce(int[] a, int idx) { } 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 @@ static int MULReduceAll(int[] a) { static void MULReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceInt256VectorTests(IntFunction fa) { 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 @@ static int MULReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceInt256VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceInt256VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int MINReduce(int[] a, int idx) { } 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 @@ static int MINReduceAll(int[] a) { static void MINReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceInt256VectorTests(IntFunction fa) { 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 @@ static int MINReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceInt256VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceInt256VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int MAXReduce(int[] a, int idx) { } 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 @@ static int MAXReduceAll(int[] a) { static void MAXReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceInt256VectorTests(IntFunction fa) { 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 @@ static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceInt256VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceInt256VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int UMINReduce(int[] a, int idx) { } 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 @@ static int UMINReduceAll(int[] a) { static void UMINReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceInt256VectorTests(IntFunction fa) { 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 @@ static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceInt256VectorTestsMasked(IntFunction fa, IntFunction int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceInt256VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static int UMAXReduce(int[] a, int idx) { } 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 @@ static int UMAXReduceAll(int[] a) { static void UMAXReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceInt256VectorTests(IntFunction fa) { 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 @@ static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceInt256VectorTestsMasked(IntFunction fa, IntFunction int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceInt256VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static int FIRST_NONZEROReduce(int[] a, int idx) { } 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 @@ static int FIRST_NONZEROReduceAll(int[] a) { static void FIRST_NONZEROReduceInt256VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceInt256VectorTests(IntFunction fa) { 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 @@ static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceInt256VectorTestsMasked(IntFunction fa, In int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueInt256VectorTests(IntFunction fm) { } 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 @@ static int SUADDReduce(int[] a, int idx) { } 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 @@ static void SUADDReduceInt256VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceInt256VectorTests(IntFunction fa) { 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 @@ static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceInt256VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void SUADDAssocInt512VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static int ANDReduce(int[] a, int idx) { } 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 @@ static int ANDReduceAll(int[] a) { static void ANDReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceInt512VectorTests(IntFunction fa) { 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 @@ static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceInt512VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceInt512VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int ORReduce(int[] a, int idx) { } 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 @@ static void ORReduceInt512VectorTests(IntFunction fa) { 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 @@ static void ORReduceInt512VectorTests(IntFunction fa) { 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 @@ static int ORReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceInt512VectorTestsMasked(IntFunction fa, IntFunction fa, IntFunction fa) { 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 @@ static void XORReduceInt512VectorTests(IntFunction fa) { 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 @@ static int XORReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceInt512VectorTestsMasked(IntFunction fa, IntFunction< 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 @@ static void XORReduceInt512VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int ADDReduce(int[] a, int idx) { } 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 @@ static void ADDReduceInt512VectorTests(IntFunction fa) { 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 @@ static void ADDReduceInt512VectorTests(IntFunction fa) { 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 @@ static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceInt512VectorTestsMasked(IntFunction fa, IntFunction< 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 @@ static void ADDReduceInt512VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int MULReduce(int[] a, int idx) { } 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 @@ static int MULReduceAll(int[] a) { static void MULReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceInt512VectorTests(IntFunction fa) { 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 @@ static int MULReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceInt512VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceInt512VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int MINReduce(int[] a, int idx) { } 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 @@ static int MINReduceAll(int[] a) { static void MINReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceInt512VectorTests(IntFunction fa) { 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 @@ static int MINReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceInt512VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceInt512VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int MAXReduce(int[] a, int idx) { } 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 @@ static int MAXReduceAll(int[] a) { static void MAXReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceInt512VectorTests(IntFunction fa) { 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 @@ static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceInt512VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceInt512VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int UMINReduce(int[] a, int idx) { } 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 @@ static int UMINReduceAll(int[] a) { static void UMINReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceInt512VectorTests(IntFunction fa) { 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 @@ static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceInt512VectorTestsMasked(IntFunction fa, IntFunction int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceInt512VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static int UMAXReduce(int[] a, int idx) { } 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 @@ static int UMAXReduceAll(int[] a) { static void UMAXReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceInt512VectorTests(IntFunction fa) { 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 @@ static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceInt512VectorTestsMasked(IntFunction fa, IntFunction int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceInt512VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static int FIRST_NONZEROReduce(int[] a, int idx) { } 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 @@ static int FIRST_NONZEROReduceAll(int[] a) { static void FIRST_NONZEROReduceInt512VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceInt512VectorTests(IntFunction fa) { 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 @@ static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceInt512VectorTestsMasked(IntFunction fa, In int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueInt512VectorTests(IntFunction fm) { } 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 @@ static int SUADDReduce(int[] a, int idx) { } 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 @@ static void SUADDReduceInt512VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceInt512VectorTests(IntFunction fa) { 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 @@ static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceInt512VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void SUADDAssocInt64VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int ANDReduce(int[] a, int idx) { } 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 @@ static int ANDReduceAll(int[] a) { static void ANDReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceInt64VectorTests(IntFunction fa) { 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 @@ static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceInt64VectorTestsMasked(IntFunction fa, IntFunction vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceInt64VectorTestsMasked(IntFunction fa, IntFunction fa) { 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 @@ static void ORReduceInt64VectorTests(IntFunction fa) { 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 @@ static int ORReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceInt64VectorTestsMasked(IntFunction fa, IntFunction fa, IntFunction fa) { 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 @@ static void XORReduceInt64VectorTests(IntFunction fa) { 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 @@ static int XORReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceInt64VectorTestsMasked(IntFunction fa, IntFunction fa, IntFunction fa) { 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 @@ static void ADDReduceInt64VectorTests(IntFunction fa) { 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 @@ static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceInt64VectorTestsMasked(IntFunction fa, IntFunction fa, IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceInt64VectorTests(IntFunction fa) { 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 @@ static int MULReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceInt64VectorTestsMasked(IntFunction fa, IntFunction vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceInt64VectorTestsMasked(IntFunction fa, IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceInt64VectorTests(IntFunction fa) { 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 @@ static int MINReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceInt64VectorTestsMasked(IntFunction fa, IntFunction vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceInt64VectorTestsMasked(IntFunction fa, IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceInt64VectorTests(IntFunction fa) { 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 @@ static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceInt64VectorTestsMasked(IntFunction fa, IntFunction vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceInt64VectorTestsMasked(IntFunction fa, IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceInt64VectorTests(IntFunction fa) { 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 @@ static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceInt64VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceInt64VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int UMAXReduce(int[] a, int idx) { } 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 @@ static int UMAXReduceAll(int[] a) { static void UMAXReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceInt64VectorTests(IntFunction fa) { 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 @@ static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceInt64VectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceInt64VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int FIRST_NONZEROReduce(int[] a, int idx) { } 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 @@ static int FIRST_NONZEROReduceAll(int[] a) { static void FIRST_NONZEROReduceInt64VectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceInt64VectorTests(IntFunction fa) { 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 @@ static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceInt64VectorTestsMasked(IntFunction fa, Int int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueInt64VectorTests(IntFunction fm) { } 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 @@ static int SUADDReduce(int[] a, int idx) { } 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 @@ static void SUADDReduceInt64VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceInt64VectorTests(IntFunction fa) { 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 @@ static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceInt64VectorTestsMasked(IntFunction fa, IntFunction 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 @@ static VectorShape getMaxBit() { 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 @@ static void SUADDAssocIntMaxVectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static int ANDReduce(int[] a, int idx) { } 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 @@ static int ANDReduceAll(int[] a) { static void ANDReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static int ANDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int ORReduce(int[] a, int idx) { } 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 @@ static void ORReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static void ORReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static int ORReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction fa, IntFunction fa) { 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 @@ static void XORReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static int XORReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction< 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 @@ static void XORReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int ADDReduce(int[] a, int idx) { } 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 @@ static void ADDReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static void ADDReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static int ADDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction< 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 @@ static void ADDReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int MULReduce(int[] a, int idx) { } 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 @@ static int MULReduceAll(int[] a) { static void MULReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static int MULReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int MINReduce(int[] a, int idx) { } 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 @@ static int MINReduceAll(int[] a) { static void MINReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static int MINReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int MAXReduce(int[] a, int idx) { } 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 @@ static int MAXReduceAll(int[] a) { static void MAXReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static int MAXReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction< int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static int UMINReduce(int[] a, int idx) { } 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 @@ static int UMINReduceAll(int[] a) { static void UMINReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static int UMINReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static int UMAXReduce(int[] a, int idx) { } 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 @@ static int UMAXReduceAll(int[] a) { static void UMAXReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static int UMAXReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = Integer.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Integer.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static int FIRST_NONZEROReduce(int[] a, int idx) { } 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 @@ static int FIRST_NONZEROReduceAll(int[] a) { static void FIRST_NONZEROReduceIntMaxVectorTests(IntFunction fa) { int[] a = fa.apply(SPECIES.length()); int[] r = fr.apply(SPECIES.length()); - int ra = (int) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - IntVector av = IntVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static int FIRST_NONZEROReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceIntMaxVectorTestsMasked(IntFunction fa, In int[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - int ra = (int) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + int ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (int) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueIntMaxVectorTests(IntFunction fm) { } 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 @@ static int SUADDReduce(int[] a, int idx) { } 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 @@ static void SUADDReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static void SUADDReduceIntMaxVectorTests(IntFunction fa) { 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 @@ static int SUADDReduceMasked(int[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceIntMaxVectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void SUADDAssocLong128VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static long ANDReduce(long[] a, int idx) { } 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 @@ static long ANDReduceAll(long[] a) { static void ANDReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceLong128VectorTests(IntFunction fa) { 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 @@ static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceLong128VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceLong128VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long ORReduce(long[] a, int idx) { } 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 @@ static void ORReduceLong128VectorTests(IntFunction fa) { 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 @@ static void ORReduceLong128VectorTests(IntFunction fa) { 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 @@ static long ORReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceLong128VectorTestsMasked(IntFunction fa, IntFunction 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 @@ static void ORReduceLong128VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static long XORReduce(long[] a, int idx) { } 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 @@ static void XORReduceLong128VectorTests(IntFunction fa) { 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 @@ static void XORReduceLong128VectorTests(IntFunction fa) { 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 @@ static long XORReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceLong128VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void XORReduceLong128VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long ADDReduce(long[] a, int idx) { } 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 @@ static void ADDReduceLong128VectorTests(IntFunction fa) { 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 @@ static void ADDReduceLong128VectorTests(IntFunction fa) { 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 @@ static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceLong128VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void ADDReduceLong128VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long MULReduce(long[] a, int idx) { } 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 @@ static long MULReduceAll(long[] a) { static void MULReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceLong128VectorTests(IntFunction fa) { 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 @@ static long MULReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceLong128VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceLong128VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long MINReduce(long[] a, int idx) { } 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 @@ static long MINReduceAll(long[] a) { static void MINReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceLong128VectorTests(IntFunction fa) { 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 @@ static long MINReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceLong128VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceLong128VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long MAXReduce(long[] a, int idx) { } 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 @@ static long MAXReduceAll(long[] a) { static void MAXReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceLong128VectorTests(IntFunction fa) { 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 @@ static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceLong128VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceLong128VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long UMINReduce(long[] a, int idx) { } 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 @@ static long UMINReduceAll(long[] a) { static void UMINReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceLong128VectorTests(IntFunction fa) { 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 @@ static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceLong128VectorTestsMasked(IntFunction fa, IntFuncti long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceLong128VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static long UMAXReduce(long[] a, int idx) { } 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 @@ static long UMAXReduceAll(long[] a) { static void UMAXReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceLong128VectorTests(IntFunction fa) { 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 @@ static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceLong128VectorTestsMasked(IntFunction fa, IntFuncti long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceLong128VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static long FIRST_NONZEROReduce(long[] a, int idx) { } 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 @@ static long FIRST_NONZEROReduceAll(long[] a) { static void FIRST_NONZEROReduceLong128VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceLong128VectorTests(IntFunction fa) { 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 @@ static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceLong128VectorTestsMasked(IntFunction fa, long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueLong128VectorTests(IntFunction fm) { } 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 @@ static long SUADDReduce(long[] a, int idx) { } 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 @@ static void SUADDReduceLong128VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceLong128VectorTests(IntFunction fa) { 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 @@ static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceLong128VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void SUADDAssocLong256VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static long ANDReduce(long[] a, int idx) { } 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 @@ static long ANDReduceAll(long[] a) { static void ANDReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceLong256VectorTests(IntFunction fa) { 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 @@ static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceLong256VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceLong256VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long ORReduce(long[] a, int idx) { } 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 @@ static void ORReduceLong256VectorTests(IntFunction fa) { 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 @@ static void ORReduceLong256VectorTests(IntFunction fa) { 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 @@ static long ORReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceLong256VectorTestsMasked(IntFunction fa, IntFunction 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 @@ static void ORReduceLong256VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static long XORReduce(long[] a, int idx) { } 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 @@ static void XORReduceLong256VectorTests(IntFunction fa) { 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 @@ static void XORReduceLong256VectorTests(IntFunction fa) { 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 @@ static long XORReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceLong256VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void XORReduceLong256VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long ADDReduce(long[] a, int idx) { } 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 @@ static void ADDReduceLong256VectorTests(IntFunction fa) { 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 @@ static void ADDReduceLong256VectorTests(IntFunction fa) { 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 @@ static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceLong256VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void ADDReduceLong256VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long MULReduce(long[] a, int idx) { } 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 @@ static long MULReduceAll(long[] a) { static void MULReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceLong256VectorTests(IntFunction fa) { 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 @@ static long MULReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceLong256VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceLong256VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long MINReduce(long[] a, int idx) { } 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 @@ static long MINReduceAll(long[] a) { static void MINReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceLong256VectorTests(IntFunction fa) { 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 @@ static long MINReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceLong256VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceLong256VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long MAXReduce(long[] a, int idx) { } 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 @@ static long MAXReduceAll(long[] a) { static void MAXReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceLong256VectorTests(IntFunction fa) { 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 @@ static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceLong256VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceLong256VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long UMINReduce(long[] a, int idx) { } 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 @@ static long UMINReduceAll(long[] a) { static void UMINReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceLong256VectorTests(IntFunction fa) { 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 @@ static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceLong256VectorTestsMasked(IntFunction fa, IntFuncti long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceLong256VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static long UMAXReduce(long[] a, int idx) { } 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 @@ static long UMAXReduceAll(long[] a) { static void UMAXReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceLong256VectorTests(IntFunction fa) { 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 @@ static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceLong256VectorTestsMasked(IntFunction fa, IntFuncti long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceLong256VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static long FIRST_NONZEROReduce(long[] a, int idx) { } 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 @@ static long FIRST_NONZEROReduceAll(long[] a) { static void FIRST_NONZEROReduceLong256VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceLong256VectorTests(IntFunction fa) { 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 @@ static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceLong256VectorTestsMasked(IntFunction fa, long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueLong256VectorTests(IntFunction fm) { } 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 @@ static long SUADDReduce(long[] a, int idx) { } 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 @@ static void SUADDReduceLong256VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceLong256VectorTests(IntFunction fa) { 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 @@ static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceLong256VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void SUADDAssocLong512VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static long ANDReduce(long[] a, int idx) { } 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 @@ static long ANDReduceAll(long[] a) { static void ANDReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceLong512VectorTests(IntFunction fa) { 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 @@ static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceLong512VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceLong512VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long ORReduce(long[] a, int idx) { } 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 @@ static void ORReduceLong512VectorTests(IntFunction fa) { 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 @@ static void ORReduceLong512VectorTests(IntFunction fa) { 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 @@ static long ORReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceLong512VectorTestsMasked(IntFunction fa, IntFunction 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 @@ static void ORReduceLong512VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static long XORReduce(long[] a, int idx) { } 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 @@ static void XORReduceLong512VectorTests(IntFunction fa) { 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 @@ static void XORReduceLong512VectorTests(IntFunction fa) { 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 @@ static long XORReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceLong512VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void XORReduceLong512VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long ADDReduce(long[] a, int idx) { } 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 @@ static void ADDReduceLong512VectorTests(IntFunction fa) { 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 @@ static void ADDReduceLong512VectorTests(IntFunction fa) { 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 @@ static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceLong512VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void ADDReduceLong512VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long MULReduce(long[] a, int idx) { } 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 @@ static long MULReduceAll(long[] a) { static void MULReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceLong512VectorTests(IntFunction fa) { 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 @@ static long MULReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceLong512VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceLong512VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long MINReduce(long[] a, int idx) { } 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 @@ static long MINReduceAll(long[] a) { static void MINReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceLong512VectorTests(IntFunction fa) { 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 @@ static long MINReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceLong512VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceLong512VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long MAXReduce(long[] a, int idx) { } 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 @@ static long MAXReduceAll(long[] a) { static void MAXReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceLong512VectorTests(IntFunction fa) { 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 @@ static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceLong512VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceLong512VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long UMINReduce(long[] a, int idx) { } 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 @@ static long UMINReduceAll(long[] a) { static void UMINReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceLong512VectorTests(IntFunction fa) { 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 @@ static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceLong512VectorTestsMasked(IntFunction fa, IntFuncti long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceLong512VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static long UMAXReduce(long[] a, int idx) { } 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 @@ static long UMAXReduceAll(long[] a) { static void UMAXReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceLong512VectorTests(IntFunction fa) { 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 @@ static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceLong512VectorTestsMasked(IntFunction fa, IntFuncti long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceLong512VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static long FIRST_NONZEROReduce(long[] a, int idx) { } 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 @@ static long FIRST_NONZEROReduceAll(long[] a) { static void FIRST_NONZEROReduceLong512VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceLong512VectorTests(IntFunction fa) { 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 @@ static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceLong512VectorTestsMasked(IntFunction fa, long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueLong512VectorTests(IntFunction fm) { } 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 @@ static long SUADDReduce(long[] a, int idx) { } 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 @@ static void SUADDReduceLong512VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceLong512VectorTests(IntFunction fa) { 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 @@ static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceLong512VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void SUADDAssocLong64VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long ANDReduce(long[] a, int idx) { } 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 @@ static long ANDReduceAll(long[] a) { static void ANDReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceLong64VectorTests(IntFunction fa) { 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 @@ static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceLong64VectorTestsMasked(IntFunction fa, IntFunction long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceLong64VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static long ORReduce(long[] a, int idx) { } 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 @@ static void ORReduceLong64VectorTests(IntFunction fa) { 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 @@ static void ORReduceLong64VectorTests(IntFunction fa) { 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 @@ static long ORReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceLong64VectorTestsMasked(IntFunction fa, IntFunction< 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 @@ static void ORReduceLong64VectorTestsMasked(IntFunction fa, IntFunction< } 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 @@ static long XORReduce(long[] a, int idx) { } 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 @@ static void XORReduceLong64VectorTests(IntFunction fa) { 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 @@ static void XORReduceLong64VectorTests(IntFunction fa) { 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 @@ static long XORReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceLong64VectorTestsMasked(IntFunction fa, IntFunction 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 @@ static void XORReduceLong64VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static long ADDReduce(long[] a, int idx) { } 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 @@ static void ADDReduceLong64VectorTests(IntFunction fa) { 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 @@ static void ADDReduceLong64VectorTests(IntFunction fa) { 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 @@ static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceLong64VectorTestsMasked(IntFunction fa, IntFunction 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 @@ static void ADDReduceLong64VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static long MULReduce(long[] a, int idx) { } 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 @@ static long MULReduceAll(long[] a) { static void MULReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceLong64VectorTests(IntFunction fa) { 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 @@ static long MULReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceLong64VectorTestsMasked(IntFunction fa, IntFunction long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceLong64VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static long MINReduce(long[] a, int idx) { } 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 @@ static long MINReduceAll(long[] a) { static void MINReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceLong64VectorTests(IntFunction fa) { 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 @@ static long MINReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceLong64VectorTestsMasked(IntFunction fa, IntFunction long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceLong64VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static long MAXReduce(long[] a, int idx) { } 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 @@ static long MAXReduceAll(long[] a) { static void MAXReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceLong64VectorTests(IntFunction fa) { 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 @@ static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceLong64VectorTestsMasked(IntFunction fa, IntFunction long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceLong64VectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static long UMINReduce(long[] a, int idx) { } 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 @@ static long UMINReduceAll(long[] a) { static void UMINReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceLong64VectorTests(IntFunction fa) { 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 @@ static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceLong64VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceLong64VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long UMAXReduce(long[] a, int idx) { } 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 @@ static long UMAXReduceAll(long[] a) { static void UMAXReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceLong64VectorTests(IntFunction fa) { 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 @@ static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceLong64VectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceLong64VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long FIRST_NONZEROReduce(long[] a, int idx) { } 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 @@ static long FIRST_NONZEROReduceAll(long[] a) { static void FIRST_NONZEROReduceLong64VectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceLong64VectorTests(IntFunction fa) { 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 @@ static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceLong64VectorTestsMasked(IntFunction fa, I long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueLong64VectorTests(IntFunction fm) { } 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 @@ static long SUADDReduce(long[] a, int idx) { } 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 @@ static void SUADDReduceLong64VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceLong64VectorTests(IntFunction fa) { 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 @@ static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceLong64VectorTestsMasked(IntFunction fa, IntFuncti 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 @@ static VectorShape getMaxBit() { 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 @@ static void SUADDAssocLongMaxVectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static long ANDReduce(long[] a, int idx) { } 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 @@ static long ANDReduceAll(long[] a) { static void ANDReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static long ANDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long ORReduce(long[] a, int idx) { } 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 @@ static void ORReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static void ORReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static long ORReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunction 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 @@ static void ORReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunction } 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 @@ static long XORReduce(long[] a, int idx) { } 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 @@ static void XORReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static void XORReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static long XORReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void XORReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long ADDReduce(long[] a, int idx) { } 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 @@ static void ADDReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static void ADDReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static long ADDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void ADDReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long MULReduce(long[] a, int idx) { } 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 @@ static long MULReduceAll(long[] a) { static void MULReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static long MULReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long MINReduce(long[] a, int idx) { } 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 @@ static long MINReduceAll(long[] a) { static void MINReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static long MINReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long MAXReduce(long[] a, int idx) { } 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 @@ static long MAXReduceAll(long[] a) { static void MAXReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static long MAXReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunctio long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static long UMINReduce(long[] a, int idx) { } 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 @@ static long UMINReduceAll(long[] a) { static void UMINReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static long UMINReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceLongMaxVectorTestsMasked(IntFunction fa, IntFuncti long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceLongMaxVectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static long UMAXReduce(long[] a, int idx) { } 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 @@ static long UMAXReduceAll(long[] a) { static void UMAXReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static long UMAXReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceLongMaxVectorTestsMasked(IntFunction fa, IntFuncti long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = Long.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Long.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceLongMaxVectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static long FIRST_NONZEROReduce(long[] a, int idx) { } 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 @@ static long FIRST_NONZEROReduceAll(long[] a) { static void FIRST_NONZEROReduceLongMaxVectorTests(IntFunction fa) { long[] a = fa.apply(SPECIES.length()); long[] r = fr.apply(SPECIES.length()); - long ra = (long) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - LongVector av = LongVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static long FIRST_NONZEROReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceLongMaxVectorTestsMasked(IntFunction fa, long[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - long ra = (long) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + long ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (long) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueLongMaxVectorTests(IntFunction fm) { } 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 @@ static long SUADDReduce(long[] a, int idx) { } 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 @@ static void SUADDReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static void SUADDReduceLongMaxVectorTests(IntFunction fa) { 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 @@ static long SUADDReduceMasked(long[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceLongMaxVectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void SUADDAssocShort128VectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static short ANDReduce(short[] a, int idx) { } 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 @@ static short ANDReduceAll(short[] a) { static void ANDReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceShort128VectorTests(IntFunction fa) { 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 @@ static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceShort128VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceShort128VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short ORReduce(short[] a, int idx) { } 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 @@ static void ORReduceShort128VectorTests(IntFunction fa) { 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 @@ static void ORReduceShort128VectorTests(IntFunction fa) { 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 @@ static short ORReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceShort128VectorTestsMasked(IntFunction fa, IntFuncti 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 @@ static void ORReduceShort128VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static short XORReduce(short[] a, int idx) { } 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 @@ static void XORReduceShort128VectorTests(IntFunction fa) { 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 @@ static void XORReduceShort128VectorTests(IntFunction fa) { 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 @@ static short XORReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceShort128VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void XORReduceShort128VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short ADDReduce(short[] a, int idx) { } 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 @@ static void ADDReduceShort128VectorTests(IntFunction fa) { 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 @@ static void ADDReduceShort128VectorTests(IntFunction fa) { 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 @@ static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceShort128VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void ADDReduceShort128VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short MULReduce(short[] a, int idx) { } 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 @@ static short MULReduceAll(short[] a) { static void MULReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceShort128VectorTests(IntFunction fa) { 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 @@ static short MULReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceShort128VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceShort128VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short MINReduce(short[] a, int idx) { } 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 @@ static short MINReduceAll(short[] a) { static void MINReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceShort128VectorTests(IntFunction fa) { 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 @@ static short MINReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceShort128VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceShort128VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short MAXReduce(short[] a, int idx) { } 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 @@ static short MAXReduceAll(short[] a) { static void MAXReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceShort128VectorTests(IntFunction fa) { 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 @@ static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceShort128VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceShort128VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short UMINReduce(short[] a, int idx) { } 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 @@ static short UMINReduceAll(short[] a) { static void UMINReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceShort128VectorTests(IntFunction fa) { 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 @@ static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceShort128VectorTestsMasked(IntFunction fa, IntFunc short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceShort128VectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static short UMAXReduce(short[] a, int idx) { } 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 @@ static short UMAXReduceAll(short[] a) { static void UMAXReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceShort128VectorTests(IntFunction fa) { 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 @@ static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceShort128VectorTestsMasked(IntFunction fa, IntFunc short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceShort128VectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static short FIRST_NONZEROReduce(short[] a, int idx) { } 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 @@ static short FIRST_NONZEROReduceAll(short[] a) { static void FIRST_NONZEROReduceShort128VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceShort128VectorTests(IntFunction fa) { 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 @@ static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceShort128VectorTestsMasked(IntFunction fa short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueShort128VectorTests(IntFunction fm) { } 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 @@ static short SUADDReduce(short[] a, int idx) { } 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 @@ static void SUADDReduceShort128VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceShort128VectorTests(IntFunction fa) { 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 @@ static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceShort128VectorTestsMasked(IntFunction fa, IntFun 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 @@ static void SUADDAssocShort256VectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static short ANDReduce(short[] a, int idx) { } 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 @@ static short ANDReduceAll(short[] a) { static void ANDReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceShort256VectorTests(IntFunction fa) { 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 @@ static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceShort256VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceShort256VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short ORReduce(short[] a, int idx) { } 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 @@ static void ORReduceShort256VectorTests(IntFunction fa) { 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 @@ static void ORReduceShort256VectorTests(IntFunction fa) { 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 @@ static short ORReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceShort256VectorTestsMasked(IntFunction fa, IntFuncti 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 @@ static void ORReduceShort256VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static short XORReduce(short[] a, int idx) { } 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 @@ static void XORReduceShort256VectorTests(IntFunction fa) { 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 @@ static void XORReduceShort256VectorTests(IntFunction fa) { 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 @@ static short XORReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceShort256VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void XORReduceShort256VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short ADDReduce(short[] a, int idx) { } 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 @@ static void ADDReduceShort256VectorTests(IntFunction fa) { 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 @@ static void ADDReduceShort256VectorTests(IntFunction fa) { 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 @@ static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceShort256VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void ADDReduceShort256VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short MULReduce(short[] a, int idx) { } 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 @@ static short MULReduceAll(short[] a) { static void MULReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceShort256VectorTests(IntFunction fa) { 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 @@ static short MULReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceShort256VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceShort256VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short MINReduce(short[] a, int idx) { } 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 @@ static short MINReduceAll(short[] a) { static void MINReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceShort256VectorTests(IntFunction fa) { 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 @@ static short MINReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceShort256VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceShort256VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short MAXReduce(short[] a, int idx) { } 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 @@ static short MAXReduceAll(short[] a) { static void MAXReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceShort256VectorTests(IntFunction fa) { 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 @@ static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceShort256VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceShort256VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short UMINReduce(short[] a, int idx) { } 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 @@ static short UMINReduceAll(short[] a) { static void UMINReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceShort256VectorTests(IntFunction fa) { 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 @@ static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceShort256VectorTestsMasked(IntFunction fa, IntFunc short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceShort256VectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static short UMAXReduce(short[] a, int idx) { } 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 @@ static short UMAXReduceAll(short[] a) { static void UMAXReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceShort256VectorTests(IntFunction fa) { 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 @@ static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceShort256VectorTestsMasked(IntFunction fa, IntFunc short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceShort256VectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static short FIRST_NONZEROReduce(short[] a, int idx) { } 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 @@ static short FIRST_NONZEROReduceAll(short[] a) { static void FIRST_NONZEROReduceShort256VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceShort256VectorTests(IntFunction fa) { 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 @@ static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceShort256VectorTestsMasked(IntFunction fa short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueShort256VectorTests(IntFunction fm) { } 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 @@ static short SUADDReduce(short[] a, int idx) { } 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 @@ static void SUADDReduceShort256VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceShort256VectorTests(IntFunction fa) { 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 @@ static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceShort256VectorTestsMasked(IntFunction fa, IntFun 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 @@ static void SUADDAssocShort512VectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static short ANDReduce(short[] a, int idx) { } 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 @@ static short ANDReduceAll(short[] a) { static void ANDReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceShort512VectorTests(IntFunction fa) { 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 @@ static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceShort512VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceShort512VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short ORReduce(short[] a, int idx) { } 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 @@ static void ORReduceShort512VectorTests(IntFunction fa) { 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 @@ static void ORReduceShort512VectorTests(IntFunction fa) { 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 @@ static short ORReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceShort512VectorTestsMasked(IntFunction fa, IntFuncti 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 @@ static void ORReduceShort512VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static short XORReduce(short[] a, int idx) { } 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 @@ static void XORReduceShort512VectorTests(IntFunction fa) { 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 @@ static void XORReduceShort512VectorTests(IntFunction fa) { 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 @@ static short XORReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceShort512VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void XORReduceShort512VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short ADDReduce(short[] a, int idx) { } 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 @@ static void ADDReduceShort512VectorTests(IntFunction fa) { 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 @@ static void ADDReduceShort512VectorTests(IntFunction fa) { 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 @@ static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceShort512VectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void ADDReduceShort512VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short MULReduce(short[] a, int idx) { } 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 @@ static short MULReduceAll(short[] a) { static void MULReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceShort512VectorTests(IntFunction fa) { 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 @@ static short MULReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceShort512VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceShort512VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short MINReduce(short[] a, int idx) { } 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 @@ static short MINReduceAll(short[] a) { static void MINReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceShort512VectorTests(IntFunction fa) { 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 @@ static short MINReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceShort512VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceShort512VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short MAXReduce(short[] a, int idx) { } 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 @@ static short MAXReduceAll(short[] a) { static void MAXReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceShort512VectorTests(IntFunction fa) { 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 @@ static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceShort512VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceShort512VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short UMINReduce(short[] a, int idx) { } 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 @@ static short UMINReduceAll(short[] a) { static void UMINReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceShort512VectorTests(IntFunction fa) { 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 @@ static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceShort512VectorTestsMasked(IntFunction fa, IntFunc short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceShort512VectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static short UMAXReduce(short[] a, int idx) { } 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 @@ static short UMAXReduceAll(short[] a) { static void UMAXReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceShort512VectorTests(IntFunction fa) { 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 @@ static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceShort512VectorTestsMasked(IntFunction fa, IntFunc short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceShort512VectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static short FIRST_NONZEROReduce(short[] a, int idx) { } 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 @@ static short FIRST_NONZEROReduceAll(short[] a) { static void FIRST_NONZEROReduceShort512VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceShort512VectorTests(IntFunction fa) { 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 @@ static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceShort512VectorTestsMasked(IntFunction fa short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueShort512VectorTests(IntFunction fm) { } 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 @@ static short SUADDReduce(short[] a, int idx) { } 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 @@ static void SUADDReduceShort512VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceShort512VectorTests(IntFunction fa) { 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 @@ static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceShort512VectorTestsMasked(IntFunction fa, IntFun 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 @@ static void SUADDAssocShort64VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short ANDReduce(short[] a, int idx) { } 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 @@ static short ANDReduceAll(short[] a) { static void ANDReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceShort64VectorTests(IntFunction fa) { 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 @@ static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceShort64VectorTestsMasked(IntFunction fa, IntFuncti short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceShort64VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static short ORReduce(short[] a, int idx) { } 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 @@ static void ORReduceShort64VectorTests(IntFunction fa) { 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 @@ static void ORReduceShort64VectorTests(IntFunction fa) { 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 @@ static short ORReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceShort64VectorTestsMasked(IntFunction fa, IntFunctio 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 @@ static void ORReduceShort64VectorTestsMasked(IntFunction fa, IntFunctio } 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 @@ static short XORReduce(short[] a, int idx) { } 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 @@ static void XORReduceShort64VectorTests(IntFunction fa) { 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 @@ static void XORReduceShort64VectorTests(IntFunction fa) { 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 @@ static short XORReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceShort64VectorTestsMasked(IntFunction fa, IntFuncti 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 @@ static void XORReduceShort64VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static short ADDReduce(short[] a, int idx) { } 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 @@ static void ADDReduceShort64VectorTests(IntFunction fa) { 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 @@ static void ADDReduceShort64VectorTests(IntFunction fa) { 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 @@ static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceShort64VectorTestsMasked(IntFunction fa, IntFuncti 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 @@ static void ADDReduceShort64VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static short MULReduce(short[] a, int idx) { } 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 @@ static short MULReduceAll(short[] a) { static void MULReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceShort64VectorTests(IntFunction fa) { 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 @@ static short MULReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceShort64VectorTestsMasked(IntFunction fa, IntFuncti short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceShort64VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static short MINReduce(short[] a, int idx) { } 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 @@ static short MINReduceAll(short[] a) { static void MINReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceShort64VectorTests(IntFunction fa) { 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 @@ static short MINReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceShort64VectorTestsMasked(IntFunction fa, IntFuncti short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceShort64VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static short MAXReduce(short[] a, int idx) { } 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 @@ static short MAXReduceAll(short[] a) { static void MAXReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceShort64VectorTests(IntFunction fa) { 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 @@ static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceShort64VectorTestsMasked(IntFunction fa, IntFuncti short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceShort64VectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static short UMINReduce(short[] a, int idx) { } 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 @@ static short UMINReduceAll(short[] a) { static void UMINReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceShort64VectorTests(IntFunction fa) { 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 @@ static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceShort64VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceShort64VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short UMAXReduce(short[] a, int idx) { } 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 @@ static short UMAXReduceAll(short[] a) { static void UMAXReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceShort64VectorTests(IntFunction fa) { 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 @@ static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceShort64VectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceShort64VectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short FIRST_NONZEROReduce(short[] a, int idx) { } 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 @@ static short FIRST_NONZEROReduceAll(short[] a) { static void FIRST_NONZEROReduceShort64VectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceShort64VectorTests(IntFunction fa) { 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 @@ static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceShort64VectorTestsMasked(IntFunction fa, short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueShort64VectorTests(IntFunction fm) { } 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 @@ static short SUADDReduce(short[] a, int idx) { } 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 @@ static void SUADDReduceShort64VectorTests(IntFunction fa) { 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 @@ static void SUADDReduceShort64VectorTests(IntFunction fa) { 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 @@ static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceShort64VectorTestsMasked(IntFunction fa, IntFunc 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 @@ static VectorShape getMaxBit() { 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 @@ static void SUADDAssocShortMaxVectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static short ANDReduce(short[] a, int idx) { } 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 @@ static short ANDReduceAll(short[] a) { static void ANDReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static short ANDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ANDReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = -1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.AND, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = -1; + ra = AND_IDENTITY; 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 @@ static void ANDReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short ORReduce(short[] a, int idx) { } 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 @@ static void ORReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static void ORReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static short ORReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ORReduceShortMaxVectorTestsMasked(IntFunction fa, IntFuncti 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 @@ static void ORReduceShortMaxVectorTestsMasked(IntFunction fa, IntFuncti } 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 @@ static short XORReduce(short[] a, int idx) { } 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 @@ static void XORReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static void XORReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static short XORReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void XORReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void XORReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short ADDReduce(short[] a, int idx) { } 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 @@ static void ADDReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static void ADDReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static short ADDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void ADDReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunct 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 @@ static void ADDReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short MULReduce(short[] a, int idx) { } 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 @@ static short MULReduceAll(short[] a) { static void MULReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static short MULReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MULReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = 1; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MUL, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = 1; + ra = MUL_IDENTITY; 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 @@ static void MULReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short MINReduce(short[] a, int idx) { } 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 @@ static short MINReduceAll(short[] a) { static void MINReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static short MINReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MINReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MIN, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = MIN_IDENTITY; 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 @@ static void MINReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short MAXReduce(short[] a, int idx) { } 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 @@ static short MAXReduceAll(short[] a) { static void MAXReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static short MAXReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void MAXReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunct short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.MAX, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = MAX_IDENTITY; 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 @@ static void MAXReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunct } 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 @@ static short UMINReduce(short[] a, int idx) { } 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 @@ static short UMINReduceAll(short[] a) { static void UMINReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static short UMINReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void UMINReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunc short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MAX_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMIN, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MAX_VALUE; + ra = UMIN_IDENTITY; 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 @@ static void UMINReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static short UMAXReduce(short[] a, int idx) { } 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 @@ static short UMAXReduceAll(short[] a) { static void UMAXReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static short UMAXReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void UMAXReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunc short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = Short.MIN_VALUE; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.UMAX, vmask); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = Short.MIN_VALUE; + ra = UMAX_IDENTITY; 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 @@ static void UMAXReduceShortMaxVectorTestsMasked(IntFunction fa, IntFunc } 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 @@ static short FIRST_NONZEROReduce(short[] a, int idx) { } 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 @@ static short FIRST_NONZEROReduceAll(short[] a) { static void FIRST_NONZEROReduceShortMaxVectorTests(IntFunction fa) { short[] a = fa.apply(SPECIES.length()); short[] r = fr.apply(SPECIES.length()); - short ra = (short) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - for (int i = 0; i < a.length; i += SPECIES.length()) { - ShortVector av = ShortVector.fromArray(SPECIES, a, i); - r[i] = av.reduceLanes(VectorOperators.FIRST_NONZERO); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void FIRST_NONZEROReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static short FIRST_NONZEROReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void FIRST_NONZEROReduceShortMaxVectorTestsMasked(IntFunction fa short[] r = fr.apply(SPECIES.length()); boolean[] mask = fm.apply(SPECIES.length()); VectorMask vmask = VectorMask.fromArray(SPECIES, mask, 0); - short ra = (short) 0; - - for (int ic = 0; ic < INVOC_COUNT; ic++) { - 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); - } - } + short ra = 0; for (int ic = 0; ic < INVOC_COUNT; ic++) { - ra = (short) 0; + ra = FIRST_NONZERO_IDENTITY; 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 @@ static void allTrueShortMaxVectorTests(IntFunction fm) { } 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 @@ static short SUADDReduce(short[] a, int idx) { } 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 @@ static void SUADDReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static void SUADDReduceShortMaxVectorTests(IntFunction fa) { 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 @@ static short SUADDReduceMasked(short[] a, int idx, boolean[] mask) { } 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 @@ static void SUADDReduceShortMaxVectorTestsMasked(IntFunction fa, IntFun 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 097/113] 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 098/113] 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 099/113] 8356684: jpackage error messages are not helpful Reviewed-by: almatvee --- .../jdk/jpackage/internal/Executor.java | 16 +- .../jdk/jpackage/internal/cli/Main.java | 62 ++++-- .../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, 439 insertions(+), 73 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.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 @@ Result execute() throws IOException { 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 @@ Result execute() throws IOException { 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 @@ private static void log(Result result, String printableOutput) throws IOExceptio 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.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.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 @@ void reportError(Throwable t) { 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 printExternalCommandError(UnexpectedResultException ex) { + var result = ex.getResult(); + var commandOutput = ((ExecutableAttributesWithCapturedOutput)result.execAttrs()).printableOutput(); + var printableCommandLine = result.execAttrs().printableCommandLine(); + + 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 isAlienException = isAlienExceptionType(t); + var isSelfContained = isSelfContained(t); - if (isAlienException || verbose) { + 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.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 @@ private static String resourceKeySuffix(OperatingSystem os) { } + @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.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 @@ static JOptSimpleOptionsBuilder buildParser(OperatingSystem os, BundlingEnvironm }); } + @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 @@ /** * 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 CommandOutputControl copy() { public interface ExecutableAttributes { List commandLine(); + + default String printableCommandLine() { + return CommandLineFormat.DEFAULT.apply(commandLine()); + } } public sealed interface Executable { @@ -812,11 +816,6 @@ public record ProcessAttributes(Optional pid, List commandLine) im 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 record ToolProviderAttributes(String name, List args) implements 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 List commandLine() { } 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 record Result( 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 @@ private UnexpectedResultException(Result value, String message) { } 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 Result getResult() { 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 @@ private Result assertExitCodeIs(List expectedExitCodes) { 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 int getExitCode() { } public String getPrintableCommandLine() { - return base.execAttrs().toString(); + return base.execAttrs().printableCommandLine(); } } @@ -515,21 +515,16 @@ public String getPrintableCommandLine() { 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 Builder printToStderr(String... str) { 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.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 void test_ErrorReporter_format(String key) { 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 @@ private static List test_ErrorReporter() { 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 @@ private enum ExceptionFormatter { 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 @@ private static Function errorMessage(Function { + 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 int run(PrintWriter out, PrintWriter err, String... args) { 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 void test_Result_no_args_ctor() { 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 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(); + return List.of("foo"); } }; @@ -295,6 +302,20 @@ public List commandLine() { 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 void mutate(CommandOutputControl coc) { } } - 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 100/113] 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 101/113] 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 102/113] 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 103/113] 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 static long toUnsignedLong(int x) { * @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 static int divideUnsigned(int dividend, int divisor) { * @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 static int compareUnsigned(long x, long y) { * @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 static long divideUnsigned(long dividend, long divisor) { * @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 104/113] 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.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 @@ private static void testVectorWithRole() throws IOException { 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 @@ private static void testVectorWithRole() throws IOException { 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 @@ private static void testVectorWithRole() throws IOException { * @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 @@ private static void testHolderWithRole() throws IOException { 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 @@ private static void testHolderWithRole() throws IOException { 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.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 @@ /** * 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 @@ default void readExternal(ObjectInput in) { } record Wibble () implements ThrowingExternalizable { + @Serial private static final long serialVersionUID = 12345678L; } @@ -77,7 +79,7 @@ record Wobble (long l) implements ThrowingExternalizable { } 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 @@ record NotSerializable2(int x) { } 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.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 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 @@ record R3 () implements Serializable { } 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 void testSerialize(Object objectToSerialize, long expectedUID) 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 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 @@ */ 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 @@ Object[][] patterns() { /** * 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 void normalOIS(String pattern, int arraySize, Object[] array) throws IOEx 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 void normalOIS(String pattern, int arraySize, Object[] array) throws IOEx * 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 void subclassedOIS(String pattern, int arraySize, Object[] array) throws 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.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 @@ Object[][] patterns() { * "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.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 @@ Object[][] globalPatternElements() { ? 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 @@ Object[][] globalPatternElements() { * 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 @@ static void globalFilter() { 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 @@ static void globalFilter() { * 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 @@ static void setGlobalFilter() { 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 @@ static void setGlobalFilter() { * * @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 @@ static void testGlobalPattern(String pattern, Object object, boolean allowed) { } 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.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 @@ private Object[][] cases() { * 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.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 @@ 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 @@ Object[][] rejectedInGlobal() { * "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 void testRejectedInGlobal(Object toDeserialized, String pattern) throws E } } - @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 @@ Object[][] allowedInGlobal() { * "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.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 @@ * * 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 @@ static Object[][] examples() { } - @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 @@ void examples(Serializable obj, ObjectInputFilter filter, Status expected) { 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 @@ void examples(Serializable obj, ObjectInputFilter filter, Status expected) { * @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 @@ void checkStatus(Serializable obj, ObjectInputFilter filter, Status expected) { 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.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 @@ private Object[][] cases() { * 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.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 @@ private static byte[] simpleStream() { 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 @@ private static boolean isValidFilterFactory() { 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 @@ static Object[][] filterCases() { } // 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 @@ void testNull() { * 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 @@ void testSecondSetShouldThrow() { 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 @@ void testSecondSetShouldThrow() { * @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 @@ void testCase(MyFilterFactory dynFilterFactory, Validator dynFilter, Validator s 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 @@ void testCase(MyFilterFactory dynFilterFactory, Validator dynFilter, Validator s @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 @@ void testDisableFailFilter() throws IOException { // 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.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 @@ void testMerge() { 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 @@ static Predicate> isInteger() { 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 @@ static Object[][] allowPredicateCases() { }; } - @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 @@ static Object[][] rejectPredicateCases() { }; } - @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 @@ void testRejectUndecided() { 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 @@ void testRejectUndecided() { 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 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 @@ interface TriConsumer< T, U, V> { * 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 @@ static Object[][] patterns() { {"maxbytes=+1024"}, {"java.base/java.util.Hashtable"}, }; - return patterns; } - @DataProvider(name="InvalidPatterns") static Object[][] invalidPatterns() { return new Object [][] { {".*"}, @@ -120,7 +117,6 @@ static Object[][] invalidPatterns() { }; } - @DataProvider(name="Limits") static Object[][] limits() { // The numbers are arbitrary > 1 return new Object[][] { @@ -133,7 +129,6 @@ static Object[][] limits() { }; } - @DataProvider(name="InvalidLimits") static Object[][] invalidLimits() { return new Object[][] { {"maxrefs=-1"}, @@ -157,7 +152,6 @@ static Object[][] invalidLimits() { * 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 @@ static Object[][] objects() { 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 @@ static Object[][] objects() { return objects; } - @DataProvider(name="Arrays") static Object[][] arrays() { return new Object[][]{ {new Object[16], 16}, @@ -244,21 +237,22 @@ static Object[][] arrays() { * @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 @@ void t1(Object object, * * @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 @@ void nonResettableFilter() { // 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 @@ void testNonSettableAfterReadObject() throws IOException, ClassNotFoundException 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 Status checkInput(FilterInfo filterInfo) { } }); 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 Status checkInput(FilterInfo filterInfo) { * 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 @@ void testReadResolveToArray(Object array, int length) throws IOException { * @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 @@ void testLimits(String name, long value) { * 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 @@ void testInvalidPatterns(String pattern) { @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 @@ static Object validate(byte[] bytes, } catch (EOFException eof) { // normal completion } catch (ClassNotFoundException cnf) { - Assert.fail("Deserializing", cnf); + Assertions.fail("Deserializing", cnf); } return null; } @@ -514,7 +499,7 @@ static class Validator implements ObjectInputFilter { @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 @@ static void testPatterns(String pattern, Object object, boolean allowed) { 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 @@ static void testPatterns(String pattern, Object object, boolean allowed) { */ 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 @@ static Object genTestObject(String pattern, boolean allowed) { 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 @@ static Object genTestObjectWildcard(String pattern, boolean allowed) { 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 @@ static Object genTestObjectWildcard(String pattern, boolean allowed) { */ 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 @@ static Object genTestLimit(String pattern, boolean allowed) { } 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 @@ private static Object genMaxBytesObject(boolean allowed, long maxBytes) { 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 105/113] 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 BevelBorder(boolean isRaised, Color darkShadow, Color lightShadow) { 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 void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { 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 FocusBorder(Color control, Color focus) { 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 void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { } } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.set(1, 1, 1, 1); return insets; @@ -130,6 +134,7 @@ public ButtonBorder(Color shadow, Color highlight, Color darkShadow, Color focus 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 void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { 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 ToggleButtonBorder(Color shadow, Color highlight, Color darkShadow, Color 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 void paintBorder(Component c, Graphics g, int x, int y, } } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.set(2, 2, 3, 3); return insets; @@ -236,6 +244,7 @@ public MenuBarBorder(Color shadow, Color highlight, Color darkShadow, Color focu 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 void paintBorder(Component c, Graphics g, int x, int y, int width, int he } } + @Override public Insets getBorderInsets(Component c, Insets insets) { insets.set(6, 6, 6, 6); return insets; @@ -297,6 +307,7 @@ protected Color getFrameShadow() { 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 @@ protected boolean isActiveFrame() { * 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 int resizePartWidth() { /** 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 @@ protected boolean drawTopBorder(Component c, Graphics g, /** 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 @@ protected boolean drawLeftBorder(Component c, Graphics g, int x, int y, /** 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 @@ protected boolean drawRightBorder(Component c, Graphics g, int x, int y, /** 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 @@ protected boolean drawBottomBorder(Component c, Graphics g, int x, int y, } // Returns true if the associated internal frame has focus. + @Override protected boolean isActiveFrame() { return frame.isSelected(); } @@ -667,6 +684,7 @@ public MotifPopupMenuBorder( * @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 void paintBorder(Component c, Graphics g, int x, int y, int width, int he * @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 MotifButtonListener(AbstractButton b ) { 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 static ComponentUI createUI(JComponent c) { // ******************************** // Create Listeners // ******************************** + @Override protected BasicButtonListener createButtonListener(AbstractButton b){ return new MotifButtonListener(b); } @@ -81,6 +82,7 @@ protected BasicButtonListener createButtonListener(AbstractButton b){ // ******************************** // Install Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -90,6 +92,7 @@ public void installDefaults(AbstractButton b) { LookAndFeel.installProperty(b, "opaque", Boolean.FALSE); } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; @@ -106,12 +109,14 @@ protected Color getSelectColor() { // ******************************** // 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 @@ protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect) { 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 static ComponentUI createUI(JComponent b) { 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 @@ protected ChangeListener createChangeListener(JComponent c) { } 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 void mouseReleased(MouseEvent e) { 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 static ComponentUI createUI(JComponent c) { return motifCheckBoxUI; } + @Override public String getPropertyPrefix() { return propertyPrefix; } @@ -67,6 +68,7 @@ public String getPropertyPrefix() { // ******************************** // Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -75,6 +77,7 @@ public void installDefaults(AbstractButton b) { } } + @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 static ComponentUI createUI(JComponent c) { return new MotifComboBoxUI(); } + @Override public void installUI(JComponent c) { super.installUI(c); arrowIcon = new MotifComboBoxArrowIcon(UIManager.getColor("controlHighlight"), @@ -72,6 +73,7 @@ public void installUI(JComponent c) { UIManager.getColor("control")); } + @Override public Dimension getMinimumSize( JComponent c ) { if ( !isMinimumSizeDirty ) { return new Dimension( cachedMinimumSize ); @@ -89,6 +91,7 @@ public Dimension getMinimumSize( JComponent c ) { return size; } + @Override protected ComboPopup createPopup() { return new MotifComboPopup( comboBox ); } @@ -106,10 +109,12 @@ public MotifComboPopup( JComboBox comboBox ) { /** * 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 @@ protected InvocationKeyHandler() { } } + @Override protected void installComponents() { if ( comboBox.isEditable() ) { addEditor(); @@ -129,11 +135,13 @@ protected void installComponents() { 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 void paint(Graphics g, JComponent c) { currentValuePane.removeAll(); } + @Override public void paintCurrentValue(Graphics g,Rectangle bounds,boolean hasFocus) { ListCellRenderer renderer = comboBox.getRenderer(); Component c; @@ -226,6 +235,7 @@ protected Rectangle rectangleForArrowIcon() { return b; } + @Override protected Rectangle rectangleForCurrentValue() { int width = comboBox.getWidth(); int height = comboBox.getHeight(); @@ -251,11 +261,13 @@ public int iconAreaWidth() { 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 ComboBoxLayoutManager extends BasicComboBoxUI.ComboBoxLayoutManager public ComboBoxLayoutManager() { MotifComboBoxUI.this.super(); } + @Override public void layoutContainer(Container parent) { if ( motifGetEditor() != null ) { Rectangle cvb = rectangleForCurrentValue(); @@ -298,6 +311,7 @@ public MotifComboBoxArrowIcon(Color lightShadow, Color darkShadow, Color fill) { } + @Override public void paintIcon(Component c, Graphics g, int xo, int yo) { int w = getIconWidth(); int h = getIconHeight(); @@ -322,10 +336,12 @@ public void paintIcon(Component c, Graphics g, int xo, int yo) { } + @Override public int getIconWidth() { return 11; } + @Override public int getIconHeight() { return 11; } @@ -336,6 +352,7 @@ public int getIconHeight() { * * @since 1.6 */ + @Override protected PropertyChangeListener createPropertyChangeListener() { return new MotifPropertyChangeListener(); } @@ -345,6 +362,7 @@ protected PropertyChangeListener createPropertyChangeListener() { */ 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 static ComponentUI createUI(JComponent c) { public MotifDesktopIconUI() { } + @Override protected void installDefaults(){ super.installDefaults(); setDefaultIcon(UIManager.getIcon("DesktopIcon.icon")); @@ -94,12 +95,15 @@ protected void installDefaults(){ 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 @@ protected DesktopIconMouseListener createDesktopIconMouseListener(){ return new DesktopIconMouseListener(); } + @Override protected void uninstallDefaults(){ super.uninstallDefaults(); desktopIcon.setLayout(null); @@ -156,6 +161,7 @@ protected void uninstallDefaults(){ desktopIcon.remove(iconLabel); } + @Override protected void uninstallListeners(){ super.uninstallListeners(); iconButton.removeActionListener(desktopIconActionListener); @@ -163,6 +169,7 @@ protected void uninstallListeners(){ sysMenuTitlePane.uninstallListeners(); } + @Override public Dimension getMinimumSize(JComponent c) { JInternalFrame iframe = desktopIcon.getInternalFrame(); @@ -180,10 +187,12 @@ public Dimension getMinimumSize(JComponent c) { 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 @@ protected class IconLabel extends JPanel { // 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 @@ void forwardEventToParent(MouseEvent e) { 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 Dimension getPreferredSize() { return new Dimension(w, LABEL_HEIGHT + LABEL_DIVIDER); } + @Override public void paint(Graphics g) { super.paint(g); @@ -308,28 +328,35 @@ protected class IconButton extends JButton { 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 @@ void forwardEventToParent(MouseEvent e) { getParent().dispatchEvent(newEvent); } + @Override @SuppressWarnings("deprecation") public boolean isFocusTraversable() { return false; @@ -355,6 +383,7 @@ public boolean isFocusTraversable() { protected class DesktopIconActionListener implements ActionListener { + @Override public void actionPerformed(ActionEvent e){ systemMenu.show(iconButton, 0, getDesktopIcon().getHeight()); } @@ -362,6 +391,7 @@ public void actionPerformed(ActionEvent e){ 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 static ComponentUI createUI(JComponent d) { public MotifDesktopPaneUI() { } + @Override protected void installDesktopManager() { desktopManager = desktop.getDesktopManager(); if(desktopManager == null) { @@ -75,6 +76,7 @@ protected void installDesktopManager() { //////////////////////////////////////////////////////////////////////////////////// @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 @@ private static class MotifDesktopManager extends DefaultDesktopManager implement 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 void setBoundsForFrame(JComponent f, int newX, int newY, } } + @Override public void beginDraggingFrame(JComponent f) { usingDragPane = false; if(f.getParent() instanceof JLayeredPane) { @@ -125,10 +129,12 @@ public void beginDraggingFrame(JComponent f) { } } + @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 void endDraggingFrame(JComponent f) { } } + @Override public void beginResizingFrame(JComponent f, int direction) { usingDragPane = false; if(f.getParent() instanceof JLayeredPane) { @@ -157,11 +164,13 @@ public void beginResizingFrame(JComponent f, int direction) { } } + @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 void endResizingFrame(JComponent f) { } } + @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 static ComponentUI createUI(JComponent c) { * * @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 MotifFileChooserUI(JFileChooser filechooser) { super(filechooser); } + @Override public String getFileName() { if(filenameTextField != null) { return filenameTextField.getText(); @@ -149,30 +150,37 @@ public String getFileName() { } } + @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 static ComponentUI createUI(JComponent c) { 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 void uninstallUI(JComponent c) { 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 Insets getInsets() { } JTextField tmp1 = new JTextField(curDirName, 35) { + @Override public Dimension getMaximumSize() { Dimension d = super.getMaximumSize(); d.height = getPreferredSize().height; @@ -340,6 +353,7 @@ public Dimension getMaximumSize() { leftPanel.add(l); JComboBox tmp2 = new JComboBox() { + @Override public Dimension getMaximumSize() { Dimension d = super.getMaximumSize(); d.height = getPreferredSize().height; @@ -417,6 +431,7 @@ public Dimension getMaximumSize() { 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 Dimension getMaximumSize() { 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 Dimension getMaximumSize() { 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 Dimension getMaximumSize() { 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 Dimension getMaximumSize() { 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 @@ private void doControlButtonsChanged(PropertyChangeEvent e) { } } + @Override public void uninstallComponents(JFileChooser fc) { fc.removeAll(); bottomPanel = null; @@ -528,6 +548,7 @@ public void uninstallComponents(JFileChooser fc) { } } + @Override protected void installStrings(JFileChooser fc) { super.installStrings(fc); @@ -555,11 +576,13 @@ private Integer getMnemonic(String key, Locale l) { 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 @@ protected JScrollPane createFilesList() { 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 @@ protected void align(JComponent c) { @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 Component getListCellRendererComponent(JList list, Object value, int i @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 MotifDirectoryListModel() { 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 void fireContentsChanged() { // 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 MotifFileListModel() { getModel().addListDataListener(this); } + @Override public int getSize() { return getModel().getFiles().size(); } @@ -733,14 +765,17 @@ public int indexOf(Object o) { 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 void fireContentsChanged() { } // PENDING(jeff) - fire the interval changed + @Override public void contentsChanged(ListDataEvent e) { fireContentsChanged(); } @@ -779,6 +815,7 @@ protected FilterComboBoxRenderer createFilterComboBoxRenderer() { */ @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 @@ protected FilterComboBoxModel() { 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 void propertyChange(PropertyChangeEvent e) { } } + @Override public void setSelectedItem(Object filter) { if(filter != null) { getFileChooser().setFileFilter((FileFilter) filter); @@ -822,6 +861,7 @@ public void setSelectedItem(Object filter) { } } + @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 Object getSelectedItem() { return getFileChooser().getFileFilter(); } + @Override public int getSize() { if(filters != null) { return filters.length; @@ -851,6 +892,7 @@ public int getSize() { } } + @Override public FileFilter getElementAt(int index) { if(index > getSize() - 1) { // This shouldn't happen. Try to recover gracefully. @@ -864,6 +906,7 @@ public FileFilter getElementAt(int index) { } } + @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 @@ private static class CheckBoxIcon implements Icon, UIResource, 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 void paintIcon(Component c, Graphics g, int x, int y) { } } + @Override public int getIconWidth() { return csize; } + @Override public int getIconHeight() { return csize; } @@ -258,6 +261,7 @@ private static class RadioButtonIcon implements Icon, UIResource, 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 void paintIcon(Component c, Graphics g, int x, int y) { } } + @Override public int getIconWidth() { return 14; } + @Override public int getIconHeight() { return 14; } @@ -315,10 +321,13 @@ public int getIconHeight() { @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 void paintIcon(Component c,Graphics g, int x, int y) @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 @@ private static class MenuArrowIcon implements Icon, UIResource, 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 void paintIcon(Component c, Graphics g, int x, int y) { } } + @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 MotifInternalFrameTitlePane(JInternalFrame frame) { 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 @@ JPopupMenu getSystemMenu() { return systemMenu; } + @Override protected void assembleSystemMenu() { systemMenu = new JPopupMenu(); JMenuItem mi = systemMenu.add(restoreAction); @@ -106,12 +111,14 @@ protected void assembleSystemMenu() { 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 @@ private static int getButtonMnemonic(String button) { } } + @Override protected void createButtons() { minimizeButton = new MinimizeButton(); minimizeButton.addActionListener(iconifyAction); @@ -146,6 +154,7 @@ protected void createButtons() { } + @Override protected void addSubComponents() { title = new Title(frame.getTitle()); title.setFont(getFont()); @@ -156,6 +165,7 @@ protected void addSubComponents() { add(maximizeButton); } + @Override public void paintComponent(Graphics g) { } @@ -165,9 +175,11 @@ void setColors(Color c, Color h, Color s) { 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 void propertyChange(PropertyChangeEvent evt) { 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 void layoutContainer(Container c) { title.setBounds(BUTTON_SIZE, 0, x, BUTTON_SIZE); } + @Override protected void showSystemMenu(){ systemMenu.show(systemButton, 0, BUTTON_SIZE); } @@ -245,23 +263,28 @@ private abstract class FrameButton extends JButton { 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 void paintComponent(Graphics g) { @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 void paintComponent(Graphics g) { @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 void paintComponent(Graphics g) { @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 @@ private class Title extends FrameButton { // 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 @@ void forwardEventToParent(MouseEvent e) { 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 MotifInternalFrameUI(JInternalFrame w) { 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 @@ protected void installDefaults() { } + @Override protected void installKeyboardActions(){ super.installKeyboardActions(); // We replace the @@ -103,6 +106,7 @@ protected void installKeyboardActions(){ } + @Override protected void uninstallDefaults() { LookAndFeel.uninstallBorder(frame); frame.setLayout(null); @@ -113,15 +117,18 @@ private JInternalFrame getFrame(){ 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 @@ protected void uninstallKeyboardActions(){ } } + @Override protected void setupMenuOpenKey(){ super.setupMenuOpenKey(); ActionMap map = SwingUtilities.getUIActionMap(frame); @@ -141,9 +149,11 @@ protected void setupMenuOpenKey(){ // 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 boolean isEnabled(){ } } + @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 boolean isEnabled(){ 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 void actionPerformed(ActionEvent e){ getUI(); micon.hideSystemMenu(); } + @Override public boolean isEnabled(){ return isKeyBindingActive(); } @@ -201,12 +216,14 @@ public boolean isEnabled(){ /** 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 @@ @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 boolean isSupportedLookAndFeel() { * 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 @@ protected void initSystemColorDefaults(UIDefaults table) } + @Override protected void initClassDefaults(UIDefaults table) { super.initClassDefaults(table); @@ -178,6 +185,7 @@ private void initResourceBundle(UIDefaults table) { } + @Override protected void initComponentDefaults(UIDefaults table) { super.initComponentDefaults(table); @@ -255,36 +263,42 @@ protected void initComponentDefaults(UIDefaults table) )); 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 Object createValue(UIDefaults table) { }; 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 Object createValue(UIDefaults table) { }; 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 Object createValue(UIDefaults table) { }; 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 Object createValue(UIDefaults table) { "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 static ComponentUI createUI(JComponent c) 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 @@ protected ChangeListener createChangeListener(JComponent c) { 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 void stateChanged(ChangeEvent e) { } 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 void mouseReleased(MouseEvent e) { 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 @@ * @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 @@ * @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 static ComponentUI createUI( JComponent x ) { // menuItem.removeChangeListener(changeListener); // } + @Override protected ChangeListener createChangeListener(JComponent c) { return new MotifChangeHandler((JMenu)c, this); } @@ -76,6 +77,7 @@ private boolean popupIsOpen(JMenu m,MenuElement[] me) { return false; } + @Override protected MouseInputListener createMouseInputListener(JComponent c) { return new MouseInputHandler(); } @@ -86,6 +88,7 @@ public MotifChangeHandler(JMenu m, MotifMenuUI ui) { } + @Override public void stateChanged(ChangeEvent e) { JMenuItem c = (JMenuItem)e.getSource(); if (c.isArmed() || c.isSelected()) { @@ -100,7 +103,9 @@ public void stateChanged(ChangeEvent e) { } 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 void mousePressed(MouseEvent e) { } } + @Override public void mouseReleased(MouseEvent e) { MenuSelectionManager manager = MenuSelectionManager.defaultManager(); @@ -139,11 +145,15 @@ public void mouseReleased(MouseEvent e) { 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 static ComponentUI createUI(JComponent x) { * 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 @@ protected Container createButtonArea() { /** * 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 void paint(Graphics g) { * 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 static ComponentUI createUI(JComponent c) { * * @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 static ComponentUI createUI( JComponent c ) return new MotifPopupMenuSeparatorUI(); } + @Override public void paint( Graphics g, JComponent c ) { Dimension s = c.getSize(); @@ -58,6 +59,7 @@ public void paint( Graphics g, JComponent c ) 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 static ComponentUI createUI(JComponent x) { /* 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 Dimension getPreferredSize(JComponent c) { 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 static ComponentUI createUI(JComponent b) { 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 @@ protected ChangeListener createChangeListener(JComponent c) { @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 void mouseReleased(MouseEvent e) { 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 static ComponentUI createUI(JComponent c) { // ******************************** // Install Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -76,6 +77,7 @@ public void installDefaults(AbstractButton b) { } } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; @@ -92,6 +94,7 @@ protected Color getFocusColor() { // ******************************** // 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 MotifScrollBarButton(int direction) } + @Override public Dimension getPreferredSize() { switch (direction) { case NORTH: @@ -76,18 +77,22 @@ public Dimension getPreferredSize() { } } + @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 static ComponentUI createUI(JComponent c) { return new MotifScrollBarUI(); } + @Override public Dimension getPreferredSize(JComponent c) { Insets insets = c.getInsets(); int dx = insets.left + insets.right; @@ -61,19 +62,23 @@ public Dimension getPreferredSize(JComponent c) { : 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 static ComponentUI createUI(JComponent b) { 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 @@ protected Dimension getThumbSize() { } } + @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 MotifSplitPaneDivider(BasicSplitPaneUI ui) { * 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 @@ else if (insets != null) { */ // 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 void paint(Graphics g) { /** * The minimums size is the same as the preferredSize */ + @Override public Dimension getMinimumSize() { return getPreferredSize(); } @@ -187,6 +190,7 @@ public Dimension getMinimumSize() { * 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 @@ private JSplitPane getSplitPane() { * 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 void mousePressed(MouseEvent e) { } } + @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 static ComponentUI createUI(JComponent x) { /** * 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 static ComponentUI createUI(JComponent tabbedPane) { // UI Installation/De-installation + @Override protected void installDefaults() { super.installDefaults(); @@ -70,6 +71,7 @@ protected void installDefaults() { unselectedTabHighlight = UIManager.getColor("TabbedPane.unselectedTabHighlight"); } + @Override protected void uninstallDefaults() { super.uninstallDefaults(); @@ -81,6 +83,7 @@ protected void uninstallDefaults() { // UI Rendering + @Override protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { @@ -104,6 +107,7 @@ protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, } } + @Override protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { @@ -126,6 +130,7 @@ protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, } } + @Override protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) { @@ -148,6 +153,7 @@ protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, } } + @Override protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, @@ -174,6 +180,7 @@ protected void paintTabBackground(Graphics g, } + @Override protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, @@ -225,6 +232,7 @@ protected void paintTabBorder(Graphics g, } + @Override protected void paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect, @@ -263,10 +271,12 @@ protected void paintFocusIndicator(Graphics g, int tabPlacement, } } + @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 static ComponentUI createUI(JComponent ta) { * * @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 static ComponentUI createUI(JComponent c) { * * @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 static ComponentUI createUI(JComponent c) { * * @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 static class MotifCaret extends DefaultCaret implements UIResource { * @param e the focus event * @see FocusListener#focusGained */ + @Override public void focusGained(FocusEvent e) { super.focusGained(e); getComponent().repaint(); @@ -88,6 +89,7 @@ public void focusGained(FocusEvent e) { * @param e the focus event * @see FocusListener#focusLost */ + @Override public void focusLost(FocusEvent e) { super.focusLost(e); getComponent().repaint(); @@ -101,6 +103,7 @@ public void focusLost(FocusEvent e) { * @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 @@ protected void damage(Rectangle r) { * @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 static ComponentUI createUI(JComponent b) { // ******************************** // Install Defaults // ******************************** + @Override public void installDefaults(AbstractButton b) { super.installDefaults(b); if(!defaults_initialized) { @@ -80,6 +81,7 @@ public void installDefaults(AbstractButton b) { LookAndFeel.installProperty(b, "opaque", Boolean.FALSE); } + @Override protected void uninstallDefaults(AbstractButton b) { super.uninstallDefaults(b); defaults_initialized = false; @@ -96,6 +98,7 @@ protected Color getSelectColor() { // ******************************** // 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 TreeLeafIcon() { 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 void paintIcon(Component c, Graphics g, int x, int y) { 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 MotifTreeUI() { 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 @@ protected void paintVerticalLine( Graphics g, JComponent c, int x, int top, int } } + @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 static Icon createExpandedIcon() { 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 void paintIcon(Component c, Graphics g, int x, int y) { 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 static Icon createCollapsedIcon() { 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 static ComponentUI createUI(JComponent x) { * 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 106/113] 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.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 @@ Launcher create(Options options) { .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 107/113] 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 108/113] 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.gc.*; import nsk.share.*; +import jdk.test.whitebox.WhiteBox; public class large001 extends ThreadedGCTest { @@ -146,8 +149,8 @@ public void run() { 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 void run() { 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 static void callHotClass(String location) throws Exception { boolean clsUnloaded = clsUnLoader.unloadClass(); clsUnLoader = null; - System.gc(); } private int runThis(String argv[], PrintStream out) throws Exception { @@ -110,12 +109,13 @@ private int runThis(String argv[], PrintStream out) throws Exception { 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 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 void loadClass(String className, String classDir) throws ClassNotFoundExc /** * 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 boolean unloadClass(ExecutionController stresser) { // 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 109/113] 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 110/113] 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 | 200 ++++-------------- .../helpers/jdk/jpackage/test/Executor.java | 4 +- .../jdk/tools/jpackage/share/LicenseTest.java | 33 ++- 9 files changed, 205 insertions(+), 192 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.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 @@ Path volumeIcon() { 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 @@ private void prepareDMGSetupScript() throws IOException { .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 @@ private void prepareConfigFiles() throws IOException { .setExternal(pkg.icon().orElse(null)) .saveToFile(volumeIcon()); - prepareLicense(); + if (pkg.licenseFile().isPresent()) { + MacDmgLicense.prepareLicensePListFile(pkg.licenseFile().get(), licensePListFile()); + } prepareDMGSetupScript(); } @@ -359,7 +340,7 @@ private void buildDMG() throws IOException { "udifrez", normalizedAbsolutePathString(finalDMG), "-xml", - normalizedAbsolutePathString(licenseFile()) + normalizedAbsolutePathString(licensePListFile()) ).retry() .setMaxAttemptsCount(10) .setAttemptTimeout(3, TimeUnit.SECONDS) @@ -441,6 +422,4 @@ private void convertProtoDmg() throws IOException { 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 - ID - 5007 - 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= + + STR_DATA_SIMPLIFIED_CHINESE + 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 + 5003 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 - - TMPL - - + Attributes 0x0000 Data - E0RlZmF1bHQgTGFuZ3VhZ2UgSUREV1JEBUNvdW50T0NOVAQqKioqTFNUQwtzeXMgbGFuZyBJRERXUkQebG9jYWwgcmVzIElEIChvZmZzZXQgZnJvbSA1MDAwRFdSRBAyLWJ5dGUgbGFuZ3VhZ2U/RFdSRAQqKioqTFNURQ== + + APPLICATION_LICENSE_TEXT + ID - 128 + 5001 Name - LPic + German SLA - - plst - Attributes - 0x0050 + 0x0000 Data - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + + APPLICATION_LICENSE_TEXT + ID - 0 + 5002 Name - + Japanese SLA - - 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 Charset charset() { 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 @@ * * 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 static void testCommon() { LICENSE_FILE)); }); + initMacDmgLicenseVerifier(test.forTypes(PackageType.MAC_DMG)); initLinuxLicenseVerifier(test.forTypes(PackageType.LINUX)); test.run(); @@ -131,6 +135,33 @@ public static void testCustomDebianCopyrightSubst() { 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 111/113] 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 112/113] 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 @@ class XorLNode : public AddNode { //------------------------------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 @@ class MaxNode : public AddNode { //------------------------------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 @@ class MaxINode : public MaxNode { //------------------------------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 @@ class MinINode : public MaxNode { //------------------------------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 @@ class MaxLNode : public MaxNode { //------------------------------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 @@ class MinLNode : public MaxNode { //------------------------------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 @@ class MaxFNode : public MaxNode { //------------------------------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 @@ class MinFNode : public MaxNode { //------------------------------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 @@ class MaxHFNode : public MaxNode { //------------------------------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 @@ class MinHFNode : public MaxNode { //------------------------------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 @@ class MaxDNode : public MaxNode { //------------------------------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 @@ class Node { 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 @@ class Node { 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 113/113] 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 @@ class Register { 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 @@ class Register { // 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 @@ class FloatRegister { 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 @@ class XMMRegister { 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 @@ class XMMRegister { // 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 {