From 0a1fa219214b985e4c7d9e612bd5cda1b0f25577 Mon Sep 17 00:00:00 2001 From: Chad Rakoczy Date: Thu, 8 Jan 2026 01:14:01 +0000 Subject: [PATCH 01/24] 8369150: NMethodRelocationTest fails when JVMTI events not published before JVM exit Reviewed-by: lmesnik, sspitsyn --- test/hotspot/jtreg/ProblemList.txt | 2 - .../NMethodRelocationTest.java | 119 ++++-------------- .../libNMethodRelocationTest.cpp | 115 +++++++++++------ test/lib/jdk/test/lib/jvmti/jvmti_common.hpp | 17 +++ 4 files changed, 121 insertions(+), 132 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 4e16e328ab4..13e1ea30a34 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -77,8 +77,6 @@ compiler/interpreter/Test6833129.java 8335266 generic-i586 compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 -serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java 8369150 generic-all - ############################################################################# # :hotspot_gc diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java index 6c465b357d7..10888dce1b4 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/NMethodRelocationTest.java @@ -23,23 +23,30 @@ /* * @test - * * @bug 8316694 * @summary Verify that nmethod relocation posts the correct JVMTI events - * @requires vm.jvmti - * @requires vm.gc == "null" | vm.gc == "Serial" + * @requires vm.jvmti & + * vm.gc != "Epsilon" & + * vm.flavor == "server" & + * !vm.emulatedClient & + * (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) * @library /test/lib /test/hotspot/jtreg * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm/native NMethodRelocationTest + * @run main/othervm/native -agentlib:NMethodRelocationTest + * --enable-native-access=ALL-UNNAMED + * -Xbootclasspath/a:. + * -Xbatch + * -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * -XX:+SegmentedCodeCache + * -XX:-TieredCompilation + * -XX:+UnlockExperimentalVMOptions + * -XX:+NMethodRelocation + * NMethodRelocationTest */ -import static compiler.whitebox.CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION; - import java.lang.reflect.Executable; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import jdk.test.lib.Asserts; import jdk.test.lib.process.OutputAnalyzer; @@ -48,52 +55,9 @@ import jdk.test.whitebox.WhiteBox; import jdk.test.whitebox.code.BlobType; import jdk.test.whitebox.code.NMethod; +import static compiler.whitebox.CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION; public class NMethodRelocationTest { - public static void main(String[] args) throws Exception { - ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder( - "-agentlib:NMethodRelocationTest", - "--enable-native-access=ALL-UNNAMED", - "-Xbootclasspath/a:.", - "-XX:+UseSerialGC", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+WhiteBoxAPI", - "-XX:+SegmentedCodeCache", - "-XX:-TieredCompilation", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+NMethodRelocation", - "DoWork"); - - OutputAnalyzer oa = new OutputAnalyzer(pb.start()); - String output = oa.getOutput(); - if (oa.getExitValue() != 0) { - System.err.println(oa.getOutput()); - throw new RuntimeException("Non-zero exit code returned from the test"); - } - Asserts.assertTrue(oa.getExitValue() == 0); - - Pattern pattern = Pattern.compile("(?m)^Relocated nmethod from (0x[0-9a-f]{16}) to (0x[0-9a-f]{16})$"); - Matcher matcher = pattern.matcher(output); - - if (matcher.find()) { - String fromAddr = matcher.group(1); - String toAddr = matcher.group(2); - - // Confirm events sent for both original and relocated nmethod - oa.shouldContain(": name: compiledMethod, code: " + fromAddr); - oa.shouldContain(": name: compiledMethod, code: " + toAddr); - oa.shouldContain(": name: compiledMethod, code: " + fromAddr); - oa.shouldContain(": name: compiledMethod, code: " + toAddr); - } else { - System.err.println(oa.getOutput()); - throw new RuntimeException("Unable to find relocation information"); - } - } -} - -class DoWork { - - protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); /** Load native library if required. */ static { @@ -107,43 +71,18 @@ class DoWork { } } - /** - * Returns value of VM option. - * - * @param name option's name - * @return value of option or {@code null}, if option doesn't exist - * @throws NullPointerException if name is null - */ - protected static String getVMOption(String name) { - Objects.requireNonNull(name); - return Objects.toString(WHITE_BOX.getVMFlag(name), null); - } + protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); - /** - * Returns value of VM option or default value. - * - * @param name option's name - * @param defaultValue default value - * @return value of option or {@code defaultValue}, if option doesn't exist - * @throws NullPointerException if name is null - * @see #getVMOption(String) - */ - protected static String getVMOption(String name, String defaultValue) { - String result = getVMOption(name); - return result == null ? defaultValue : result; - } + native static boolean shouldExit(); - public static void main(String argv[]) throws Exception { - run(); - } - - public static void run() throws Exception { - Executable method = DoWork.class.getDeclaredMethod("compiledMethod"); + public static void main(String[] argv) throws Exception { + Executable method = NMethodRelocationTest.class.getDeclaredMethod("compiledMethod"); WHITE_BOX.testSetDontInlineMethod(method, true); WHITE_BOX.enqueueMethodForCompilation(method, COMP_LEVEL_FULL_OPTIMIZATION); - while (WHITE_BOX.isMethodQueuedForCompilation(method)) { - Thread.onSpinWait(); + + if (!WHITE_BOX.isMethodCompiled(method)) { + throw new AssertionError("Method not compiled"); } NMethod originalNMethod = NMethod.get(method, false); @@ -164,13 +103,9 @@ class DoWork { WHITE_BOX.deoptimizeAll(); - WHITE_BOX.fullGC(); - WHITE_BOX.fullGC(); - - WHITE_BOX.lockCompilation(); - - System.out.printf("Relocated nmethod from 0x%016x to 0x%016x%n", originalNMethod.code_begin, relocatedNMethod.code_begin); - System.out.flush(); + while (!shouldExit()) { + WHITE_BOX.fullGC(); + } } public static long compiledMethod() { diff --git a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp index 41ba6b10608..7f8ab7b78bc 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/NMethodRelocation/libNMethodRelocationTest.cpp @@ -21,10 +21,20 @@ * questions. */ +#include #include -#include -#include -#include +#include "jvmti_common.hpp" + +extern "C" { + +// Track nmethod addresses for LOAD and UNLOAD events +static const void* first_load_addr = nullptr; +static const void* second_load_addr = nullptr; +static const void* first_unload_addr = nullptr; +static const void* second_unload_addr = nullptr; + +// Keep track of test completion +static std::atomic should_exit{false}; /** * Callback for COMPILED_METHOD_LOAD event. @@ -34,18 +44,27 @@ callbackCompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method, jint code_size, const void* code_addr, jint map_length, const jvmtiAddrLocationMap* map, const void* compile_info) { - char* name = nullptr; - char* sig = nullptr; - if (jvmti->GetMethodName(method, &name, &sig, nullptr) != JVMTI_ERROR_NONE) { - printf(" [Could not retrieve method name]\n"); - fflush(stdout); + // Only track events for "compiledMethod" + char* name = get_method_name(jvmti, method); + if (strcmp(name, "compiledMethod") != 0) { return; } - printf(": name: %s, code: 0x%016" PRIxPTR "\n", - name, (uintptr_t)code_addr); - fflush(stdout); + LOG(": name: %s, code: 0x%016" PRIxPTR "\n", name, (uintptr_t)code_addr); + + if (first_load_addr == nullptr) { + first_load_addr = code_addr; + } else if (second_load_addr == nullptr) { + second_load_addr = code_addr; + + // Verify that the addresses are different + if (first_load_addr == second_load_addr) { + fatal("Load events for 'compiledMethod' are expected to use different addresses"); + } + } else { + fatal("Received too many load events for 'compiledMethod'"); + } } /** @@ -54,25 +73,50 @@ callbackCompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method, JNIEXPORT void JNICALL callbackCompiledMethodUnload(jvmtiEnv* jvmti, jmethodID method, const void* code_addr) { - char* name = nullptr; - char* sig = nullptr; - if (jvmti->GetMethodName(method, &name, &sig, nullptr) != JVMTI_ERROR_NONE) { - printf(" [Could not retrieve method name]\n"); - fflush(stdout); + // Only track events for "compiledMethod" + char* name = get_method_name(jvmti, method); + if (strcmp(name, "compiledMethod") != 0) { return; } - printf(": name: %s, code: 0x%016" PRIxPTR "\n", - name, (uintptr_t)code_addr); - fflush(stdout); + + LOG(": name: %s, code: 0x%016" PRIxPTR "\n", name, (uintptr_t)code_addr); + + // Validate both loads have occurred + if (first_load_addr == nullptr || second_load_addr == nullptr) { + fatal("UNLOAD event for 'compiledMethod' occurred before both LOAD events"); + } + + if (first_unload_addr == nullptr) { + first_unload_addr = code_addr; + } else if (second_unload_addr == nullptr) { + second_unload_addr = code_addr; + + // Verify that the addresses are different + if (first_unload_addr == second_unload_addr) { + fatal("Unload events for 'compiledMethod' are expected to use different addresses"); + } + + // LOAD and UNLOAD events should report the same two addresses, but the order of + // the UNLOADs is not guaranteed, since the GC may unload either nmethod first. + if ((first_load_addr == first_unload_addr && second_load_addr == second_unload_addr) || + (first_load_addr == second_unload_addr && second_load_addr == first_unload_addr)) { + + // Update should_exit to signal test completion + should_exit.store(true); + } else { + fatal("Address mismatch for 'compiledMethod' events"); + } + } else { + fatal("Received too many unload events for 'compiledMethod'"); + } } JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { jvmtiEnv* jvmti = nullptr; - jvmtiError error; if (jvm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_0) != JNI_OK) { - printf("Unable to access JVMTI!\n"); + LOG("Unable to access JVMTI!\n"); return JNI_ERR; } @@ -80,11 +124,8 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) jvmtiCapabilities caps; memset(&caps, 0, sizeof(caps)); caps.can_generate_compiled_method_load_events = 1; - error = jvmti->AddCapabilities(&caps); - if (error != JVMTI_ERROR_NONE) { - printf("ERROR: Unable to add capabilities, error=%d\n", error); - return JNI_ERR; - } + jvmtiError error = jvmti->AddCapabilities(&caps); + check_jvmti_error(error, "Unable to add capabilities"); // Set event callbacks jvmtiEventCallbacks eventCallbacks; @@ -92,23 +133,21 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) eventCallbacks.CompiledMethodLoad = callbackCompiledMethodLoad; eventCallbacks.CompiledMethodUnload = callbackCompiledMethodUnload; error = jvmti->SetEventCallbacks(&eventCallbacks, sizeof(eventCallbacks)); - if (error != JVMTI_ERROR_NONE) { - printf("ERROR: Unable to set event callbacks, error=%d\n", error); - return JNI_ERR; - } + check_jvmti_error(error, "Unable to set event callbacks"); // Enable events error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, nullptr); - if (error != JVMTI_ERROR_NONE) { - printf("ERROR: Unable to enable COMPILED_METHOD_LOAD event, error=%d\n", error); - return JNI_ERR; - } + check_jvmti_error(error, "Unable to enable COMPILED_METHOD_LOAD event"); error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, nullptr); - if (error != JVMTI_ERROR_NONE) { - printf("ERROR: Unable to enable COMPILED_METHOD_UNLOAD event, error=%d\n", error); - return JNI_ERR; - } + check_jvmti_error(error, "Unable to enable COMPILED_METHOD_UNLOAD event"); return JNI_OK; } + +JNIEXPORT jboolean JNICALL +Java_NMethodRelocationTest_shouldExit(JNIEnv *env, jclass cls) { + return should_exit.load(); +} + +} diff --git a/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp b/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp index 11600bd524b..3832d934b1e 100644 --- a/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp +++ b/test/lib/jdk/test/lib/jvmti/jvmti_common.hpp @@ -123,6 +123,12 @@ char* julong_to_string(julong value, char *string) { return string; } +static void +fatal(const char* msg) { + LOG("FATAL ERROR: %s\n", msg); + abort(); +} + static void fatal(JNIEnv* jni, const char* msg) { jni->FatalError(msg); @@ -313,6 +319,17 @@ get_thread_name(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread) { return tname; } +static char* +get_method_name(jvmtiEnv *jvmti, jmethodID method) { + char* mname = nullptr; + jvmtiError err; + + err = jvmti->GetMethodName(method, &mname, nullptr, nullptr); + check_jvmti_error(err, "get_method_name: error in JVMTI GetMethodName call"); + + return mname; +} + static char* get_method_name(jvmtiEnv *jvmti, JNIEnv* jni, jmethodID method) { char* mname = nullptr; From 70669d0585c708e04befe0f9ba945f6154f9afec Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Thu, 8 Jan 2026 04:43:06 +0000 Subject: [PATCH 02/24] 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 03/24] 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 04/24] 8374434: Several JShell tests report JUnit discovery warnings Reviewed-by: jpai --- test/langtools/jdk/jshell/ErrorTranslationTest.java | 3 +-- test/langtools/jdk/jshell/IdGeneratorTest.java | 3 +-- test/langtools/jdk/jshell/KullaCompletenessStressTest.java | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/test/langtools/jdk/jshell/ErrorTranslationTest.java b/test/langtools/jdk/jshell/ErrorTranslationTest.java index 1aa1b2c41dd..235f4443004 100644 --- a/test/langtools/jdk/jshell/ErrorTranslationTest.java +++ b/test/langtools/jdk/jshell/ErrorTranslationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -149,7 +149,6 @@ public class ErrorTranslationTest extends ReplToolTesting { return sb.toString(); } - @Test public String getKind(Diagnostic.Kind kind) { switch (kind) { case WARNING: diff --git a/test/langtools/jdk/jshell/IdGeneratorTest.java b/test/langtools/jdk/jshell/IdGeneratorTest.java index 6c7f6177e03..521c87b0265 100644 --- a/test/langtools/jdk/jshell/IdGeneratorTest.java +++ b/test/langtools/jdk/jshell/IdGeneratorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,6 @@ import org.junit.jupiter.api.Test; public class IdGeneratorTest { - @Test public JShell.Builder getBuilder() { TestingInputStream inStream = new TestingInputStream(); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); diff --git a/test/langtools/jdk/jshell/KullaCompletenessStressTest.java b/test/langtools/jdk/jshell/KullaCompletenessStressTest.java index 69830880dcb..16274fa924e 100644 --- a/test/langtools/jdk/jshell/KullaCompletenessStressTest.java +++ b/test/langtools/jdk/jshell/KullaCompletenessStressTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,13 +32,11 @@ import java.io.File; import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class KullaCompletenessStressTest extends CompletenessStressTest { @Override - @Test public File[] getDirectoriesToTest() { String src = System.getProperty("test.src"); File file; From 1a6da4499cf8805ff3e1e517fbca81c2eeb987a9 Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Thu, 8 Jan 2026 08:14:57 +0000 Subject: [PATCH 05/24] 8374467: Incorrect ranges in jdk.internal.util.ByteArray JavaDoc Reviewed-by: rriggs --- .../share/classes/jdk/internal/util/ByteArray.java | 14 +++++++------- .../jdk/internal/util/ByteArrayLittleEndian.java | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArray.java b/src/java.base/share/classes/jdk/internal/util/ByteArray.java index 94395df2c34..92b9dbbef19 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArray.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -241,7 +241,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length] + * the range [0, array.length - 1] * @see #getBoolean(byte[], int) */ public static void setBoolean(byte[] array, int offset, boolean value) { @@ -328,7 +328,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 4] * @see #getFloat(byte[], int) */ public static void setFloat(byte[] array, int offset, float value) { @@ -350,7 +350,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 4] * @see #getFloatRaw(byte[], int) */ public static void setFloatRaw(byte[] array, int offset, float value) { @@ -368,7 +368,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 4] + * the range [0, array.length - 8] * @see #getLong(byte[], int) */ public static void setLong(byte[] array, int offset, long value) { @@ -387,7 +387,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 8] * @see #getDouble(byte[], int) */ public static void setDouble(byte[] array, int offset, double value) { @@ -409,7 +409,7 @@ public final class ByteArray { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 8] * @see #getDoubleRaw(byte[], int) */ public static void setDoubleRaw(byte[] array, int offset, double value) { diff --git a/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java b/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java index fcd7ca2b9bf..cc50686429b 100644 --- a/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java +++ b/src/java.base/share/classes/jdk/internal/util/ByteArrayLittleEndian.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -241,7 +241,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length] + * the range [0, array.length - 1] * @see #getBoolean(byte[], int) */ public static void setBoolean(byte[] array, int offset, boolean value) { @@ -328,7 +328,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 4] * @see #getFloat(byte[], int) */ public static void setFloat(byte[] array, int offset, float value) { @@ -350,7 +350,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 4] * @see #getFloatRaw(byte[], int) */ public static void setFloatRaw(byte[] array, int offset, float value) { @@ -368,7 +368,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 4] + * the range [0, array.length - 8] * @see #getLong(byte[], int) */ public static void setLong(byte[] array, int offset, long value) { @@ -387,7 +387,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 8] * @see #getDouble(byte[], int) */ public static void setDouble(byte[] array, int offset, double value) { @@ -409,7 +409,7 @@ public final class ByteArrayLittleEndian { * @param offset where setting (writing) in the array should begin * @param value value to set in the array * @throws IndexOutOfBoundsException if the provided {@code offset} is outside - * the range [0, array.length - 2] + * the range [0, array.length - 8] * @see #getDoubleRaw(byte[], int) */ public static void setDoubleRaw(byte[] array, int offset, double value) { From a71326a0e2660158fdb85282da4b59ce61c66ee3 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 8 Jan 2026 08:32:02 +0000 Subject: [PATCH 06/24] 8374528: C2 SuperWord: TestAliasingFuzzer.java strengthen no-multiversioning IR rule Reviewed-by: chagedorn, mhaessig --- .../superword/TestAliasingFuzzer.java | 45 +++++++------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java index 5d20ce659b9..825d5ca60bc 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestAliasingFuzzer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1052,35 +1052,20 @@ public class TestAliasingFuzzer { case Aliasing.CONTAINER_DIFFERENT, Aliasing.CONTAINER_SAME_ALIASING_NEVER, Aliasing.CONTAINER_UNKNOWN_ALIASING_NEVER -> - // We would have liked to check that there is no multiversioning. - // - // But sadly there are some cases that have issues with RCE and/or - // predicates, and so we end up using multiversioning anyway. We - // should fix those cases eventually, to strengthen the checks here. - // - // The array cases are a little more tame, and do not have the same - // issues as the MemorySegment cases. - (containerKind == ContainerKind.ARRAY) - ? """ - // Aliasing check should never fail at runtime, so the predicate - // should never fail, and we do not have to use multiversioning. - // Failure could have a few causes: - // - issues with doing RCE / missing predicates - // -> other loop-opts need to be fixed - // - predicate fails: recompile with multiversioning - // -> logic in runtime check may be wrong - @IR(counts = {".*multiversion.*", "= 0"}, - phase = CompilePhase.PRINT_IDEAL, - applyIf = {"UseAutoVectorizationPredicate", "true"}, - applyIfPlatform = {"64-bit", "true"}, - applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) - """ - : """ - // Due to cases like JDK-8360204 and JDK-8365982, there can be issues - // with RCE leading cases where we remove predicates and then unroll again - // and then end up multiversioning. These cases seem relatively rare but - // prevent us from asserting that there is never multiversioning in these cases. - """; + """ + // Aliasing check should never fail at runtime, so the predicate + // should never fail, and we do not have to use multiversioning. + // Failure could have a few causes: + // - issues with doing RCE / missing predicates + // -> other loop-opts need to be fixed + // - predicate fails: recompile with multiversioning + // -> logic in runtime check may be wrong + @IR(counts = {".*multiversion.*", "= 0"}, + phase = CompilePhase.PRINT_IDEAL, + applyIf = {"UseAutoVectorizationPredicate", "true"}, + applyIfPlatform = {"64-bit", "true"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + """; case Aliasing.CONTAINER_SAME_ALIASING_UNKNOWN, Aliasing.CONTAINER_UNKNOWN_ALIASING_UNKNOWN -> """ From 08ff16f0aa8eaa9596da52d568720c69c897f3c5 Mon Sep 17 00:00:00 2001 From: Ramkumar Sunderbabu Date: Thu, 8 Jan 2026 09:25:11 +0000 Subject: [PATCH 07/24] 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 08/24] 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 09/24] 8374718: Generation of CompilerProperties can fail in subtle ways Reviewed-by: jlahoda --- .../tools/propertiesparser/gen/ClassGenerator.java | 9 +++++++-- .../resources/templates.properties | 2 +- .../classes/com/sun/tools/javac/code/Lint.java | 14 +++++++++----- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/make/langtools/tools/propertiesparser/gen/ClassGenerator.java b/make/langtools/tools/propertiesparser/gen/ClassGenerator.java index 247537b4676..14e8c4fb00a 100644 --- a/make/langtools/tools/propertiesparser/gen/ClassGenerator.java +++ b/make/langtools/tools/propertiesparser/gen/ClassGenerator.java @@ -286,7 +286,7 @@ public class ClassGenerator { diagnosticFlags.isEmpty() ? StubKind.DIAGNOSTIC_FLAGS_EMPTY.format() : StubKind.DIAGNOSTIC_FLAGS_NON_EMPTY.format(diagnosticFlags), - StubKind.LINT_CATEGORY.format("\"" + lintCategory + "\""), + StubKind.LINT_CATEGORY.format(toLintFieldName(lintCategory)), "\"" + keyParts[0] + "\"", "\"" + Stream.of(keyParts).skip(2).collect(Collectors.joining(".")) + "\"", javadoc); @@ -314,7 +314,7 @@ public class ClassGenerator { diagnosticFlags.isEmpty() ? StubKind.DIAGNOSTIC_FLAGS_EMPTY.format() : StubKind.DIAGNOSTIC_FLAGS_NON_EMPTY.format(diagnosticFlags), - StubKind.LINT_CATEGORY.format("\"" + lintCategory + "\""), + StubKind.LINT_CATEGORY.format(toLintFieldName(lintCategory)), "\"" + keyParts[0] + "\"", "\"" + Stream.of(keyParts).skip(2).collect(Collectors.joining(".")) + "\"", argNames.stream().collect(Collectors.joining(", "))); @@ -329,6 +329,11 @@ public class ClassGenerator { } } + String toLintFieldName(String lintCategory) { + return lintCategory.toUpperCase() + .replaceAll("-", "_"); + } + /** * Form the name of a factory method/field given a resource key. */ diff --git a/make/langtools/tools/propertiesparser/resources/templates.properties b/make/langtools/tools/propertiesparser/resources/templates.properties index 81a9be2552c..f8ff07a878f 100644 --- a/make/langtools/tools/propertiesparser/resources/templates.properties +++ b/make/langtools/tools/propertiesparser/resources/templates.properties @@ -87,7 +87,7 @@ suppress.warnings=\ @SuppressWarnings("rawtypes")\n lint.category=\ - LintCategory.get({0}).get() + LintCategory.{0} diagnostic.flags.empty=\ EnumSet.noneOf(DiagnosticFlag.class) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java index 3a8b4e5dbea..773c573c201 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -37,11 +37,7 @@ import java.util.Set; import java.util.stream.Stream; import com.sun.tools.javac.main.Option; -import com.sun.tools.javac.tree.JCTree.*; -import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; -import com.sun.tools.javac.util.JCDiagnostic.LintWarning; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Options; @@ -178,6 +174,14 @@ public class Lint { /** * Categories of warnings that can be generated by the compiler. + * Each lint category has a logical name (a string), which is the string used e.g. in a {@code SuppressWarning} annotation. + * To ensure automation, the enum field name for a lint category string {@code C} should be obtained by: + *
    + *
  1. capitalize all the letters in {@code C}, and
  2. + *
  3. replacing any occurrence of {@code -} with {@code _}
  4. + *
+ * For instance, the lint category string {@code dangling-doc-comments} corresponds to the enum field + * {@code DANGLING_DOC_COMMENTS}. */ public enum LintCategory { /** @@ -320,7 +324,7 @@ public class Lint { /** * Warn about unchecked operations on raw types. */ - RAW("rawtypes"), + RAWTYPES("rawtypes"), /** * Warn about use of deprecated-for-removal items. From c5159fc9fa0fd81dec629cd821b3411b4a6df967 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Thu, 8 Jan 2026 11:07:08 +0000 Subject: [PATCH 10/24] 8374328: Convert simple AtomicAccess uses in gc/shared to use Atomic Reviewed-by: dholmes, tschatzl --- .../share/gc/shared/barrierSetNMethod.cpp | 7 +++--- .../share/gc/shared/concurrentGCThread.cpp | 14 +++++------ .../share/gc/shared/concurrentGCThread.hpp | 7 +++--- src/hotspot/share/gc/shared/gcLocker.cpp | 23 ++++++++----------- src/hotspot/share/gc/shared/gcLocker.hpp | 7 +++--- .../share/gc/shared/gcLocker.inline.hpp | 8 +++---- src/hotspot/share/gc/shared/pretouchTask.cpp | 8 +++---- src/hotspot/share/gc/shared/pretouchTask.hpp | 6 +++-- .../share/gc/shared/suspendibleThreadSet.cpp | 8 +++---- .../share/gc/shared/suspendibleThreadSet.hpp | 8 +++---- src/hotspot/share/gc/shared/workerThread.cpp | 15 +++++++----- src/hotspot/share/gc/shared/workerThread.hpp | 7 +++--- 12 files changed, 62 insertions(+), 56 deletions(-) diff --git a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp index cb5d6b5a886..ab94bae079a 100644 --- a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp +++ b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ #include "memory/universe.hpp" #include "oops/access.inline.hpp" #include "oops/method.inline.hpp" +#include "runtime/atomic.hpp" #include "runtime/frame.inline.hpp" #include "runtime/javaThread.hpp" #include "runtime/threads.hpp" @@ -196,8 +197,8 @@ int BarrierSetNMethod::nmethod_stub_entry_barrier(address* return_address_ptr) { // Diagnostic option to force deoptimization 1 in 10 times. It is otherwise // a very rare event. if (DeoptimizeNMethodBarriersALot && !nm->is_osr_method()) { - static volatile uint32_t counter=0; - if (AtomicAccess::add(&counter, 1u) % 10 == 0) { + static Atomic counter{0}; + if (counter.add_then_fetch(1u) % 10 == 0) { may_enter = false; } } diff --git a/src/hotspot/share/gc/shared/concurrentGCThread.cpp b/src/hotspot/share/gc/shared/concurrentGCThread.cpp index ac281f82f8a..ed6c1b4d283 100644 --- a/src/hotspot/share/gc/shared/concurrentGCThread.cpp +++ b/src/hotspot/share/gc/shared/concurrentGCThread.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ */ #include "gc/shared/concurrentGCThread.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/init.hpp" #include "runtime/jniHandles.hpp" #include "runtime/mutexLocker.hpp" @@ -48,7 +48,7 @@ void ConcurrentGCThread::run() { // Signal thread has terminated MonitorLocker ml(Terminator_lock); - AtomicAccess::release_store(&_has_terminated, true); + _has_terminated.release_store(true); ml.notify_all(); } @@ -57,21 +57,21 @@ void ConcurrentGCThread::stop() { assert(!has_terminated(), "Invalid state"); // Signal thread to terminate - AtomicAccess::release_store_fence(&_should_terminate, true); + _should_terminate.release_store_fence(true); stop_service(); // Wait for thread to terminate MonitorLocker ml(Terminator_lock); - while (!_has_terminated) { + while (!_has_terminated.load_relaxed()) { ml.wait(); } } bool ConcurrentGCThread::should_terminate() const { - return AtomicAccess::load_acquire(&_should_terminate); + return _should_terminate.load_acquire(); } bool ConcurrentGCThread::has_terminated() const { - return AtomicAccess::load_acquire(&_has_terminated); + return _has_terminated.load_acquire(); } diff --git a/src/hotspot/share/gc/shared/concurrentGCThread.hpp b/src/hotspot/share/gc/shared/concurrentGCThread.hpp index 630abeaeb9f..0c764546045 100644 --- a/src/hotspot/share/gc/shared/concurrentGCThread.hpp +++ b/src/hotspot/share/gc/shared/concurrentGCThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,14 +25,15 @@ #ifndef SHARE_GC_SHARED_CONCURRENTGCTHREAD_HPP #define SHARE_GC_SHARED_CONCURRENTGCTHREAD_HPP +#include "runtime/atomic.hpp" #include "runtime/javaThread.hpp" #include "runtime/nonJavaThread.hpp" #include "utilities/debug.hpp" class ConcurrentGCThread: public NamedThread { private: - volatile bool _should_terminate; - volatile bool _has_terminated; + Atomic _should_terminate; + Atomic _has_terminated; protected: void create_and_start(ThreadPriority prio = NearMaxPriority); diff --git a/src/hotspot/share/gc/shared/gcLocker.cpp b/src/hotspot/share/gc/shared/gcLocker.cpp index 01d17b1117d..898588e6b06 100644 --- a/src/hotspot/share/gc/shared/gcLocker.cpp +++ b/src/hotspot/share/gc/shared/gcLocker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ #include "logging/log.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaThread.inline.hpp" #include "runtime/safepoint.hpp" @@ -60,16 +60,13 @@ public: }; Monitor* GCLocker::_lock; -volatile bool GCLocker::_is_gc_request_pending; +Atomic GCLocker::_is_gc_request_pending{false}; -DEBUG_ONLY(uint64_t GCLocker::_verify_in_cr_count;) +DEBUG_ONLY(Atomic GCLocker::_verify_in_cr_count{0};) void GCLocker::initialize() { assert(JNICritical_lock != nullptr, "inv"); _lock = JNICritical_lock; - _is_gc_request_pending = false; - - DEBUG_ONLY(_verify_in_cr_count = 0;) } bool GCLocker::is_active() { @@ -84,11 +81,11 @@ bool GCLocker::is_active() { void GCLocker::block() { // _lock is held from the beginning of block() to the end of of unblock(). _lock->lock(); - assert(AtomicAccess::load(&_is_gc_request_pending) == false, "precondition"); + assert(_is_gc_request_pending.load_relaxed() == false, "precondition"); GCLockerTimingDebugLogger logger("Thread blocked to start GC."); - AtomicAccess::store(&_is_gc_request_pending, true); + _is_gc_request_pending.store_relaxed(true); // The _is_gc_request_pending and _jni_active_critical (inside // in_critical_atomic()) variables form a Dekker duality. On the GC side, the @@ -112,14 +109,14 @@ void GCLocker::block() { #ifdef ASSERT // Matching the storestore in GCLocker::exit. OrderAccess::loadload(); - assert(AtomicAccess::load(&_verify_in_cr_count) == 0, "inv"); + assert(_verify_in_cr_count.load_relaxed() == 0, "inv"); #endif } void GCLocker::unblock() { - assert(AtomicAccess::load(&_is_gc_request_pending) == true, "precondition"); + assert(_is_gc_request_pending.load_relaxed() == true, "precondition"); - AtomicAccess::store(&_is_gc_request_pending, false); + _is_gc_request_pending.store_relaxed(false); _lock->unlock(); } @@ -139,7 +136,7 @@ void GCLocker::enter_slow(JavaThread* current_thread) { // Same as fast path. OrderAccess::fence(); - if (!AtomicAccess::load(&_is_gc_request_pending)) { + if (!_is_gc_request_pending.load_relaxed()) { return; } diff --git a/src/hotspot/share/gc/shared/gcLocker.hpp b/src/hotspot/share/gc/shared/gcLocker.hpp index c0e28b66539..be3c0c3593e 100644 --- a/src/hotspot/share/gc/shared/gcLocker.hpp +++ b/src/hotspot/share/gc/shared/gcLocker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "gc/shared/gcCause.hpp" #include "memory/allStatic.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutex.hpp" // GCLocker provides synchronization between the garbage collector (GC) and @@ -43,11 +44,11 @@ class GCLocker: public AllStatic { static Monitor* _lock; - static volatile bool _is_gc_request_pending; + static Atomic _is_gc_request_pending; #ifdef ASSERT // Debug-only: to track the number of java threads in critical-region. - static uint64_t _verify_in_cr_count; + static Atomic _verify_in_cr_count; #endif static void enter_slow(JavaThread* current_thread); diff --git a/src/hotspot/share/gc/shared/gcLocker.inline.hpp b/src/hotspot/share/gc/shared/gcLocker.inline.hpp index 050b9570280..135bd9eab62 100644 --- a/src/hotspot/share/gc/shared/gcLocker.inline.hpp +++ b/src/hotspot/share/gc/shared/gcLocker.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,13 +38,13 @@ void GCLocker::enter(JavaThread* current_thread) { // Matching the fence in GCLocker::block. OrderAccess::fence(); - if (AtomicAccess::load(&_is_gc_request_pending)) { + if (_is_gc_request_pending.load_relaxed()) { current_thread->exit_critical(); // slow-path enter_slow(current_thread); } - DEBUG_ONLY(AtomicAccess::add(&_verify_in_cr_count, (uint64_t)1);) + DEBUG_ONLY(_verify_in_cr_count.add_then_fetch(1u);) } else { current_thread->enter_critical(); } @@ -55,7 +55,7 @@ void GCLocker::exit(JavaThread* current_thread) { #ifdef ASSERT if (current_thread->in_last_critical()) { - AtomicAccess::add(&_verify_in_cr_count, (uint64_t)-1); + _verify_in_cr_count.sub_then_fetch(1u); // Matching the loadload in GCLocker::block. OrderAccess::storestore(); } diff --git a/src/hotspot/share/gc/shared/pretouchTask.cpp b/src/hotspot/share/gc/shared/pretouchTask.cpp index cc84c8c449d..c999c98ea99 100644 --- a/src/hotspot/share/gc/shared/pretouchTask.cpp +++ b/src/hotspot/share/gc/shared/pretouchTask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,7 @@ #include "gc/shared/gc_globals.hpp" #include "gc/shared/pretouchTask.hpp" #include "logging/log.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "runtime/os.hpp" #include "utilities/align.hpp" @@ -52,11 +52,11 @@ size_t PretouchTask::chunk_size() { void PretouchTask::work(uint worker_id) { while (true) { - char* cur_start = AtomicAccess::load(&_cur_addr); + char* cur_start = _cur_addr.load_relaxed(); char* cur_end = cur_start + MIN2(_chunk_size, pointer_delta(_end_addr, cur_start, 1)); if (cur_start >= cur_end) { break; - } else if (cur_start == AtomicAccess::cmpxchg(&_cur_addr, cur_start, cur_end)) { + } else if (cur_start == _cur_addr.compare_exchange(cur_start, cur_end)) { os::pretouch_memory(cur_start, cur_end, _page_size); } // Else attempt to claim chunk failed, so try again. } diff --git a/src/hotspot/share/gc/shared/pretouchTask.hpp b/src/hotspot/share/gc/shared/pretouchTask.hpp index 7c66c8b717c..6355d61edf7 100644 --- a/src/hotspot/share/gc/shared/pretouchTask.hpp +++ b/src/hotspot/share/gc/shared/pretouchTask.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,9 +26,11 @@ #define SHARE_GC_SHARED_PRETOUCH_HPP #include "gc/shared/workerThread.hpp" +#include "runtime/atomic.hpp" +#include "utilities/globalDefinitions.hpp" class PretouchTask : public WorkerTask { - char* volatile _cur_addr; + Atomic _cur_addr; char* const _end_addr; size_t _page_size; size_t _chunk_size; diff --git a/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp b/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp index 83783b31ad9..834a3d04c00 100644 --- a/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp +++ b/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ uint SuspendibleThreadSet::_nthreads = 0; uint SuspendibleThreadSet::_nthreads_stopped = 0; -volatile bool SuspendibleThreadSet::_suspend_all = false; +Atomic SuspendibleThreadSet::_suspend_all{false}; double SuspendibleThreadSet::_suspend_all_start = 0.0; static Semaphore* _synchronize_wakeup = nullptr; @@ -96,7 +96,7 @@ void SuspendibleThreadSet::synchronize() { { MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); assert(!should_yield(), "Only one at a time"); - AtomicAccess::store(&_suspend_all, true); + _suspend_all.store_relaxed(true); if (is_synchronized()) { return; } @@ -127,6 +127,6 @@ void SuspendibleThreadSet::desynchronize() { MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); assert(should_yield(), "STS not synchronizing"); assert(is_synchronized(), "STS not synchronized"); - AtomicAccess::store(&_suspend_all, false); + _suspend_all.store_relaxed(false); ml.notify_all(); } diff --git a/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp b/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp index 38568c015bc..72fca5302e7 100644 --- a/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp +++ b/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ #define SHARE_GC_SHARED_SUSPENDIBLETHREADSET_HPP #include "memory/allocation.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" // A SuspendibleThreadSet is a set of threads that can be suspended. // A thread can join and later leave the set, and periodically yield. @@ -43,7 +43,7 @@ class SuspendibleThreadSet : public AllStatic { private: static uint _nthreads; static uint _nthreads_stopped; - static volatile bool _suspend_all; + static Atomic _suspend_all; static double _suspend_all_start; static bool is_synchronized(); @@ -59,7 +59,7 @@ private: public: // Returns true if an suspension is in progress. - static bool should_yield() { return AtomicAccess::load(&_suspend_all); } + static bool should_yield() { return _suspend_all.load_relaxed(); } // Suspends the current thread if a suspension is in progress. static void yield() { diff --git a/src/hotspot/share/gc/shared/workerThread.cpp b/src/hotspot/share/gc/shared/workerThread.cpp index 3a999da59dc..7a9404a195a 100644 --- a/src/hotspot/share/gc/shared/workerThread.cpp +++ b/src/hotspot/share/gc/shared/workerThread.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,7 @@ WorkerTaskDispatcher::WorkerTaskDispatcher() : void WorkerTaskDispatcher::coordinator_distribute_task(WorkerTask* task, uint num_workers) { // No workers are allowed to read the state variables until they have been signaled. _task = task; - _not_finished = num_workers; + _not_finished.store_relaxed(num_workers); // Dispatch 'num_workers' number of tasks. _start_semaphore.signal(num_workers); @@ -51,9 +51,12 @@ void WorkerTaskDispatcher::coordinator_distribute_task(WorkerTask* task, uint nu _end_semaphore.wait(); // No workers are allowed to read the state variables after the coordinator has been signaled. - assert(_not_finished == 0, "%d not finished workers?", _not_finished); +#ifdef ASSERT + uint not_finished = _not_finished.load_relaxed(); + assert(not_finished == 0, "%u not finished workers?", not_finished); +#endif // ASSERT _task = nullptr; - _started = 0; + _started.store_relaxed(0); } void WorkerTaskDispatcher::worker_run_task() { @@ -61,7 +64,7 @@ void WorkerTaskDispatcher::worker_run_task() { _start_semaphore.wait(); // Get and set worker id. - const uint worker_id = AtomicAccess::fetch_then_add(&_started, 1u); + const uint worker_id = _started.fetch_then_add(1u); WorkerThread::set_worker_id(worker_id); // Run task. @@ -70,7 +73,7 @@ void WorkerTaskDispatcher::worker_run_task() { // Mark that the worker is done with the task. // The worker is not allowed to read the state variables after this line. - const uint not_finished = AtomicAccess::sub(&_not_finished, 1u); + const uint not_finished = _not_finished.sub_then_fetch(1u); // The last worker signals to the coordinator that all work is completed. if (not_finished == 0) { diff --git a/src/hotspot/share/gc/shared/workerThread.hpp b/src/hotspot/share/gc/shared/workerThread.hpp index d4e92797039..a1f7282abe4 100644 --- a/src/hotspot/share/gc/shared/workerThread.hpp +++ b/src/hotspot/share/gc/shared/workerThread.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "gc/shared/gcId.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "runtime/nonJavaThread.hpp" #include "runtime/semaphore.hpp" #include "utilities/debug.hpp" @@ -58,8 +59,8 @@ class WorkerTaskDispatcher { // The task currently being dispatched to the WorkerThreads. WorkerTask* _task; - volatile uint _started; - volatile uint _not_finished; + Atomic _started; + Atomic _not_finished; // Semaphore used to start the WorkerThreads. Semaphore _start_semaphore; From 78b1ca6cc14e1a92bf25cbcfb687067ac17af92b Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 8 Jan 2026 12:44:08 +0000 Subject: [PATCH 11/24] 8374711: Hotspot runtime/CommandLine/OptionsValidation/TestOptionsWithRanges fails without printing the option name Reviewed-by: mdoerr, dholmes --- .../OptionsValidation/common/optionsvalidation/JVMOption.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java b/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java index 78a508343d6..d87ca25fc3c 100644 --- a/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java +++ b/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java @@ -116,7 +116,7 @@ public abstract class JVMOption { default: throw new Error("Expected only \"int\", \"intx\", \"size_t\", " + "\"uint\", \"uintx\", \"uint64_t\", or \"double\" " - + "option types! Got " + type + " type!"); + + "option types! Got " + type + " type for option " + name + "!"); } return parameter; From ec657349ff654dcb41b9f17178aeea638329101e Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 8 Jan 2026 16:28:10 +0000 Subject: [PATCH 12/24] 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 13/24] 8372377: Test java/io/File/GetXSpace.java failed: The system cannot find the path specified Reviewed-by: alanb, jpai --- test/jdk/java/io/File/GetXSpace.java | 46 ++++++++++++++++------------ test/jdk/java/io/File/libGetXSpace.c | 11 +++---- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/test/jdk/java/io/File/GetXSpace.java b/test/jdk/java/io/File/GetXSpace.java index 96ce4ede1b2..f1529c18fb2 100644 --- a/test/jdk/java/io/File/GetXSpace.java +++ b/test/jdk/java/io/File/GetXSpace.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,6 @@ import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import jdk.test.lib.Platform; -import jdk.test.lib.Platform; import static java.lang.System.err; import static java.lang.System.out; @@ -103,16 +102,11 @@ public class GetXSpace { private final long free; private final long available; - Space(String name) { + Space(String name) throws IOException { this.name = name; long[] sizes = new long[4]; if (Platform.isWindows() && isCDDrive(name)) { - try { - getCDDriveSpace(name, sizes); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException("can't get CDDrive sizes"); - } + getCDDriveSpace(name, sizes); } else { if (getSpace(name, sizes)) System.err.println("WARNING: total space is estimated"); @@ -170,7 +164,7 @@ public class GetXSpace { return al; } - private static void compare(Space s) { + private static void compare(Space s) throws IOException { File f = new File(s.name()); long ts = f.getTotalSpace(); long fs = f.getFreeSpace(); @@ -318,7 +312,7 @@ public class GetXSpace { } } - private static int testFile(Path dir) { + private static int testFile(Path dir) throws IOException { String dirName = dir.toString(); out.format("--- Testing %s%n", dirName); compare(new Space(dir.getRoot().toString())); @@ -333,10 +327,11 @@ public class GetXSpace { return fail != 0 ? 1 : 0; } - private static int testVolumes() { + private static int testVolumes() throws IOException { out.println("--- Testing volumes"); // Find all of the partitions on the machine and verify that the sizes - // returned by File::getXSpace are equivalent to those from getSpace or getCDDriveSpace + // returned by File::getXSpace are equivalent to those from getSpace + // or getCDDriveSpace ArrayList l; try { l = paths(); @@ -350,7 +345,18 @@ public class GetXSpace { throw new RuntimeException("no partitions?"); for (var p : l) { - Space s = new Space(p); + Space s; + try { + s = new Space(p); + } catch (IOException x) { + // Avoid failing for transient file systems on Windows + if (Platform.isWindows()) { + File f = new File(p); + if (!f.exists()) + continue; + } + throw new IOException("Failure for volume " + p, x); + } compare(s); compareZeroNonExist(); compareZeroExist(); @@ -408,19 +414,19 @@ public class GetXSpace { // size[2] free space: number of free bytes in the volume // size[3] usable space: number of bytes available to the caller // - private static native boolean getSpace0(String root, long[] space); + private static native boolean getSpace0(String root, long[] space) + throws IOException; private static native boolean isCDDrive(String root); - private static boolean getSpace(String root, long[] space) { + private static boolean getSpace(String root, long[] space) + throws IOException { try { return getSpace0(root, space); - } catch (RuntimeException e) { + } catch (IOException e) { File f = new File(root); - boolean exists = f.exists(); - boolean readable = f.canRead(); System.err.printf("getSpace0 failed for %s (%s, %s)%n", - root, exists, readable); + root, f.exists(), f.canRead()); throw e; } } diff --git a/test/jdk/java/io/File/libGetXSpace.c b/test/jdk/java/io/File/libGetXSpace.c index 59805e0adcb..9297721b8f4 100644 --- a/test/jdk/java/io/File/libGetXSpace.c +++ b/test/jdk/java/io/File/libGetXSpace.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -85,7 +85,7 @@ Java_GetXSpace_getSpace0 BOOL hres = pfnGetDiskSpaceInformation(path, &diskSpaceInfo); (*env)->ReleaseStringChars(env, root, strchars); if (FAILED(hres)) { - JNU_ThrowByNameWithLastError(env, "java/lang/RuntimeException", + JNU_ThrowByNameWithLastError(env, "java/io/IOException", "GetDiskSpaceInformationW"); return totalSpaceIsEstimated; } @@ -113,7 +113,7 @@ Java_GetXSpace_getSpace0 &totalNumberOfBytes, &totalNumberOfFreeBytes); (*env)->ReleaseStringChars(env, root, strchars); if (FAILED(hres)) { - JNU_ThrowByNameWithLastError(env, "java/lang/RuntimeException", + JNU_ThrowByNameWithLastError(env, "java/io/IOException", "GetDiskFreeSpaceExW"); return totalSpaceIsEstimated; } @@ -131,8 +131,7 @@ Java_GetXSpace_getSpace0 char* chars = (char*)malloc((len + 1)*sizeof(char)); if (chars == NULL) { (*env)->ReleaseStringChars(env, root, strchars); - JNU_ThrowByNameWithLastError(env, "java/lang/RuntimeException", - "malloc"); + JNU_ThrowOutOfMemoryError(env, "malloc"); return JNI_FALSE; } @@ -146,7 +145,7 @@ Java_GetXSpace_getSpace0 int result = statfs(chars, &buf); free(chars); if (result < 0) { - JNU_ThrowByNameWithLastError(env, "java/lang/RuntimeException", + JNU_ThrowByNameWithLastError(env, "java/io/IOException", strerror(errno)); return totalSpaceIsEstimated; } From fa2eb626478806dc64fe03d8729f53f7ed26a172 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Thu, 8 Jan 2026 16:34:39 +0000 Subject: [PATCH 14/24] 8367949: JFR: MethodTrace double-counts methods that catch their own exceptions Reviewed-by: mgronlun --- .../jfr/internal/tracing/Instrumentation.java | 22 +- .../jdk/jfr/internal/tracing/Method.java | 2 +- .../jdk/jfr/internal/tracing/Transform.java | 199 ++++++++++++++++-- .../jfr/event/tracing/TestConstructors.java | 167 +++++++++++++++ .../event/tracing/TestInstrumentation.java | 4 + 5 files changed, 363 insertions(+), 31 deletions(-) create mode 100644 test/jdk/jdk/jfr/event/tracing/TestConstructors.java diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java index dbafca4ed3c..e06d361b203 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Instrumentation.java @@ -58,7 +58,13 @@ final class Instrumentation { } public void addMethod(long methodId, String name, String signature, int modification) { - modificationMap.put(name + signature, new Method(methodId, Modification.valueOf(modification), className + "::" + name)); + Method method = new Method( + methodId, + Modification.valueOf(modification), + name.equals(""), + className + "::" + name + ); + modificationMap.put(name + signature, method); } public List getMethods() { @@ -71,7 +77,7 @@ final class Instrumentation { ClassModel classModel = classFile.parse(bytecode); byte[] generated = classFile.build(classModel.thisClass().asSymbol(), classBuilder -> { for (var ce : classModel) { - if (modifyClassElement(classBuilder, ce)) { + if (modifyClassElement(classModel, classBuilder, ce)) { modified[0] = true; } else { classBuilder.with(ce); @@ -93,7 +99,7 @@ final class Instrumentation { } } - private boolean modifyClassElement(ClassBuilder classBuilder, ClassElement ce) { + private boolean modifyClassElement(ClassModel classModel, ClassBuilder classBuilder, ClassElement ce) { if (ce instanceof MethodModel mm) { String method = mm.methodName().stringValue(); String signature = mm.methodType().stringValue(); @@ -102,15 +108,15 @@ final class Instrumentation { if (tm != null) { Modification m = tm.modification(); if (m.tracing() || m.timing()) { - return modifyMethod(classBuilder, mm, tm); + return modifyMethod(classModel, classBuilder, mm, tm); } } } return false; } - private boolean modifyMethod(ClassBuilder classBuilder, MethodModel m, Method method) { - var code = m.code(); + private boolean modifyMethod(ClassModel classModel, ClassBuilder classBuilder, MethodModel methodModel, Method method) { + var code = methodModel.code(); if (code.isPresent()) { if (classLoader == null && ExcludeList.containsMethod(method.name())) { String msg = "Risk of recursion, skipping bytecode generation of " + method.name(); @@ -118,9 +124,9 @@ final class Instrumentation { return false; } MethodTransform s = MethodTransform.ofStateful( - () -> MethodTransform.transformingCode(new Transform(method)) + () -> MethodTransform.transformingCode(new Transform(classModel, code.get(), method)) ); - classBuilder.transformMethod(m, s); + classBuilder.transformMethod(methodModel, s); return true; } return false; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java index d685083153d..d85e458e9d5 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Method.java @@ -31,7 +31,7 @@ import jdk.jfr.internal.Logger; /** * Class that holds information about an instrumented method. */ -record Method(long methodId, Modification modification, String name) { +record Method(long methodId, Modification modification, boolean constructor, String name) { @Override public String toString() { return name + (modification.timing() ? " +timing" : " -timing") + (modification.tracing() ? " +tracing" : " -tracing") + " (Method ID: " + String.format("0x%08X)", methodId); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java index cd65a119cee..377eede7925 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tracing/Transform.java @@ -24,13 +24,19 @@ */ package jdk.jfr.internal.tracing; +import java.lang.classfile.ClassModel; import java.lang.classfile.CodeBuilder; import java.lang.classfile.CodeElement; +import java.lang.classfile.CodeModel; import java.lang.classfile.CodeTransform; +import java.lang.classfile.Label; import java.lang.classfile.TypeKind; +import java.lang.classfile.instruction.InvokeInstruction; import java.lang.classfile.instruction.ReturnInstruction; import java.lang.classfile.instruction.ThrowInstruction; import java.lang.constant.ClassDesc; +import java.util.ArrayList; +import java.util.List; import jdk.jfr.internal.util.Bytecode; import jdk.jfr.internal.util.Bytecode.MethodDesc; @@ -43,59 +49,208 @@ import jdk.jfr.tracing.MethodTracer; * The method ID is determined by native code. */ final class Transform implements CodeTransform { + private static class TryBlock { + Label start; + Label end; + } private static final ClassDesc METHOD_TRACER_CLASS = ClassDesc.of(MethodTracer.class.getName()); private static final MethodDesc TRACE_METHOD = MethodDesc.of("trace", "(JJ)V"); private static final MethodDesc TIMING_METHOD = MethodDesc.of("timing", "(JJ)V"); private static final MethodDesc TRACE_TIMING_METHOD = MethodDesc.of("traceTiming", "(JJ)V"); private static final MethodDesc TIMESTAMP_METHOD = MethodDesc.of("timestamp", "()J"); + private final List tryBlocks = new ArrayList<>(); + private final boolean simplifiedInstrumentation; + private final ClassModel classModel; private final Method method; private int timestampSlot = -1; - Transform(Method method) { + Transform(ClassModel classModel, CodeModel model, Method method) { this.method = method; + this.classModel = classModel; + // The JVMS (not the JLS) allows multiple mutually exclusive super/this. + // invocations in a constructor body as long as only one lies on any given + // execution path. For example, this is valid bytecode: + // + // Foo(boolean value) { + // if (value) { + // staticMethodThatMayThrow(); + // super(); + // } else { + // try { + // if (value == 0) { + // throw new Exception(""); + // } + // } catch (Throwable t) { + // throw t; + // } + // super(); + // } + // } + // + // If such a method is found, instrumentation falls back to instrumenting only + // RET and ATHROW. This can cause exceptions to be missed or counted twice. + // + // An effect of this heuristic is that constructors like the one below + // will also trigger simplified instrumentation. + // + // class Bar { + // } + // + // class Foo extends Bar { + // Foo() { + // new Bar(); + // } + // } + // + // java.lang.Object:: with zero constructor invocations should use simplified instrumentation + this.simplifiedInstrumentation = method.constructor() && constructorInvocations(model.elementList()) != 1; + } + + private int constructorInvocations(List elementList) { + int count = 0; + for (CodeElement e : elementList) { + if (isConstructorInvocation(e)) { + count++; + } + } + return count; + } + + private boolean isConstructorInvocation(CodeElement element) { + if (element instanceof InvokeInstruction inv && inv.name().equalsString("")) { + if (classModel.thisClass().equals(inv.owner())) { + return true; + } + if (classModel.superclass().isPresent()) { + return classModel.superclass().get().equals(inv.owner()); + } + } + return false; } @Override - public final void accept(CodeBuilder builder, CodeElement element) { + public void accept(CodeBuilder builder, CodeElement element) { + if (simplifiedInstrumentation) { + acceptSimplifiedInstrumentation(builder, element); + return; + } + if (method.constructor()) { + acceptConstructor(builder, element, isConstructorInvocation(element)); + } else { + acceptMethod(builder, element); + } + } + + @Override + public void atEnd(CodeBuilder builder) { + endTryBlock(builder); + for (TryBlock block : tryBlocks) { + addCatchHandler(block, builder); + } + } + + private void acceptConstructor(CodeBuilder builder, CodeElement element, boolean isConstructorInvocation) { + if (timestampSlot == -1) { + timestampSlot = invokeTimestamp(builder); + builder.lstore(timestampSlot); + if (!isConstructorInvocation) { + beginTryBlock(builder); + } + } + if (isConstructorInvocation) { + endTryBlock(builder); + builder.with(element); + beginTryBlock(builder); + return; + } + if (element instanceof ReturnInstruction) { + addTracing(builder); + } + builder.with(element); + } + + private void endTryBlock(CodeBuilder builder) { + if (tryBlocks.isEmpty()) { + return; + } + TryBlock last = tryBlocks.getLast(); + if (last.end == null) { + last.end = builder.newBoundLabel(); + } + } + + private void beginTryBlock(CodeBuilder builder) { + TryBlock block = new TryBlock(); + block.start = builder.newBoundLabel(); + tryBlocks.add(block); + } + + private void acceptSimplifiedInstrumentation(CodeBuilder builder, CodeElement element) { if (timestampSlot == -1) { timestampSlot = invokeTimestamp(builder); builder.lstore(timestampSlot); } if (element instanceof ReturnInstruction || element instanceof ThrowInstruction) { - builder.lload(timestampSlot); - builder.ldc(method.methodId()); - Modification modification = method.modification(); - boolean objectInit = method.name().equals("java.lang.Object::"); - String suffix = objectInit ? "ObjectInit" : ""; - if (modification.timing()) { - if (modification.tracing()) { - invokeTraceTiming(builder, suffix); - } else { - invokeTiming(builder, suffix); - } - } else { - if (modification.tracing()) { - invokeTrace(builder, suffix); - } - } + addTracing(builder); } builder.with(element); } - public static void invokeTiming(CodeBuilder builder, String suffix) { + private void acceptMethod(CodeBuilder builder, CodeElement element) { + if (timestampSlot == -1) { + timestampSlot = invokeTimestamp(builder); + builder.lstore(timestampSlot); + beginTryBlock(builder); + } + if (element instanceof ReturnInstruction) { + addTracing(builder); + } + builder.with(element); + } + + private void addCatchHandler(TryBlock block, CodeBuilder builder) { + Label catchHandler = builder.newBoundLabel(); + int exceptionSlot = builder.allocateLocal(TypeKind.REFERENCE); + builder.astore(exceptionSlot); + addTracing(builder); + builder.aload(exceptionSlot); + builder.athrow(); + builder.exceptionCatchAll(block.start, block.end, catchHandler); + } + + private void addTracing(CodeBuilder builder) { + builder.lload(timestampSlot); + builder.ldc(method.methodId()); + Modification modification = method.modification(); + boolean objectInit = method.name().equals("java.lang.Object::"); + String suffix = objectInit ? "ObjectInit" : ""; + if (modification.timing()) { + if (modification.tracing()) { + invokeTraceTiming(builder, suffix); + } else { + invokeTiming(builder, suffix); + } + } else { + if (modification.tracing()) { + invokeTrace(builder, suffix); + } + } + } + + private static void invokeTiming(CodeBuilder builder, String suffix) { builder.invokestatic(METHOD_TRACER_CLASS, TIMING_METHOD.name() + suffix, TIMING_METHOD.descriptor()); } - public static void invokeTrace(CodeBuilder builder, String suffix) { + private static void invokeTrace(CodeBuilder builder, String suffix) { builder.invokestatic(METHOD_TRACER_CLASS, TRACE_METHOD.name() + suffix, TRACE_METHOD.descriptor()); } - public static void invokeTraceTiming(CodeBuilder builder, String suffix) { + private static void invokeTraceTiming(CodeBuilder builder, String suffix) { builder.invokestatic(METHOD_TRACER_CLASS, TRACE_TIMING_METHOD.name() + suffix, TRACE_TIMING_METHOD.descriptor()); } - public static int invokeTimestamp(CodeBuilder builder) { + private static int invokeTimestamp(CodeBuilder builder) { Bytecode.invokestatic(builder, METHOD_TRACER_CLASS, TIMESTAMP_METHOD); return builder.allocateLocal(TypeKind.LONG); } diff --git a/test/jdk/jdk/jfr/event/tracing/TestConstructors.java b/test/jdk/jdk/jfr/event/tracing/TestConstructors.java new file mode 100644 index 00000000000..26548646b49 --- /dev/null +++ b/test/jdk/jdk/jfr/event/tracing/TestConstructors.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.event.tracing; + +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Tests that constructors are instrumented correctly. + * @requires vm.flagless + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm -Xlog:jfr+methodtrace=debug + * jdk.jfr.event.tracing.TestConstructors + **/ +public class TestConstructors { + static private void methodThatThrows() { + throw new RuntimeException(); + } + + public static class Cat { + Cat() { + new String(); + methodThatThrows(); + super(); + methodThatThrows(); + } + } + + public static class Dog { + Dog() { + super(); + methodThatThrows(); + } + } + + public static class Tiger { + Tiger() { + methodThatThrows(); + super(); + } + } + + public static class Zebra { + Zebra(boolean shouldThrow) { + this(shouldThrow ? 1 : 0); + } + + Zebra(int shouldThrow) { + if (shouldThrow == 1) { + throw new RuntimeException(); + } + } + } + + public static class Snake { + Snake() { + try { + throw new RuntimeException(); + } catch (Exception e) { + // Ignore + } + super(); + } + } + + public static void main(String... args) throws Exception { + try (Recording r = new Recording()) { + r.enable("jdk.MethodTrace").with("filter", Dog.class.getName() + ";" + Cat.class.getName() + ";" + Tiger.class.getName() + ";" + Zebra.class.getName() + ";" + Snake.class.getName()); + r.start(); + try { + new Cat(); + } catch (Exception e) { + // ignore + } + try { + new Dog(); + } catch (Exception e) { + // ignore + } + try { + new Tiger(); + } catch (Exception e) { + // ignore + } + try { + new Zebra(true); + } catch (Exception e) { + // ignore + } + try { + new Zebra(false); + } catch (Exception e) { + // ignore + } + try { + new Snake(); + } catch (Exception e) { + // ignore + } + r.stop(); + List events = Events.fromRecording(r); + var methods = buildMethodMap(events); + if (methods.size() != 5) { + throw new Exception("Expected 5 different methods"); + } + assertMethodCount(methods, "Cat", 1); + assertMethodCount(methods, "Dog", 1); + assertMethodCount(methods, "Snake", 1); + assertMethodCount(methods, "Tiger", 1); + assertMethodCount(methods, "Zebra", 3); + } + } + + private static void assertMethodCount(Map methods, String className, int expectedCount) throws Exception { + String name = TestConstructors.class.getName() + "$" + className + "::"; + Long count = methods.get(name); + if (count == null) { + throw new Exception("Could not find traced method " + name); + } + if (count != expectedCount) { + throw new Exception("Expected " + expectedCount + " trace event for " + name); + } + } + + private static Map buildMethodMap(List events) { + Map map = new TreeMap<>(); + for (RecordedEvent e : events) { + RecordedMethod m = e.getValue("method"); + String name = m.getType().getName() + "::" + m.getName(); + map.compute(name, (_, value) -> (value == null) ? 1 : value + 1); + } + for (var e : map.entrySet()) { + System.out.println(e.getKey() + " " + e.getValue()); + } + return map; + } +} \ No newline at end of file diff --git a/test/jdk/jdk/jfr/event/tracing/TestInstrumentation.java b/test/jdk/jdk/jfr/event/tracing/TestInstrumentation.java index 834d4ab4989..5709c95812a 100644 --- a/test/jdk/jdk/jfr/event/tracing/TestInstrumentation.java +++ b/test/jdk/jdk/jfr/event/tracing/TestInstrumentation.java @@ -93,6 +93,8 @@ public class TestInstrumentation { assertMethod(map, "exception", 2); assertMethod(map, "switchExpression", 3); assertMethod(map, "recursive", 4); + assertMethod(map, "deepException", 1); + assertMethod(map, "whileTrue", 1); assertMethod(map, "multipleReturns", 5); if (!map.isEmpty()) { throw new Exception("Found unexpected methods " + map.keySet()); @@ -105,6 +107,8 @@ public class TestInstrumentation { assertMethod(map, "exception", 2); assertMethod(map, "switchExpression", 3); assertMethod(map, "recursive", 4); + assertMethod(map, "deepException", 1); + assertMethod(map, "whileTrue", 1); assertMethod(map, "multipleReturns", 5); for (var entry : map.entrySet()) { long invocations = entry.getValue(); From c834e4c641bf6c73e88b93c0cdba40a83f3192c1 Mon Sep 17 00:00:00 2001 From: Jonas Norlinder Date: Thu, 8 Jan 2026 16:46:28 +0000 Subject: [PATCH 15/24] 8373647: Avoid fstat when opening file for write with RandomAccessFile or FileOutputStream Reviewed-by: redestad, alanb --- .../unix/native/libjava/io_util_md.c | 31 ++++++++++++------- .../org/openjdk/bench/java/io/FileWrite.java | 28 ++++++++++++++++- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/java.base/unix/native/libjava/io_util_md.c b/src/java.base/unix/native/libjava/io_util_md.c index 2e81cbd05c2..bcac334191c 100644 --- a/src/java.base/unix/native/libjava/io_util_md.c +++ b/src/java.base/unix/native/libjava/io_util_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "jvm.h" #include "io_util.h" #include "io_util_md.h" +#include #include #include @@ -75,20 +76,26 @@ FD handleOpen(const char *path, int oflag, int mode) { FD fd; RESTARTABLE(open(path, oflag, mode), fd); - if (fd != -1) { - struct stat buf; - int result; - RESTARTABLE(fstat(fd, &buf), result); - if (result != -1) { - if (S_ISDIR(buf.st_mode)) { - close(fd); - errno = EISDIR; - fd = -1; - } - } else { + // No further checking is needed if the file is not a + // directory or open returned an error + if (fd == -1 || ((oflag & O_ACCMODE) != O_RDONLY) != 0) { + return fd; + } + + // FileInputStream is specified to throw if the + // file is a directory + struct stat buf; + int result; + RESTARTABLE(fstat(fd, &buf), result); + if (result != -1) { + if (S_ISDIR(buf.st_mode)) { close(fd); + errno = EISDIR; fd = -1; } + } else { + close(fd); + fd = -1; } return fd; } diff --git a/test/micro/org/openjdk/bench/java/io/FileWrite.java b/test/micro/org/openjdk/bench/java/io/FileWrite.java index 21b0c2f8f54..c5a5aee70d8 100644 --- a/test/micro/org/openjdk/bench/java/io/FileWrite.java +++ b/test/micro/org/openjdk/bench/java/io/FileWrite.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ package org.openjdk.bench.java.io; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.RandomAccessFile; import java.io.IOException; import java.util.concurrent.TimeUnit; @@ -81,4 +82,29 @@ public class FileWrite { } } + @State(Scope.Benchmark) + @Warmup(iterations = 3, time = 2) + @Measurement(iterations = 5, time = 5) + @BenchmarkMode(Mode.SampleTime) + @OutputTimeUnit(TimeUnit.NANOSECONDS) + @Threads(1) + @Fork(value = 10) + public static class OpenFileForWritingBench { + final byte[] payload = "something".getBytes(); + final String path = System.getProperty("os.name", "unknown").toLowerCase().contains("win") ? "NUL" : "/dev/null"; + + @Benchmark + public void testFileOutputStream() throws IOException { + try (FileOutputStream f = new FileOutputStream(path)) { + f.write(payload); + } + } + + @Benchmark + public void testRandomAccessFile() throws IOException { + try (RandomAccessFile f = new RandomAccessFile(path, "rw")) { + f.write(payload); + } + } + } } From 7e1051bfcc01aad538376c86354e16e25d2eaf7a Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Thu, 8 Jan 2026 16:46:48 +0000 Subject: [PATCH 16/24] 8352728: InternalError loading java.security due to Windows parent folder permissions Reviewed-by: weijun, mullan --- .../share/classes/java/security/Security.java | 37 +++-- .../ExtraFileAndIncludes.java} | 154 ++++++++++-------- .../SecurityPropFile/LinuxAnonymousFiles.java | 83 ++++++++++ .../SecurityPropFile/SecurityPropFile.file | 1 - .../SecurityPropFile/SecurityPropFile.java | 42 ----- .../WindowsParentDirPermissions.java | 84 ++++++++++ 6 files changed, 273 insertions(+), 128 deletions(-) rename test/jdk/java/security/Security/{ConfigFileTest.java => SecurityPropFile/ExtraFileAndIncludes.java} (88%) create mode 100644 test/jdk/java/security/Security/SecurityPropFile/LinuxAnonymousFiles.java delete mode 100644 test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.file delete mode 100644 test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.java create mode 100644 test/jdk/java/security/Security/SecurityPropFile/WindowsParentDirPermissions.java diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index 6969fe8a8e1..30a22b05742 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; @@ -112,7 +113,7 @@ public final class Security { private static Path currentPath; - private static final Set activePaths = new HashSet<>(); + private static final List activePaths = new ArrayList<>(); static void loadAll() { // first load the master properties file to @@ -262,30 +263,40 @@ public final class Security { } } + private static void checkCyclicInclude(Path path) { + for (Path activePath : activePaths) { + try { + if (Files.isSameFile(path, activePath)) { + throw new InternalError( + "Cyclic include of '" + path + "'"); + } + } catch (IOException e) { + if (sdebug != null) { + sdebug.println("skipped exception when checking for " + + "cyclic inclusion of " + path + ":"); + e.printStackTrace(); + } + } + } + } + private static void loadFromPath(Path path, LoadingMode mode) throws IOException { - boolean isRegularFile = Files.isRegularFile(path); - if (isRegularFile) { - path = path.toRealPath(); - } else if (Files.isDirectory(path)) { + if (Files.isDirectory(path)) { throw new IOException("Is a directory"); - } else { - path = path.toAbsolutePath(); - } - if (activePaths.contains(path)) { - throw new InternalError("Cyclic include of '" + path + "'"); } try (InputStream is = Files.newInputStream(path)) { + checkCyclicInclude(path); reset(mode); Path previousPath = currentPath; - currentPath = isRegularFile ? path : null; + currentPath = Files.isRegularFile(path) ? path : null; activePaths.add(path); try { debugLoad(true, path); props.load(is); debugLoad(false, path); } finally { - activePaths.remove(path); + activePaths.removeLast(); currentPath = previousPath; } } diff --git a/test/jdk/java/security/Security/ConfigFileTest.java b/test/jdk/java/security/Security/SecurityPropFile/ExtraFileAndIncludes.java similarity index 88% rename from test/jdk/java/security/Security/ConfigFileTest.java rename to test/jdk/java/security/Security/SecurityPropFile/ExtraFileAndIncludes.java index caf657005e1..4cf723f856a 100644 --- a/test/jdk/java/security/Security/ConfigFileTest.java +++ b/test/jdk/java/security/Security/SecurityPropFile/ExtraFileAndIncludes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -62,13 +62,13 @@ import java.util.stream.Stream; * @test * @summary Tests security properties passed through java.security, * java.security.properties or included from other properties files. - * @bug 8155246 8292297 8292177 8281658 8319332 + * @bug 4303068 8155246 8292297 8292177 8281658 8319332 * @modules java.base/sun.net.www * @library /test/lib - * @run main ConfigFileTest + * @run main ExtraFileAndIncludes */ -public class ConfigFileTest { +public class ExtraFileAndIncludes { static final String SEPARATOR_THIN = "----------------------------"; private static void printTestHeader(String testName) { @@ -91,7 +91,8 @@ public class ConfigFileTest { } else { // Executed by the test JVM. try (FilesManager filesMgr = new FilesManager()) { - for (Method m : ConfigFileTest.class.getDeclaredMethods()) { + for (Method m : + ExtraFileAndIncludes.class.getDeclaredMethods()) { if (m.getName().startsWith("test")) { printTestHeader(m.getName()); Executor.run(m, filesMgr); @@ -120,7 +121,7 @@ public class ConfigFileTest { static void testIncludeBasic(Executor ex, FilesManager filesMgr) throws Exception { PropsFile masterFile = filesMgr.newMasterFile(); - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(ExtraMode.FILE_URI); PropsFile file0 = filesMgr.newFile("file0.properties"); PropsFile file1 = filesMgr.newFile("dir1/file1.properties"); PropsFile file2 = filesMgr.newFile("dir1/dir2/file2.properties"); @@ -130,7 +131,7 @@ public class ConfigFileTest { file2.addAbsoluteInclude(file1); ex.setMasterFile(masterFile); - ex.setExtraFile(extraFile, Executor.ExtraMode.FILE_URI, false); + ex.setExtraFile(extraFile, false); ex.assertSuccess(); } @@ -152,7 +153,7 @@ public class ConfigFileTest { static void testIncludeWithOverrideAll(Executor ex, FilesManager filesMgr) throws Exception { PropsFile masterFile = filesMgr.newMasterFile(); - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(ExtraMode.HTTP_SERVED); PropsFile file0 = filesMgr.newFile("file0.properties"); PropsFile file1 = filesMgr.newFile("dir1/file1.properties"); @@ -160,40 +161,40 @@ public class ConfigFileTest { extraFile.addAbsoluteInclude(file1); ex.setMasterFile(masterFile); - ex.setExtraFile(extraFile, Executor.ExtraMode.HTTP_SERVED, true); + ex.setExtraFile(extraFile, true); ex.assertSuccess(); } static void extraPropertiesByHelper(Executor ex, FilesManager filesMgr, - Executor.ExtraMode mode) throws Exception { - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraMode mode) throws Exception { + ExtraPropsFile extraFile = filesMgr.newExtraFile(mode); PropsFile file0 = filesMgr.newFile("file0.properties"); extraFile.addRelativeInclude(file0); ex.setMasterFile(filesMgr.newMasterFile()); - ex.setExtraFile(extraFile, mode, true); + ex.setExtraFile(extraFile, true); ex.assertSuccess(); } static void testExtraPropertiesByPathAbsolute(Executor ex, FilesManager filesMgr) throws Exception { - extraPropertiesByHelper(ex, filesMgr, Executor.ExtraMode.PATH_ABS); + extraPropertiesByHelper(ex, filesMgr, ExtraMode.PATH_ABS); } static void testExtraPropertiesByPathRelative(Executor ex, FilesManager filesMgr) throws Exception { - extraPropertiesByHelper(ex, filesMgr, Executor.ExtraMode.PATH_REL); + extraPropertiesByHelper(ex, filesMgr, ExtraMode.PATH_REL); } static void specialCharsIncludes(Executor ex, FilesManager filesMgr, - char specialChar, Executor.ExtraMode extraMode, - boolean useRelativeIncludes) throws Exception { + char specialChar, ExtraMode extraMode, boolean useRelativeIncludes) + throws Exception { String suffix = specialChar + ".properties"; ExtraPropsFile extraFile; PropsFile file0, file1; try { - extraFile = filesMgr.newExtraFile("extra" + suffix); + extraFile = filesMgr.newExtraFile("extra" + suffix, extraMode); file0 = filesMgr.newFile("file0" + suffix); file1 = filesMgr.newFile("file1" + suffix); } catch (InvalidPathException ipe) { @@ -210,20 +211,18 @@ public class ConfigFileTest { extraFile.addAbsoluteInclude(file1); ex.setMasterFile(filesMgr.newMasterFile()); - ex.setExtraFile(extraFile, extraMode, false); + ex.setExtraFile(extraFile, false); ex.assertSuccess(); } static void testUnicodeIncludes1(Executor ex, FilesManager filesMgr) throws Exception { - specialCharsIncludes(ex, filesMgr, '\u2022', - Executor.ExtraMode.PATH_ABS, true); + specialCharsIncludes(ex, filesMgr, '\u2022', ExtraMode.PATH_ABS, true); } static void testUnicodeIncludes2(Executor ex, FilesManager filesMgr) throws Exception { - specialCharsIncludes(ex, filesMgr, '\u2022', - Executor.ExtraMode.FILE_URI, true); + specialCharsIncludes(ex, filesMgr, '\u2022', ExtraMode.FILE_URI, true); } static void testUnicodeIncludes3(Executor ex, FilesManager filesMgr) @@ -232,7 +231,7 @@ public class ConfigFileTest { // file:/tmp/extra•.properties are supported for the extra file. // However, relative includes are not allowed in these cases. specialCharsIncludes(ex, filesMgr, '\u2022', - Executor.ExtraMode.RAW_FILE_URI1, false); + ExtraMode.RAW_FILE_URI1, false); } static void testUnicodeIncludes4(Executor ex, FilesManager filesMgr) @@ -241,19 +240,17 @@ public class ConfigFileTest { // file:///tmp/extra•.properties are supported for the extra file. // However, relative includes are not allowed in these cases. specialCharsIncludes(ex, filesMgr, '\u2022', - Executor.ExtraMode.RAW_FILE_URI2, false); + ExtraMode.RAW_FILE_URI2, false); } static void testSpaceIncludes1(Executor ex, FilesManager filesMgr) throws Exception { - specialCharsIncludes(ex, filesMgr, ' ', - Executor.ExtraMode.PATH_ABS, true); + specialCharsIncludes(ex, filesMgr, ' ', ExtraMode.PATH_ABS, true); } static void testSpaceIncludes2(Executor ex, FilesManager filesMgr) throws Exception { - specialCharsIncludes(ex, filesMgr, ' ', - Executor.ExtraMode.FILE_URI, true); + specialCharsIncludes(ex, filesMgr, ' ', ExtraMode.FILE_URI, true); } static void testSpaceIncludes3(Executor ex, FilesManager filesMgr) @@ -261,8 +258,7 @@ public class ConfigFileTest { // Backward compatibility check. Malformed URLs such as // file:/tmp/extra .properties are supported for the extra file. // However, relative includes are not allowed in these cases. - specialCharsIncludes(ex, filesMgr, ' ', - Executor.ExtraMode.RAW_FILE_URI1, false); + specialCharsIncludes(ex, filesMgr, ' ', ExtraMode.RAW_FILE_URI1, false); } static void testSpaceIncludes4(Executor ex, FilesManager filesMgr) @@ -270,8 +266,7 @@ public class ConfigFileTest { // Backward compatibility check. Malformed URLs such as // file:///tmp/extra .properties are supported for the extra file. // However, relative includes are not allowed in these cases. - specialCharsIncludes(ex, filesMgr, ' ', - Executor.ExtraMode.RAW_FILE_URI2, false); + specialCharsIncludes(ex, filesMgr, ' ', ExtraMode.RAW_FILE_URI2, false); } static void notOverrideOnFailureHelper(Executor ex, FilesManager filesMgr, @@ -370,13 +365,13 @@ public class ConfigFileTest { static void testCannotResolveRelativeFromHTTPServed(Executor ex, FilesManager filesMgr) throws Exception { - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(ExtraMode.HTTP_SERVED); PropsFile file0 = filesMgr.newFile("file0.properties"); extraFile.addRelativeInclude(file0); ex.setMasterFile(filesMgr.newMasterFile()); - ex.setExtraFile(extraFile, Executor.ExtraMode.HTTP_SERVED, true); + ex.setExtraFile(extraFile, true); ex.assertError("InternalError: Cannot resolve '" + file0.fileName + "' relative path when included from a non-regular " + "properties file (e.g. HTTP served file)"); @@ -394,14 +389,15 @@ public class ConfigFileTest { masterFile.addRelativeInclude(file0); ex.setMasterFile(masterFile); - ex.assertError( - "InternalError: Cyclic include of '" + masterFile.path + "'"); + ex.assertError("Cyclic include"); + ex.getOutputAnalyzer().stderrShouldMatch("\\QInternalError: Cyclic " + + "include of '\\E[^']+\\Q" + masterFile.fileName + "'\\E"); } static void testCannotIncludeURL(Executor ex, FilesManager filesMgr) throws Exception { PropsFile masterFile = filesMgr.newMasterFile(); - ExtraPropsFile extraFile = filesMgr.newExtraFile(); + ExtraPropsFile extraFile = filesMgr.newExtraFile(ExtraMode.HTTP_SERVED); masterFile.addRawProperty("include", extraFile.url.toString()); @@ -432,8 +428,7 @@ public class ConfigFileTest { // Launch a JDK without a master java.security file present, but with an // extra file passed. Since the "security.overridePropertiesFile=true" // security property is missing, it should fail anyway. - ex.setExtraFile( - filesMgr.newExtraFile(), Executor.ExtraMode.FILE_URI, true); + ex.setExtraFile(filesMgr.newExtraFile(ExtraMode.FILE_URI), true); ex.assertError("InternalError: Error loading java.security file"); } } @@ -455,17 +450,24 @@ sealed class PropsFile permits ExtraPropsFile { static Include of(PropsFile propsFile, String value) { return new Include(propsFile, value); } + + void assertProcessed(OutputAnalyzer oa) { + oa.shouldContain("processing include: '" + value + "'"); + oa.shouldContain("finished processing " + propsFile.displayPath); + } } protected final List includes = new ArrayList<>(); protected final PrintWriter writer; protected boolean includedFromExtra = false; + protected Path displayPath; final String fileName; final Path path; PropsFile(String fileName, Path path) throws IOException { this.fileName = fileName; this.path = path; + this.displayPath = path; this.writer = new PrintWriter(Files.newOutputStream(path, StandardOpenOption.CREATE, StandardOpenOption.APPEND), true); } @@ -513,8 +515,9 @@ sealed class PropsFile permits ExtraPropsFile { } void addRelativeInclude(PropsFile propsFile) { - addIncludeDefinition(Include.of(propsFile, - path.getParent().relativize(propsFile.path).toString())); + Path rel = path.getParent().relativize(propsFile.path); + addIncludeDefinition(Include.of(propsFile, rel.toString())); + propsFile.displayPath = displayPath.getParent().resolve(rel); } void assertApplied(OutputAnalyzer oa) { @@ -522,8 +525,7 @@ sealed class PropsFile permits ExtraPropsFile { FilesManager.APPLIED_PROP_VALUE); for (Include include : includes) { include.propsFile.assertApplied(oa); - oa.shouldContain("processing include: '" + include.value + "'"); - oa.shouldContain("finished processing " + include.propsFile.path); + include.assertProcessed(oa); } } @@ -534,8 +536,7 @@ sealed class PropsFile permits ExtraPropsFile { if (!include.propsFile.includedFromExtra) { include.propsFile.assertWasOverwritten(oa); } - oa.shouldContain("processing include: '" + include.value + "'"); - oa.shouldContain("finished processing " + include.propsFile.path); + include.assertProcessed(oa); } } @@ -556,13 +557,24 @@ sealed class PropsFile permits ExtraPropsFile { } } +enum ExtraMode { + HTTP_SERVED, FILE_URI, RAW_FILE_URI1, RAW_FILE_URI2, PATH_ABS, PATH_REL +} + final class ExtraPropsFile extends PropsFile { + private static final Path CWD = Path.of(".").toAbsolutePath(); private final Map systemProps = new LinkedHashMap<>(); + private final ExtraMode mode; final URI url; - ExtraPropsFile(String fileName, URI url, Path path) throws IOException { + ExtraPropsFile(String fileName, URI url, Path path, ExtraMode mode) + throws IOException { super(fileName, path); this.url = url; + this.mode = mode; + if (mode == ExtraMode.PATH_REL) { + this.displayPath = CWD.relativize(path); + } } @Override @@ -578,14 +590,25 @@ final class ExtraPropsFile extends PropsFile { super.addIncludeDefinition(include); } + String getSysPropValue() { + return switch (mode) { + case HTTP_SERVED -> url.toString(); + case FILE_URI -> path.toUri().toString(); + case RAW_FILE_URI1 -> "file:" + path; + case RAW_FILE_URI2 -> + "file://" + (path.startsWith("/") ? "" : "/") + path; + case PATH_ABS, PATH_REL -> displayPath.toString(); + }; + } + Map getSystemProperties() { return Collections.unmodifiableMap(systemProps); } } final class FilesManager implements Closeable { - private static final Path ROOT_DIR = - Path.of(ConfigFileTest.class.getSimpleName()).toAbsolutePath(); + private static final Path ROOT_DIR = Path.of( + ExtraFileAndIncludes.class.getSimpleName()).toAbsolutePath(); private static final Path PROPS_DIR = ROOT_DIR.resolve("properties"); private static final Path JDK_DIR = ROOT_DIR.resolve("jdk"); private static final Path MASTER_FILE = @@ -684,11 +707,11 @@ final class FilesManager implements Closeable { propsFile.addComment("Property to determine if this properties file " + "was parsed and not overwritten:"); propsFile.addRawProperty(fileName, APPLIED_PROP_VALUE); - propsFile.addComment(ConfigFileTest.SEPARATOR_THIN); + propsFile.addComment(ExtraFileAndIncludes.SEPARATOR_THIN); propsFile.addComment("Property to be overwritten by every properties " + "file (master, extra or included):"); propsFile.addRawProperty(LAST_FILE_PROP_NAME, fileName); - propsFile.addComment(ConfigFileTest.SEPARATOR_THIN); + propsFile.addComment(ExtraFileAndIncludes.SEPARATOR_THIN); createdFiles.add(propsFile); return propsFile; } @@ -702,16 +725,17 @@ final class FilesManager implements Closeable { return newFile(MASTER_FILE, PropsFile::new); } - ExtraPropsFile newExtraFile() throws IOException { - return newExtraFile("extra.properties"); + ExtraPropsFile newExtraFile(ExtraMode mode) throws IOException { + return newExtraFile("extra.properties", mode); } - ExtraPropsFile newExtraFile(String extraFileName) throws IOException { + ExtraPropsFile newExtraFile(String extraFileName, ExtraMode mode) + throws IOException { return (ExtraPropsFile) newFile(PROPS_DIR.resolve(extraFileName), (fileName, path) -> { URI uri = serverUri.resolve(ParseUtil.encodePath( ROOT_DIR.relativize(path).toString())); - return new ExtraPropsFile(fileName, uri, path); + return new ExtraPropsFile(fileName, uri, path, mode); }); } @@ -719,7 +743,7 @@ final class FilesManager implements Closeable { for (PropsFile propsFile : createdFiles) { System.err.println(); System.err.println(propsFile.path.toString()); - System.err.println(ConfigFileTest.SEPARATOR_THIN.repeat(3)); + System.err.println(ExtraFileAndIncludes.SEPARATOR_THIN.repeat(3)); try (Stream lines = Files.lines(propsFile.path)) { long lineNumber = 1L; Iterator it = lines.iterator(); @@ -757,9 +781,6 @@ final class FilesManager implements Closeable { } final class Executor { - enum ExtraMode { - HTTP_SERVED, FILE_URI, RAW_FILE_URI1, RAW_FILE_URI2, PATH_ABS, PATH_REL - } static final String RUNNER_ARG = "runner"; static final String INITIAL_PROP_LOG_MSG = "Initial security property: "; private static final String OVERRIDING_LOG_MSG = @@ -769,7 +790,6 @@ final class Executor { INITIAL_PROP_LOG_MSG + "postInitTest=shouldNotRecord", INITIAL_PROP_LOG_MSG + "include=", }; - private static final Path CWD = Path.of(".").toAbsolutePath(); private static final String JAVA_SEC_PROPS = "java.security.properties"; private static final String CLASS_PATH = Objects.requireNonNull( System.getProperty("test.classes"), "unspecified test.classes"); @@ -812,20 +832,10 @@ final class Executor { this.masterPropsFile = masterPropsFile; } - void setExtraFile(ExtraPropsFile extraPropsFile, ExtraMode mode, - boolean overrideAll) { + void setExtraFile(ExtraPropsFile extraPropsFile, boolean overrideAll) { this.extraPropsFile = extraPropsFile; expectedOverrideAll = overrideAll; - setRawExtraFile(switch (mode) { - case HTTP_SERVED -> extraPropsFile.url.toString(); - case FILE_URI -> extraPropsFile.path.toUri().toString(); - case RAW_FILE_URI1 -> "file:" + extraPropsFile.path; - case RAW_FILE_URI2 -> "file://" + - (extraPropsFile.path.startsWith("/") ? "" : "/") + - extraPropsFile.path; - case PATH_ABS -> extraPropsFile.path.toString(); - case PATH_REL -> CWD.relativize(extraPropsFile.path).toString(); - }, overrideAll); + setRawExtraFile(extraPropsFile.getSysPropValue(), overrideAll); } void setIgnoredExtraFile(String extraPropsFile, boolean overrideAll) { @@ -841,7 +851,7 @@ final class Executor { List command = new ArrayList<>(jvmArgs); Collections.addAll(command, Utils.getTestJavaOpts()); addSystemPropertiesAsJvmArgs(command); - command.add(ConfigFileTest.class.getSimpleName()); + command.add(ExtraFileAndIncludes.class.getSimpleName()); command.add(RUNNER_ARG); oa = ProcessTools.executeProcess(new ProcessBuilder(command)); oa.shouldHaveExitValue(successExpected ? 0 : 1); diff --git a/test/jdk/java/security/Security/SecurityPropFile/LinuxAnonymousFiles.java b/test/jdk/java/security/Security/SecurityPropFile/LinuxAnonymousFiles.java new file mode 100644 index 00000000000..7ca2a7c0f8b --- /dev/null +++ b/test/jdk/java/security/Security/SecurityPropFile/LinuxAnonymousFiles.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026, Red Hat, Inc. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.process.ProcessTools; + +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; + +/* + * @test + * @summary Ensures the java executable is able to load extra security + * properties files from anonymous files and pipes. + * @bug 8352728 + * @requires os.family == "linux" + * @modules java.base/java.io:+open + * @library /test/lib + * @run main LinuxAnonymousFiles + */ + +public class LinuxAnonymousFiles { + private static final String TEST_PROP = "property.name=PROPERTY_VALUE"; + + private static final class AnonymousFile implements AutoCloseable { + public final Path fdPath; + private final FileInputStream fis; + + private AnonymousFile(CharSequence content) throws Exception { + Path tmp = Files.createTempFile("anonymous-file-", ""); + Files.writeString(tmp, content + System.lineSeparator()); + fis = new FileInputStream(tmp.toFile()); + Files.delete(tmp); + // Now the file is regular but anonymous, and will be unlinked + // when we close the last file descriptor referring to it. The + // fis instance ensures we keep it alive until close() is invoked. + Field field = FileDescriptor.class.getDeclaredField("fd"); + field.setAccessible(true); + int fd = field.getInt(fis.getFD()); + fdPath = Path.of("/proc/self").toRealPath().resolve("fd/" + fd); + } + + @Override + public void close() throws IOException { + fis.close(); + } + } + + public static void main(String[] args) throws Exception { + Path java = Path.of(System.getProperty("test.jdk"), "bin", "java"); + try (AnonymousFile af = new AnonymousFile("include /dev/stdin")) { + ProcessTools.executeProcess(new ProcessBuilder(java.toString(), + "-Djava.security.debug=properties", + "-Djava.security.properties=" + af.fdPath, + "-XshowSettings:security:properties", "-version"), + TEST_PROP).shouldHaveExitValue(0).shouldContain(TEST_PROP); + } + System.out.println("TEST PASS - OK"); + } +} diff --git a/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.file b/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.file deleted file mode 100644 index 2b4c08c6903..00000000000 --- a/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.file +++ /dev/null @@ -1 +0,0 @@ -policy.url.2=file:${test.src}/SecurityPropFile.policy diff --git a/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.java b/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.java deleted file mode 100644 index b0ba6c60854..00000000000 --- a/test/jdk/java/security/Security/SecurityPropFile/SecurityPropFile.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @bug 4303068 - * @summary be allowed to specify the security properties file - * as a -D system property - * - * @run main/othervm -Djava.security.properties=${test.src}/SecurityPropFile.file -Djava.security.debug=properties SecurityPropFile - */ - -public class SecurityPropFile { - public static void main(String[] args) { - System.out.println(java.security.Security.getProperty - ("policy.provider")); - System.out.println(java.security.Security.getProperty - ("policy.url.1")); - System.out.println(java.security.Security.getProperty - ("policy.url.2")); - } -} diff --git a/test/jdk/java/security/Security/SecurityPropFile/WindowsParentDirPermissions.java b/test/jdk/java/security/Security/SecurityPropFile/WindowsParentDirPermissions.java new file mode 100644 index 00000000000..a41fd2f3535 --- /dev/null +++ b/test/jdk/java/security/Security/SecurityPropFile/WindowsParentDirPermissions.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026, Red Hat, Inc. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.util.FileUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.AclEntry; +import java.nio.file.attribute.AclEntryType; +import java.nio.file.attribute.AclFileAttributeView; +import java.util.List; + +/* + * @test + * @summary Ensures java.security is loadable in Windows, even when the user + * does not have permissions on one of the parent directories. + * @bug 8352728 + * @requires os.family == "windows" + * @library /test/lib + * @run main WindowsParentDirPermissions + */ + +public class WindowsParentDirPermissions { + private static AutoCloseable restrictedAcl(Path path) throws IOException { + AclFileAttributeView view = + Files.getFileAttributeView(path, AclFileAttributeView.class); + List originalAcl = List.copyOf(view.getAcl()); + view.setAcl(List.of(AclEntry.newBuilder().setType(AclEntryType.DENY) + .setPrincipal(Files.getOwner(path)).build())); + return () -> view.setAcl(originalAcl); + } + + public static void main(String[] args) throws Exception { + Path temp = Files.createTempDirectory("JDK-8352728-tmp-"); + try (AutoCloseable a1 = () -> FileUtils.deleteFileTreeUnchecked(temp)) { + // Copy the jdk to a different directory + Path originalJdk = Path.of(System.getProperty("test.jdk")); + Path jdk = temp.resolve("jdk-parent-dir", "jdk"); + Files.createDirectories(jdk); + FileUtils.copyDirectory(originalJdk, jdk); + + // Remove current user permissions from jdk-parent-dir + try (AutoCloseable a2 = restrictedAcl(jdk.getParent())) { + // Make sure the permissions are affecting the current user + try { + jdk.toRealPath(); + throw new jtreg.SkippedException("Must run non-elevated!"); + } catch (IOException expected) { } + + // Execute the copied jdk, ensuring java.security.Security is + // loaded (i.e. use -XshowSettings:security:properties) + ProcessTools.executeProcess(new ProcessBuilder( + List.of(jdk.resolve("bin", "java.exe").toString(), + "-Djava.security.debug=properties", + "-XshowSettings:security:properties", + "-version"))).shouldHaveExitValue(0); + } + } + System.out.println("TEST PASS - OK"); + } +} From afd216ec3f5bfd1be88c6f4d4f53b763205c4fee Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Thu, 8 Jan 2026 17:19:12 +0000 Subject: [PATCH 17/24] 8374752: Add more JLS links to javax.lang.model.element.* Reviewed-by: liach --- .../javax/lang/model/element/ExecutableElement.java | 9 ++++++++- .../javax/lang/model/element/PackageElement.java | 5 ++++- .../javax/lang/model/element/Parameterizable.java | 6 +++++- .../javax/lang/model/element/QualifiedNameable.java | 6 +++++- .../classes/javax/lang/model/element/TypeElement.java | 11 ++++++++++- .../lang/model/element/TypeParameterElement.java | 7 ++++++- .../javax/lang/model/element/VariableElement.java | 10 +++++++++- 7 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java b/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java index 18923463248..cc0924fda61 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/ExecutableElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,13 @@ import javax.lang.model.type.*; * clause, among other restrictions; see JLS {@jls 9.6.1} for details. * * @see ExecutableType + * @jls 8.4 Method Declarations + * @jls 8.6 Instance Initializers + * @jls 8.7 Static Initializers + * @jls 8.8 Constructor Declarations + * @jls 9.4 Method Declarations + * @jls 9.6.1 Annotation Interface Elements + * * @since 1.6 */ public interface ExecutableElement extends Element, Parameterizable { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/PackageElement.java b/src/java.compiler/share/classes/javax/lang/model/element/PackageElement.java index 651bbe70eee..79b1fb84683 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/PackageElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/PackageElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,6 +58,8 @@ import javax.lang.model.type.TypeMirror; * * * @see javax.lang.model.util.Elements#getPackageOf + * @jls 7.4 Package Declarations + * * @since 1.6 */ public interface PackageElement extends Element, QualifiedNameable { @@ -87,6 +89,7 @@ public interface PackageElement extends Element, QualifiedNameable { * @return the fully qualified name of this package, or an * empty name if this is an unnamed package * @jls 6.7 Fully Qualified Names and Canonical Names + * @jls 7.4.1 Named Packages */ Name getQualifiedName(); diff --git a/src/java.compiler/share/classes/javax/lang/model/element/Parameterizable.java b/src/java.compiler/share/classes/javax/lang/model/element/Parameterizable.java index cf0bbdfba37..a4280fbcf29 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/Parameterizable.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/Parameterizable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,10 @@ import java.util.List; /** * A mixin interface for an element that has type parameters. * + * @jls 4.5 Parameterized Types + * @jls 8.4.4 Generic Methods + * @jls 8.8.4 Generic Constructors + * * @since 1.7 */ public interface Parameterizable extends Element { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/QualifiedNameable.java b/src/java.compiler/share/classes/javax/lang/model/element/QualifiedNameable.java index 1b0316f893f..555d90c1b13 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/QualifiedNameable.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/QualifiedNameable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,10 @@ package javax.lang.model.element; /** * A mixin interface for an element that has a qualified name. * + * @jls 6.5.3.2 Qualified Package Names + * @jls 6.5.5.2 Qualified Type Names + * @jls 6.7 Fully Qualified Names and Canonical Names + * * @since 1.7 */ public interface QualifiedNameable extends Element { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java b/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java index abf95bcefad..3dcc8e8c7fd 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/TypeElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -80,7 +80,16 @@ import javax.lang.model.util.*; * javax.lang.model.util.Elements#getAllTypeElements(CharSequence) * queried for} in the configured environment * + * * @see DeclaredType + * @jls 8.1 Class Declarations + * @jls 8.5 Member Class and Interface Declarations + * @jls 8.9 Enum Classes + * @jls 8.10 Record Classes + * @jls 9.1 Interface Declarations + * @jls 9.5 Member Class and Interface Declarations + * @jls 9.6 Annotation Interfaces + * * @since 1.6 */ public interface TypeElement extends Element, Parameterizable, QualifiedNameable { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/TypeParameterElement.java b/src/java.compiler/share/classes/javax/lang/model/element/TypeParameterElement.java index cbaa8969126..abd06a3e038 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/TypeParameterElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/TypeParameterElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,11 @@ import javax.lang.model.type.TypeVariable; * A type parameter declares a {@link TypeVariable}. * * @see TypeVariable + * @jls 8.1.2 Generic Classes and Type Parameters + * @jls 8.4.4 Generic Methods + * @jls 8.8.4 Generic Constructors + * @jls 9.1.2 Generic Interfaces and Type Parameters + * * @since 1.6 */ public interface TypeParameterElement extends Element { diff --git a/src/java.compiler/share/classes/javax/lang/model/element/VariableElement.java b/src/java.compiler/share/classes/javax/lang/model/element/VariableElement.java index 4d2b2582901..b8f42c46b1e 100644 --- a/src/java.compiler/share/classes/javax/lang/model/element/VariableElement.java +++ b/src/java.compiler/share/classes/javax/lang/model/element/VariableElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,14 @@ import javax.lang.model.type.TypeKind; * parameter, local variable, resource variable, or exception * parameter. * + * @jls 8.3 Field Declaration + * @jls 8.9.1 Enum Constants + * @jls 8.4.1 Formal Parameters + * @jls 8.8.1 Formal Parameters + * @jls 14.4 Local Variable Declarations + * @jls 14.20 The {@code try} statement + * @jls 14.20.3 {@code try}-with-resources + * @since 1.6 */ public interface VariableElement extends Element { From 92abc6dfe43a2c1f10dcfcf1e197fc9369f70ee3 Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Thu, 8 Jan 2026 17:35:43 +0000 Subject: [PATCH 18/24] 8369282: Distrust TLS server certificates anchored by Chunghwa ePKI Root CA Reviewed-by: mullan --- .../security/validator/CADistrustPolicy.java | 18 ++- .../security/validator/ChunghwaTLSPolicy.java | 103 ++++++++++++++++++ .../share/conf/security/java.security | 6 +- .../distrust/Chunghwa.java | 69 ++++++++++++ .../chunghwa/chunghwaepkirootca-chain.pem | 50 +++++++++ 5 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 src/java.base/share/classes/sun/security/validator/ChunghwaTLSPolicy.java create mode 100644 test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Chunghwa.java create mode 100644 test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/chunghwa/chunghwaepkirootca-chain.pem diff --git a/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java b/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java index 9c64402c123..44c9ed2d075 100644 --- a/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java +++ b/src/java.base/share/classes/sun/security/validator/CADistrustPolicy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -83,6 +83,22 @@ enum CADistrustPolicy { } CamerfirmaTLSPolicy.checkDistrust(chain); } + }, + + /** + * Distrust TLS Server certificates anchored by the Chunghwa ePKI root CA + * and issued after March 17, 2026. If enabled, this policy is currently + * enforced by the PKIX and SunX509 TrustManager implementations + * of the SunJSSE provider implementation. + */ + CHUNGHWA_TLS { + void checkDistrust(String variant, X509Certificate[] chain) + throws ValidatorException { + if (!variant.equals(Validator.VAR_TLS_SERVER)) { + return; + } + ChunghwaTLSPolicy.checkDistrust(chain); + } }; /** diff --git a/src/java.base/share/classes/sun/security/validator/ChunghwaTLSPolicy.java b/src/java.base/share/classes/sun/security/validator/ChunghwaTLSPolicy.java new file mode 100644 index 00000000000..114f5043fbf --- /dev/null +++ b/src/java.base/share/classes/sun/security/validator/ChunghwaTLSPolicy.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.validator; + +import java.security.cert.X509Certificate; +import java.time.LocalDate; +import java.time.Month; +import java.time.ZoneOffset; +import java.util.Date; +import java.util.Map; +import java.util.Set; + +import sun.security.util.Debug; +import sun.security.x509.X509CertImpl; + +/** + * This class checks if Chunghwa issued TLS Server certificates should be + * restricted. + */ +final class ChunghwaTLSPolicy { + + private static final Debug debug = Debug.getInstance("certpath"); + + // SHA-256 certificate fingerprint of distrusted root for TLS + // cacerts alias: chunghwaepkirootca + // DN: OU=ePKI Root Certification Authority, + // O="Chunghwa Telecom Co., Ltd.", C=TW + private static final String FINGERPRINT = + "C0A6F4DC63A24BFDCF54EF2A6A082A0A72DE35803E2FF5FF527AE5D87206DFD5"; + + // Any TLS Server certificate that is anchored by the Chunghwa + // root above and is issued after this date will be distrusted. + private static final LocalDate MARCH_17_2026 = + LocalDate.of(2026, Month.MARCH, 17); + + /** + * This method assumes the eeCert is a TLS Server Cert and chains back to + * the anchor. + * + * @param chain the end-entity's certificate chain. The end entity cert + * is at index 0, the trust anchor at index n-1. + * @throws ValidatorException if the certificate is distrusted + */ + static void checkDistrust(X509Certificate[] chain) + throws ValidatorException { + X509Certificate anchor = chain[chain.length-1]; + String fp = fingerprint(anchor); + if (fp == null) { + throw new ValidatorException("Cannot generate fingerprint for " + + "trust anchor of TLS server certificate"); + } + if (FINGERPRINT.equalsIgnoreCase(fp)) { + Date notBefore = chain[0].getNotBefore(); + LocalDate ldNotBefore = LocalDate.ofInstant(notBefore.toInstant(), + ZoneOffset.UTC); + // reject if certificate is issued after March 17, 2026 + checkNotBefore(ldNotBefore, MARCH_17_2026, anchor); + } + } + + private static String fingerprint(X509Certificate cert) { + return X509CertImpl.getFingerprint("SHA-256", cert, debug); + } + + // Check whether the certificate's notBeforeDate is after the + // distrust date for the anchor (root CA). Throw ValidatorException + // if it is after the distrust date. + private static void checkNotBefore(LocalDate notBeforeDate, + LocalDate distrustDate, X509Certificate anchor) + throws ValidatorException { + if (notBeforeDate.isAfter(distrustDate)) { + throw new ValidatorException + ("TLS Server certificate issued after " + distrustDate + + " and anchored by a distrusted legacy Chunghwa root CA: " + + anchor.getSubjectX500Principal(), + ValidatorException.T_UNTRUSTED_CERT, anchor); + } + } + + private ChunghwaTLSPolicy() {} +} diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 3d26c1dcd96..b5cbce413b2 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -1437,6 +1437,9 @@ jdk.sasl.disabledMechanisms= # CAMERFIRMA_TLS : Distrust TLS Server certificates anchored by # a Camerfirma root CA and issued after April 15, 2025. # +# CHUNGHWA_TLS : Distrust TLS Server certificates anchored by +# a Chunghwa root CA and issued after March 17, 2026. +# # Leading and trailing whitespace surrounding each value are ignored. # Unknown values are ignored. If the property is commented out or set to the # empty String, no policies are enforced. @@ -1448,7 +1451,8 @@ jdk.sasl.disabledMechanisms= # jdk.certpath.disabledAlgorithms; those restrictions are still enforced even # if this property is not enabled. # -jdk.security.caDistrustPolicies=SYMANTEC_TLS,ENTRUST_TLS,CAMERFIRMA_TLS +jdk.security.caDistrustPolicies=SYMANTEC_TLS,ENTRUST_TLS,CAMERFIRMA_TLS,\ + CHUNGHWA_TLS # # FilePermission path canonicalization diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Chunghwa.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Chunghwa.java new file mode 100644 index 00000000000..8512640bb01 --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/Chunghwa.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.net.ssl.X509TrustManager; +import java.io.File; +import java.security.Security; +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Date; + +/* + * @test + * @bug 8369282 + * @summary Check that TLS Server certificates chaining back to distrusted + * Chunghwa root are invalid + * @library /test/lib + * @modules java.base/sun.security.validator + * @run main/othervm Chunghwa after policyOn invalid + * @run main/othervm Chunghwa after policyOff valid + * @run main/othervm Chunghwa before policyOn valid + * @run main/othervm Chunghwa before policyOff valid + */ + +public class Chunghwa { + + private static final String CERT_PATH = "chains" + File.separator + "chunghwa"; + + // The ePKI root has a test certificate chain stored in a file + // named "-chain.pem". + private static final String ROOT_TO_TEST = "chunghwaepkirootca"; + + // Date after the restrictions take effect + private static final ZonedDateTime DISTRUST_DATE = + LocalDate.of(2026, 03, 18).atStartOfDay(ZoneOffset.UTC); + + public static void main(String[] args) throws Exception { + + Distrust distrust = new Distrust(args); + + X509TrustManager[] tms = new X509TrustManager[]{ + distrust.getTMF("PKIX", null), + distrust.getTMF("SunX509", null) + }; + + Date notBefore = distrust.getNotBefore(DISTRUST_DATE); + distrust.testCertificateChain(CERT_PATH, notBefore, tms, ROOT_TO_TEST); + } +} diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/chunghwa/chunghwaepkirootca-chain.pem b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/chunghwa/chunghwaepkirootca-chain.pem new file mode 100644 index 00000000000..fdaa6ee04df --- /dev/null +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/distrust/chains/chunghwa/chunghwaepkirootca-chain.pem @@ -0,0 +1,50 @@ +Owner: CN=HiPKI Root CA - G1, + O="Chunghwa Telecom Co., Ltd.", C=TW +Issuer: OU=ePKI Root Certification Authority, + O="Chunghwa Telecom Co., Ltd.", C=TW +Serial number: 23fba648360e15e92ba78aedb67a0ae5 +Valid from: Wed Dec 20 19:11:23 MST 2023 until: Tue Dec 19 08:59:59 MST 2034 +Certificate fingerprints: + SHA1: 87:F1:DD:3B:8E:F1:E0:8C:A8:CA:CB:9B:CE:4E:26:5A:E4:4E:05:F2 + SHA256: 68:07:C9:72:35:C5:EC:60:90:26:9A:4B:5F:ED:FA:B4:69:86:E4:2F:4D:67:D2:ED:DD:CF:6E:45:CF:0D:FA:80 +Signature algorithm name: SHA256withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 + +-----BEGIN CERTIFICATE----- +MIIGjDCCBHSgAwIBAgIQI/umSDYOFekrp4rttnoK5TANBgkqhkiG9w0BAQsFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0yMzEyMjEwMjExMjNaFw0zNDEyMTkxNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQ +S0kgUm9vdCBDQSAtIEcxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA +9B5/UnMyDHPkvRN0o9QwqNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh +8Ge6zCFovkRTv4354twvVcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux5 +5199QmQ5eiY29yTw1S+6lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEu +iAU+TCK72h8q3VJGZDnzQs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRt +U6M9/Aes1MU3guvklQgZKILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfd +hSi8MEyr48KxRURHH+CKFgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXT +T3OUM3ECoWqj1jOXTyFjHluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK +9p/7qxj3ccC2HTHsOyDry+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8 +b3ti6RZsR1pl8w4Rm0bZ/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8Pg +cSojt/ewsTu8mL3WmKgMa/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NV +vxaXxA/VLGGEqnKG/uY6fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaOCAVMwggFPMB8G +A1UdIwQYMBaAFB4M97Zn8uGSJglFwFU5Lnc/QkqiMB0GA1UdDgQWBBTydxf6Xqj+ +9j1x1Wi6yUYMONivsDAOBgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYv +aHR0cDovL2VjYS5oaW5ldC5uZXQvcmVwb3NpdG9yeS9DUkxfU0hBMi9DQS5jcmww +gYIGCCsGAQUFBwEBBHYwdDA7BggrBgEFBQcwAoYvaHR0cDovL2VjYS5oaW5ldC5u +ZXQvcmVwb3NpdG9yeS9DZXJ0cy9lQ0FHMS5jcnQwNQYIKwYBBQUHMAGGKWh0dHA6 +Ly9vY3NwLmVjYS5oaW5ldC5uZXQvT0NTUC9vY3NwRzFzaGEyMBIGA1UdEwEB/wQI +MAYBAf8CAQEwIgYDVR0gBBswGTAIBgZngQwBAgIwDQYLKwYBBAGBtyNkAAMwDQYJ +KoZIhvcNAQELBQADggIBACY9pps8fqk3p8Xqv/qr26I1aFA4jOEG3VWd2bqn68Y9 +InOMZozTMVh7iOnOfat7mEqn/RNhikvR5MOV3qAeg4gwgNb1OMuGltwfXWGiuGeT +vhimsV6E2hhJFAmZyXtfuoV9vSrnr1a5pCWqhVYWSCvoAQ/8Kv0tATKbIe21CYXz +NIo7O9QBSXt0BiaP9+CVQtJAYYuy2MNAcXgzgL4rownrYYAixhPmkxQE0Dt1gVbW +s2htBLJGse0z1fJDblY0Zar4t2ly+kIScx5DhRrrd8XKMK0YvID9Ythb+ao8m7Wd +Kymqr36benGL3GsvmSypLPlqZtfEqVITFhXwQiL8ruxoL+3WfNQJ09x0iV4xaP+E +bZSLLVzIiyhU49YdFHaqKyAJQvzgF2Za3DOwQWlP7OngtUx0ScEGHsoo78AM+Y0T +eLFxmr82kuyH18wZkUT9bLZlot11P2aC8VTprBGr+jEAMJjpmEjSA83ja/ttmqgh +qjj29Jnw3Lgy91XIhzBFMxMYo+hhYeBRmBFWl5+Y5oxBgPVLZpDJvg2rKa8xdqim +KgvF0DMKHntE0hhVy7JfUCnKovNQ0pf0NodLfjpqcCS2GBZ1mNcsW2MG2uBPANcn +LRXmt7N4XX11mctQTADwt8yZZ+2HDrST4kghOz+FXgftrPBdtDtM0T6WJcHWR1uS +-----END CERTIFICATE----- From 1fb5030ab351a52b4a7455cbdd57f5b50aab9bd5 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Thu, 8 Jan 2026 17:58:35 +0000 Subject: [PATCH 19/24] 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 20/24] 8374639: Static archive with AOTClassLinking breaks dynamic archive Reviewed-by: coleenp, matsaave --- .../share/cds/aotConstantPoolResolver.cpp | 6 +- src/hotspot/share/cds/aotMetaspace.cpp | 3 +- src/hotspot/share/cds/archiveBuilder.cpp | 4 +- src/hotspot/share/cds/dynamicArchive.cpp | 119 +----------- src/hotspot/share/cds/dynamicArchive.hpp | 16 +- ...DynamicDumpWithAOTLinkedStaticArchive.java | 60 +++++++ .../appcds/dynamicArchive/ArrayKlasses.java | 170 ------------------ .../test-classes/ArrayKlassesApp.java | 77 -------- test/lib/jdk/test/lib/cds/CDSAppTester.java | 17 +- .../jdk/test/lib/cds/SimpleCDSAppTester.java | 25 ++- 10 files changed, 106 insertions(+), 391 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java delete mode 100644 test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArrayKlasses.java delete mode 100644 test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ArrayKlassesApp.java diff --git a/src/hotspot/share/cds/aotConstantPoolResolver.cpp b/src/hotspot/share/cds/aotConstantPoolResolver.cpp index c4bb26f6fb1..93145940955 100644 --- a/src/hotspot/share/cds/aotConstantPoolResolver.cpp +++ b/src/hotspot/share/cds/aotConstantPoolResolver.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -116,6 +116,10 @@ bool AOTConstantPoolResolver::is_class_resolution_deterministic(InstanceKlass* c return false; } } else if (resolved_class->is_objArray_klass()) { + if (CDSConfig::is_dumping_dynamic_archive()) { + // This is difficult to handle. See JDK-8374639 + return false; + } Klass* elem = ObjArrayKlass::cast(resolved_class)->bottom_klass(); if (elem->is_instance_klass()) { return is_class_resolution_deterministic(cp_holder, InstanceKlass::cast(elem)); diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 3824a2be3e2..79d789e0c70 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2159,7 +2159,6 @@ void AOTMetaspace::initialize_shared_spaces() { intptr_t* buffer = (intptr_t*)dynamic_mapinfo->serialized_data(); ReadClosure rc(&buffer, (intptr_t)SharedBaseAddress); DynamicArchive::serialize(&rc); - DynamicArchive::setup_array_klasses(); } LogStreamHandle(Info, aot) lsh; diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 3c7f25a950b..6bbefea5cd9 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -984,8 +984,6 @@ void ArchiveBuilder::make_klasses_shareable() { #undef STATS_FORMAT #undef STATS_PARAMS - - DynamicArchive::make_array_klasses_shareable(); } void ArchiveBuilder::make_training_data_shareable() { diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp index 8fae8dabf8c..d39cf3775e4 100644 --- a/src/hotspot/share/cds/dynamicArchive.cpp +++ b/src/hotspot/share/cds/dynamicArchive.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,6 +48,7 @@ #include "logging/log.hpp" #include "memory/metaspaceClosure.hpp" #include "memory/resourceArea.hpp" +#include "oops/array.hpp" #include "oops/klass.inline.hpp" #include "runtime/arguments.hpp" #include "runtime/os.hpp" @@ -95,7 +96,6 @@ public: void sort_methods(InstanceKlass* ik) const; void remark_pointers_for_instance_klass(InstanceKlass* k, bool should_mark) const; void write_archive(char* serialized_data, AOTClassLocationConfig* cl_config); - void gather_array_klasses(); public: // Do this before and after the archive dump to see if any corruption @@ -132,7 +132,6 @@ public: init_header(); gather_source_objs(); - gather_array_klasses(); reserve_buffer(); log_info(cds, dynamic)("Copying %d klasses and %d symbols", @@ -159,7 +158,6 @@ public: ArchiveBuilder::OtherROAllocMark mark; SystemDictionaryShared::write_to_archive(false); cl_config = AOTClassLocationConfig::dumptime()->write_to_archive(); - DynamicArchive::dump_array_klasses(); serialized_data = ro_region()->top(); WriteClosure wc(ro_region()); @@ -175,8 +173,6 @@ public: write_archive(serialized_data, cl_config); release_header(); - DynamicArchive::post_dump(); - post_dump(); verify_universe("After CDS dynamic dump"); @@ -185,30 +181,6 @@ public: virtual void iterate_roots(MetaspaceClosure* it) { AOTArtifactFinder::all_cached_classes_do(it); SystemDictionaryShared::dumptime_classes_do(it); - iterate_primitive_array_klasses(it); - } - - void iterate_primitive_array_klasses(MetaspaceClosure* it) { - for (int i = T_BOOLEAN; i <= T_LONG; i++) { - assert(is_java_primitive((BasicType)i), "sanity"); - Klass* k = Universe::typeArrayKlass((BasicType)i); // this give you "[I", etc - assert(AOTMetaspace::in_aot_cache_static_region((void*)k), - "one-dimensional primitive array should be in static archive"); - ArrayKlass* ak = ArrayKlass::cast(k); - while (ak != nullptr && ak->in_aot_cache()) { - Klass* next_k = ak->array_klass_or_null(); - if (next_k != nullptr) { - ak = ArrayKlass::cast(next_k); - } else { - ak = nullptr; - } - } - if (ak != nullptr) { - assert(ak->dimension() > 1, "sanity"); - // this is the lowest dimension that's not in the static archive - it->push(&ak); - } - } } }; @@ -367,26 +339,6 @@ void DynamicArchiveBuilder::write_archive(char* serialized_data, AOTClassLocatio log_info(cds, dynamic)("%d klasses; %d symbols", klasses()->length(), symbols()->length()); } -void DynamicArchiveBuilder::gather_array_klasses() { - for (int i = 0; i < klasses()->length(); i++) { - if (klasses()->at(i)->is_objArray_klass()) { - ObjArrayKlass* oak = ObjArrayKlass::cast(klasses()->at(i)); - Klass* elem = oak->element_klass(); - if (AOTMetaspace::in_aot_cache_static_region(elem)) { - // Only capture the array klass whose element_klass is in the static archive. - // During run time, setup (see DynamicArchive::setup_array_klasses()) is needed - // so that the element_klass can find its array klasses from the dynamic archive. - DynamicArchive::append_array_klass(oak); - } else { - // The element_klass and its array klasses are in the same archive. - assert(!AOTMetaspace::in_aot_cache_static_region(oak), - "we should not gather klasses that are already in the static archive"); - } - } - } - log_debug(aot)("Total array klasses gathered for dynamic archive: %d", DynamicArchive::num_array_klasses()); -} - class VM_PopulateDynamicDumpSharedSpace: public VM_Heap_Sync_Operation { DynamicArchiveBuilder _builder; public: @@ -403,76 +355,9 @@ public: } }; -// _array_klasses and _dynamic_archive_array_klasses only hold the array klasses -// which have element klass in the static archive. -GrowableArray* DynamicArchive::_array_klasses = nullptr; -Array* DynamicArchive::_dynamic_archive_array_klasses = nullptr; - void DynamicArchive::serialize(SerializeClosure* soc) { SymbolTable::serialize_shared_table_header(soc, false); SystemDictionaryShared::serialize_dictionary_headers(soc, false); - soc->do_ptr(&_dynamic_archive_array_klasses); -} - -void DynamicArchive::append_array_klass(ObjArrayKlass* ak) { - if (_array_klasses == nullptr) { - _array_klasses = new (mtClassShared) GrowableArray(50, mtClassShared); - } - _array_klasses->append(ak); -} - -void DynamicArchive::dump_array_klasses() { - assert(CDSConfig::is_dumping_dynamic_archive(), "sanity"); - if (_array_klasses != nullptr) { - ArchiveBuilder* builder = ArchiveBuilder::current(); - int num_array_klasses = _array_klasses->length(); - _dynamic_archive_array_klasses = - ArchiveBuilder::new_ro_array(num_array_klasses); - for (int i = 0; i < num_array_klasses; i++) { - builder->write_pointer_in_buffer(_dynamic_archive_array_klasses->adr_at(i), _array_klasses->at(i)); - } - } -} - -void DynamicArchive::setup_array_klasses() { - if (_dynamic_archive_array_klasses != nullptr) { - for (int i = 0; i < _dynamic_archive_array_klasses->length(); i++) { - ObjArrayKlass* oak = _dynamic_archive_array_klasses->at(i); - Klass* elm = oak->element_klass(); - assert(AOTMetaspace::in_aot_cache_static_region((void*)elm), "must be"); - - if (elm->is_instance_klass()) { - assert(InstanceKlass::cast(elm)->array_klasses() == nullptr, "must be"); - InstanceKlass::cast(elm)->set_array_klasses(oak); - } else { - assert(elm->is_array_klass(), "sanity"); - assert(ArrayKlass::cast(elm)->higher_dimension() == nullptr, "must be"); - ArrayKlass::cast(elm)->set_higher_dimension(oak); - } - } - log_debug(aot)("Total array klasses read from dynamic archive: %d", _dynamic_archive_array_klasses->length()); - } -} - -void DynamicArchive::make_array_klasses_shareable() { - if (_array_klasses != nullptr) { - int num_array_klasses = _array_klasses->length(); - for (int i = 0; i < num_array_klasses; i++) { - ObjArrayKlass* k = ArchiveBuilder::current()->get_buffered_addr(_array_klasses->at(i)); - k->remove_unshareable_info(); - } - } -} - -void DynamicArchive::post_dump() { - if (_array_klasses != nullptr) { - delete _array_klasses; - _array_klasses = nullptr; - } -} - -int DynamicArchive::num_array_klasses() { - return _array_klasses != nullptr ? _array_klasses->length() : 0; } void DynamicArchive::dump_impl(bool jcmd_request, const char* archive_name, TRAPS) { diff --git a/src/hotspot/share/cds/dynamicArchive.hpp b/src/hotspot/share/cds/dynamicArchive.hpp index c42c4b7dfde..63a48e252ee 100644 --- a/src/hotspot/share/cds/dynamicArchive.hpp +++ b/src/hotspot/share/cds/dynamicArchive.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,13 +26,8 @@ #define SHARE_CDS_DYNAMICARCHIVE_HPP #include "cds/filemap.hpp" -#include "classfile/compactHashtable.hpp" #include "memory/allStatic.hpp" -#include "memory/memRegion.hpp" -#include "oops/array.hpp" -#include "oops/oop.hpp" #include "utilities/exceptions.hpp" -#include "utilities/growableArray.hpp" #include "utilities/macros.hpp" #if INCLUDE_CDS @@ -59,22 +54,13 @@ public: }; class DynamicArchive : AllStatic { -private: - static GrowableArray* _array_klasses; - static Array* _dynamic_archive_array_klasses; public: static void dump_for_jcmd(const char* archive_name, TRAPS); static void dump_at_exit(JavaThread* current); static void dump_impl(bool jcmd_request, const char* archive_name, TRAPS); static bool is_mapped() { return FileMapInfo::dynamic_info() != nullptr; } static bool validate(FileMapInfo* dynamic_info); - static void dump_array_klasses(); - static void setup_array_klasses(); - static void append_array_klass(ObjArrayKlass* oak); static void serialize(SerializeClosure* soc); - static void make_array_klasses_shareable(); - static void post_dump(); - static int num_array_klasses(); }; #endif // INCLUDE_CDS #endif // SHARE_CDS_DYNAMICARCHIVE_HPP diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java new file mode 100644 index 00000000000..8014ed685ee --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + + +/* + * @test + * @bug 8374639 + * @requires vm.cds.supports.aot.class.linking + * @library /test/lib + * @build DynamicDumpWithAOTLinkedStaticArchive jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar TestApp + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. DynamicDumpWithAOTLinkedStaticArchive + */ + +import jdk.test.lib.cds.SimpleCDSAppTester; +import jdk.test.lib.process.OutputAnalyzer; + +public class DynamicDumpWithAOTLinkedStaticArchive { + public static void main(String... args) throws Exception { + SimpleCDSAppTester.of("DynamicDumpWithAOTLinkedStaticArchive") + .classpath("app.jar") + .appCommandLine("TestApp") + .setGenerateBaseArchive(true) + .setBaseArchiveOptions("-XX:+AOTClassLinking") + .setProductionChecker((OutputAnalyzer out) -> { + out.shouldContain("HelloWorld"); + }) + .runDynamicWorkflow(); + } +} + +class TestApp { + public static void main(String[] args) { + System.out.println("HelloWorld"); + System[][][] x = new System[0][0][0]; + System.out.println(x); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArrayKlasses.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArrayKlasses.java deleted file mode 100644 index e476382c8c9..00000000000 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ArrayKlasses.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -/* - * @test - * @summary handling of the existence of InstanceKlass::array_klasses() - * @requires vm.cds - * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds - * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes - * @build ArrayKlassesApp - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar ArrayKlasses.jar ArrayKlassesApp - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. ArrayKlasses - */ - -import jdk.test.lib.helpers.ClassFileInstaller; - -public class ArrayKlasses extends DynamicArchiveTestBase { - public static void main(String[] args) throws Exception { - runTest(ArrayKlasses::test); - } - - static void test() throws Exception { - String topArchiveName = getNewArchiveName(); - final String appJar = ClassFileInstaller.getJarPath("ArrayKlasses.jar"); - final String mainClass = "ArrayKlassesApp"; - final String runtimeLogOptions = - "-Xlog:class+load,class+load+array=debug,cds+dynamic=debug,cds=debug,aot+unshareable=trace"; - - // Case 1 - // Create a dynamic archive with the ArrayKlassesApp app class and its - // array classes. - dump2(null, topArchiveName, - "-Xlog:cds+dynamic=debug,cds+class=debug", - "-cp", appJar, mainClass) - .assertNormalExit(output -> { - output.shouldMatch("cds.class.*klasses.*array \\[LArrayKlassesApp;") - .shouldMatch("cds.class.*klasses.*array \\[\\[LArrayKlassesApp;") - .shouldMatch("cds.class.*klasses.*array \\[\\[\\[LArrayKlassesApp;"); - }); - - // Case 1 - // At runtime , the ArrayKlasesApp and its array class should be loaded - // from the dynamic archive. - run2(null, topArchiveName, runtimeLogOptions, - "-cp", appJar, mainClass) - .assertNormalExit(output -> { - output.shouldContain("ArrayKlassesApp source: shared objects file (top)") - .shouldContain("restore: ArrayKlassesApp with class loader: jdk.internal.loader.ClassLoaders$AppClassLoader") - .shouldContain("[LArrayKlassesApp; source: shared objects file (top)") - .shouldContain("restore: [LArrayKlassesApp; with class loader: jdk.internal.loader.ClassLoaders$AppClassLoader") - .shouldContain("[[LArrayKlassesApp; source: shared objects file (top)") - .shouldContain("restore: [[LArrayKlassesApp; with class loader: jdk.internal.loader.ClassLoaders$AppClassLoader") - .shouldContain("[[[LArrayKlassesApp; source: shared objects file (top)") - .shouldContain("restore: [[[LArrayKlassesApp; with class loader: jdk.internal.loader.ClassLoaders$AppClassLoader") - .shouldHaveExitValue(0); - }); - - // Case 2 - // Create a dynamic archive with the array classes of java/util/Date which - // is in the default CDS archive. - topArchiveName = getNewArchiveName(); - dump2(null, topArchiveName, - "-Xlog:class+load,cds+dynamic=debug,cds+class=debug", - "-cp", appJar, mainClass, "system") - .assertNormalExit(output -> { - output.shouldContain("java.util.Date source: shared objects file") - .shouldMatch("cds.class.*klasses.*array \\[Ljava.util.Date;") - .shouldMatch("cds.class.*klasses.*array \\[\\[Ljava.util.Date;") - .shouldMatch("cds.class.*klasses.*array \\[\\[\\[Ljava.util.Date;"); - }); - - // Case 2 - // At runtime, the java/util/Date class should be loaded from the default - // CDS archive; its array class should be loaded from the dynamic archive. - run2(null, topArchiveName, runtimeLogOptions, - "-cp", appJar, mainClass, "system") - .assertNormalExit(output -> { - output.shouldContain("java.util.Date source: shared objects file") - .shouldContain("restore: java.util.Date with class loader: boot") - .shouldContain("[Ljava.util.Date; source: shared objects file (top)") - .shouldContain("restore: [Ljava.util.Date; with class loader: boot") - .shouldContain("[[Ljava.util.Date; source: shared objects file (top)") - .shouldContain("restore: [[Ljava.util.Date; with class loader: boot") - .shouldContain("[[[Ljava.util.Date; source: shared objects file (top)") - .shouldContain("restore: [[[Ljava.util.Date; with class loader: boot") - .shouldHaveExitValue(0); - }); - - // Case 3 - // Create a dynamic archive with primitive arrays [[J and [[[J with [J - // already in the default CDS archive - topArchiveName = getNewArchiveName(); - dump2(null, topArchiveName, - "-Xlog:class+load,cds+dynamic=debug,cds+class=debug", - "-cp", appJar, mainClass, "primitive") - .assertNormalExit(output -> { - output.shouldMatch("cds.class.*klasses.*array \\[\\[J") - .shouldMatch("cds.class.*klasses.*array \\[\\[\\[J"); - }); - - // Case 3 - // At runtime, the [J should be loaded from the default CDS archive; - // the higher-dimension array should be loaded from the dynamic archive. - run2(null, topArchiveName, runtimeLogOptions, - "-cp", appJar, mainClass, "primitive") - .assertNormalExit(output -> { - output.shouldContain("[J source: shared objects file") - .shouldContain("restore: [J with class loader: boot") - .shouldContain("[[J source: shared objects file (top)") - .shouldContain("restore: [[J with class loader: boot") - .shouldContain("[[[J source: shared objects file (top)") - .shouldContain("restore: [[[J with class loader: boot") - .shouldHaveExitValue(0); - }); - - // Case 4 - // Create a dynamic archive with 2-, 3- and 4-dimension arrays of java/lang/Integer. - // The java/lang/Integer class and the 1-dimension array is in the default archive. - topArchiveName = getNewArchiveName(); - dump2(null, topArchiveName, - "-Xlog:class+load,cds+dynamic=debug,cds+class=debug", - "-cp", appJar, mainClass, "integer-array") - .assertNormalExit(output -> { - output.shouldMatch("cds.class.*klasses.*array \\[\\[Ljava.lang.Integer;") - .shouldMatch("cds.class.*klasses.*array \\[\\[\\[Ljava.lang.Integer;") - .shouldMatch("cds.class.*klasses.*array \\[\\[\\[\\[Ljava.lang.Integer;"); - }); - - // Case 4 - // At runtime, the 4-dimension array of java/lang/Integer should be - // loaded from the dynamic archive. - run2(null, topArchiveName, runtimeLogOptions, - "-cp", appJar, mainClass, "integer-array") - .assertNormalExit(output -> { - output.shouldContain("java.lang.Integer source: shared objects file") - .shouldContain("restore: java.lang.Integer with class loader: boot") - .shouldContain("restore: [Ljava.lang.Integer; with class loader: boot") - .shouldContain("[[Ljava.lang.Integer; source: shared objects file (top)") - .shouldContain("restore: [[Ljava.lang.Integer; with class loader: boot") - .shouldContain("[[[Ljava.lang.Integer; source: shared objects file (top)") - .shouldContain("restore: [[[Ljava.lang.Integer; with class loader: boot") - .shouldContain("[[[[Ljava.lang.Integer; source: shared objects file (top)") - .shouldContain("restore: [[[[Ljava.lang.Integer; with class loader: boot") - .shouldHaveExitValue(0); - }); - } -} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ArrayKlassesApp.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ArrayKlassesApp.java deleted file mode 100644 index 3c78e3e8dbe..00000000000 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ArrayKlassesApp.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -import java.lang.reflect.Array; -import java.util.Date; - -public class ArrayKlassesApp { - public static void main(String args[]) { - if (args.length == 1) { - if (args[0].equals("system")) { - Date[][][] array = new Date[1][2][2]; - int count = 0; - for (int i=0; i<1; i++) { - for (int j=0; j<2; j++) { - for (int k=0; k<2; k++) { - array[i][j][k] = new Date(); - count++; - array[i][j][k].setTime(20000 * count); - } - } - } - } else if (args[0].equals("primitive")) { - long[][][] larray = new long[1][2][2]; - long lcount = 0; - for (int i=0; i<1; i++) { - for (int j=0; j<2; j++) { - for (int k=0; k<2; k++) { - lcount++; - larray[i][j][k] = lcount; - } - } - } - } else if (args[0].equals("integer-array")) { - Integer[][][][] iarray = new Integer[4][4][4][4]; - int count = 0; - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - for (int k = 0; k < 4; k++) { - for (int l = 0; l < 4; l++) { - count++; - iarray[i][j][k][l] = new Integer(count); - } - } - } - } - System.out.println(iarray); - System.out.println(iarray.getClass()); - } - } else { - Object x = Array.newInstance(ArrayKlassesApp.class, 3,3,3); - System.out.println(x); - System.out.println(x.getClass()); - System.out.println(Array.getLength(x)); - } - } -} diff --git a/test/lib/jdk/test/lib/cds/CDSAppTester.java b/test/lib/jdk/test/lib/cds/CDSAppTester.java index fd244c6acc6..18356dd8aa9 100644 --- a/test/lib/jdk/test/lib/cds/CDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/CDSAppTester.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,6 +57,8 @@ abstract public class CDSAppTester { private int numProductionRuns = 0; private String whiteBoxJar = null; private boolean inOneStepTraining = false; + private boolean generateBaseArchive = false; + private String[] baseArchiveOptions = new String[0]; /** * All files created in the CDS/AOT workflow will be name + extension. E.g. @@ -343,7 +345,7 @@ abstract public class CDSAppTester { // VM options used by this test, we need to create a temporary static archive to be used with -XX:ArchiveClassesAtExit. private String getBaseArchiveForDynamicArchive() throws Exception { WhiteBox wb = WhiteBox.getWhiteBox(); - if (wb.isSharingEnabled()) { + if (wb.isSharingEnabled() && !generateBaseArchive) { // This current JVM is able to use a default CDS archive included by the JDK, so // if we launch a JVM child process (with the same set of options as the current JVM), // that process is also able to use the same default CDS archive for creating @@ -356,6 +358,7 @@ abstract public class CDSAppTester { if (!f.exists()) { CDSOptions opts = new CDSOptions(); opts.setArchiveName(tempBaseArchiveFile); + opts.addSuffix(baseArchiveOptions); opts.addSuffix("-Djava.class.path="); OutputAnalyzer out = CDSTestUtils.createArchive(opts); CDSTestUtils.checkBaseDump(out); @@ -364,6 +367,16 @@ abstract public class CDSAppTester { } } + public CDSAppTester setGenerateBaseArchive(boolean b) { + this.generateBaseArchive = b; + return this; + } + + public CDSAppTester setBaseArchiveOptions(String... opts) { + this.baseArchiveOptions = opts; + return this; + } + private OutputAnalyzer dumpDynamicArchive() throws Exception { RunMode runMode = RunMode.DUMP_DYNAMIC; String[] cmdLine = new String[0]; diff --git a/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java b/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java index c869cfa366b..a8bba9c76e2 100644 --- a/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,9 +58,11 @@ public class SimpleCDSAppTester { private String modulepath; private String[] appCommandLine; private String[] vmArgs = new String[] {}; + private Tester tester; private SimpleCDSAppTester(String name) { this.name = name; + this.tester = new Tester(name); } public static SimpleCDSAppTester of(String name) { @@ -101,6 +103,16 @@ public class SimpleCDSAppTester { return this; } + public SimpleCDSAppTester setGenerateBaseArchive(boolean b) { + tester.setGenerateBaseArchive(b); + return this; + } + + public SimpleCDSAppTester setBaseArchiveOptions(String... opts) { + tester.setBaseArchiveOptions(opts); + return this; + } + public SimpleCDSAppTester setTrainingChecker(BiConsumer checker) { this.trainingChecker = checker; return this; @@ -181,17 +193,22 @@ public class SimpleCDSAppTester { } public SimpleCDSAppTester runStaticWorkflow() throws Exception { - (new Tester(name)).runStaticWorkflow(); + tester.runStaticWorkflow(); + return this; + } + + public SimpleCDSAppTester runDynamicWorkflow() throws Exception { + tester.runDynamicWorkflow(); return this; } public SimpleCDSAppTester runAOTWorkflow() throws Exception { - (new Tester(name)).runAOTWorkflow(); + tester.runAOTWorkflow(); return this; } public SimpleCDSAppTester run(String args[]) throws Exception { - (new Tester(name)).run(args); + tester.run(args); return this; } } From 8212993ac331d8761ddb7c0eef23dbfcc6ca0c7d Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Thu, 8 Jan 2026 18:51:25 +0000 Subject: [PATCH 21/24] 8374540: Add comment describing implementation choices of Math.fma Reviewed-by: rgiulietti --- .../share/classes/java/lang/Math.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java index 0f39ecf0a8a..55659bed57b 100644 --- a/src/java.base/share/classes/java/lang/Math.java +++ b/src/java.base/share/classes/java/lang/Math.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2378,6 +2378,20 @@ public final class Math { */ @IntrinsicCandidate public static double fma(double a, double b, double c) { + // Implementation note: this method is intentionally coded in + // a straightforward manner relying on BigDecimal for the + // heavy-lifting of the numerical computation. It would be + // possible for the computation to be done solely using binary + // floating-point and integer operations, at the cost of more + // complicated logic. Since most processors have hardware + // support for fma and this method is an intrinsic candidate, + // the software implementation below would only be used on + // processors without native fma support (and also possibly on + // processors with native fma support while running in the + // interpreter). Therefore, the direct performance of the code + // is less of a concern than the code's simplicity, + // maintainability, and testability. + /* * Infinity and NaN arithmetic is not quite the same with two * roundings as opposed to just one so the simple expression @@ -2492,6 +2506,8 @@ public final class Math { */ @IntrinsicCandidate public static float fma(float a, float b, float c) { + // See implementation note in fma(double, double, double). + if (Float.isFinite(a) && Float.isFinite(b) && Float.isFinite(c)) { if (a == 0.0 || b == 0.0) { return a * b + c; // Handled signed zero cases From 1342db0bde25c111b25f4339ae2a858dc3b15687 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Thu, 8 Jan 2026 19:02:06 +0000 Subject: [PATCH 22/24] 8374051: Incorrect parameterized testing of exceptions in AbstractDateTimeTest.java Reviewed-by: naoto, rriggs --- .../tck/java/time/AbstractDateTimeTest.java | 227 +++++++----------- .../java/time/tck/java/time/TCKInstant.java | 5 +- .../java/time/tck/java/time/TCKLocalDate.java | 4 +- .../java/time/tck/java/time/TCKLocalTime.java | 5 +- .../java/time/tck/java/time/TCKMonthDay.java | 4 +- .../time/tck/java/time/TCKOffsetDateTime.java | 5 +- .../time/tck/java/time/TCKOffsetTime.java | 5 +- .../java/time/tck/java/time/TCKYearMonth.java | 4 +- .../time/tck/java/time/TCKZonedDateTime.java | 4 +- 9 files changed, 112 insertions(+), 151 deletions(-) diff --git a/test/jdk/java/time/tck/java/time/AbstractDateTimeTest.java b/test/jdk/java/time/tck/java/time/AbstractDateTimeTest.java index f96ab522517..cf138419c83 100644 --- a/test/jdk/java/time/tck/java/time/AbstractDateTimeTest.java +++ b/test/jdk/java/time/tck/java/time/AbstractDateTimeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,8 +59,11 @@ */ package tck.java.time; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.DateTimeException; import java.time.temporal.TemporalAccessor; @@ -68,8 +71,8 @@ import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; import java.util.List; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import test.java.time.temporal.MockFieldNoValue; @@ -99,183 +102,137 @@ public abstract class AbstractDateTimeTest extends AbstractTCKTest { //----------------------------------------------------------------------- // isSupported(TemporalField) //----------------------------------------------------------------------- - @Test() - public void basicTest_isSupported_TemporalField_supported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : validFields()) { - assertEquals(true, sample.isSupported(field), "Failed on " + sample + " " + field); - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_isSupported_TemporalField_supported(TemporalAccessor sample) { + for (TemporalField field : validFields()) { + assertTrue(sample.isSupported(field), "Failed on " + sample + " " + field); } } - @Test() - public void basicTest_isSupported_TemporalField_unsupported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : invalidFields()) { - assertEquals(false, sample.isSupported(field), "Failed on " + sample + " " + field); - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_isSupported_TemporalField_unsupported(TemporalAccessor sample) { + for (TemporalField field : invalidFields()) { + assertFalse(sample.isSupported(field), "Failed on " + sample + " " + field); } } - @Test() - public void basicTest_isSupported_TemporalField_null() { - for (TemporalAccessor sample : samples()) { - assertEquals(false, sample.isSupported(null), "Failed on " + sample); - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_isSupported_TemporalField_null(TemporalAccessor sample) { + assertFalse(sample.isSupported(null), "Failed on " + sample); } //----------------------------------------------------------------------- // range(TemporalField) //----------------------------------------------------------------------- - @Test() - public void basicTest_range_TemporalField_supported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : validFields()) { - sample.range(field); // no exception - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_range_TemporalField_supported(TemporalAccessor sample) { + for (TemporalField field : validFields()) { + assertDoesNotThrow(() -> sample.range(field)); } } - @Test() - public void basicTest_range_TemporalField_unsupported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : invalidFields()) { - try { - sample.range(field); - fail("Failed on " + sample + " " + field); - } catch (DateTimeException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_range_TemporalField_unsupported(TemporalAccessor sample) { + for (TemporalField field : invalidFields()) { + assertThrows(DateTimeException.class, + () -> sample.range(field), "Failed on " + sample + " " + field); } } - @Test() - public void basicTest_range_TemporalField_null() { - for (TemporalAccessor sample : samples()) { - try { - sample.range(null); - fail("Failed on " + sample); - } catch (NullPointerException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_range_TemporalField_null(TemporalAccessor sample) { + assertThrows(NullPointerException.class, + () -> sample.range(null), "Failed on " + sample); } //----------------------------------------------------------------------- // get(TemporalField) //----------------------------------------------------------------------- - @Test() - public void basicTest_get_TemporalField_supported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : validFields()) { - if (sample.range(field).isIntValue()) { - sample.get(field); // no exception - } else { - try { - sample.get(field); - fail("Failed on " + sample + " " + field); - } catch (DateTimeException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_get_TemporalField_supported(TemporalAccessor sample) { + for (TemporalField field : validFields()) { + if (sample.range(field).isIntValue()) { + assertDoesNotThrow(() -> sample.get(field)); + } else { + assertThrows(DateTimeException.class, + () -> sample.get(field), "Failed on " + sample + " " + field); } } } - @Test() - public void basicTest_get_TemporalField_unsupported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : invalidFields()) { - try { - sample.get(field); - fail("Failed on " + sample + " " + field); - } catch (DateTimeException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_get_TemporalField_unsupported(TemporalAccessor sample) { + for (TemporalField field : invalidFields()) { + assertThrows(DateTimeException.class, + () -> sample.get(field), "Failed on " + sample + " " + field); } } - @Test - public void test_get_TemporalField_invalidField() { - Assertions.assertThrows(DateTimeException.class, () -> { - for (TemporalAccessor sample : samples()) { - sample.get(MockFieldNoValue.INSTANCE); - } - }); + @ParameterizedTest + @MethodSource("samples") + public void test_get_TemporalField_invalidField(TemporalAccessor sample) { + assertThrows(DateTimeException.class, + () -> sample.get(MockFieldNoValue.INSTANCE)); } - @Test() - public void basicTest_get_TemporalField_null() { - for (TemporalAccessor sample : samples()) { - try { - sample.get(null); - fail("Failed on " + sample); - } catch (NullPointerException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_get_TemporalField_null(TemporalAccessor sample) { + assertThrows(NullPointerException.class, + () -> sample.get(null), "Failed on " + sample); } //----------------------------------------------------------------------- // getLong(TemporalField) //----------------------------------------------------------------------- - @Test() - public void basicTest_getLong_TemporalField_supported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : validFields()) { - sample.getLong(field); // no exception - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_getLong_TemporalField_supported(TemporalAccessor sample) { + for (TemporalField field : validFields()) { + sample.getLong(field); } } - @Test() - public void basicTest_getLong_TemporalField_unsupported() { - for (TemporalAccessor sample : samples()) { - for (TemporalField field : invalidFields()) { - try { - sample.getLong(field); - fail("Failed on " + sample + " " + field); - } catch (DateTimeException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_getLong_TemporalField_unsupported(TemporalAccessor sample) { + for (TemporalField field : invalidFields()) { + assertThrows(DateTimeException.class, + () -> sample.getLong(field), "Failed on " + sample + " " + field); } } - @Test - public void test_getLong_TemporalField_invalidField() { - Assertions.assertThrows(DateTimeException.class, () -> { - for (TemporalAccessor sample : samples()) { - sample.getLong(MockFieldNoValue.INSTANCE); - } - }); + @ParameterizedTest + @MethodSource("samples") + public void test_getLong_TemporalField_invalidField(TemporalAccessor sample) { + assertThrows(DateTimeException.class, + () -> sample.getLong(MockFieldNoValue.INSTANCE)); } - @Test() - public void basicTest_getLong_TemporalField_null() { - for (TemporalAccessor sample : samples()) { - try { - sample.getLong(null); - fail("Failed on " + sample); - } catch (NullPointerException ex) { - // expected - } - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_getLong_TemporalField_null(TemporalAccessor sample) { + assertThrows(NullPointerException.class, + () -> sample.getLong(null), "Failed on " + sample); } //----------------------------------------------------------------------- - @Test - public void basicTest_query() { - for (TemporalAccessor sample : samples()) { - assertEquals("foo", sample.query(new TemporalQuery() { - @Override - public String queryFrom(TemporalAccessor temporal) { - return "foo"; - } - })); - } + @ParameterizedTest + @MethodSource("samples") + public void basicTest_query(TemporalAccessor sample) { + assertEquals("foo", sample.query(new TemporalQuery() { + @Override + public String queryFrom(TemporalAccessor temporal) { + return "foo"; + } + })); } - } diff --git a/test/jdk/java/time/tck/java/time/TCKInstant.java b/test/jdk/java/time/tck/java/time/TCKInstant.java index 4a512d37665..8c245d69810 100644 --- a/test/jdk/java/time/tck/java/time/TCKInstant.java +++ b/test/jdk/java/time/tck/java/time/TCKInstant.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -139,7 +139,8 @@ public class TCKInstant extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_12345_123456789, Instant.MIN, Instant.MAX, Instant.EPOCH}; + TemporalAccessor[] array = {Instant.ofEpochSecond(12345, 123456789), + Instant.MIN, Instant.MAX, Instant.EPOCH}; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKLocalDate.java b/test/jdk/java/time/tck/java/time/TCKLocalDate.java index 281007e7665..132aa31ed97 100644 --- a/test/jdk/java/time/tck/java/time/TCKLocalDate.java +++ b/test/jdk/java/time/tck/java/time/TCKLocalDate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -170,7 +170,7 @@ public class TCKLocalDate extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_2007_07_15, LocalDate.MAX, LocalDate.MIN, }; + TemporalAccessor[] array = {LocalDate.of(2007, 7, 15), LocalDate.MAX, LocalDate.MIN, }; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKLocalTime.java b/test/jdk/java/time/tck/java/time/TCKLocalTime.java index bdec611ad76..e2fc4ea2ca7 100644 --- a/test/jdk/java/time/tck/java/time/TCKLocalTime.java +++ b/test/jdk/java/time/tck/java/time/TCKLocalTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -161,7 +161,8 @@ public class TCKLocalTime extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_12_30_40_987654321, LocalTime.MIN, LocalTime.MAX, LocalTime.MIDNIGHT, LocalTime.NOON}; + TemporalAccessor[] array = {LocalTime.of(12, 30, 40, 987654321), + LocalTime.MIN, LocalTime.MAX, LocalTime.MIDNIGHT, LocalTime.NOON}; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKMonthDay.java b/test/jdk/java/time/tck/java/time/TCKMonthDay.java index 84326a4c47a..36ad4af069e 100644 --- a/test/jdk/java/time/tck/java/time/TCKMonthDay.java +++ b/test/jdk/java/time/tck/java/time/TCKMonthDay.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -118,7 +118,7 @@ public class TCKMonthDay extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_07_15, }; + TemporalAccessor[] array = {MonthDay.of(7, 15), }; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKOffsetDateTime.java b/test/jdk/java/time/tck/java/time/TCKOffsetDateTime.java index f2b98ee9972..6ba0318890f 100644 --- a/test/jdk/java/time/tck/java/time/TCKOffsetDateTime.java +++ b/test/jdk/java/time/tck/java/time/TCKOffsetDateTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -167,7 +167,8 @@ public class TCKOffsetDateTime extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_2008_6_30_11_30_59_000000500, OffsetDateTime.MIN, OffsetDateTime.MAX}; + TemporalAccessor[] array = {OffsetDateTime.of(2008, 6, 30, 11, 30, 59, 500, OFFSET_PONE), + OffsetDateTime.MIN, OffsetDateTime.MAX}; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKOffsetTime.java b/test/jdk/java/time/tck/java/time/TCKOffsetTime.java index 350b21574fa..7ea9504edbc 100644 --- a/test/jdk/java/time/tck/java/time/TCKOffsetTime.java +++ b/test/jdk/java/time/tck/java/time/TCKOffsetTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -149,7 +149,8 @@ public class TCKOffsetTime extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_11_30_59_500_PONE, OffsetTime.MIN, OffsetTime.MAX}; + TemporalAccessor[] array = {OffsetTime.of(11, 30, 59, 500, OFFSET_PONE), + OffsetTime.MIN, OffsetTime.MAX}; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKYearMonth.java b/test/jdk/java/time/tck/java/time/TCKYearMonth.java index 3f4fda2a924..e39bbe90cf9 100644 --- a/test/jdk/java/time/tck/java/time/TCKYearMonth.java +++ b/test/jdk/java/time/tck/java/time/TCKYearMonth.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -134,7 +134,7 @@ public class TCKYearMonth extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_2008_06, }; + TemporalAccessor[] array = {YearMonth.of(2008, 6), }; return Arrays.asList(array); } diff --git a/test/jdk/java/time/tck/java/time/TCKZonedDateTime.java b/test/jdk/java/time/tck/java/time/TCKZonedDateTime.java index 847035f121a..73adbbba88c 100644 --- a/test/jdk/java/time/tck/java/time/TCKZonedDateTime.java +++ b/test/jdk/java/time/tck/java/time/TCKZonedDateTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -176,7 +176,7 @@ public class TCKZonedDateTime extends AbstractDateTimeTest { //----------------------------------------------------------------------- @Override protected List samples() { - TemporalAccessor[] array = {TEST_DATE_TIME, }; + TemporalAccessor[] array = {ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30, 59, 500), ZONE_0100), }; return Arrays.asList(array); } From 982aa3f8ead84817be5373c3257d48feab1758d3 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 8 Jan 2026 19:47:01 +0000 Subject: [PATCH 23/24] 8336654: [lworld] Tests depending on sun.awt.AppContext can fail when run with migrated classes Reviewed-by: serb, azvegint --- .../classes/com/apple/laf/AquaUtils.java | 41 +++++-- .../share/classes/sun/awt/AppContext.java | 19 --- .../classes/sun/awt/image/ImageCache.java | 8 +- .../swing/Security/6657138/bug6657138.java | 108 ------------------ 4 files changed, 33 insertions(+), 143 deletions(-) delete mode 100644 test/jdk/javax/swing/Security/6657138/bug6657138.java diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java index da1785a9a63..5e68cc7a82c 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaUtils.java @@ -35,8 +35,6 @@ import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.UIResource; -import sun.awt.AppContext; - import sun.lwawt.macosx.CPlatformWindow; import sun.swing.SwingUtilities2; @@ -150,13 +148,34 @@ final class AquaUtils { protected abstract T create(); } - abstract static class RecyclableSingleton { - final T get() { - return AppContext.getSoftReferenceValue(this, () -> getInstance()); - } + abstract static class LazySingleton { + T instance; - void reset() { - AppContext.getAppContext().remove(this); + final T get() { + if (instance == null) { + instance = getInstance(); + } + return instance; + } + + abstract T getInstance(); + } + + abstract static class RecyclableSingleton { + + SoftReference ref; + + final T get() { + T instance; + if (ref != null) { + instance = ref.get(); + if (instance != null) { + return instance; + } + } + instance = getInstance(); + ref = new SoftReference<>(instance); + return instance; } abstract T getInstance(); @@ -197,11 +216,11 @@ final class AquaUtils { protected abstract V getInstance(K key); } - private static final RecyclableSingleton enableAnimations = new RecyclableSingleton() { + private static final LazySingleton enableAnimations = new LazySingleton() { @Override protected Boolean getInstance() { - final String sizeProperty = System.getProperty(ANIMATIONS_PROPERTY); - return !"false".equals(sizeProperty); // should be true by default + final String animationsProperty = System.getProperty(ANIMATIONS_PROPERTY); + return !"false".equals(animationsProperty); // should be true by default } }; private static boolean animationsEnabled() { diff --git a/src/java.desktop/share/classes/sun/awt/AppContext.java b/src/java.desktop/share/classes/sun/awt/AppContext.java index 39df737badb..01b1dd09887 100644 --- a/src/java.desktop/share/classes/sun/awt/AppContext.java +++ b/src/java.desktop/share/classes/sun/awt/AppContext.java @@ -47,7 +47,6 @@ import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; /** * The AppContext is a table referenced by ThreadGroup which stores @@ -735,24 +734,6 @@ public final class AppContext { } return changeSupport.getPropertyChangeListeners(propertyName); } - - public static T getSoftReferenceValue(Object key, - Supplier supplier) { - - final AppContext appContext = AppContext.getAppContext(); - @SuppressWarnings("unchecked") - SoftReference ref = (SoftReference) appContext.get(key); - if (ref != null) { - final T object = ref.get(); - if (object != null) { - return object; - } - } - final T object = supplier.get(); - ref = new SoftReference<>(object); - appContext.put(key, ref); - return object; - } } final class MostRecentKeyValue { diff --git a/src/java.desktop/share/classes/sun/awt/image/ImageCache.java b/src/java.desktop/share/classes/sun/awt/image/ImageCache.java index eed1aa5bdb7..b7ac69923da 100644 --- a/src/java.desktop/share/classes/sun/awt/image/ImageCache.java +++ b/src/java.desktop/share/classes/sun/awt/image/ImageCache.java @@ -29,7 +29,6 @@ import java.awt.*; import java.lang.ref.*; import java.util.*; import java.util.concurrent.locks.*; -import sun.awt.AppContext; /** * ImageCache - A fixed pixel count sized cache of Images keyed by arbitrary @@ -37,8 +36,6 @@ import sun.awt.AppContext; * dropped by the GC if heap memory gets tight. When our size hits max pixel * count least recently requested images are removed first. * - * The ImageCache must be used from the thread with an AppContext only. - * */ public final class ImageCache { @@ -56,9 +53,10 @@ public final class ImageCache { // Reference queue for tracking lost softreferences to images in the cache private final ReferenceQueue referenceQueue = new ReferenceQueue<>(); + private static final ImageCache instance = new ImageCache(); + public static ImageCache getInstance() { - return AppContext.getSoftReferenceValue(ImageCache.class, - () -> new ImageCache()); + return instance; } ImageCache(final int maxPixelCount) { diff --git a/test/jdk/javax/swing/Security/6657138/bug6657138.java b/test/jdk/javax/swing/Security/6657138/bug6657138.java deleted file mode 100644 index 4670d57595c..00000000000 --- a/test/jdk/javax/swing/Security/6657138/bug6657138.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @bug 6657138 - * @summary Verifies that buttons and labels don't share their ui's across appContexts - * @author Alexander Potochkin - * @modules java.desktop/sun.awt - */ - -import sun.awt.SunToolkit; - -import javax.swing.*; -import javax.swing.plaf.ButtonUI; -import javax.swing.plaf.ComponentUI; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class bug6657138 implements Runnable { - - private static Map> componentMap = - Collections.synchronizedMap( - new HashMap>()); - - public void run() { - SunToolkit.createNewAppContext(); - try { - testUIMap(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static void testUIMap() throws Exception { - UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels(); - Set components = componentMap.keySet(); - for (JComponent c : components) { - Map uiMap = componentMap.get(c); - - for (UIManager.LookAndFeelInfo laf : lafs) { - if ("Nimbus".equals(laf.getName())) { - // for some unclear reasons - // Nimbus ui delegate for a button is null - // when this method is called from the new AppContext - continue; - } - String className = laf.getClassName(); - try { - UIManager.setLookAndFeel(className); - } catch (final UnsupportedLookAndFeelException ignored) { - continue; - } - ComponentUI ui = UIManager.getUI(c); - if (ui == null) { - throw new RuntimeException("UI is null for " + c); - } - if (ui == uiMap.get(laf.getName())) { - throw new RuntimeException( - "Two AppContexts share the same UI delegate! \n" + - c + "\n" + ui); - } - uiMap.put(laf.getName(), ui); - } - } - } - - public static void main(String[] args) throws Exception { - componentMap.put(new JButton("JButton"), - new HashMap()); - componentMap.put(new JToggleButton("JToggleButton"), - new HashMap()); - componentMap.put(new JRadioButton("JRadioButton"), - new HashMap()); - componentMap.put(new JCheckBox("JCheckBox"), - new HashMap()); - componentMap.put(new JCheckBox("JLabel"), - new HashMap()); - testUIMap(); - ThreadGroup group = new ThreadGroup("6657138"); - Thread thread = new Thread(group, new bug6657138()); - thread.start(); - thread.join(); - } -} - From 385c4f8180d30c0e41b848eb4b2c1c8788211422 Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Thu, 8 Jan 2026 20:46:38 +0000 Subject: [PATCH 24/24] 8373714: Shenandoah: Register heuristic penalties following a degenerated GC Reviewed-by: wkemper --- .../gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp | 4 ++-- .../gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp | 2 +- .../share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp | 2 +- .../share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp | 2 +- .../gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp | 4 ++-- .../gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp | 2 +- src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp | 4 ++++ 7 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index 41535f302d7..7a8bd55c795 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -183,8 +183,8 @@ void ShenandoahAdaptiveHeuristics::record_success_concurrent() { } } -void ShenandoahAdaptiveHeuristics::record_success_degenerated() { - ShenandoahHeuristics::record_success_degenerated(); +void ShenandoahAdaptiveHeuristics::record_degenerated() { + ShenandoahHeuristics::record_degenerated(); // Adjust both trigger's parameters in the case of a degenerated GC because // either of them should have triggered earlier to avoid this case. adjust_margin_of_error(DEGENERATE_PENALTY_SD); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp index 66bfc3375a3..1ba18f37c2b 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp @@ -114,7 +114,7 @@ public: virtual void record_cycle_start() override; virtual void record_success_concurrent() override; - virtual void record_success_degenerated() override; + virtual void record_degenerated() override; virtual void record_success_full() override; virtual bool should_start_gc() override; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index 478c5696188..eb740cfac61 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -243,7 +243,7 @@ void ShenandoahHeuristics::record_success_concurrent() { adjust_penalty(Concurrent_Adjust); } -void ShenandoahHeuristics::record_success_degenerated() { +void ShenandoahHeuristics::record_degenerated() { adjust_penalty(Degenerated_Penalty); } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp index 3cd2cb1d171..fb8cfb36353 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -218,7 +218,7 @@ public: virtual void record_success_concurrent(); - virtual void record_success_degenerated(); + virtual void record_degenerated(); virtual void record_success_full(); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index 8bf068df0a8..029a4dd98fb 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -766,10 +766,10 @@ void ShenandoahOldHeuristics::record_success_concurrent() { this->ShenandoahHeuristics::record_success_concurrent(); } -void ShenandoahOldHeuristics::record_success_degenerated() { +void ShenandoahOldHeuristics::record_degenerated() { // Forget any triggers that occurred while OLD GC was ongoing. If we really need to start another, it will retrigger. clear_triggers(); - this->ShenandoahHeuristics::record_success_degenerated(); + this->ShenandoahHeuristics::record_degenerated(); } void ShenandoahOldHeuristics::record_success_full() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp index 288d3d68e56..f38194c1ee7 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp @@ -201,7 +201,7 @@ public: void record_success_concurrent() override; - void record_success_degenerated() override; + void record_degenerated() override; void record_success_full() override; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index cd079d29afe..333bdbc6e72 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -313,8 +313,12 @@ void ShenandoahDegenGC::op_degenerated() { policy->record_degenerated(_generation->is_young(), _abbreviated, progress); if (progress) { heap->notify_gc_progress(); + _generation->heuristics()->record_degenerated(); } else if (!heap->mode()->is_generational() || policy->generational_should_upgrade_degenerated_gc()) { + // Upgrade to full GC, register full-GC impact on heuristics. op_degenerated_futile(); + } else { + _generation->heuristics()->record_degenerated(); } }