From 28e96d76b0c7a792cc88bb9183e9cb6a83fcaba2 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Fri, 27 Mar 2026 17:57:30 +0000 Subject: [PATCH 01/25] 8377976: GenShen: Explicit GC requests must clear concurrent gc request cancellation Reviewed-by: kdnilsen --- .../shenandoah/shenandoahGenerationalControlThread.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index ec33e671053..064f43ffd6b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -121,10 +121,11 @@ void ShenandoahGenerationalControlThread::check_for_request(ShenandoahGCRequest& assert(request.generation != nullptr, "Must know which generation to use for degenerated cycle"); } } else { - if (request.cause == GCCause::_shenandoah_concurrent_gc) { - // This is a regulator request. It is also possible that the regulator "canceled" an old mark, - // so we can clear that here. This clear operation will only clear the cancellation if it is - // a regulator request. + if (request.cause == GCCause::_shenandoah_concurrent_gc || ShenandoahCollectorPolicy::is_explicit_gc(request.cause)) { + // This is a regulator request or an explicit gc request. Note that an explicit gc request is allowed to + // "upgrade" a regulator request. It is possible that the regulator "canceled" an old mark, so we must + // clear that cancellation here or the explicit gc cycle will erroneously detect it as a cancellation. + // This clear operation will only clear the cancellation if it was set by regulator request. _heap->clear_cancellation(GCCause::_shenandoah_concurrent_gc); } request.generation = _requested_generation; From 79668b007db71921938d7639ba15f8ce44682f4f Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Fri, 27 Mar 2026 18:13:38 +0000 Subject: [PATCH 02/25] 8380710: nmethod::finalize_relocations() should be called before ICache::invalidate_range() is called Reviewed-by: adinn, dlong --- src/hotspot/share/asm/codeBuffer.cpp | 4 ---- src/hotspot/share/code/codeBlob.cpp | 4 ++++ src/hotspot/share/code/nmethod.cpp | 6 ++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index ba525588f32..c6078c0ceee 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -32,7 +32,6 @@ #include "oops/methodCounters.hpp" #include "oops/methodData.hpp" #include "oops/oop.inline.hpp" -#include "runtime/icache.hpp" #include "runtime/safepointVerifiers.hpp" #include "utilities/align.hpp" #include "utilities/copy.hpp" @@ -745,9 +744,6 @@ void CodeBuffer::copy_code_to(CodeBlob* dest_blob) { // Done moving code bytes; were they the right size? assert((int)align_up(dest.total_content_size(), oopSize) == dest_blob->content_size(), "sanity"); - - // Flush generated code - ICache::invalidate_range(dest_blob->code_begin(), dest_blob->code_size()); } // Move all my code into another code buffer. Consult applicable diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 5b68ce48c87..9961381c31d 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -40,6 +40,7 @@ #include "prims/forte.hpp" #include "prims/jvmtiExport.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/icache.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaFrameAnchor.hpp" #include "runtime/jniHandles.inline.hpp" @@ -324,6 +325,9 @@ RuntimeBlob::RuntimeBlob( align_up(cb->total_relocation_size(), oopSize)) { cb->copy_code_and_locs_to(this); + + // Flush generated code + ICache::invalidate_range(code_begin(), code_size()); } void RuntimeBlob::free(RuntimeBlob* blob) { diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 5a6ed8ab3ed..0e2aa208854 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -66,6 +66,7 @@ #include "runtime/flags/flagSetting.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/icache.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/orderAccess.hpp" #include "runtime/os.hpp" @@ -1253,6 +1254,9 @@ void nmethod::post_init() { finalize_relocations(); + // Flush generated code + ICache::invalidate_range(code_begin(), code_size()); + Universe::heap()->register_nmethod(this); DEBUG_ONLY(Universe::heap()->verify_nmethod(this)); @@ -1584,8 +1588,6 @@ nmethod* nmethod::relocate(CodeBlobType code_blob_type) { // Attempt to start using the copy if (nm_copy->make_in_use()) { - ICache::invalidate_range(nm_copy->code_begin(), nm_copy->code_size()); - methodHandle mh(Thread::current(), nm_copy->method()); nm_copy->method()->set_code(mh, nm_copy); From 7ffc4a4fb4cc520c4f469865645764045e72cb26 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Fri, 27 Mar 2026 19:23:39 +0000 Subject: [PATCH 03/25] 8381019: Remove AppContext usage from AccessBridge Reviewed-by: serb, kizune --- .../accessibility/AccessibleContext.java | 19 +--- .../share/classes/module-info.java | 3 +- .../share/classes/sun/awt/AWTAccessor.java | 2 - .../accessibility/internal/AccessBridge.java | 89 +++---------------- 4 files changed, 13 insertions(+), 100 deletions(-) diff --git a/src/java.desktop/share/classes/javax/accessibility/AccessibleContext.java b/src/java.desktop/share/classes/javax/accessibility/AccessibleContext.java index 096ca3aef44..e7fc58b0825 100644 --- a/src/java.desktop/share/classes/javax/accessibility/AccessibleContext.java +++ b/src/java.desktop/share/classes/javax/accessibility/AccessibleContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,6 @@ import java.beans.PropertyChangeSupport; import java.util.Locale; import sun.awt.AWTAccessor; -import sun.awt.AppContext; /** * {@code AccessibleContext} represents the minimum information all accessible @@ -84,24 +83,8 @@ public abstract class AccessibleContext { */ protected AccessibleContext() {} - /** - * The {@code AppContext} that should be used to dispatch events for this - * {@code AccessibleContext}. - */ - private volatile AppContext targetAppContext; - static { AWTAccessor.setAccessibleContextAccessor(new AWTAccessor.AccessibleContextAccessor() { - @Override - public void setAppContext(AccessibleContext accessibleContext, AppContext appContext) { - accessibleContext.targetAppContext = appContext; - } - - @Override - public AppContext getAppContext(AccessibleContext accessibleContext) { - return accessibleContext.targetAppContext; - } - @Override public Object getNativeAXResource(AccessibleContext accessibleContext) { return accessibleContext.nativeAXResource; diff --git a/src/java.desktop/share/classes/module-info.java b/src/java.desktop/share/classes/module-info.java index 6d34d934194..57392f71321 100644 --- a/src/java.desktop/share/classes/module-info.java +++ b/src/java.desktop/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -115,7 +115,6 @@ module java.desktop { // qualified exports may be inserted at build time // see make/GensrcModuleInfo.gmk exports sun.awt to - jdk.accessibility, jdk.unsupported.desktop; exports java.awt.dnd.peer to jdk.unsupported.desktop; diff --git a/src/java.desktop/share/classes/sun/awt/AWTAccessor.java b/src/java.desktop/share/classes/sun/awt/AWTAccessor.java index f98118171b4..143d7e6198c 100644 --- a/src/java.desktop/share/classes/sun/awt/AWTAccessor.java +++ b/src/java.desktop/share/classes/sun/awt/AWTAccessor.java @@ -755,8 +755,6 @@ public final class AWTAccessor { * An accessor object for the AccessibleContext class */ public interface AccessibleContextAccessor { - void setAppContext(AccessibleContext accessibleContext, AppContext appContext); - AppContext getAppContext(AccessibleContext accessibleContext); Object getNativeAXResource(AccessibleContext accessibleContext); void setNativeAXResource(AccessibleContext accessibleContext, Object value); } diff --git a/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java b/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java index c932d0f73ff..718acf6a6b8 100644 --- a/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java +++ b/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.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 @@ -120,9 +120,6 @@ import com.sun.java.accessibility.util.AccessibilityEventMonitor; import com.sun.java.accessibility.util.EventQueueMonitor; import com.sun.java.accessibility.util.SwingEventMonitor; import com.sun.java.accessibility.util.Translator; -import sun.awt.AWTAccessor; -import sun.awt.AppContext; -import sun.awt.SunToolkit; /* * Note: This class has to be public. It's loaded from the VM like this: @@ -5292,7 +5289,6 @@ public final class AccessBridge { ac = a.getAccessibleContext(); } if (ac != null) { - InvocationUtils.registerAccessibleContext(ac, AppContext.getAppContext()); accessBridge.debugString("[INFO]: AccessibleContext: " + ac); String propertyName = e.getPropertyName(); @@ -5385,11 +5381,9 @@ public final class AccessBridge { if (e.getOldValue() instanceof AccessibleContext) { oldAC = (AccessibleContext) e.getOldValue(); - InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext()); } if (e.getNewValue() instanceof AccessibleContext) { newAC = (AccessibleContext) e.getNewValue(); - InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext()); } accessBridge.debugString("[INFO]: - about to call propertyChildChange() old AC: " + oldAC + "new AC: " + newAC); accessBridge.propertyChildChange(e, ac, oldAC, newAC); @@ -5455,8 +5449,6 @@ public final class AccessBridge { prevAC = newAC; accessBridge.debugString("[INFO]: - about to call propertyActiveDescendentChange() AC: " + ac + " old AC: " + oldAC + "new AC: " + newAC); - InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext()); - InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext()); accessBridge.propertyActiveDescendentChange(e, ac, oldAC, newAC); } @@ -5493,14 +5485,12 @@ public final class AccessBridge { // selected. The menu itself is selected. FocusEvent e = new FocusEvent(penult, FocusEvent.FOCUS_GAINED); AccessibleContext context = penult.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, SunToolkit.targetToAppContext(penult)); accessBridge.focusGained(e, context); } else if (penult instanceof JPopupMenu) { // This is a popup with an item selected FocusEvent e = new FocusEvent(last, FocusEvent.FOCUS_GAINED); AccessibleContext focusedAC = last.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(last)); accessBridge.debugString("[INFO]: - about to call focusGained() AC: " + focusedAC); accessBridge.focusGained(e, focusedAC); } @@ -5511,7 +5501,6 @@ public final class AccessBridge { FocusEvent e = new FocusEvent(focusOwner, FocusEvent.FOCUS_GAINED); AccessibleContext focusedAC = focusOwner.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(focusOwner)); accessBridge.debugString("[INFO]: - about to call focusGained() AC: " + focusedAC); accessBridge.focusGained(e, focusedAC); } @@ -5524,7 +5513,6 @@ public final class AccessBridge { if (a != null) { accessBridge.debugString("[INFO]: - about to call focusLost() AC: " + a.getAccessibleContext()); AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.focusLost(e, context); } } @@ -5538,7 +5526,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.caretUpdate(e, context); } } @@ -5553,7 +5540,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.mouseClicked(e, context); } } @@ -5564,7 +5550,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.mouseEntered(e, context); } } @@ -5575,7 +5560,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.mouseExited(e, context); } } @@ -5586,7 +5570,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.mousePressed(e, context); } } @@ -5597,7 +5580,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.mouseReleased(e, context); } } @@ -5611,7 +5593,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.menuCanceled(e, context); } } @@ -5622,7 +5603,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.menuDeselected(e, context); } } @@ -5633,7 +5613,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.menuSelected(e, context); } } @@ -5644,7 +5623,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.popupMenuCanceled(e, context); } } @@ -5655,7 +5633,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.popupMenuWillBecomeInvisible(e, context); } } @@ -5666,7 +5643,6 @@ public final class AccessBridge { Accessible a = Translator.getAccessible(e.getSource()); if (a != null) { AccessibleContext context = a.getAccessibleContext(); - InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext()); accessBridge.popupMenuWillBecomeVisible(e, context); } } @@ -7227,8 +7203,7 @@ public final class AccessBridge { private static class InvocationUtils { /** - * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible} - * and waits for it to finish blocking the caller thread. + * Invokes a {@code Callable} and waits for it to finish blocking the caller thread. * * @param callable the {@code Callable} to invoke * @param accessibleTable the {@code AccessibleExtendedTable} which would be used to find the right context @@ -7246,8 +7221,7 @@ public final class AccessBridge { } /** - * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible} - * and waits for it to finish blocking the caller thread. + * Invokes a {@code Callable} and waits for it to finish blocking the caller thread. * * @param callable the {@code Callable} to invoke * @param accessible the {@code Accessible} which would be used to find the right context @@ -7269,8 +7243,7 @@ public final class AccessBridge { } /** - * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Component} - * and waits for it to finish blocking the caller thread. + * Invokes a {@code Callable} and waits for it to finish blocking the caller thread. * * @param callable the {@code Callable} to invoke * @param component the {@code Component} which would be used to find the right context @@ -7281,12 +7254,11 @@ public final class AccessBridge { */ public static T invokeAndWait(final Callable callable, final Component component) { - return invokeAndWait(callable, SunToolkit.targetToAppContext(component)); + return invokeAndWait(callable); } /** - * Invokes a {@code Callable} in the {@code AppContext} mapped to the given {@code AccessibleContext} - * and waits for it to finish blocking the caller thread. + * Invokes a {@code Callable} and waits for it to finish blocking the caller thread. * * @param callable the {@code Callable} to invoke * @param accessibleContext the {@code AccessibleContext} which would be used to determine the right @@ -7297,45 +7269,26 @@ public final class AccessBridge { */ public static T invokeAndWait(final Callable callable, final AccessibleContext accessibleContext) { - AppContext targetContext = AWTAccessor.getAccessibleContextAccessor() - .getAppContext(accessibleContext); - if (targetContext != null) { - return invokeAndWait(callable, targetContext); - } else { - // Normally this should not happen, unmapped context provided and - // the target AppContext is unknown. - - // Try to recover in case the context is a translator. - if (accessibleContext instanceof Translator) { - Object source = ((Translator)accessibleContext).getSource(); - if (source instanceof Component) { - return invokeAndWait(callable, (Component)source); - } - } - } - throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleContext); + return invokeAndWait(callable); } - private static T invokeAndWait(final Callable callable, - final AppContext targetAppContext) { + private static T invokeAndWait(final Callable callable) { final CallableWrapper wrapper = new CallableWrapper(callable); try { - invokeAndWait(wrapper, targetAppContext); + invokeAndWait(wrapper); T result = wrapper.getResult(); - updateAppContextMap(result, targetAppContext); return result; } catch (final Exception e) { throw new RuntimeException(e); } } - private static void invokeAndWait(final Runnable runnable, - final AppContext appContext) + private static void invokeAndWait(final Runnable runnable) throws InterruptedException, InvocationTargetException { - EventQueue eq = SunToolkit.getSystemEventQueueImplPP(appContext); Object lock = new Object(); Toolkit source = Toolkit.getDefaultToolkit(); + EventQueue eq = source.getSystemEventQueue(); InvocationEvent event = new InvocationEvent(source, runnable, lock, true); synchronized (lock) { @@ -7349,26 +7302,6 @@ public final class AccessBridge { } } - /** - * Maps the {@code AccessibleContext} to the {@code AppContext} which should be used - * to dispatch events related to the {@code AccessibleContext} - * @param accessibleContext the {@code AccessibleContext} for the mapping - * @param targetContext the {@code AppContext} for the mapping - */ - public static void registerAccessibleContext(final AccessibleContext accessibleContext, - final AppContext targetContext) { - if (accessibleContext != null) { - AWTAccessor.getAccessibleContextAccessor().setAppContext(accessibleContext, targetContext); - } - } - - private static void updateAppContextMap(final T accessibleContext, - final AppContext targetContext) { - if (accessibleContext instanceof AccessibleContext) { - registerAccessibleContext((AccessibleContext)accessibleContext, targetContext); - } - } - private static class CallableWrapper implements Runnable { private final Callable callable; private volatile T object; From 7e4ac140d9faad938bda3e60a00cc5e8ed0fe7d7 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Fri, 27 Mar 2026 19:55:32 +0000 Subject: [PATCH 04/25] 8380686: GenerateOopMap logging crashes in rewriter Reviewed-by: matsaave, phubner --- .../share/interpreter/bytecodeTracer.cpp | 30 +++++++------ .../jtreg/runtime/interpreter/JsrLogging.jasm | 43 +++++++++++++++++++ .../runtime/interpreter/JsrLoggingTest.java | 40 +++++++++++++++++ 3 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/interpreter/JsrLogging.jasm create mode 100644 test/hotspot/jtreg/runtime/interpreter/JsrLoggingTest.java diff --git a/src/hotspot/share/interpreter/bytecodeTracer.cpp b/src/hotspot/share/interpreter/bytecodeTracer.cpp index 2610a8a2bd6..4578a3eec4e 100644 --- a/src/hotspot/share/interpreter/bytecodeTracer.cpp +++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp @@ -52,9 +52,9 @@ class BytecodePrinter { Bytecodes::Code _code; address _next_pc; // current decoding position int _flags; - bool _is_linked; + bool _use_cp_cache; - bool is_linked() const { return _is_linked; } + bool use_cp_cache() const { return _use_cp_cache; } void align() { _next_pc = align_up(_next_pc, sizeof(jint)); } int get_byte() { return *(jbyte*) _next_pc++; } // signed int get_index_u1() { return *(address)_next_pc++; } // returns 0x00 - 0xff as an int @@ -69,7 +69,7 @@ class BytecodePrinter { bool is_wide() const { return _is_wide; } Bytecodes::Code raw_code() const { return Bytecodes::Code(_code); } ConstantPool* constants() const { return method()->constants(); } - ConstantPoolCache* cpcache() const { assert(is_linked(), "must be"); return constants()->cache(); } + ConstantPoolCache* cpcache() const { assert(use_cp_cache(), "must be"); return constants()->cache(); } void print_constant(int i, outputStream* st); void print_cpcache_entry(int cpc_index, outputStream* st); @@ -94,8 +94,9 @@ class BytecodePrinter { ResourceMark rm; bool method_changed = _current_method != method(); _current_method = method(); - _is_linked = method->method_holder()->is_linked(); - assert(_is_linked, "this function must be called on methods that are already executing"); + _use_cp_cache = method->constants()->cache() != nullptr; + assert(method->method_holder()->is_linked(), + "this function must be called on methods that are already executing"); if (method_changed) { // Note 1: This code will not work as expected with true MT/MP. @@ -150,7 +151,8 @@ class BytecodePrinter { // BytecodeStream, which will skip wide bytecodes. void trace(const methodHandle& method, address bcp, outputStream* st) { _current_method = method(); - _is_linked = method->method_holder()->is_linked(); + // This may be called during linking after bytecodes are rewritten to point to the cpCache. + _use_cp_cache = method->constants()->cache() != nullptr; ResourceMark rm; Bytecodes::Code code = Bytecodes::code_at(method(), bcp); // Set is_wide @@ -301,7 +303,7 @@ void BytecodePrinter::print_invokedynamic(int indy_index, int cp_index, outputSt if (ClassPrinter::has_mode(_flags, ClassPrinter::PRINT_DYNAMIC)) { print_bsm(cp_index, st); - if (is_linked()) { + if (use_cp_cache()) { ResolvedIndyEntry* indy_entry = constants()->resolved_indy_entry_at(indy_index); st->print(" ResolvedIndyEntry: "); indy_entry->print_on(st); @@ -365,7 +367,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { { int cp_index; if (Bytecodes::uses_cp_cache(raw_code())) { - assert(is_linked(), "fast ldc bytecode must be in linked classes"); + assert(use_cp_cache(), "fast ldc bytecode must be in linked classes"); int obj_index = get_index_u1(); cp_index = constants()->object_to_cp_index(obj_index); } else { @@ -380,7 +382,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { { int cp_index; if (Bytecodes::uses_cp_cache(raw_code())) { - assert(is_linked(), "fast ldc bytecode must be in linked classes"); + assert(use_cp_cache(), "fast ldc bytecode must be in linked classes"); int obj_index = get_native_index_u2(); cp_index = constants()->object_to_cp_index(obj_index); } else { @@ -510,7 +512,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { case Bytecodes::_getfield: { int cp_index; - if (is_linked()) { + if (use_cp_cache()) { int field_index = get_native_index_u2(); cp_index = cpcache()->resolved_field_entry_at(field_index)->constant_pool_index(); } else { @@ -525,7 +527,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { case Bytecodes::_invokestatic: { int cp_index; - if (is_linked()) { + if (use_cp_cache()) { int method_index = get_native_index_u2(); ResolvedMethodEntry* method_entry = cpcache()->resolved_method_entry_at(method_index); cp_index = method_entry->constant_pool_index(); @@ -533,7 +535,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { if (raw_code() == Bytecodes::_invokehandle && ClassPrinter::has_mode(_flags, ClassPrinter::PRINT_METHOD_HANDLE)) { - assert(is_linked(), "invokehandle is only in rewritten methods"); + assert(use_cp_cache(), "invokehandle is only in rewritten methods"); method_entry->print_on(st); if (method_entry->has_appendix()) { st->print(" appendix: "); @@ -550,7 +552,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { case Bytecodes::_invokeinterface: { int cp_index; - if (is_linked()) { + if (use_cp_cache()) { int method_index = get_native_index_u2(); cp_index = cpcache()->resolved_method_entry_at(method_index)->constant_pool_index(); } else { @@ -566,7 +568,7 @@ void BytecodePrinter::print_attributes(int bci, outputStream* st) { { int indy_index; int cp_index; - if (is_linked()) { + if (use_cp_cache()) { indy_index = get_native_index_u4(); cp_index = constants()->resolved_indy_entry_at(indy_index)->constant_pool_index(); } else { diff --git a/test/hotspot/jtreg/runtime/interpreter/JsrLogging.jasm b/test/hotspot/jtreg/runtime/interpreter/JsrLogging.jasm new file mode 100644 index 00000000000..ac1d0fffa49 --- /dev/null +++ b/test/hotspot/jtreg/runtime/interpreter/JsrLogging.jasm @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +public super class JsrLogging version 49:0 +{ + static Field i:I; + + public static Method test:"()V" + stack 3 locals 1 + { + nop; + jsr LABEL; + bipush 66; + LABEL: + bipush 55; + putstatic Field i:"I"; + getstatic Field java/lang/System.out:"Ljava/io/PrintStream;"; + ldc String "hello"; + invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V"; + return; + } +} diff --git a/test/hotspot/jtreg/runtime/interpreter/JsrLoggingTest.java b/test/hotspot/jtreg/runtime/interpreter/JsrLoggingTest.java new file mode 100644 index 00000000000..36a8f7e9b76 --- /dev/null +++ b/test/hotspot/jtreg/runtime/interpreter/JsrLoggingTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8380686 + * @summary Ensure logging works while processing jsr while linking. + * @library /test/lib + * @compile JsrLogging.jasm + * @run main/othervm -Xlog:generateoopmap=debug JsrLoggingTest + */ + +public class JsrLoggingTest { + public static void main(String[] args) { + for (int i = 0; i < 10; ++i) { + JsrLogging.test(); + } + System.out.println("PASSED"); + } +} From ac242550fea72a3c37e61014f5204f72e27e2cbd Mon Sep 17 00:00:00 2001 From: Jeremy Wood Date: Sat, 28 Mar 2026 09:33:31 +0000 Subject: [PATCH 05/25] 8379347: VoiceOver Doesn't Correctly Identify JToggleButtons as "toggle buttons" Reviewed-by: honkar, kizune, prr --- .../awt/a11y/CheckboxAccessibility.m | 14 +++- .../awt/a11y/CommonComponentAccessibility.m | 1 + .../8379347/VoiceOverToggleButtonRole.java | 69 +++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 test/jdk/javax/accessibility/8379347/VoiceOverToggleButtonRole.java diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m index f2dbf60d92d..a5faf255440 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CheckboxAccessibility.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,6 +36,18 @@ return NSAccessibilityCheckBoxRole; } +- (NSAccessibilitySubrole _Nullable)accessibilitySubrole +{ + JNIEnv *env = [ThreadUtilities getJNIEnv]; + if (env != NULL) { + NSString *javaRole = [self javaRole]; + if ([javaRole isEqualToString:@"togglebutton"]) { + return NSAccessibilityToggleSubrole; + } + } + return [super accessibilitySubrole]; +} + - (id _Nonnull) accessibilityValue { AWT_ASSERT_APPKIT_THREAD; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m index 0f0a395c597..45e8f981f50 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m @@ -136,6 +136,7 @@ static jobject sAccessibilityClass = NULL; [rolesMap setObject:@"StaticTextAccessibility" forKey:@"label"]; [rolesMap setObject:@"RadiobuttonAccessibility" forKey:@"radiobutton"]; [rolesMap setObject:@"CheckboxAccessibility" forKey:@"checkbox"]; + [rolesMap setObject:@"CheckboxAccessibility" forKey:@"togglebutton"]; [rolesMap setObject:@"SliderAccessibility" forKey:@"slider"]; [rolesMap setObject:@"ScrollAreaAccessibility" forKey:@"scrollpane"]; [rolesMap setObject:@"ScrollBarAccessibility" forKey:@"scrollbar"]; diff --git a/test/jdk/javax/accessibility/8379347/VoiceOverToggleButtonRole.java b/test/jdk/javax/accessibility/8379347/VoiceOverToggleButtonRole.java new file mode 100644 index 00000000000..31264f69dd8 --- /dev/null +++ b/test/jdk/javax/accessibility/8379347/VoiceOverToggleButtonRole.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.swing.JToggleButton; +import javax.swing.JFrame; + +/* + * @test + * @key headful + * @bug 8379347 + * @summary manual test for VoiceOver reading JToggleButtons correctly + * @requires os.family == "mac" + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual VoiceOverToggleButtonRole + */ + +public class VoiceOverToggleButtonRole { + public static void main(String[] args) throws Exception { + String INSTRUCTIONS = """ + INSTRUCTIONS (Mac-only): + 1. Open VoiceOver + 2. Move the VoiceOver cursor over the JToggleButton. + 3. Observe how VoiceOver identifies the toggle button. + + Expected behavior: VoiceOver should identify it as a + "toggle button" initially. (VO does still say "to select + or deselect this checkbox", though.) + + If you select the link using "Accessibility Inspector": + it should identify its subrole as AXToggle. + """; + + PassFailJFrame.builder() + .title("VoiceOverToggleButtonRole Instruction") + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(VoiceOverToggleButtonRole::createUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createUI() { + JFrame frame = new JFrame(); + frame.getContentPane().add(new JToggleButton("JToggleButton")); + frame.pack(); + return frame; + } +} From 6520c95a79ee2e5a249827c49c75b0db48d63209 Mon Sep 17 00:00:00 2001 From: Andrew Dinn Date: Sat, 28 Mar 2026 17:36:58 +0000 Subject: [PATCH 06/25] 8380771: Add missing CodeBlob Vptr implementations Reviewed-by: kvn, asmehra --- src/hotspot/share/code/codeBlob.cpp | 30 +++++------ src/hotspot/share/code/codeBlob.hpp | 77 +++++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 23 deletions(-) diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 9961381c31d..a7d939e590d 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -79,8 +79,10 @@ const BufferBlob::Vptr BufferBlob::_vpntr; const RuntimeStub::Vptr RuntimeStub::_vpntr; const SingletonBlob::Vptr SingletonBlob::_vpntr; const DeoptimizationBlob::Vptr DeoptimizationBlob::_vpntr; +const SafepointBlob::Vptr SafepointBlob::_vpntr; #ifdef COMPILER2 const ExceptionBlob::Vptr ExceptionBlob::_vpntr; +const UncommonTrapBlob::Vptr UncommonTrapBlob::_vpntr; #endif // COMPILER2 const UpcallStub::Vptr UpcallStub::_vpntr; @@ -386,7 +388,7 @@ void RuntimeBlob::trace_new_stub(RuntimeBlob* stub, const char* name1, const cha // Implementation of BufferBlob BufferBlob::BufferBlob(const char* name, CodeBlobKind kind, int size, uint16_t header_size) -: RuntimeBlob(name, kind, size, header_size) + : RuntimeBlob(name, kind, size, header_size) {} BufferBlob* BufferBlob::create(const char* name, uint buffer_size) { @@ -621,8 +623,8 @@ DeoptimizationBlob::DeoptimizationBlob( int unpack_with_reexecution_offset, int frame_size ) -: SingletonBlob("DeoptimizationBlob", CodeBlobKind::Deoptimization, cb, - size, sizeof(DeoptimizationBlob), frame_size, oop_maps) + : SingletonBlob("DeoptimizationBlob", CodeBlobKind::Deoptimization, cb, + size, sizeof(DeoptimizationBlob), frame_size, oop_maps) { _unpack_offset = unpack_offset; _unpack_with_exception = unpack_with_exception_offset; @@ -671,8 +673,8 @@ UncommonTrapBlob::UncommonTrapBlob( OopMapSet* oop_maps, int frame_size ) -: SingletonBlob("UncommonTrapBlob", CodeBlobKind::UncommonTrap, cb, - size, sizeof(UncommonTrapBlob), frame_size, oop_maps) + : SingletonBlob("UncommonTrapBlob", CodeBlobKind::UncommonTrap, cb, + size, sizeof(UncommonTrapBlob), frame_size, oop_maps) {} @@ -703,8 +705,8 @@ ExceptionBlob::ExceptionBlob( OopMapSet* oop_maps, int frame_size ) -: SingletonBlob("ExceptionBlob", CodeBlobKind::Exception, cb, - size, sizeof(ExceptionBlob), frame_size, oop_maps) + : SingletonBlob("ExceptionBlob", CodeBlobKind::Exception, cb, + size, sizeof(ExceptionBlob), frame_size, oop_maps) {} @@ -737,8 +739,8 @@ SafepointBlob::SafepointBlob( OopMapSet* oop_maps, int frame_size ) -: SingletonBlob("SafepointBlob", CodeBlobKind::Safepoint, cb, - size, sizeof(SafepointBlob), frame_size, oop_maps) + : SingletonBlob(cb->name(), CodeBlobKind::Safepoint, cb, + size, sizeof(SafepointBlob), frame_size, oop_maps) {} @@ -755,7 +757,7 @@ SafepointBlob* SafepointBlob::create( blob = new (size) SafepointBlob(cb, size, oop_maps, frame_size); } - trace_new_stub(blob, "SafepointBlob"); + trace_new_stub(blob, "SafepointBlob - ", blob->name()); return blob; } @@ -895,7 +897,7 @@ void CodeBlob::dump_for_addr(address addr, outputStream* st, bool verbose) const } } if (is_nmethod()) { - nmethod* nm = (nmethod*)this; + nmethod* nm = as_nmethod(); ResourceMark rm; st->print(INTPTR_FORMAT " is at entry_point+%d in (nmethod*)" INTPTR_FORMAT, p2i(addr), (int)(addr - nm->entry_point()), p2i(nm)); @@ -931,7 +933,7 @@ void RuntimeStub::print_on_impl(outputStream* st) const { RuntimeBlob::print_on_impl(st); st->print("Runtime Stub (" INTPTR_FORMAT "): ", p2i(this)); st->print_cr("%s", name()); - Disassembler::decode((RuntimeBlob*)this, st); + Disassembler::decode((CodeBlob*)this, st); } void RuntimeStub::print_value_on_impl(outputStream* st) const { @@ -942,7 +944,7 @@ void SingletonBlob::print_on_impl(outputStream* st) const { ttyLocker ttyl; RuntimeBlob::print_on_impl(st); st->print_cr("%s", name()); - Disassembler::decode((RuntimeBlob*)this, st); + Disassembler::decode((CodeBlob*)this, st); } void SingletonBlob::print_value_on_impl(outputStream* st) const { @@ -960,7 +962,7 @@ void UpcallStub::print_on_impl(outputStream* st) const { oop recv = JNIHandles::resolve(_receiver); st->print("Receiver MH="); recv->print_on(st); - Disassembler::decode((RuntimeBlob*)this, st); + Disassembler::decode((CodeBlob*)this, st); } void UpcallStub::print_value_on_impl(outputStream* st) const { diff --git a/src/hotspot/share/code/codeBlob.hpp b/src/hotspot/share/code/codeBlob.hpp index 1f9cf0adcc3..6a1686b80e2 100644 --- a/src/hotspot/share/code/codeBlob.hpp +++ b/src/hotspot/share/code/codeBlob.hpp @@ -98,7 +98,9 @@ enum class CodeBlobKind : u1 { class UpcallStub; // for as_upcall_stub() class RuntimeStub; // for as_runtime_stub() class JavaFrameAnchor; // for UpcallStub::jfa_for_frame +class BufferBlob; class AdapterBlob; +class SingletonBlob; class ExceptionBlob; class DeoptimizationBlob; class SafepointBlob; @@ -185,8 +187,19 @@ public: // Typing bool is_nmethod() const { return _kind == CodeBlobKind::Nmethod; } - bool is_buffer_blob() const { return _kind == CodeBlobKind::Buffer; } + // we may want to check for an actual buffer blob or subtype instance + bool is_buffer_blob(bool strict=true) const { + if (strict) { + return _kind == CodeBlobKind::Buffer; + } else { + return (_kind == CodeBlobKind::Buffer || + _kind == CodeBlobKind::Adapter || + _kind == CodeBlobKind::Vtable || + _kind == CodeBlobKind::MHAdapter); + } + } bool is_runtime_stub() const { return _kind == CodeBlobKind::RuntimeStub; } + // singleton blobs are never directly implemented bool is_deoptimization_stub() const { return _kind == CodeBlobKind::Deoptimization; } #ifdef COMPILER2 bool is_uncommon_trap_stub() const { return _kind == CodeBlobKind::UncommonTrap; } @@ -196,6 +209,12 @@ public: bool is_exception_stub() const { return false; } #endif bool is_safepoint_stub() const { return _kind == CodeBlobKind::Safepoint; } + bool is_singleton_blob() const { + return (is_deoptimization_stub() || + is_uncommon_trap_stub() || + is_exception_stub() || + is_safepoint_stub()); + } bool is_adapter_blob() const { return _kind == CodeBlobKind::Adapter; } bool is_vtable_blob() const { return _kind == CodeBlobKind::Vtable; } bool is_method_handles_adapter_blob() const { return _kind == CodeBlobKind::MHAdapter; } @@ -205,8 +224,12 @@ public: nmethod* as_nmethod_or_null() const { return is_nmethod() ? (nmethod*) this : nullptr; } nmethod* as_nmethod() const { assert(is_nmethod(), "must be nmethod"); return (nmethod*) this; } CodeBlob* as_codeblob() const { return (CodeBlob*) this; } + // we may want to force an actual buffer blob or subtype instance + BufferBlob* as_buffer_blob(bool strict = true) const { assert(is_buffer_blob(), "must be %sbuffer blob", (strict ? "strict " : "")); return (BufferBlob*) this; } AdapterBlob* as_adapter_blob() const { assert(is_adapter_blob(), "must be adapter blob"); return (AdapterBlob*) this; } ExceptionBlob* as_exception_blob() const { assert(is_exception_stub(), "must be exception stub"); return (ExceptionBlob*) this; } + // this will always return a subtype instance + SingletonBlob* as_singleton_blob() const { assert(is_singleton_blob(), "must be singleton blob"); return (SingletonBlob*) this; } DeoptimizationBlob* as_deoptimization_blob() const { assert(is_deoptimization_stub(), "must be deopt stub"); return (DeoptimizationBlob*) this; } SafepointBlob* as_safepoint_blob() const { assert(is_safepoint_stub(), "must be safepoint stub"); return (SafepointBlob*) this; } UpcallStub* as_upcall_stub() const { assert(is_upcall_stub(), "must be upcall stub"); return (UpcallStub*) this; } @@ -387,10 +410,10 @@ class BufferBlob: public RuntimeBlob { class Vptr : public RuntimeBlob::Vptr { void print_on(const CodeBlob* instance, outputStream* st) const override { - ((const BufferBlob*)instance)->print_on_impl(st); + instance->as_buffer_blob(false)->print_on_impl(st); } void print_value_on(const CodeBlob* instance, outputStream* st) const override { - ((const BufferBlob*)instance)->print_value_on_impl(st); + instance->as_buffer_blob(false)->print_value_on_impl(st); } }; @@ -486,10 +509,17 @@ class RuntimeStub: public RuntimeBlob { address entry_point() const { return code_begin(); } + void post_restore_impl() { + trace_new_stub(this, "RuntimeStub - ", name()); + } + void print_on_impl(outputStream* st) const; void print_value_on_impl(outputStream* st) const; class Vptr : public RuntimeBlob::Vptr { + void post_restore(CodeBlob* instance) const override { + instance->as_runtime_stub()->post_restore_impl(); + } void print_on(const CodeBlob* instance, outputStream* st) const override { instance->as_runtime_stub()->print_on_impl(st); } @@ -531,10 +561,10 @@ class SingletonBlob: public RuntimeBlob { class Vptr : public RuntimeBlob::Vptr { void print_on(const CodeBlob* instance, outputStream* st) const override { - ((const SingletonBlob*)instance)->print_on_impl(st); + instance->as_singleton_blob()->print_on_impl(st); } void print_value_on(const CodeBlob* instance, outputStream* st) const override { - ((const SingletonBlob*)instance)->print_value_on_impl(st); + instance->as_singleton_blob()->print_value_on_impl(st); } }; @@ -605,20 +635,28 @@ class DeoptimizationBlob: public SingletonBlob { _uncommon_trap_offset = offset; assert(contains(code_begin() + _uncommon_trap_offset), "must be PC inside codeblob"); } - address uncommon_trap() const { return code_begin() + _uncommon_trap_offset; } + address uncommon_trap() const { return (EnableJVMCI ? code_begin() + _uncommon_trap_offset : nullptr); } void set_implicit_exception_uncommon_trap_offset(int offset) { _implicit_exception_uncommon_trap_offset = offset; assert(contains(code_begin() + _implicit_exception_uncommon_trap_offset), "must be PC inside codeblob"); } - address implicit_exception_uncommon_trap() const { return code_begin() + _implicit_exception_uncommon_trap_offset; } + address implicit_exception_uncommon_trap() const { return (EnableJVMCI ? code_begin() + _implicit_exception_uncommon_trap_offset : nullptr); } #endif // INCLUDE_JVMCI + void post_restore_impl() { + trace_new_stub(this, "DeoptimizationBlob"); + } + void print_value_on_impl(outputStream* st) const; class Vptr : public SingletonBlob::Vptr { + void post_restore(CodeBlob* instance) const override { + instance->as_deoptimization_blob()->post_restore_impl(); + } + void print_value_on(const CodeBlob* instance, outputStream* st) const override { - ((const DeoptimizationBlob*)instance)->print_value_on_impl(st); + instance->as_deoptimization_blob()->print_value_on_impl(st); } }; @@ -648,6 +686,16 @@ class UncommonTrapBlob: public SingletonBlob { OopMapSet* oop_maps, int frame_size ); + void post_restore_impl() { + trace_new_stub(this, "UncommonTrapBlob"); + } + class Vptr : public SingletonBlob::Vptr { + void post_restore(CodeBlob* instance) const override { + instance->as_uncommon_trap_blob()->post_restore_impl(); + } + }; + + static const Vptr _vpntr; }; @@ -678,7 +726,7 @@ class ExceptionBlob: public SingletonBlob { class Vptr : public SingletonBlob::Vptr { void post_restore(CodeBlob* instance) const override { - ((ExceptionBlob*)instance)->post_restore_impl(); + instance->as_exception_blob()->post_restore_impl(); } }; @@ -708,6 +756,17 @@ class SafepointBlob: public SingletonBlob { OopMapSet* oop_maps, int frame_size ); + + void post_restore_impl() { + trace_new_stub(this, "SafepointBlob - ", name()); + } + class Vptr : public SingletonBlob::Vptr { + void post_restore(CodeBlob* instance) const override { + instance->as_safepoint_blob()->post_restore_impl(); + } + }; + + static const Vptr _vpntr; }; //---------------------------------------------------------------------------------------------------- From 66a34be54a43c110f8dd577775fc213ed912faa7 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Sun, 29 Mar 2026 02:01:46 +0000 Subject: [PATCH 07/25] 8273874: LdapClient can trigger memory leak Reviewed-by: dfuchs --- .../classes/com/sun/jndi/ldap/Connection.java | 9 +- .../classes/com/sun/jndi/ldap/EventQueue.java | 9 +- .../sun/jndi/ldap/NamingEventNotifier.java | 6 +- test/jdk/com/sun/jndi/ldap/LdapTCCLTest.java | 323 ++++++++++++++++++ 4 files changed, 336 insertions(+), 11 deletions(-) create mode 100644 test/jdk/com/sun/jndi/ldap/LdapTCCLTest.java diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java index dcb739a8697..1e0a924f12c 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java @@ -57,6 +57,8 @@ import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.SSLPeerUnverifiedException; import javax.security.sasl.SaslException; +import jdk.internal.misc.InnocuousThread; + /** * A thread that creates a connection to an LDAP server. * After the connection, the thread reads from the connection. @@ -112,9 +114,6 @@ import javax.security.sasl.SaslException; * for v2. * %%% made public for access by LdapSasl %%% * - * @author Vincent Ryan - * @author Rosanna Lee - * @author Jagane Sundar */ public final class Connection implements Runnable { @@ -254,7 +253,7 @@ public final class Connection implements Runnable { throw ce; } - worker = new Thread(this); + worker = InnocuousThread.newSystemThread("LDAP Connection", this); worker.setDaemon(true); worker.start(); } @@ -912,7 +911,7 @@ public final class Connection implements Runnable { // //////////////////////////////////////////////////////////////////////////// - + @Override public void run() { byte inbuf[]; // Buffer for reading incoming bytes int inMsgId; // Message id of incoming response diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java b/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java index 4f1cb9ec6a7..7d45d058c68 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/EventQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,13 +36,13 @@ import javax.naming.event.NamingListener; import javax.naming.ldap.UnsolicitedNotificationEvent; import javax.naming.ldap.UnsolicitedNotificationListener; +import jdk.internal.misc.InnocuousThread; + /** * Package private class used by EventSupport to dispatch events. * This class implements an event queue, and a dispatcher thread that * dequeues and dispatches events from the queue. * - * Pieces stolen from sun.misc.Queue. - * * @author Bill Shannon (from javax.mail.event) * @author Rosanna Lee (modified for JNDI-related events) */ @@ -71,7 +71,7 @@ final class EventQueue implements Runnable { // package private EventQueue() { - qThread = new Thread(this); + qThread = InnocuousThread.newSystemThread("LDAP Event Dispatcher", this); qThread.setDaemon(true); // not a user thread qThread.start(); } @@ -141,6 +141,7 @@ final class EventQueue implements Runnable { /** * Pull events off the queue and dispatch them. */ + @Override public void run() { QueueElement qe; diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/NamingEventNotifier.java b/src/java.naming/share/classes/com/sun/jndi/ldap/NamingEventNotifier.java index 40a8173b768..0e30c1c1d38 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/NamingEventNotifier.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/NamingEventNotifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ import javax.naming.ldap.LdapName; import java.util.Vector; import com.sun.jndi.toolkit.ctx.Continuation; +import jdk.internal.misc.InnocuousThread; /** * Gathers information to generate events by using the Persistent Search @@ -86,7 +87,7 @@ final class NamingEventNotifier implements Runnable { namingListeners = new Vector<>(); namingListeners.addElement(firstListener); - worker = new Thread(this); + worker = InnocuousThread.newSystemThread("LDAP Event Notifier", this); worker.setDaemon(true); // not a user thread worker.start(); } @@ -111,6 +112,7 @@ final class NamingEventNotifier implements Runnable { * For each result, create the appropriate NamingEvent and * queue to be dispatched to listeners. */ + @Override public void run() { try { Continuation cont = new Continuation(); diff --git a/test/jdk/com/sun/jndi/ldap/LdapTCCLTest.java b/test/jdk/com/sun/jndi/ldap/LdapTCCLTest.java new file mode 100644 index 00000000000..21f355442d7 --- /dev/null +++ b/test/jdk/com/sun/jndi/ldap/LdapTCCLTest.java @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Collection; +import java.util.Collections; +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.event.EventContext; +import javax.naming.event.NamingEvent; +import javax.naming.event.NamingExceptionEvent; +import javax.naming.event.ObjectChangeListener; + +import jdk.test.lib.net.URIBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +/* + * @test + * @bug 8273874 + * @summary Verify that the system threads that LdapContext creates for its internal + * use do not hold on to the application specific context classloaders + * @comment The test uses ThreadGroup.enumerate() to find live threads and check their + * context classsloader, ThreadGroup of virtual threads don't enumerate threads, + * so we skip this test when the main thread is a virtual thread. + * @requires test.thread.factory != "Virtual" + * @library lib/ /test/lib + * @build BaseLdapServer + * LdapMessage + * jdk.test.lib.net.URIBuilder + * @run junit ${test.main.class} + */ +class LdapTCCLTest { + + private static final String LOOKUP_NAME = "CN=duke"; + + private static final byte BER_TYPE_LDAP_SEQUENCE = 0x30; + private static final byte BER_TYPE_INTEGER = 0x02; + private static final byte BER_TYPE_OCTET_STRING = 0x04; + private static final byte BER_TYPE_ENUM = 0x0a; + private static final byte BER_TYPE_LDAP_SEARCH_RESULT_ENTRY_OP = 0x64; + private static final byte BER_TYPE_LDAP_SEARCH_RESULT_DONE_OP = 0x65; + private static final byte LDAP_SEARCH_RESULT_DONE_SUCCESS = 0x00; + + private static Server server; + private static Hashtable envProps; + + @BeforeAll + static void beforeAll() throws Exception { + server = new Server(); + server.start(); + System.err.println("server started at " + server.getInetAddress() + + ":" + server.getPort()); + + final Hashtable props = new Hashtable<>(); + props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + final String providerUrl = URIBuilder.newBuilder() + .scheme("ldap") + .host(server.getInetAddress().getHostAddress()) + .port(server.getPort()) + .build().toString(); + props.put(Context.PROVIDER_URL, providerUrl); + // explicitly set LDAP version to 3 to prevent LDAP BIND requests + // during LdapCtx instantiation + props.put("java.naming.ldap.version", "3"); + + envProps = props; + } + + @AfterAll + static void afterAll() throws Exception { + if (server != null) { + System.err.println("stopping server " + server.getInetAddress() + + ":" + server.getPort()); + server.close(); + } + } + + /* + * Sets a test specific Thread context classloader and then creates a InitialContext + * backed by a LdapCtxFactory and looks up an arbitrary name. The test then verifies + * that none of the live Threads (including any created for the internal LDAP connection + * management) have their context classloader set to the test specific classloader. + */ + @Test + void testLdapCtxCreation() throws Exception { + try (final URLClassLoader urlc = new URLClassLoader(new URL[0])) { + final ClassLoader previous = Thread.currentThread().getContextClassLoader(); + Context ctx = null; + try { + // switch the TCCL to a test specific one + Thread.currentThread().setContextClassLoader(urlc); + + // create the LDAP Context and initiate a lookup() + // to allow for the underlying LDAP connection management + // infrastructure to create the necessary Thread(s) + ctx = new InitialContext(envProps); + + System.err.println("issuing ldap request against " + + envProps.get(Context.PROVIDER_URL) + " using context " + ctx); + final Object result = ctx.lookup(LOOKUP_NAME); + System.err.println("lookup returned " + result); + assertNotNull(result, "Context.lookup() returned null"); + + // verify that none of the live Thread(s) other than the current Thread + // have their TCCL set to the one set by the test. i.e. verify that the + // context classloader hasn't leaked into Thread(s) that may have been + // created by the LDAP connection management code. + assertTCCL(urlc, Collections.singleton(Thread.currentThread())); + } finally { + if (ctx != null) { + ctx.close(); + } + Thread.currentThread().setContextClassLoader(previous); + } + } + } + + /* + * Sets a test specific Thread context classloader and then creates a InitialContext + * backed by a LdapCtxFactory and adds a NamingListener. The test then verifies + * that none of the live Threads (including any newly created ones during the + * NamingListener registration) have their context classloader set to the test + * specific classloader. + */ + @Test + void testAddNamingListener() throws Exception { + try (final URLClassLoader urlc = new URLClassLoader(new URL[0])) { + final ClassLoader previous = Thread.currentThread().getContextClassLoader(); + EventContext ctx = null; + try { + // switch the TCCL to a test specific one + Thread.currentThread().setContextClassLoader(urlc); + + ctx = (EventContext) (new InitialContext(envProps).lookup(LOOKUP_NAME)); + // add a NamingListener to exercise the Thread creation in the internals + // of LdapCtx + ctx.addNamingListener(LOOKUP_NAME, EventContext.OBJECT_SCOPE, new Listener()); + // verify that none of the live Thread(s) other than the current Thread + // have their TCCL set to the one set by the test. i.e. verify that the + // context classloader hasn't leaked into Thread(s) that may have been + // created by the LDAP naming listener code. + assertTCCL(urlc, Collections.singleton(Thread.currentThread())); + } finally { + if (ctx != null) { + ctx.close(); + } + Thread.currentThread().setContextClassLoader(previous); + } + } + } + + /* + * Verifies that none of the live threads have their context classloader set to + * the given "notExpected" classloader. + */ + private static void assertTCCL(final ClassLoader notExpected, + final Collection threadsToIgnore) { + ThreadGroup topMostThreadGroup = Thread.currentThread().getThreadGroup(); + assertNotNull(topMostThreadGroup, + "ThreadGroup for current thread " + Thread.currentThread() + " was null"); + while (topMostThreadGroup.getParent() != null) { + topMostThreadGroup = topMostThreadGroup.getParent(); + } + // recursively enumerate the threads in the top most thread group + final Thread[] threads = new Thread[1024]; + final int numThreads = topMostThreadGroup.enumerate(threads); + // verify the threads + int numFailedThreads = 0; + final StringBuilder diagnosticMsg = new StringBuilder(); + for (int i = 0; i < numThreads; i++) { + final Thread t = threads[i]; + if (threadsToIgnore.contains(t)) { + continue; // skip verification of the thread + } + System.err.println("verifying " + t); + if (t.getContextClassLoader() == notExpected) { + // Thread has an unexpected context classloader + numFailedThreads++; + // for debugging track the stacktrace of the thread + // that failed the check + diagnosticMsg.append("FAILED - ").append(t) + .append(" is using unexpected context classloader: ") + .append(notExpected) + .append(", its current activity is:\n"); + for (StackTraceElement ste : t.getStackTrace()) { + diagnosticMsg.append("\t").append(ste).append("\n"); + } + } + } + if (numFailedThreads > 0) { + // for debugging print out the stacktrace of the + // Thread(s) that failed the check + System.err.println(diagnosticMsg); + fail(numFailedThreads + " Thread(s) had unexpected context classloader " + + notExpected); + } + } + + private static final class Server extends BaseLdapServer { + + private Server() throws IOException { + super(); + } + + // handles and responds to the incoming LDAP request + @Override + protected void handleRequest(final Socket socket, + final LdapMessage request, + final OutputStream out) throws IOException { + switch (request.getOperation()) { + case SEARCH_REQUEST: { + System.err.println("responding to SEARCH_REQUEST with id: " + + request.getMessageID() + " on socket " + socket); + // write out a search response + final byte[] rsp = makeSearchResponse((byte) request.getMessageID(), LOOKUP_NAME); + out.write(rsp); + out.flush(); + System.err.println("wrote response for message: " + request.getMessageID()); + break; + } + default: { + throw new IOException("unexpected operation type: " + request.getOperation() + + ", request: " + request); + } + } + } + + // constructs and returns a byte[] response containing the following (in that order): + // - Search Result Entry + // - Search Result Done + private static byte[] makeSearchResponse(final byte msgId, final String dn) + throws IOException { + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + final byte msgIdLen = 1; + + // write the BER elements + // each BER element is 3 parts: + // Type, length, value + + // Search Result Entry BER element (refer to LDAPv3 wire format for details) + bout.write(BER_TYPE_LDAP_SEQUENCE); + bout.write(dn.length() + 9); + bout.write(new byte[]{BER_TYPE_INTEGER, msgIdLen, msgId}); + bout.write(BER_TYPE_LDAP_SEARCH_RESULT_ENTRY_OP); + bout.write(dn.length() + 2); + bout.write(BER_TYPE_OCTET_STRING); + bout.write(dn.length()); + bout.write(dn.getBytes(US_ASCII)); + bout.write(BER_TYPE_LDAP_SEQUENCE); + // 0 length for the LDAP sequence, implying no attributes in this Search Result Entry + bout.write(0); + + bout.write(makeSearchResultDone(msgId)); + + return bout.toByteArray(); + } + + // Search Result Done BER element (refer to LDAPv3 wire format for details) + private static byte[] makeSearchResultDone(final byte msgId) throws IOException { + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + final byte msgIdLen = 1; + final String matchedDN = ""; + bout.write(BER_TYPE_LDAP_SEQUENCE); + bout.write(matchedDN.length() + 12); + bout.write(new byte[]{BER_TYPE_INTEGER, msgIdLen, msgId}); + bout.write(BER_TYPE_LDAP_SEARCH_RESULT_DONE_OP); + bout.write(7); + bout.write(new byte[]{BER_TYPE_ENUM, 1, LDAP_SEARCH_RESULT_DONE_SUCCESS}); + // the matched DN + bout.write(BER_TYPE_OCTET_STRING); + bout.write(matchedDN.length()); + bout.write(matchedDN.getBytes(US_ASCII)); + // 0 length implies no diagnostic message + bout.write(new byte[]{BER_TYPE_OCTET_STRING, 0}); + return bout.toByteArray(); + } + } + + private static final class Listener implements ObjectChangeListener { + + @Override + public void namingExceptionThrown(final NamingExceptionEvent evt) { + System.err.println("namingExceptionThrown() called for " + evt); + } + + @Override + public void objectChanged(final NamingEvent evt) { + System.err.println("objectChanged() called for " + evt); + } + } +} From eb96bfb2a538e60f4ff0c682100931d3cf8da8d2 Mon Sep 17 00:00:00 2001 From: jonghoonpark Date: Mon, 30 Mar 2026 02:58:19 +0000 Subject: [PATCH 08/25] 8379873: Remove undefined debugging declarations in os_windows.cpp Reviewed-by: dholmes, ayang --- src/hotspot/os/windows/os_windows.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 18d047348cb..9d8fb45f0d1 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -2528,12 +2528,6 @@ LONG Handle_Exception(struct _EXCEPTION_POINTERS* exceptionInfo, return EXCEPTION_CONTINUE_EXECUTION; } - -// Used for PostMortemDump -extern "C" void safepoints(); -extern "C" void find(int x); -extern "C" void events(); - // According to Windows API documentation, an illegal instruction sequence should generate // the 0xC000001C exception code. However, real world experience shows that occasionnaly // the execution of an illegal instruction can generate the exception code 0xC000001E. This From 7527da081b777f280144af5841874729a671e9c5 Mon Sep 17 00:00:00 2001 From: Trupti Patil Date: Mon, 30 Mar 2026 04:40:26 +0000 Subject: [PATCH 09/25] 8377534: Test java/awt/print/PrinterJob/PrintNullString.java fails with FAILURE: No IAE for empty iterator, int Reviewed-by: aivanov, prr --- .../awt/print/PrinterJob/PrintNullString.java | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/test/jdk/java/awt/print/PrinterJob/PrintNullString.java b/test/jdk/java/awt/print/PrinterJob/PrintNullString.java index 2bcee65fe9b..8feb50602f7 100644 --- a/test/jdk/java/awt/print/PrinterJob/PrintNullString.java +++ b/test/jdk/java/awt/print/PrinterJob/PrintNullString.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ import javax.print.attribute.standard.Destination; /* * @test - * @bug 4223328 + * @bug 4223328 4138921 * @summary Printer graphics must throw expected exceptions * @key printer * @run main PrintNullString @@ -106,12 +106,8 @@ public class PrintNullString implements Printable { g2d.drawString("caught expected NPE for null iterator, int", 20, 120); } - try { - g2d.drawString(emptyIterator, 20, 140); - throw new RuntimeException("FAILURE: No IAE for empty iterator, int"); - } catch (IllegalArgumentException e) { - g2d.drawString("caught expected IAE for empty iterator, int", 20, 140); - } + g2d.drawString(emptyIterator, 20, 140); + g2d.drawString("OK for empty iterator, int", 20, 140); // API 4: null & empty drawString(Iterator, float, int); try { @@ -121,12 +117,8 @@ public class PrintNullString implements Printable { g2d.drawString("caught expected NPE for null iterator, float", 20, 160); } - try { - g2d.drawString(emptyIterator, 20.0f, 180.0f); - throw new RuntimeException("FAILURE: No IAE for empty iterator, float"); - } catch (IllegalArgumentException e) { - g2d.drawString("caught expected IAE for empty iterator, float", 20, 180); - } + g2d.drawString(emptyIterator, 20.0f, 180.0f); + g2d.drawString("OK for empty iterator, float", 20.0f, 100.f); return PAGE_EXISTS; } From ca3fe721ba23a1304089b71c1b58940f16a0d053 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Mon, 30 Mar 2026 04:54:17 +0000 Subject: [PATCH 10/25] 8357089: Remove VFORK launch mechanism from Process implementation (linux) Reviewed-by: rriggs, andrew --- .../unix/classes/java/lang/ProcessImpl.java | 37 ++--- .../unix/native/libjava/ProcessImpl_md.c | 153 ++++-------------- src/java.base/unix/native/libjava/childproc.c | 52 ++---- src/java.base/unix/native/libjava/childproc.h | 1 - test/jdk/java/lang/ProcessBuilder/Basic.java | 10 -- .../ConcNativeForkTest.java | 10 -- .../ProcessBuilder/FDLeakTest/FDLeakTest.java | 8 - .../ProcessBuilder/NonPipelineLeaksFD.java | 8 - .../PipesCloseOnExecTest.java | 10 -- .../lang/ProcessBuilder/RejectVFORKMode.java | 59 +++++++ .../TestChildSignalDisposition.java | 11 +- 11 files changed, 110 insertions(+), 249 deletions(-) create mode 100644 test/jdk/java/lang/ProcessBuilder/RejectVFORKMode.java diff --git a/src/java.base/unix/classes/java/lang/ProcessImpl.java b/src/java.base/unix/classes/java/lang/ProcessImpl.java index 00b51fb3389..d9a4547848f 100644 --- a/src/java.base/unix/classes/java/lang/ProcessImpl.java +++ b/src/java.base/unix/classes/java/lang/ProcessImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -82,8 +82,7 @@ final class ProcessImpl extends Process { private static enum LaunchMechanism { // order IS important! FORK, - POSIX_SPAWN, - VFORK + POSIX_SPAWN } /** @@ -98,29 +97,16 @@ final class ProcessImpl extends Process { try { // Should be value of a LaunchMechanism enum - LaunchMechanism lm = LaunchMechanism.valueOf(s.toUpperCase(Locale.ROOT)); - switch (OperatingSystem.current()) { - case LINUX: { - // All options are valid for Linux, but VFORK is deprecated and results - // in a warning - if (lm == LaunchMechanism.VFORK) { - System.err.println("VFORK MODE DEPRECATED"); - System.err.println(""" - The VFORK launch mechanism has been deprecated for being dangerous. - It will be removed in a future java version. Either remove the - jdk.lang.Process.launchMechanism property (preferred) or use FORK mode - instead (-Djdk.lang.Process.launchMechanism=FORK). - """); - } - return lm; - } - case AIX: - case MACOS: - if (lm != LaunchMechanism.VFORK) { - return lm; // All but VFORK are valid - } - break; + String launchMechanism = s.toUpperCase(Locale.ROOT); + if (launchMechanism.equals("VFORK") && OperatingSystem.isLinux()) { + launchMechanism = "FORK"; + System.err.println(String.format(""" + The VFORK launch mechanism has been removed. Switching to %s instead. + Please remove the jdk.lang.Process.launchMechanism property (preferred) + or use FORK mode instead (-Djdk.lang.Process.launchMechanism=FORK).%n + """, launchMechanism)); } + return LaunchMechanism.valueOf(launchMechanism); } catch (IllegalArgumentException e) { } @@ -266,7 +252,6 @@ final class ProcessImpl extends Process { *
      *   1 - fork(2) and exec(2)
      *   2 - posix_spawn(3P)
-     *   3 - vfork(2) and exec(2)
      * 
* @param fds an array of three file descriptors. * Indexes 0, 1, and 2 correspond to standard input, diff --git a/src/java.base/unix/native/libjava/ProcessImpl_md.c b/src/java.base/unix/native/libjava/ProcessImpl_md.c index f7d91166e76..69af948d2da 100644 --- a/src/java.base/unix/native/libjava/ProcessImpl_md.c +++ b/src/java.base/unix/native/libjava/ProcessImpl_md.c @@ -60,51 +60,33 @@ * changing paths... * - then exec(2) the target binary * - * There are three ways to fork off: + * On the OS-side are three ways to fork off, but we only use two of them: * - * A) fork(2). Portable and safe (no side effects) but may fail with ENOMEM on - * all Unices when invoked from a VM with a high memory footprint. On Unices - * with strict no-overcommit policy this problem is most visible. + * A) fork(2). Portable and safe (no side effects) but could fail on very ancient + * Unices that don't employ COW on fork(2). The modern platforms we support + * (Linux, MacOS, AIX) all do. It may have a small performance penalty compared + * to modern posix_spawn(3) implementations - see below. + * fork(2) can be used by specifying -Djdk.lang.Process.launchMechanism=FORK when starting + * the (parent process) JVM. * - * This is because forking the VM will first create a child process with - * theoretically the same memory footprint as the parent - even if you plan - * to follow up with exec'ing a tiny binary. In reality techniques like - * copy-on-write etc mitigate the problem somewhat but we still run the risk - * of hitting system limits. + * B) vfork(2): Portable and fast but very unsafe. For details, see JDK-8357090. + * We supported this mode in older releases but removed support for it in JDK 27. + * Modern posix_spawn(3) implementations use techniques similar to vfork(2), but + * in a much safer way * - * For a Linux centric description of this problem, see the documentation on - * /proc/sys/vm/overcommit_memory in Linux proc(5). - * - * B) vfork(2): Portable and fast but very unsafe. It bypasses the memory - * problems related to fork(2) by starting the child in the memory image of - * the parent. Things that can go wrong include: - * - Programming errors in the child process before the exec(2) call may - * trash memory in the parent process, most commonly the stack of the - * thread invoking vfork. - * - Signals received by the child before the exec(2) call may be at best - * misdirected to the parent, at worst immediately kill child and parent. - * - * This is mitigated by very strict rules about what one is allowed to do in - * the child process between vfork(2) and exec(2), which is basically nothing. - * However, we always broke this rule by doing the pre-exec work between - * vfork(2) and exec(2). - * - * Also note that vfork(2) has been deprecated by the OpenGroup, presumably - * because of its many dangers. - * - * C) clone(2): This is a Linux specific call which gives the caller fine - * grained control about how exactly the process fork is executed. It is - * powerful, but Linux-specific. - * - * Aside from these three possibilities there is a forth option: posix_spawn(3). - * Where fork/vfork/clone all fork off the process and leave pre-exec work and - * calling exec(2) to the user, posix_spawn(3) offers the user fork+exec-like - * functionality in one package, similar to CreateProcess() on Windows. - * - * It is not a system call in itself, but usually a wrapper implemented within - * the libc in terms of one of (fork|vfork|clone)+exec - so whether or not it - * has advantages over calling the naked (fork|vfork|clone) functions depends - * on how posix_spawn(3) is implemented. + * C) posix_spawn(3): Where fork/vfork/clone all fork off the process and leave + * pre-exec work and calling exec(2) to the user, posix_spawn(3) offers the user + * fork+exec-like functionality in one package, similar to CreateProcess() on Windows. + * It is not a system call, but a wrapper implemented in user-space libc in terms + * of one of (fork|vfork|clone)+exec - so whether or not it has advantages over calling + * the naked (fork|vfork|clone) functions depends on how posix_spawn(3) is implemented. + * Modern posix_spawn(3) implementations, on Linux, use clone(2) with CLONE_VM | CLONE_VFORK, + * giving us the best ratio between performance and safety. + * Note however, that posix_spawn(3) can be buggy, depending on the libc implementation. + * E.g., on MacOS, it is still fully not POSIX-compliant. Therefore, we need to retain the + * FORK mode as a backup. + * Posix_spawn mode is used by default, but can be explicitly enabled using + * -Djdk.lang.Process.launchMechanism=POSIX_SPAWN when starting the (parent process) JVM. * * Note that when using posix_spawn(3), we exec twice: first a tiny binary called * the jspawnhelper, then in the jspawnhelper we do the pre-exec work and exec a @@ -117,58 +99,14 @@ * --- Linux-specific --- * * How does glibc implement posix_spawn? - * (see: sysdeps/posix/spawni.c for glibc < 2.24, - * sysdeps/unix/sysv/linux/spawni.c for glibc >= 2.24): * - * 1) Before glibc 2.4 (released 2006), posix_spawn(3) used just fork(2)/exec(2). - * This would be bad for the JDK since we would risk the known memory issues with - * fork(2). But since this only affects glibc variants which have long been - * phased out by modern distributions, this is irrelevant. + * Before glibc 2.4 (released 2006), posix_spawn(3) used just fork(2)/exec(2). From + * glibc 2.4 up to and including 2.23, it used either fork(2) or vfork(2). None of these + * versions still matter. * - * 2) Between glibc 2.4 and glibc 2.23, posix_spawn uses either fork(2) or - * vfork(2) depending on how exactly the user called posix_spawn(3): - * - * - * The child process is created using vfork(2) instead of fork(2) when - * either of the following is true: - * - * * the spawn-flags element of the attributes object pointed to by - * attrp contains the GNU-specific flag POSIX_SPAWN_USEVFORK; or - * - * * file_actions is NULL and the spawn-flags element of the attributes - * object pointed to by attrp does not contain - * POSIX_SPAWN_SETSIGMASK, POSIX_SPAWN_SETSIGDEF, - * POSIX_SPAWN_SETSCHEDPARAM, POSIX_SPAWN_SETSCHEDULER, - * POSIX_SPAWN_SETPGROUP, or POSIX_SPAWN_RESETIDS. - * - * - * Due to the way the JDK calls posix_spawn(3), it would therefore call vfork(2). - * So we would avoid the fork(2) memory problems. However, there still remains the - * risk associated with vfork(2). But it is smaller than were we to call vfork(2) - * directly since we use the jspawnhelper, moving all pre-exec work off to after - * the first exec, thereby reducing the vulnerable time window. - * - * 3) Since glibc >= 2.24, glibc uses clone+exec: - * - * new_pid = CLONE (__spawni_child, STACK (stack, stack_size), stack_size, - * CLONE_VM | CLONE_VFORK | SIGCHLD, &args); - * - * This is even better than (2): - * - * CLONE_VM means we run in the parent's memory image, as with (2) - * CLONE_VFORK means parent waits until we exec, as with (2) - * - * However, error possibilities are further reduced since: - * - posix_spawn(3) passes a separate stack for the child to run on, eliminating - * the danger of trashing the forking thread's stack in the parent process. - * - posix_spawn(3) takes care to temporarily block all incoming signals to the - * child process until the first exec(2) has been called, - * - * TL;DR - * Calling posix_spawn(3) for glibc - * (2) < 2.24 is not perfect but still better than using plain vfork(2), since - * the chance of an error happening is greatly reduced - * (3) >= 2.24 is the best option - portable, fast and as safe as possible. + * Since glibc >= 2.24, glibc uses clone+exec with CLONE_VM | CLONE_VFORK to emulate vfork + * performance but without the inherent dangers (we run inside the parent's memory image + * and stop the parent for as long as it takes the child process to exec). * * --- * @@ -180,7 +118,6 @@ * * * - * * Based on the above analysis, we are currently defaulting to posix_spawn() * on all Unices including Linux. */ @@ -489,28 +426,6 @@ static int copystrings(char *buf, int offset, const char * const *arg) { __attribute_noinline__ #endif -/* vfork(2) is deprecated on Darwin */ -#ifndef __APPLE__ -static pid_t -vforkChild(ChildStuff *c) { - volatile pid_t resultPid; - - /* - * We separate the call to vfork into a separate function to make - * very sure to keep stack of child from corrupting stack of parent, - * as suggested by the scary gcc warning: - * warning: variable 'foo' might be clobbered by 'longjmp' or 'vfork' - */ - resultPid = vfork(); - - if (resultPid == 0) { - childProcess(c); - } - assert(resultPid != 0); /* childProcess never returns */ - return resultPid; -} -#endif - static pid_t forkChild(ChildStuff *c) { pid_t resultPid; @@ -734,11 +649,6 @@ spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) static pid_t startChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) { switch (c->mode) { -/* vfork(2) is deprecated on Darwin*/ - #ifndef __APPLE__ - case MODE_VFORK: - return vforkChild(c); - #endif case MODE_FORK: return forkChild(c); case MODE_POSIX_SPAWN: @@ -872,9 +782,6 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, if (resultPid < 0) { char * failMessage = "unknown"; switch (c->mode) { - case MODE_VFORK: - failMessage = "vfork failed"; - break; case MODE_FORK: failMessage = "fork failed"; break; diff --git a/src/java.base/unix/native/libjava/childproc.c b/src/java.base/unix/native/libjava/childproc.c index 83ee782482f..6bc15dfb40c 100644 --- a/src/java.base/unix/native/libjava/childproc.c +++ b/src/java.base/unix/native/libjava/childproc.c @@ -271,31 +271,6 @@ initVectorFromBlock(const char**vector, const char* block, int count) vector[count] = NULL; } -/** - * Exec FILE as a traditional Bourne shell script (i.e. one without #!). - * If we could do it over again, we would probably not support such an ancient - * misfeature, but compatibility wins over sanity. The original support for - * this was imported accidentally from execvp(). - */ -static void -execve_as_traditional_shell_script(const char *file, - const char *argv[], - const char *const envp[]) -{ - /* Use the extra word of space provided for us in argv by caller. */ - const char *argv0 = argv[0]; - const char *const *end = argv; - while (*end != NULL) - ++end; - memmove(argv+2, argv+1, (end-argv) * sizeof(*end)); - argv[0] = "/bin/sh"; - argv[1] = file; - execve(argv[0], (char **) argv, (char **) envp); - /* Can't even exec /bin/sh? Big trouble, but let's soldier on... */ - memmove(argv+1, argv+2, (end-argv) * sizeof(*end)); - argv[0] = argv0; -} - /** * Like execve(2), except that in case of ENOEXEC, FILE is assumed to * be a shell script and the system default shell is invoked to run it. @@ -305,16 +280,9 @@ execve_with_shell_fallback(int mode, const char *file, const char *argv[], const char *const envp[]) { - if (mode == MODE_VFORK) { - /* shared address space; be very careful. */ - execve(file, (char **) argv, (char **) envp); - if (errno == ENOEXEC) - execve_as_traditional_shell_script(file, argv, envp); - } else { - /* unshared address space; we can mutate environ. */ - environ = (char **) envp; - execvp(file, (char **) argv); - } + /* unshared address space; we can mutate environ. */ + environ = (char **) envp; + execvp(file, (char **) argv); } /** @@ -430,7 +398,7 @@ childProcess(void *arg) #endif /* File descriptor setup for non-Posix-spawn mode */ - if (p->mode != MODE_POSIX_SPAWN) { + if (p->mode == MODE_FORK) { /* Close the parent sides of the pipes. Closing pipe fds here is redundant, since markDescriptorsCloseOnExec() @@ -482,7 +450,7 @@ childProcess(void *arg) /* We moved the fail pipe fd */ fail_pipe_fd = FAIL_FILENO; - } /* end: FORK/VFORK mode */ + } /* end: FORK mode */ assert(fail_pipe_fd == FAIL_FILENO); @@ -508,12 +476,10 @@ childProcess(void *arg) goto WhyCantJohnnyExec; } - // Reset any mask signals from parent, but not in VFORK mode - if (p->mode != MODE_VFORK) { - sigset_t unblock_signals; - sigemptyset(&unblock_signals); - sigprocmask(SIG_SETMASK, &unblock_signals, NULL); - } + // Reset any mask signals from parent + sigset_t unblock_signals; + sigemptyset(&unblock_signals); + sigprocmask(SIG_SETMASK, &unblock_signals, NULL); // Children should be started with default signal disposition for SIGPIPE if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) { diff --git a/src/java.base/unix/native/libjava/childproc.h b/src/java.base/unix/native/libjava/childproc.h index 27414e60137..0b02df7f3dd 100644 --- a/src/java.base/unix/native/libjava/childproc.h +++ b/src/java.base/unix/native/libjava/childproc.h @@ -81,7 +81,6 @@ extern char **environ; */ #define MODE_FORK 1 #define MODE_POSIX_SPAWN 2 -#define MODE_VFORK 3 typedef struct _ChildStuff { diff --git a/test/jdk/java/lang/ProcessBuilder/Basic.java b/test/jdk/java/lang/ProcessBuilder/Basic.java index 70d6101c1a5..b150690ef48 100644 --- a/test/jdk/java/lang/ProcessBuilder/Basic.java +++ b/test/jdk/java/lang/ProcessBuilder/Basic.java @@ -50,16 +50,6 @@ * @run main/othervm/native/timeout=360 -Djdk.lang.Process.launchMechanism=fork Basic */ -/* - * @test id=VFORK - * @modules java.base/java.lang:open - * java.base/java.io:open - * java.base/jdk.internal.misc - * @requires (os.family == "linux") - * @library /test/lib - * @run main/othervm/timeout=300 -Djdk.lang.Process.launchMechanism=vfork Basic - */ - import java.lang.ProcessBuilder.Redirect; import java.lang.ProcessHandle; import static java.lang.ProcessBuilder.Redirect.*; diff --git a/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/ConcNativeForkTest.java b/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/ConcNativeForkTest.java index f02e4991302..904a0e12f98 100644 --- a/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/ConcNativeForkTest.java +++ b/test/jdk/java/lang/ProcessBuilder/ConcNativeForkTest/ConcNativeForkTest.java @@ -42,16 +42,6 @@ * @run main/othervm/manual -Djdk.lang.Process.launchMechanism=FORK ConcNativeForkTest */ -/* - * @test id=VFORK - * @bug 8377907 - * @summary Test that demonstrates the hanging-parent-on-native-concurrent-forks problem - * @requires os.family == "linux" - * @requires vm.flagless - * @library /test/lib - * @run main/othervm/manual -Djdk.lang.Process.launchMechanism=VFORK ConcNativeForkTest - */ - public class ConcNativeForkTest { // How this works: diff --git a/test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java b/test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java index aee85e2335f..02c60395f17 100644 --- a/test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java +++ b/test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java @@ -38,14 +38,6 @@ * @run main/othervm/native -Djdk.lang.Process.launchMechanism=fork -agentlib:FDLeaker FDLeakTest */ -/** - * @test id=vfork - * @summary Check that we don't leak FDs to child processes - * @requires os.family == "linux" - * @library /test/lib - * @run main/othervm/native -Djdk.lang.Process.launchMechanism=vfork -agentlib:FDLeaker FDLeakTest - */ - import jdk.test.lib.process.ProcessTools; public class FDLeakTest { // This test has two native parts: diff --git a/test/jdk/java/lang/ProcessBuilder/NonPipelineLeaksFD.java b/test/jdk/java/lang/ProcessBuilder/NonPipelineLeaksFD.java index be2c4af3bbe..e43255d8c52 100644 --- a/test/jdk/java/lang/ProcessBuilder/NonPipelineLeaksFD.java +++ b/test/jdk/java/lang/ProcessBuilder/NonPipelineLeaksFD.java @@ -32,14 +32,6 @@ import java.io.*; * @run main/othervm -Djdk.lang.Process.launchMechanism=fork NonPipelineLeaksFD */ -/* - * @test id=VFORK - * @summary Check that we don't accumulate leaked FDs in the parent process - * @requires os.family == "linux" - * @library /test/lib - * @run main/othervm -Djdk.lang.Process.launchMechanism=vfork NonPipelineLeaksFD - */ - /* * @test id=POSIX_SPAWN * @summary Check that we don't accumulate leaked FDs in the parent process diff --git a/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/PipesCloseOnExecTest.java b/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/PipesCloseOnExecTest.java index 1e988f0496c..8390caae59a 100644 --- a/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/PipesCloseOnExecTest.java +++ b/test/jdk/java/lang/ProcessBuilder/PipesCloseOnExecTest/PipesCloseOnExecTest.java @@ -32,16 +32,6 @@ * @run main/othervm/native -Djdk.lang.Process.launchMechanism=FORK PipesCloseOnExecTest */ -/* - * @test id=VFORK - * @bug 8377907 - * @summary Check that we don't open pipes without CLOEXCEC - * @requires os.family == "linux" - * @requires vm.flagless - * @library /test/lib - * @run main/othervm/native -Djdk.lang.Process.launchMechanism=VFORK PipesCloseOnExecTest - */ - /* * @test id=POSIX_SPAWN * @bug 8377907 diff --git a/test/jdk/java/lang/ProcessBuilder/RejectVFORKMode.java b/test/jdk/java/lang/ProcessBuilder/RejectVFORKMode.java new file mode 100644 index 00000000000..db8387821f5 --- /dev/null +++ b/test/jdk/java/lang/ProcessBuilder/RejectVFORKMode.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corp. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8357089 + * @summary Check that specifying VFORK correctly falls back to FORK with a clear warning to stderr. + * @requires (os.family == "linux") + * @library /test/lib + * @run main RejectVFORKMode GRANDPARENT + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class RejectVFORKMode { + public static void main(String[] args) throws Exception { + + switch (args[0]) { + case "PARENT" -> { + ProcessBuilder pb = new ProcessBuilder("sh", "-c", "echo 'Child Process'; exit 12;"); + // This should result in a (written to this process' stderr) warning about VFORK mode. + // But child should have been started successfully. + OutputAnalyzer outputAnalyzer = ProcessTools.executeCommand(pb); + outputAnalyzer.shouldHaveExitValue(12); + outputAnalyzer.shouldContain("Child Process"); + System.exit(0); + } + case "GRANDPARENT" -> { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Djdk.lang.Process.launchMechanism=VFORK", RejectVFORKMode.class.getName(), "PARENT"); + OutputAnalyzer outputAnalyzer = ProcessTools.executeCommand(pb); + outputAnalyzer.shouldHaveExitValue(0); + outputAnalyzer.shouldContain("The VFORK launch mechanism has been removed. Switching to FORK instead."); + } + default -> throw new RuntimeException("Bad arg"); + } + } +} diff --git a/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/TestChildSignalDisposition.java b/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/TestChildSignalDisposition.java index 50fe054ee34..80b63b765da 100644 --- a/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/TestChildSignalDisposition.java +++ b/test/jdk/java/lang/ProcessBuilder/childSignalDisposition/TestChildSignalDisposition.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,15 +39,6 @@ * @run main/othervm/native -Djdk.lang.Process.launchMechanism=fork -agentlib:ChangeSignalDisposition TestChildSignalDisposition */ -/** - * @test id=vfork - * @bug 8364611 - * @summary Check that childs start with SIG_DFL as SIGPIPE disposition - * @requires os.family == "linux" - * @library /test/lib - * @run main/othervm/native -Djdk.lang.Process.launchMechanism=vfork -agentlib:ChangeSignalDisposition TestChildSignalDisposition - */ - import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; public class TestChildSignalDisposition { From 61df7cc8b91365e487591ec8402e797a25790a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Mon, 30 Mar 2026 06:27:30 +0000 Subject: [PATCH 11/25] 8380988: C2: Unexpected node in SuperWord truncation: UModI/UDivI Reviewed-by: epeter, jkarthikeyan --- src/hotspot/share/opto/superword.cpp | 2 + .../vectorization/TestSubwordTruncation.java | 46 ++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index d878b2b1d3d..53845a94c1c 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -2500,7 +2500,9 @@ static bool can_subword_truncate(Node* in, const Type* type) { switch (opc) { case Op_AbsI: case Op_DivI: + case Op_UDivI: case Op_ModI: + case Op_UModI: case Op_MinI: case Op_MaxI: case Op_CMoveI: diff --git a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java index 2f6296e41d2..0a80a59efd4 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestSubwordTruncation.java @@ -29,7 +29,7 @@ import compiler.lib.generators.*; /* * @test - * @bug 8350177 8362171 8369881 8342095 + * @bug 8350177 8362171 8369881 8342095 8380988 * @summary Ensure that truncation of subword vectors produces correct results * @library /test/lib / * @run driver compiler.vectorization.TestSubwordTruncation @@ -448,6 +448,50 @@ public class TestSubwordTruncation { } } + @Test + @IR(counts = { IRNode.UMOD_I, ">0" }) + @Arguments(setup = "setupByteArray") + public Object[] testUMod(final byte[] in) { + int n = G.next().intValue(); + for (int i = 1; i < SIZE; i++) { + in[i] = (byte) Integer.remainderUnsigned(n, i); + } + + return new Object[] { Integer.valueOf(n), in }; + } + + @Check(test = "testUMod") + public void checkTestUMod(Object[] vals) { + int n = (Integer) vals[0]; + byte[] res = (byte[]) vals[1]; + for (int i = 1; i < SIZE; i++) { + byte val = (byte) Integer.remainderUnsigned(n, i); + Asserts.assertEQ(res[i], val); + } + } + + @Test + @IR(counts = { IRNode.UDIV_I, ">0" }) + @Arguments(setup = "setupByteArray") + public Object[] testUDiv(final byte[] in) { + int n = G.next().intValue(); + for (int i = 1; i < SIZE; i++) { + in[i] = (byte) Integer.divideUnsigned(n, i); + } + + return new Object[] { Integer.valueOf(n), in }; + } + + @Check(test = "testUDiv") + public void checkTestUDiv(Object[] vals) { + int n = (Integer) vals[0]; + byte[] res = (byte[]) vals[1]; + for (int i = 1; i < SIZE; i++) { + byte val = (byte) Integer.divideUnsigned(n, i); + Asserts.assertEQ(res[i], val); + } + } + @Test @IR(counts = { IRNode.CMP_LT_MASK, ">0" }) @Arguments(setup = "setupByteArray") From bbbd5b9984c909773227123ad31b1dd8336df0d9 Mon Sep 17 00:00:00 2001 From: Stefan Johansson Date: Mon, 30 Mar 2026 06:29:43 +0000 Subject: [PATCH 12/25] 8380773: Extend ContainerMemoryUsageEvent to include host memory usage Reviewed-by: mgronlun, tkiriyama, sgehwolf --- src/hotspot/share/jfr/jni/jfrJniMethod.cpp | 9 +++++++++ src/hotspot/share/jfr/jni/jfrJniMethod.hpp | 2 ++ src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp | 1 + .../jdk/jfr/events/ContainerMemoryUsageEvent.java | 5 +++++ .../share/classes/jdk/jfr/internal/JDKEvents.java | 1 + src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java | 6 ++++++ test/hotspot/jtreg/containers/docker/TestJFREvents.java | 4 +++- 7 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp index 885484020bd..0183bf634f6 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp @@ -410,6 +410,15 @@ JVM_ENTRY_NO_ENV(jlong, jfr_host_total_swap_memory(JNIEnv* env, jclass jvm)) return static_cast(total_swap_space); JVM_END +JVM_ENTRY_NO_ENV(jlong, jfr_host_memory_usage(JNIEnv* env, jclass jvm)) + physical_memory_size_type memory_usage = 0; + if (!os::Machine::used_memory(memory_usage)) { + // Return -1 to signal failure to get memory usage. + return static_cast(-1); + } + return static_cast(memory_usage); +JVM_END + JVM_ENTRY_NO_ENV(void, jfr_emit_data_loss(JNIEnv* env, jclass jvm, jlong bytes)) EventDataLoss::commit(bytes, min_jlong); JVM_END diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp index 9769df57bd3..bcdaf7a99b7 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp @@ -163,6 +163,8 @@ jlong JNICALL jfr_host_total_memory(JNIEnv* env, jclass jvm); jlong JNICALL jfr_host_total_swap_memory(JNIEnv* env, jclass jvm); +jlong JNICALL jfr_host_memory_usage(JNIEnv* env, jclass jvm); + void JNICALL jfr_emit_data_loss(JNIEnv* env, jclass jvm, jlong bytes); jlong JNICALL jfr_register_stack_filter(JNIEnv* env, jclass jvm, jobjectArray classes, jobjectArray methods); diff --git a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp index 2979f5c5c2d..0813289e840 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethodRegistration.cpp @@ -101,6 +101,7 @@ JfrJniMethodRegistration::JfrJniMethodRegistration(JNIEnv* env) { (char*)"isContainerized", (char*)"()Z", (void*) jfr_is_containerized, (char*)"hostTotalMemory", (char*)"()J", (void*) jfr_host_total_memory, (char*)"hostTotalSwapMemory", (char*)"()J", (void*) jfr_host_total_swap_memory, + (char*)"hostMemoryUsage", (char*)"()J", (void*) jfr_host_memory_usage, (char*)"emitDataLoss", (char*)"(J)V", (void*)jfr_emit_data_loss, (char*)"registerStackFilter", (char*)"([Ljava/lang/String;[Ljava/lang/String;)J", (void*)jfr_register_stack_filter, (char*)"unregisterStackFilter", (char*)"(J)V", (void*)jfr_unregister_stack_filter, diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/ContainerMemoryUsageEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/ContainerMemoryUsageEvent.java index 285952d0c66..9a83877a967 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/ContainerMemoryUsageEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/ContainerMemoryUsageEvent.java @@ -50,4 +50,9 @@ public final class ContainerMemoryUsageEvent extends AbstractPeriodicEvent { @Description("Amount of physical memory and swap space, in bytes, that is currently allocated in the current container") @DataAmount public long swapMemoryUsage; + + @Label("Host Memory Usage") + @Description("Amount of physical memory, in bytes, that is currently allocated in the host system") + @DataAmount + public long hostMemoryUsage; } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java index 53d40c1e5e1..79bc614545e 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java @@ -181,6 +181,7 @@ public final class JDKEvents { event.memoryFailCount = containerMetrics.getMemoryFailCount(); event.memoryUsage = containerMetrics.getMemoryUsage(); event.swapMemoryUsage = containerMetrics.getMemoryAndSwapUsage(); + event.hostMemoryUsage = JVM.hostMemoryUsage(); event.commit(); } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java index 24c92e81a4c..2aa7dfd6e97 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JVM.java @@ -657,6 +657,12 @@ public final class JVM { */ public static native long hostTotalSwapMemory(); + /** + * Returns the amount of memory used in the host system whether or not this + * JVM runs in a container. + */ + public static native long hostMemoryUsage(); + /** * Emit a jdk.DataLoss event for the specified amount of bytes. * diff --git a/test/hotspot/jtreg/containers/docker/TestJFREvents.java b/test/hotspot/jtreg/containers/docker/TestJFREvents.java index d46c422723b..c3112310479 100644 --- a/test/hotspot/jtreg/containers/docker/TestJFREvents.java +++ b/test/hotspot/jtreg/containers/docker/TestJFREvents.java @@ -162,6 +162,7 @@ public class TestJFREvents { String memoryFailCountFld = "memoryFailCount"; String memoryUsageFld = "memoryUsage"; String swapMemoryUsageFld = "swapMemoryUsage"; + String hostMemoryUsageFld = "hostMemoryUsage"; DockerTestUtils.dockerRunJava( commonDockerOpts() @@ -169,7 +170,8 @@ public class TestJFREvents { .shouldHaveExitValue(0) .shouldContain(memoryFailCountFld) .shouldContain(memoryUsageFld) - .shouldContain(swapMemoryUsageFld); + .shouldContain(swapMemoryUsageFld) + .shouldContain(hostMemoryUsageFld); } private static void testIOUsage() throws Exception { From 0885a0c4c548532e87533467315b48465c8b6e20 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Mon, 30 Mar 2026 06:35:55 +0000 Subject: [PATCH 13/25] 4696824: In Metal and other L&F Button.focusInputMap, CheckBox.focusInputMap ... are same Reviewed-by: tr, prr --- .../swing/plaf/motif/MotifLookAndFeel.java | 27 +++++----------- .../swing/plaf/metal/MetalLookAndFeel.java | 32 ++++++++----------- 2 files changed, 21 insertions(+), 38 deletions(-) diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java index d254443b8d1..5263d248f45 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java @@ -537,6 +537,10 @@ public class MotifLookAndFeel extends BasicLookAndFeel @SuppressWarnings("deprecation") final int metaMask = KeyEvent.META_MASK; + Object commonInputMap = new UIDefaults.LazyInputMap(new Object[] { + "SPACE", "pressed", + "released SPACE", "released" + }); Object[] defaults = { "Desktop.background", table.get("desktop"), @@ -593,20 +597,13 @@ public class MotifLookAndFeel extends BasicLookAndFeel "Button.foreground", table.get("controlText"), "Button.select", table.get("controlLightShadow"), "Button.font", dialogPlain12, - "Button.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "Button.focusInputMap", commonInputMap, "CheckBox.textIconGap", 8, "CheckBox.margin", new InsetsUIResource(4, 2, 4, 2), "CheckBox.icon", checkBoxIcon, "CheckBox.focus", table.get("activeCaptionBorder"), - "CheckBox.focusInputMap", - new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "CheckBox.focusInputMap", commonInputMap, "RadioButton.margin", new InsetsUIResource(4, 2, 4, 2), "RadioButton.textIconGap", 8, @@ -615,22 +612,14 @@ public class MotifLookAndFeel extends BasicLookAndFeel "RadioButton.icon", radioButtonIcon, "RadioButton.focus", table.get("activeCaptionBorder"), "RadioButton.icon", radioButtonIcon, - "RadioButton.focusInputMap", - new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "RadioButton.focusInputMap", commonInputMap, "ToggleButton.border", toggleButtonBorder, "ToggleButton.background", table.get("control"), "ToggleButton.foreground", table.get("controlText"), "ToggleButton.focus", table.get("controlText"), "ToggleButton.select", table.get("controlLightShadow"), - "ToggleButton.focusInputMap", - new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "ToggleButton.focusInputMap", commonInputMap, // Menus "Menu.border", menuMarginBorder, diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java index 7c56c681423..461c597cc5f 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -706,6 +706,11 @@ public class MetalLookAndFeel extends BasicLookAndFeel // DEFAULTS TABLE // + Object commonInputMap = new UIDefaults.LazyInputMap(new Object[] { + "SPACE", "pressed", + "released SPACE", "released" + }); + Object[] defaults = { // *** Auditory Feedback "AuditoryCues.defaultCueList", defaultCueList, @@ -791,6 +796,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel }), + + // Buttons "Button.defaultButtonFollowsFocus", Boolean.FALSE, "Button.disabledText", inactiveControlTextColor, @@ -798,10 +805,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel "Button.border", buttonBorder, "Button.font", controlTextValue, "Button.focus", focusColor, - "Button.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "Button.focusInputMap", commonInputMap, + // Button default margin is (2, 14, 2, 14), defined in // BasicLookAndFeel via "Button.margin" UI property. @@ -810,11 +815,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel "CheckBox.font", controlTextValue, "CheckBox.focus", focusColor, "CheckBox.icon",(LazyValue) t -> MetalIconFactory.getCheckBoxIcon(), - "CheckBox.focusInputMap", - new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "CheckBox.focusInputMap", commonInputMap, + // margin is 2 all the way around, BasicBorders.RadioButtonBorder // (checkbox uses RadioButtonBorder) is 2 all the way around too. "CheckBox.totalInsets", new Insets(4, 4, 4, 4), @@ -824,11 +826,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "RadioButton.icon",(LazyValue) t -> MetalIconFactory.getRadioButtonIcon(), "RadioButton.font", controlTextValue, "RadioButton.focus", focusColor, - "RadioButton.focusInputMap", - new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "RadioButton.focusInputMap", commonInputMap, // margin is 2 all the way around, BasicBorders.RadioButtonBorder // is 2 all the way around too. "RadioButton.totalInsets", new Insets(4, 4, 4, 4), @@ -838,11 +836,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "ToggleButton.focus", focusColor, "ToggleButton.border", toggleButtonBorder, "ToggleButton.font", controlTextValue, - "ToggleButton.focusInputMap", - new UIDefaults.LazyInputMap(new Object[] { - "SPACE", "pressed", - "released SPACE", "released" - }), + "ToggleButton.focusInputMap", commonInputMap, // File View From 45f9039513d08923e019b50ae5007d9339b9731f Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Mon, 30 Mar 2026 06:48:49 +0000 Subject: [PATCH 14/25] 8380030: [AIX] unify dladdr between hotspot and libawt Reviewed-by: dholmes, stuefe, erikj --- make/common/modules/LauncherCommon.gmk | 10 +-- src/hotspot/os/aix/porting_aix.cpp | 6 +- src/hotspot/os/posix/include/jvm_md.h | 15 +++- .../aix/native/libawt/porting_aix.c | 87 ------------------- .../aix/native/libawt/porting_aix.h | 40 --------- .../unix/native/libawt/awt/awt_LoadLibrary.c | 4 +- 6 files changed, 26 insertions(+), 136 deletions(-) delete mode 100644 src/java.desktop/aix/native/libawt/porting_aix.c delete mode 100644 src/java.desktop/aix/native/libawt/porting_aix.h diff --git a/make/common/modules/LauncherCommon.gmk b/make/common/modules/LauncherCommon.gmk index 859494861b2..8d45142ef4a 100644 --- a/make/common/modules/LauncherCommon.gmk +++ b/make/common/modules/LauncherCommon.gmk @@ -36,16 +36,16 @@ include $(TOPDIR)/make/ToolsJdk.gmk LAUNCHER_SRC := $(TOPDIR)/src/java.base/share/native/launcher -ifeq ($(call isTargetOs, aix), true) - ADD_PLATFORM_INCLUDE_DIR := -I$(TOPDIR)/src/java.base/aix/native/include -endif - LAUNCHER_CFLAGS += -I$(TOPDIR)/src/java.base/share/native/launcher \ -I$(TOPDIR)/src/java.base/share/native/libjli \ - $(ADD_PLATFORM_INCLUDE_DIR) \ -I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/libjli \ -I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS)/native/libjli \ # + +ifeq ($(call isTargetOs, aix), true) + LAUNCHER_CFLAGS += -I$(TOPDIR)/src/java.base/aix/native/include +endif + MACOSX_PLIST_DIR := $(TOPDIR)/src/java.base/macosx/native/launcher JAVA_MANIFEST := $(TOPDIR)/src/java.base/windows/native/launcher/java.manifest diff --git a/src/hotspot/os/aix/porting_aix.cpp b/src/hotspot/os/aix/porting_aix.cpp index b3f878fbfdd..f0527136d90 100644 --- a/src/hotspot/os/aix/porting_aix.cpp +++ b/src/hotspot/os/aix/porting_aix.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2024 SAP SE. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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. * @@ -426,6 +426,10 @@ int dladdr(void* addr, Dl_info* info) { } +int JVM_dladdr(void* addr, Dl_info* info) { + return dladdr(addr, info); +} + ///////////////////////////////////////////////////////////////////////////// // Native callstack dumping diff --git a/src/hotspot/os/posix/include/jvm_md.h b/src/hotspot/os/posix/include/jvm_md.h index eb8e1f0d7e9..061ef17aaae 100644 --- a/src/hotspot/os/posix/include/jvm_md.h +++ b/src/hotspot/os/posix/include/jvm_md.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -62,6 +62,19 @@ #define JVM_X_OK X_OK #define JVM_F_OK F_OK +#if defined(AIX) +#include "jni_md.h" +#include "dl_info.h" + +#ifdef __cplusplus +extern "C" { +#endif +JNIEXPORT int JVM_dladdr(void* addr, Dl_info* info); +#ifdef __cplusplus +} +#endif +#endif + /* * File I/O */ diff --git a/src/java.desktop/aix/native/libawt/porting_aix.c b/src/java.desktop/aix/native/libawt/porting_aix.c deleted file mode 100644 index d8688c212d7..00000000000 --- a/src/java.desktop/aix/native/libawt/porting_aix.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2012, 2026 SAP SE. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * 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. - * - */ - -#include -#include -#include -#include - -#include "porting_aix.h" - -static unsigned char dladdr_buffer[0x8000]; - -static void fill_dll_info(void) { - int rc = loadquery(L_GETINFO,dladdr_buffer, sizeof(dladdr_buffer)); - if (rc == -1) { - fprintf(stderr, "loadquery failed (%d %s)", errno, strerror(errno)); - fflush(stderr); - } -} - -static int dladdr_dont_reload(void* addr, Dl_info* info) { - const struct ld_info* p = (struct ld_info*) dladdr_buffer; - memset((void *)info, 0, sizeof(Dl_info)); - for (;;) { - if (addr >= p->ldinfo_textorg && - (char*)addr < (char*)(p->ldinfo_textorg) + p->ldinfo_textsize) { - info->dli_fname = p->ldinfo_filename; - return 1; - } - if (!p->ldinfo_next) { - break; - } - p = (struct ld_info*)(((char*)p) + p->ldinfo_next); - } - return 0; -} - -#ifdef __cplusplus -extern "C" -#endif -int dladdr(void *addr, Dl_info *info) { - static int loaded = 0; - if (!loaded) { - fill_dll_info(); - loaded = 1; - } - if (!addr) { - return 0; - } - /* Address could be AIX function descriptor? */ - void* const addr0 = *( (void**) addr ); - int rc = dladdr_dont_reload(addr, info); - if (rc == 0) { - rc = dladdr_dont_reload(addr0, info); - if (rc == 0) { - fill_dll_info(); /* refill, maybe loadquery info is outdated */ - rc = dladdr_dont_reload(addr, info); - if (rc == 0) { - rc = dladdr_dont_reload(addr0, info); - } - } - } - return rc; -} diff --git a/src/java.desktop/aix/native/libawt/porting_aix.h b/src/java.desktop/aix/native/libawt/porting_aix.h deleted file mode 100644 index 04d11590915..00000000000 --- a/src/java.desktop/aix/native/libawt/porting_aix.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2012, 2026 SAP SE. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * 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. - * - */ - -/* - * Aix' own version of dladdr(). - * This function tries to mimic dladdr(3) on Linux - * (see http://linux.die.net/man/3/dladdr) - * dladdr(3) is not POSIX but a GNU extension, and is not available on AIX. - * - */ - -#include "dl_info.h" - -#ifdef __cplusplus -extern "C" -#endif -int dladdr(void *addr, Dl_info *info); diff --git a/src/java.desktop/unix/native/libawt/awt/awt_LoadLibrary.c b/src/java.desktop/unix/native/libawt/awt/awt_LoadLibrary.c index cd07c347c9e..af50fdbb5c0 100644 --- a/src/java.desktop/unix/native/libawt/awt/awt_LoadLibrary.c +++ b/src/java.desktop/unix/native/libawt/awt/awt_LoadLibrary.c @@ -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 @@ -36,7 +36,7 @@ #include #ifdef AIX -#include "porting_aix.h" /* For the 'dladdr' function. */ +#define dladdr JVM_dladdr #endif #ifdef DEBUG From cd9724565d295e125484fb2933c0479ceabbaabe Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 30 Mar 2026 08:37:25 +0000 Subject: [PATCH 15/25] 8381205: GHA: Upgrade Node.js 20 to 24 Reviewed-by: erikj --- .github/actions/build-jtreg/action.yml | 6 +++--- .github/actions/do-build/action.yml | 4 ++-- .github/actions/get-bootjdk/action.yml | 2 +- .github/actions/get-bundles/action.yml | 6 +++--- .github/actions/get-gtest/action.yml | 2 +- .github/actions/get-jtreg/action.yml | 2 +- .github/actions/get-msys2/action.yml | 2 +- .github/actions/upload-bundles/action.yml | 2 +- .github/workflows/build-alpine-linux.yml | 2 +- .github/workflows/build-cross-compile.yml | 4 ++-- .github/workflows/build-linux.yml | 2 +- .github/workflows/build-macos.yml | 2 +- .github/workflows/build-windows.yml | 2 +- .github/workflows/main.yml | 2 +- .github/workflows/test.yml | 6 +++--- 15 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/actions/build-jtreg/action.yml b/.github/actions/build-jtreg/action.yml index a9c046e9dd9..334812e8341 100644 --- a/.github/actions/build-jtreg/action.yml +++ b/.github/actions/build-jtreg/action.yml @@ -37,13 +37,13 @@ runs: - name: 'Check cache for already built JTReg' id: get-cached - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: jtreg/installed key: jtreg-${{ steps.version.outputs.value }} - name: 'Checkout the JTReg source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: openjdk/jtreg ref: jtreg-${{ steps.version.outputs.value }} @@ -61,7 +61,7 @@ runs: if: (steps.get-cached.outputs.cache-hit != 'true') - name: 'Upload JTReg artifact' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: bundles-jtreg-${{ steps.version.outputs.value }} path: jtreg/installed diff --git a/.github/actions/do-build/action.yml b/.github/actions/do-build/action.yml index 6f2c2ce0218..6f6bbdabb68 100644 --- a/.github/actions/do-build/action.yml +++ b/.github/actions/do-build/action.yml @@ -66,7 +66,7 @@ runs: shell: bash - name: 'Upload build logs' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: failure-logs-${{ inputs.platform }}${{ inputs.debug-suffix }} path: failure-logs @@ -74,7 +74,7 @@ runs: # This is the best way I found to abort the job with an error message - name: 'Notify about build failures' - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: core.setFailed('Build failed. See summary for details.') if: steps.check.outputs.failure == 'true' diff --git a/.github/actions/get-bootjdk/action.yml b/.github/actions/get-bootjdk/action.yml index 312fb642c82..d531358b7dd 100644 --- a/.github/actions/get-bootjdk/action.yml +++ b/.github/actions/get-bootjdk/action.yml @@ -65,7 +65,7 @@ runs: - name: 'Check cache for BootJDK' id: get-cached-bootjdk - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: bootjdk/jdk key: boot-jdk-${{ inputs.platform }}-${{ steps.sha256.outputs.value }} diff --git a/.github/actions/get-bundles/action.yml b/.github/actions/get-bundles/action.yml index a356aa9fd8d..55fa0e842d2 100644 --- a/.github/actions/get-bundles/action.yml +++ b/.github/actions/get-bundles/action.yml @@ -54,14 +54,14 @@ runs: steps: - name: 'Download bundles artifact' id: download-bundles - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }} path: bundles continue-on-error: true - name: 'Download bundles artifact (retry)' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }} path: bundles @@ -69,7 +69,7 @@ runs: - name: 'Download static bundles artifact' id: download-static-bundles - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }}${{ inputs.static-suffix }} path: bundles diff --git a/.github/actions/get-gtest/action.yml b/.github/actions/get-gtest/action.yml index 7a329460a6e..bc53fa2a3b1 100644 --- a/.github/actions/get-gtest/action.yml +++ b/.github/actions/get-gtest/action.yml @@ -40,7 +40,7 @@ runs: var: GTEST_VERSION - name: 'Checkout GTest source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: google/googletest ref: 'v${{ steps.version.outputs.value }}' diff --git a/.github/actions/get-jtreg/action.yml b/.github/actions/get-jtreg/action.yml index 36c895fc59d..8c75ae10c7f 100644 --- a/.github/actions/get-jtreg/action.yml +++ b/.github/actions/get-jtreg/action.yml @@ -41,7 +41,7 @@ runs: - name: 'Download JTReg artifact' id: download-jtreg - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: bundles-jtreg-${{ steps.version.outputs.value }} path: jtreg/installed diff --git a/.github/actions/get-msys2/action.yml b/.github/actions/get-msys2/action.yml index 308230ebf2e..7351a120ac4 100644 --- a/.github/actions/get-msys2/action.yml +++ b/.github/actions/get-msys2/action.yml @@ -31,7 +31,7 @@ runs: steps: - name: 'Install MSYS2' id: msys2 - uses: msys2/setup-msys2@v2.28.0 + uses: msys2/setup-msys2@v2.31.0 with: install: 'autoconf tar unzip zip make' path-type: minimal diff --git a/.github/actions/upload-bundles/action.yml b/.github/actions/upload-bundles/action.yml index 78fb0a94bfd..94308002ea7 100644 --- a/.github/actions/upload-bundles/action.yml +++ b/.github/actions/upload-bundles/action.yml @@ -87,7 +87,7 @@ runs: shell: bash - name: 'Upload bundles artifact' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }}${{ inputs.static-suffix }}${{ inputs.bundle-suffix }} path: bundles diff --git a/.github/workflows/build-alpine-linux.yml b/.github/workflows/build-alpine-linux.yml index c39962fa07f..6863da9016e 100644 --- a/.github/workflows/build-alpine-linux.yml +++ b/.github/workflows/build-alpine-linux.yml @@ -74,7 +74,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Install toolchain and dependencies' run: | diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index a0642d469aa..99b6c40606c 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -94,7 +94,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Get the BootJDK' id: bootjdk @@ -122,7 +122,7 @@ jobs: - name: 'Check cache for sysroot' id: get-cached-sysroot - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: sysroot key: sysroot-${{ matrix.debian-arch }}-${{ hashFiles('./.github/workflows/build-cross-compile.yml') }} diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 791b53a3f04..c501670439e 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -84,7 +84,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Get the BootJDK' id: bootjdk diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 484e616fad7..435576f4afd 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -75,7 +75,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Get the BootJDK' id: bootjdk diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 4dafc016a99..3bb50a137ec 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -83,7 +83,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Get MSYS2' uses: ./.github/actions/get-msys2 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 85ec75f343c..20be196b128 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -75,7 +75,7 @@ jobs: steps: - name: 'Checkout the scripts' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: sparse-checkout: | .github diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8f33454305e..b240b42fb97 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -128,7 +128,7 @@ jobs: steps: - name: 'Checkout the JDK source' - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: 'Get MSYS2' uses: ./.github/actions/get-msys2 @@ -239,7 +239,7 @@ jobs: if: always() - name: 'Upload test results' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: path: results name: ${{ steps.package.outputs.artifact-name }} @@ -247,7 +247,7 @@ jobs: # This is the best way I found to abort the job with an error message - name: 'Notify about test failures' - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: core.setFailed('${{ steps.run-tests.outputs.error-message }}') if: steps.run-tests.outputs.failure == 'true' From 29e1ee2eccd59e665827e0d42c490261002cf99e Mon Sep 17 00:00:00 2001 From: Martin Doerr Date: Mon, 30 Mar 2026 08:58:21 +0000 Subject: [PATCH 16/25] 8380565: PPC64: deoptimization stub should save vector registers Co-authored-by: Richard Reingruber Reviewed-by: rrich, dbriemann --- src/hotspot/cpu/ppc/registerMap_ppc.cpp | 46 ++ src/hotspot/cpu/ppc/registerMap_ppc.hpp | 8 +- src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp | 27 +- src/hotspot/cpu/ppc/vm_version_ppc.cpp | 3 +- .../vectorapi/TestVectorReallocation.java | 414 ++++++++++++++++++ 5 files changed, 483 insertions(+), 15 deletions(-) create mode 100644 src/hotspot/cpu/ppc/registerMap_ppc.cpp create mode 100644 test/hotspot/jtreg/compiler/vectorapi/TestVectorReallocation.java diff --git a/src/hotspot/cpu/ppc/registerMap_ppc.cpp b/src/hotspot/cpu/ppc/registerMap_ppc.cpp new file mode 100644 index 00000000000..2e7f8af89d3 --- /dev/null +++ b/src/hotspot/cpu/ppc/registerMap_ppc.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "runtime/registerMap.hpp" + +address RegisterMap::pd_location(VMReg base_reg, int slot_idx) const { + if (base_reg->is_VectorRegister()) { + // Not all physical slots belonging to a VectorRegister have corresponding + // valid VMReg locations in the RegisterMap. + // (See RegisterSaver::push_frame_reg_args_and_save_live_registers.) + // However, the slots are always saved to the stack in a contiguous region + // of memory so we can calculate the address of the upper slots by + // offsetting from the base address. + assert(base_reg->is_concrete(), "must pass base reg"); + address base_location = location(base_reg, nullptr); + if (base_location != nullptr) { + intptr_t offset_in_bytes = slot_idx * VMRegImpl::stack_slot_size; + return base_location + offset_in_bytes; + } else { + return nullptr; + } + } else { + return location(base_reg->next(slot_idx), nullptr); + } +} diff --git a/src/hotspot/cpu/ppc/registerMap_ppc.hpp b/src/hotspot/cpu/ppc/registerMap_ppc.hpp index 01eb642107c..607c712d10f 100644 --- a/src/hotspot/cpu/ppc/registerMap_ppc.hpp +++ b/src/hotspot/cpu/ppc/registerMap_ppc.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2013 SAP SE. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,9 +35,7 @@ // Since there is none, we just return null. address pd_location(VMReg reg) const { return nullptr; } - address pd_location(VMReg base_reg, int slot_idx) const { - return location(base_reg->next(slot_idx), nullptr); - } + address pd_location(VMReg base_reg, int slot_idx) const; // no PD state to clear or copy: void pd_clear() {} diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 5260ed978ff..53644210415 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -102,7 +102,7 @@ class RegisterSaver { // During deoptimization only the result registers need to be restored // all the other values have already been extracted. - static void restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes); + static void restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes, bool save_vectors); // Constants and data structures: @@ -349,7 +349,7 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble } // Note that generate_oop_map in the following loop is only used for the - // polling_page_vectors_safepoint_handler_blob. + // polling_page_vectors_safepoint_handler_blob and the deopt_blob. // The order in which the vector contents are stored depends on Endianess and // the utilized instructions (PowerArchitecturePPC64). assert(is_aligned(offset, StackAlignmentInBytes), "should be"); @@ -361,6 +361,7 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble __ stxvp(as_VectorRegister(reg_num).to_vsr(), offset, R1_SP); // Note: The contents were read in the same order (see loadV16_Power9 node in ppc.ad). + // RegisterMap::pd_location only uses the first VMReg for each VectorRegister. if (generate_oop_map) { map->set_callee_saved(VMRegImpl::stack2reg(offset >> 2), RegisterSaver_LiveVecRegs[i LITTLE_ENDIAN_ONLY(+1) ].vmreg); @@ -380,6 +381,7 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble __ stxvd2x(as_VectorRegister(reg_num)->to_vsr(), R31, R1_SP); } // Note: The contents were read in the same order (see loadV16_Power8 / loadV16_Power9 node in ppc.ad). + // RegisterMap::pd_location only uses the first VMReg for each VectorRegister. if (generate_oop_map) { VMReg vsr = RegisterSaver_LiveVecRegs[i].vmreg; map->set_callee_saved(VMRegImpl::stack2reg(offset >> 2), vsr); @@ -566,10 +568,14 @@ void RegisterSaver::restore_argument_registers_and_pop_frame(MacroAssembler*masm } // Restore the registers that might be holding a result. -void RegisterSaver::restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes) { +void RegisterSaver::restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes, bool save_vectors) { const int regstosave_num = sizeof(RegisterSaver_LiveRegs) / sizeof(RegisterSaver::LiveRegType); - const int register_save_size = regstosave_num * reg_size; // VS registers not relevant here. + const int vecregstosave_num = save_vectors ? (sizeof(RegisterSaver_LiveVecRegs) / + sizeof(RegisterSaver::LiveRegType)) + : 0; + const int register_save_size = regstosave_num * reg_size + vecregstosave_num * vec_reg_size; + const int register_save_offset = frame_size_in_bytes - register_save_size; // restore all result registers (ints and floats) @@ -598,7 +604,7 @@ void RegisterSaver::restore_result_registers(MacroAssembler* masm, int frame_siz offset += reg_size; } - assert(offset == frame_size_in_bytes, "consistency check"); + assert(offset == frame_size_in_bytes - (save_vectors ? vecregstosave_num * vec_reg_size : 0), "consistency check"); } // Is vector's size (in bytes) bigger than a size saved by default? @@ -2909,7 +2915,8 @@ void SharedRuntime::generate_deopt_blob() { map = RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, &first_frame_size_in_bytes, /*generate_oop_map=*/ true, - RegisterSaver::return_pc_is_lr); + RegisterSaver::return_pc_is_lr, + /*save_vectors*/ SuperwordUseVSX); assert(map != nullptr, "OopMap must have been created"); __ li(exec_mode_reg, Deoptimization::Unpack_deopt); @@ -2943,7 +2950,8 @@ void SharedRuntime::generate_deopt_blob() { RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, &first_frame_size_in_bytes, /*generate_oop_map=*/ false, - RegisterSaver::return_pc_is_pre_saved); + RegisterSaver::return_pc_is_pre_saved, + /*save_vectors*/ SuperwordUseVSX); // Deopt during an exception. Save exec mode for unpack_frames. __ li(exec_mode_reg, Deoptimization::Unpack_exception); @@ -2958,7 +2966,8 @@ void SharedRuntime::generate_deopt_blob() { RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, &first_frame_size_in_bytes, /*generate_oop_map=*/ false, - RegisterSaver::return_pc_is_pre_saved); + RegisterSaver::return_pc_is_pre_saved, + /*save_vectors*/ SuperwordUseVSX); __ li(exec_mode_reg, Deoptimization::Unpack_reexecute); #endif @@ -2984,7 +2993,7 @@ void SharedRuntime::generate_deopt_blob() { // Restore only the result registers that have been saved // by save_volatile_registers(...). - RegisterSaver::restore_result_registers(masm, first_frame_size_in_bytes); + RegisterSaver::restore_result_registers(masm, first_frame_size_in_bytes, /*save_vectors*/ SuperwordUseVSX); // reload the exec mode from the UnrollBlock (it might have changed) __ lwz(exec_mode_reg, in_bytes(Deoptimization::UnrollBlock::unpack_kind_offset()), unroll_block_reg); diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp index e471f5a6e4f..0b69ef7d25a 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp @@ -25,6 +25,7 @@ #include "asm/assembler.inline.hpp" #include "asm/macroAssembler.inline.hpp" +#include "compiler/compilerDefinitions.inline.hpp" #include "compiler/disassembler.hpp" #include "jvm.h" #include "memory/resourceArea.hpp" @@ -105,7 +106,7 @@ void VM_Version::initialize() { if (PowerArchitecturePPC64 >= 9) { // Performance is good since Power9. - if (FLAG_IS_DEFAULT(SuperwordUseVSX)) { + if (FLAG_IS_DEFAULT(SuperwordUseVSX) && CompilerConfig::is_c2_enabled()) { FLAG_SET_ERGO(SuperwordUseVSX, true); } } diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorReallocation.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorReallocation.java new file mode 100644 index 00000000000..1bdc6ea726d --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorReallocation.java @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2026 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package compiler.vectorapi; + +import java.util.Arrays; + +import compiler.lib.ir_framework.*; + +import jdk.incubator.vector.ByteVector; +import jdk.incubator.vector.ShortVector; +import jdk.incubator.vector.IntVector; +import jdk.incubator.vector.LongVector; +import jdk.incubator.vector.FloatVector; +import jdk.incubator.vector.DoubleVector; +import jdk.incubator.vector.VectorSpecies; + +import static jdk.test.lib.Asserts.*; + +/** + * @test + * @bug 8380565 + * @library /test/lib / + * @summary Test deoptimization involving vector reallocation + * @modules jdk.incubator.vector + * @requires vm.opt.final.MaxVectorSize == null | vm.opt.final.MaxVectorSize >= 16 + * + * @run driver compiler.vectorapi.TestVectorReallocation + */ + +public class TestVectorReallocation { + + private static final VectorSpecies B_SPECIES = ByteVector.SPECIES_PREFERRED; + private static final VectorSpecies S_SPECIES = ShortVector.SPECIES_PREFERRED; + private static final VectorSpecies I_SPECIES = IntVector.SPECIES_PREFERRED; + private static final VectorSpecies L_SPECIES = LongVector.SPECIES_PREFERRED; + private static final VectorSpecies F_SPECIES = FloatVector.SPECIES_PREFERRED; + private static final VectorSpecies D_SPECIES = DoubleVector.SPECIES_PREFERRED; + private static final int B_LENGTH = B_SPECIES.length(); + private static final int S_LENGTH = S_SPECIES.length(); + private static final int I_LENGTH = I_SPECIES.length(); + private static final int L_LENGTH = L_SPECIES.length(); + private static final int F_LENGTH = F_SPECIES.length(); + private static final int D_LENGTH = D_SPECIES.length(); + + // The input arrays for the @Test methods match the length of the preferred species for each type + private static byte[] b_a; + private static short[] s_a; + private static int[] i_a; + private static long[] l_a; + private static float[] f_a; + private static double[] d_a; + + // The output arrays for the @Test methods + private static byte[] b_r; + private static short[] s_r; + private static int[] i_r; + private static long[] l_r; + private static float[] f_r; + private static double[] d_r; + + public static void main(String[] args) { + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector"); + } + + // The test methods annotated with @Test are warmed up by the framework. The calls are indirect + // through the runner methods annotated with @Run. Note that each @Test method has its own instance of + // the test class TestVectorReallocation as receiver of the calls. + // + // The @Test methods just copy the elements of the input array (0, 1, 2, 3, ...) to the output array + // by means of a vector add operation. The added value is computed but actually always zero. The + // computation is done in a loop with a virtual call that is inlined based on class hierarchy analysis + // when the method gets compiled. + // + // The final call after warmup of the now compiled @Test method is performed concurrently in a second + // thread. Before the variable `loopIterations` is set very high such that the loop runs practically + // infinitely. While the loop is running, a class with an overridden version of the method `value` + // (called in the loop) is loaded. This invalidates the result of the class hierarchy analysis that + // there is just one implementation of the method and causes deoptimization where the vector `v0` used + // in the @Test method is reallocated from a register to the java heap. Finally it is verified that + // input and ouput arrays are equal. + // + // NB: each @Test needs its own Zero class for the desired result of the class hierarchy analysis. + + volatile boolean enteredLoop; + volatile long loopIterations; + + void sharedRunner(RunInfo runInfo, Runnable test, Runnable loadOverridingClass, Runnable verify) { + enteredLoop = false; + if (runInfo.isWarmUp()) { + loopIterations = 100; + test.run(); + } else { + loopIterations = 1L << 60; // basically infinite + Thread t = Thread.ofPlatform().start(test); + waitUntilLoopEntered(); + loadOverridingClass.run(); // invalidates inlining causing deoptimization/reallocation of v0 + loopIterations = 0; + waitUntilLoopLeft(); + joinThread(t); + verify.run(); // verify that input and ouput arrays are equal + } + } + + ///////////////////////////////////////////////////////////////////////////////////// + // byte + + static class ByteZero { + volatile byte zero; + byte value() { + return zero; + } + } + volatile ByteZero byteZero = new ByteZero(); + + @Run(test = "byteIdentityWithReallocation") + void byteIdentityWithReallocation_runner(RunInfo runInfo) { + sharedRunner(runInfo, () -> byteIdentityWithReallocation(), () -> { + // Loading the class with the overridden method will cause deoptimization and reallocation of v0 + byteZero = new ByteZero() { + @Override + byte value() { + return super.value(); // override but doing the same + } + }; + }, + () -> assertTrue(Arrays.equals(b_a, b_r), "Input/Output arrays differ")); + } + + @Test + @IR(counts = {IRNode.ADD_VB, " >0 "}) + void byteIdentityWithReallocation() { + ByteVector v0 = ByteVector.fromArray(B_SPECIES, b_a, 0); + byte zeroSum = 0; + enteredLoop = true; + for (long i = 0; i < loopIterations; i++) { + zeroSum += byteZero.value(); // inlined based on class hierarchy analysis + } + v0.add(zeroSum).intoArray(b_r, 0); + enteredLoop = false; + } + + ///////////////////////////////////////////////////////////////////////////////////// + // short + + static class ShortZero { + volatile short zero; + short value() { + return zero; + } + } + volatile ShortZero shortZero = new ShortZero(); + + @Run(test = "shortIdentityWithReallocation") + void shortIdentityWithReallocation_runner(RunInfo runInfo) { + sharedRunner(runInfo, () -> shortIdentityWithReallocation(), () -> { + // Loading the class with the overridden method will cause deoptimization and reallocation of v0 + shortZero = new ShortZero() { + @Override + short value() { + return super.value(); // override but doing the same + } + }; + }, + () -> assertTrue(Arrays.equals(s_a, s_r), "Input/Output arrays differ")); + } + + @Test + @IR(counts = {IRNode.ADD_VS, " >0 "}) + void shortIdentityWithReallocation() { + ShortVector v0 = ShortVector.fromArray(S_SPECIES, s_a, 0); + short zeroSum = 0; + enteredLoop = true; + for (long i = 0; i < loopIterations; i++) { + zeroSum += shortZero.value(); // inlined based on class hierarchy analysis + } + v0.add(zeroSum).intoArray(s_r, 0); + enteredLoop = false; + } + + ///////////////////////////////////////////////////////////////////////////////////// + // int + + static class IntZero { + volatile int zero; + int value() { + return zero; + } + } + volatile IntZero intZero = new IntZero(); + + @Run(test = "intIdentityWithReallocation") + void intIdentityWithReallocation_runner(RunInfo runInfo) { + sharedRunner(runInfo, () -> intIdentityWithReallocation(), () -> { + // Loading the class with the overridden method will cause deoptimization and reallocation of v0 + intZero = new IntZero() { + @Override + int value() { + return super.value(); // override but doing the same + } + }; + }, + () -> assertTrue(Arrays.equals(i_a, i_r), "Input/Output arrays differ")); + } + + @Test + @IR(counts = {IRNode.ADD_VI, " >0 "}) + void intIdentityWithReallocation() { + IntVector v0 = IntVector.fromArray(I_SPECIES, i_a, 0); + int zeroSum = 0; + enteredLoop = true; + for (long i = 0; i < loopIterations; i++) { + zeroSum += intZero.value(); // inlined based on class hierarchy analysis + } + v0.add(zeroSum).intoArray(i_r, 0); + enteredLoop = false; + } + + ///////////////////////////////////////////////////////////////////////////////////// + // long + + static class LongZero { + volatile long zero; + long value() { + return zero; + } + } + volatile LongZero longZero = new LongZero(); + + @Run(test = "longIdentityWithReallocation") + void longIdentityWithReallocation_runner(RunInfo runInfo) { + sharedRunner(runInfo, () -> longIdentityWithReallocation(), () -> { + // Loading the class with the overridden method will cause deoptimization and reallocation of v0 + longZero = new LongZero() { + @Override + long value() { + return super.value(); // override but doing the same + } + }; + }, + () -> assertTrue(Arrays.equals(l_a, l_r), "Input/Output arrays differ")); + } + + @Test + @IR(counts = {IRNode.ADD_VL, " >0 "}) + void longIdentityWithReallocation() { + LongVector v0 = LongVector.fromArray(L_SPECIES, l_a, 0); + long zeroSum = 0; + enteredLoop = true; + for (long i = 0; i < loopIterations; i++) { + zeroSum += longZero.value(); // inlined based on class hierarchy analysis + } + v0.add(zeroSum).intoArray(l_r, 0); + enteredLoop = false; + } + + ///////////////////////////////////////////////////////////////////////////////////// + // float + + static class FloatZero { + volatile float zero; + float value() { + return zero; + } + } + volatile FloatZero floatZero = new FloatZero(); + + @Run(test = "floatIdentityWithReallocation") + void floatIdentityWithReallocation_runner(RunInfo runInfo) { + sharedRunner(runInfo, () -> floatIdentityWithReallocation(), () -> { + // Loading the class with the overridden method will cause deoptimization and reallocation of v0 + floatZero = new FloatZero() { + @Override + float value() { + return super.value(); // override but doing the same + } + }; + }, + () -> assertTrue(Arrays.equals(f_a, f_r), "Input/Output arrays differ")); + } + + @Test + @IR(counts = {IRNode.ADD_VF, " >0 "}) + void floatIdentityWithReallocation() { + FloatVector v0 = FloatVector.fromArray(F_SPECIES, f_a, 0); + float zeroSum = 0; + enteredLoop = true; + for (long i = 0; i < loopIterations; i++) { + zeroSum += floatZero.value(); // inlined based on class hierarchy analysis + } + v0.add(zeroSum).intoArray(f_r, 0); + enteredLoop = false; + } + + ///////////////////////////////////////////////////////////////////////////////////// + // double + + static class DoubleZero { + volatile double zero; + double value() { + return zero; + } + } + volatile DoubleZero doubleZero = new DoubleZero(); + + @Run(test = "doubleIdentityWithReallocation") + void doubleIdentityWithReallocation_runner(RunInfo runInfo) { + sharedRunner(runInfo, () -> doubleIdentityWithReallocation(), () -> { + // Loading the class with the overridden method will cause deoptimization and reallocation of v0 + doubleZero = new DoubleZero() { + @Override + double value() { + return super.value(); // override but doing the same + } + }; + }, + () -> assertTrue(Arrays.equals(d_a, d_r), "Input/Output arrays differ")); + } + + @Test + @IR(counts = {IRNode.ADD_VD, " >0 "}) + void doubleIdentityWithReallocation() { + DoubleVector v0 = DoubleVector.fromArray(D_SPECIES, d_a, 0); + double zeroSum = 0; + enteredLoop = true; + for (long i = 0; i < loopIterations; i++) { + zeroSum += doubleZero.value(); // inlined based on class hierarchy analysis + } + v0.add(zeroSum).intoArray(d_r, 0); + enteredLoop = false; + } + + ///////////////////////////////////////////////////////////////////////////////////// + + private void waitUntilLoopEntered() { + while (!enteredLoop) { + sleep(10); + } + } + + private void waitUntilLoopLeft() { + while (enteredLoop) { + sleep(10); + } + } + + private static void sleep(int ms) { + try { + Thread.sleep(ms); + } catch (InterruptedException e) { /* ignore */ } + } + + private static void joinThread(Thread t) { + try { + t.join(); + } catch (InterruptedException e) { /* ignore */ } + } + + static { + b_a = new byte[B_LENGTH]; + s_a = new short[S_LENGTH]; + i_a = new int[I_LENGTH]; + l_a = new long[L_LENGTH]; + f_a = new float[F_LENGTH]; + d_a = new double[D_LENGTH]; + + b_r = new byte[B_LENGTH]; + s_r = new short[S_LENGTH]; + i_r = new int[I_LENGTH]; + l_r = new long[L_LENGTH]; + f_r = new float[F_LENGTH]; + d_r = new double[D_LENGTH]; + + for (int i = 0; i < b_a.length ; i++) { + b_a[i] = (byte)i; + } + for (int i = 0; i < s_a.length ; i++) { + s_a[i] = (short)i; + } + for (int i = 0; i < i_a.length ; i++) { + i_a[i] = i; + } + for (int i = 0; i < l_a.length ; i++) { + l_a[i] = i; + } + for (int i = 0; i < f_a.length ; i++) { + f_a[i] = i; + } + for (int i = 0; i < d_a.length ; i++) { + d_a[i] = i; + } + } +} From 4d7c13950954950201834e9d4afebdd36a522a81 Mon Sep 17 00:00:00 2001 From: Casper Norrbin Date: Mon, 30 Mar 2026 09:28:51 +0000 Subject: [PATCH 17/25] 8379415: Contended classes can leave unused alignment padding Reviewed-by: coleenp, fparain, lfoltan, liach --- .../share/classfile/fieldLayoutBuilder.cpp | 24 +- .../share/classfile/fieldLayoutBuilder.hpp | 4 +- .../runtime/contended/MixedPrimitives.java | 356 ++++++++++++++++++ 3 files changed, 376 insertions(+), 8 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/contended/MixedPrimitives.java diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp b/src/hotspot/share/classfile/fieldLayoutBuilder.cpp index a87e12edc96..8a9f5946091 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.cpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.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 @@ -596,11 +596,13 @@ void FieldLayoutBuilder::regular_field_sorting() { } } -void FieldLayoutBuilder::insert_contended_padding(LayoutRawBlock* slot) { +LayoutRawBlock* FieldLayoutBuilder::insert_contended_padding(LayoutRawBlock* slot) { + LayoutRawBlock* padding = nullptr; if (ContendedPaddingWidth > 0) { - LayoutRawBlock* padding = new LayoutRawBlock(LayoutRawBlock::PADDING, ContendedPaddingWidth); + padding = new LayoutRawBlock(LayoutRawBlock::PADDING, ContendedPaddingWidth); _layout->insert(slot, padding); } + return padding; } // Computation of regular classes layout is an evolution of the previous default layout @@ -620,10 +622,14 @@ void FieldLayoutBuilder::compute_regular_layout() { regular_field_sorting(); if (_is_contended) { - _layout->set_start(_layout->last_block()); // insertion is currently easy because the current strategy doesn't try to fill holes // in super classes layouts => the _start block is by consequence the _last_block - insert_contended_padding(_layout->start()); + _layout->set_start(_layout->last_block()); + LayoutRawBlock* padding = insert_contended_padding(_layout->start()); + if (padding != nullptr) { + // Setting the padding block as start ensures we do not insert past it. + _layout->set_start(padding); + } need_tail_padding = true; } @@ -639,7 +645,13 @@ void FieldLayoutBuilder::compute_regular_layout() { for (int i = 0; i < _contended_groups.length(); i++) { FieldGroup* cg = _contended_groups.at(i); LayoutRawBlock* start = _layout->last_block(); - insert_contended_padding(start); + LayoutRawBlock* padding = insert_contended_padding(start); + + // Do not insert fields past the padding block. + if (padding != nullptr) { + start = padding; + } + _layout->add(cg->primitive_fields(), start); _layout->add(cg->oop_fields(), start); need_tail_padding = true; diff --git a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp index 82bbaefc623..eab0b7d08a9 100644 --- a/src/hotspot/share/classfile/fieldLayoutBuilder.hpp +++ b/src/hotspot/share/classfile/fieldLayoutBuilder.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -250,7 +250,7 @@ class FieldLayoutBuilder : public ResourceObj { void build_layout(); void compute_regular_layout(); - void insert_contended_padding(LayoutRawBlock* slot); + LayoutRawBlock* insert_contended_padding(LayoutRawBlock* slot); private: void prologue(); diff --git a/test/hotspot/jtreg/runtime/contended/MixedPrimitives.java b/test/hotspot/jtreg/runtime/contended/MixedPrimitives.java new file mode 100644 index 00000000000..a7165578831 --- /dev/null +++ b/test/hotspot/jtreg/runtime/contended/MixedPrimitives.java @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.Class; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import jdk.internal.misc.Unsafe; +import jdk.internal.vm.annotation.Contended; + +/* + * @test + * @summary \@Contended with different sized primitive types + * @modules java.base/jdk.internal.misc + * @modules java.base/jdk.internal.vm.annotation + * @run main/othervm -XX:-RestrictContended MixedPrimitives + */ +public class MixedPrimitives { + + private static final Unsafe U = Unsafe.getUnsafe(); + private static final int ADDRESS_SIZE; + private static final int HEADER_SIZE; + + static { + // When running with CompressedOops on 64-bit platform, the address size + // reported by Unsafe is still 8, while the real reference fields are 4 bytes long. + // Try to guess the reference field size with this naive trick. + try { + long off1 = U.objectFieldOffset(CompressedOopsClass.class.getField("obj1")); + long off2 = U.objectFieldOffset(CompressedOopsClass.class.getField("obj2")); + ADDRESS_SIZE = (int) Math.abs(off2 - off1); + HEADER_SIZE = (int) Math.min(off1, off2); + } catch (NoSuchFieldException e) { + throw new ExceptionInInitializerError(e); + } + } + + static class CompressedOopsClass { + public Object obj1; + public Object obj2; + } + + public static boolean arePaddedPairwise(Class klass, String field1, String field2) throws Exception { + Field f1 = klass.getDeclaredField(field1); + Field f2 = klass.getDeclaredField(field2); + + if (isStatic(f1) != isStatic(f2)) { + return true; // these guys are in naturally disjoint locations + } + + int diff = offset(f1) - offset(f2); + if (diff < 0) { + // f1 is first + return (offset(f2) - (offset(f1) + getSize(f1))) > 64; + } else { + // f2 is first + return (offset(f1) - (offset(f2) + getSize(f2))) > 64; + } + } + + public static boolean isPadded(Class klass, String field1) throws Exception { + Field f1 = klass.getDeclaredField(field1); + + if (isStatic(f1)) { + return offset(f1) > 128 + 64; + } + + return offset(f1) > 64; + } + + public static boolean sameLayout(Class klass1, Class klass2) throws Exception { + try { + for (Field f1 : klass1.getDeclaredFields()) { + Field f2 = klass2.getDeclaredField(f1.getName()); + if (offset(f1) != offset(f2)) { + return false; + } + } + + for (Field f2 : klass1.getDeclaredFields()) { + Field f1 = klass2.getDeclaredField(f2.getName()); + if (offset(f1) != offset(f2)) { + return false; + } + } + } catch (Exception e) { + System.err.println("Failed getting layout from class: " + e.getMessage()); + return false; + } + + return true; + } + + public static boolean isStatic(Field field) { + return Modifier.isStatic(field.getModifiers()); + } + + public static int offset(Field field) { + if (isStatic(field)) { + return (int) U.staticFieldOffset(field); + } else { + return (int) U.objectFieldOffset(field); + } + } + + public static int getSize(Field field) { + Class type = field.getType(); + if (type == byte.class) { return 1; } + if (type == boolean.class) { return 1; } + if (type == short.class) { return 2; } + if (type == char.class) { return 2; } + if (type == int.class) { return 4; } + if (type == float.class) { return 4; } + if (type == long.class) { return 8; } + if (type == double.class) { return 8; } + return ADDRESS_SIZE; + } + + public static void main(String[] args) throws Exception { + int failures = 0; + + failures += Test1.checkLayout(); + failures += Test2.checkLayout(); + failures += Test3.checkLayout(); + failures += Test4.checkLayout(); + failures += Test5.checkLayout(); + failures += Test6.checkLayout(); + failures += Test7.checkLayout(); + failures += Test8.checkLayout(); + failures += Test9.checkLayout(); + + if (!sameLayout(Test4.class, Test7.class)) { + System.err.println("Test4 and Test7 have different layouts"); + failures += 1; + } + + if (!sameLayout(Test5.class, Test6.class)) { + System.err.println("Test5 and Test6 have different layouts"); + failures += 1; + } + + if (!sameLayout(Test8.class, Test9.class)) { + System.err.println("Test8 and Test9 have different layouts"); + failures += 1; + } + + System.out.println(failures == 0 ? "Test PASSES" : "Test FAILS"); + if (failures > 0) { + throw new Error("Test failed. Incurred " + failures + " failures."); + } + } + + // naturally packed + public static class Test1 { + private long long1; + private int int1; + private short short1; + + public static int checkLayout() throws Exception { + if (arePaddedPairwise(Test1.class, "long1", "int1") || + arePaddedPairwise(Test1.class, "long1", "short1") || + arePaddedPairwise(Test1.class, "int1", "short1") || + isPadded(Test1.class, "long1") || + isPadded(Test1.class, "int1") || + isPadded(Test1.class, "short1")) { + System.err.println("Test1 failed"); + return 1; + } + return 0; + } + } + + // long1 is padded + public static class Test2 { + @Contended private long long1; + private int int1; + private short short1; + + public static int checkLayout() throws Exception { + if (!arePaddedPairwise(Test2.class, "long1", "int1") || + !arePaddedPairwise(Test2.class, "long1", "short1") || + arePaddedPairwise(Test2.class, "int1", "short1") || + !isPadded(Test2.class, "long1") || + isPadded(Test2.class, "int1") || + isPadded(Test2.class, "short1")) { + System.err.println("Test2 failed"); + return 1; + } + return 0; + } + } + + // both fields are padded + public static class Test3 { + @Contended private long long1; + @Contended private int int1; + @Contended private short short1; + + public static int checkLayout() throws Exception { + if (!arePaddedPairwise(Test3.class, "long1", "int1") || + !arePaddedPairwise(Test3.class, "long1", "short1") || + !arePaddedPairwise(Test3.class, "int1", "short1") || + !isPadded(Test3.class, "long1") || + !isPadded(Test3.class, "int1") || + !isPadded(Test3.class, "short1")) { + System.err.println("Test3 failed"); + return 1; + } + return 0; + } + } + + // fields are padded in the singular group + public static class Test4 { + @Contended("sameGroup") private long long1; + @Contended("sameGroup") private int int1; + @Contended("sameGroup") private short short1; + + public static int checkLayout() throws Exception { + if (arePaddedPairwise(Test4.class, "long1", "int1") || + arePaddedPairwise(Test4.class, "long1", "short1") || + arePaddedPairwise(Test4.class, "int1", "short1") || + !isPadded(Test4.class, "long1") || + !isPadded(Test4.class, "int1") || + !isPadded(Test4.class, "short1")) { + System.err.println("Test4 failed"); + return 1; + } + return 0; + } + } + + // fields are padded in disjoint groups + public static class Test5 { + @Contended("diffGroup1") private long long1; + @Contended("diffGroup2") private int int1; + @Contended("diffGroup3") private short short1; + + public static int checkLayout() throws Exception { + if (!arePaddedPairwise(Test5.class, "long1", "int1") || + !arePaddedPairwise(Test5.class, "long1", "short1") || + !arePaddedPairwise(Test5.class, "int1", "short1") || + !isPadded(Test5.class, "long1") || + !isPadded(Test5.class, "int1") || + !isPadded(Test5.class, "short1")) { + System.err.println("Test5 failed"); + return 1; + } + return 0; + } + } + + // fields are padded in disjoint groups + public static class Test6 { + @Contended private long long1; + @Contended("diffGroup2") private int int1; + @Contended("diffGroup3") private short short1; + + public static int checkLayout() throws Exception { + if (!arePaddedPairwise(Test6.class, "long1", "int1") || + !arePaddedPairwise(Test6.class, "long1", "short1") || + !arePaddedPairwise(Test6.class, "int1", "short1") || + !isPadded(Test6.class, "long1") || + !isPadded(Test6.class, "int1") || + !isPadded(Test6.class, "short1")) { + System.err.println("Test6 failed"); + return 1; + } + return 0; + } + } + + // fields are padded in the singular group + @Contended + public static class Test7 { + private long long1; + private int int1; + private short short1; + + public static int checkLayout() throws Exception { + if (arePaddedPairwise(Test7.class, "long1", "int1") || + arePaddedPairwise(Test7.class, "long1", "short1") || + arePaddedPairwise(Test7.class, "int1", "short1") || + !isPadded(Test7.class, "long1") || + !isPadded(Test7.class, "int1") || + !isPadded(Test7.class, "short1")) { + System.err.println("Test7 failed"); + return 1; + } + return 0; + } + } + + // all fields are padded as the group, and one field is padded specifically + @Contended + public static class Test8 { + @Contended private long long1; + private int int1; + private short short1; + + public static int checkLayout() throws Exception { + if (!arePaddedPairwise(Test8.class, "long1", "int1") || + !arePaddedPairwise(Test8.class, "long1", "short1") || + arePaddedPairwise(Test8.class, "int1", "short1") || + !isPadded(Test8.class, "long1") || + !isPadded(Test8.class, "int1") || + !isPadded(Test8.class, "short1")) { + System.err.println("Test8 failed"); + return 1; + } + return 0; + } + } + + // all fields are padded as the group, and one field is padded specifically + @Contended + public static class Test9 { + @Contended("group") private long long1; + private int int1; + private short short1; + + public static int checkLayout() throws Exception { + if (!arePaddedPairwise(Test9.class, "long1", "int1") || + !arePaddedPairwise(Test9.class, "long1", "short1") || + arePaddedPairwise(Test9.class, "int1", "short1") || + !isPadded(Test9.class, "long1") || + !isPadded(Test9.class, "int1") || + !isPadded(Test9.class, "short1")) { + System.err.println("Test9 failed"); + return 1; + } + return 0; + } + } + +} + From 3eaeb9b1ad82d165798a986a2d9378d52af0ca38 Mon Sep 17 00:00:00 2001 From: Christian Hagedorn Date: Mon, 30 Mar 2026 10:25:58 +0000 Subject: [PATCH 18/25] 8380945: [IR Framework] Test VM is finished before TestFrameworkSocket is ready to accept connection Reviewed-by: mchevalier, thartmann --- .../ir_framework/driver/TestVMProcess.java | 1 + .../shared/TestFrameworkSocket.java | 49 +++++++++++++++---- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java index 00a9b93d124..bfffbdbec7e 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java @@ -68,6 +68,7 @@ public class TestVMProcess { this.cmds = new ArrayList<>(); TestFrameworkSocket socket = new TestFrameworkSocket(); try (socket) { + socket.start(); prepareTestVMFlags(additionalFlags, socket, testClass, helperClasses, defaultWarmup, allowNotCompilable, testClassesOnBootClassPath); start(); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java index 77d560952f1..44063a4bd43 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java @@ -42,9 +42,13 @@ public class TestFrameworkSocket implements AutoCloseable { private final int serverSocketPort; private final ServerSocket serverSocket; - private boolean running; - private final ExecutorService executor; - private Future javaFuture; + private final ExecutorService acceptExecutor; + private final ExecutorService clientExecutor; + + // Make these volatile such that the main thread can observe an update written by the worker threads in the executor + // services to avoid stale values. + private volatile boolean running; + private volatile Future javaFuture; public TestFrameworkSocket() { try { @@ -54,29 +58,53 @@ public class TestFrameworkSocket implements AutoCloseable { throw new TestFrameworkException("Failed to create TestFramework server socket", e); } serverSocketPort = serverSocket.getLocalPort(); - executor = Executors.newCachedThreadPool(); + acceptExecutor = Executors.newSingleThreadExecutor(); + clientExecutor = Executors.newCachedThreadPool(); if (TestFramework.VERBOSE) { System.out.println("TestFramework server socket uses port " + serverSocketPort); } - start(); } public String getPortPropertyFlag() { return "-D" + SERVER_PORT_PROPERTY + "=" + serverSocketPort; } - private void start() { + public void start() { running = true; - executor.submit(this::acceptLoop); + CountDownLatch calledAcceptLoopLatch = new CountDownLatch(1); + startAcceptLoop(calledAcceptLoopLatch); + } + + private void startAcceptLoop(CountDownLatch calledAcceptLoopLatch) { + acceptExecutor.submit(() -> acceptLoop(calledAcceptLoopLatch)); + waitUntilAcceptLoopRuns(calledAcceptLoopLatch); + } + + private void waitUntilAcceptLoopRuns(CountDownLatch calledAcceptLoopLatch) { + try { + if (!calledAcceptLoopLatch.await(10, TimeUnit.SECONDS)) { + throw new IllegalStateException("acceptLoop did not start in time"); + } + } catch (Exception e) { + throw new TestFrameworkException("Could not start TestFrameworkSocket", e); + } } /** * Main loop to wait for new client connections and handling them upon connection request. */ - private void acceptLoop() { + private void acceptLoop(CountDownLatch calledAcceptLoopLatch) { + calledAcceptLoopLatch.countDown(); while (running) { try { acceptNewClientConnection(); + } catch (SocketException e) { + if (!running || serverSocket.isClosed()) { + // Normal shutdown + return; + } + running = false; + throw new TestFrameworkException("Server socket error", e); } catch (TestFrameworkException e) { running = false; throw e; @@ -101,7 +129,7 @@ public class TestFrameworkSocket implements AutoCloseable { * over that connection. */ private void submitTask(Socket client, BufferedReader reader) { - javaFuture = executor.submit(new TestVmMessageReader(client, reader)); + javaFuture = clientExecutor.submit(new TestVmMessageReader(client, reader)); } @Override @@ -112,7 +140,8 @@ public class TestFrameworkSocket implements AutoCloseable { } catch (IOException e) { throw new TestFrameworkException("Could not close socket", e); } - executor.shutdown(); + acceptExecutor.shutdown(); + clientExecutor.shutdown(); } public TestVMData testVmData(String hotspotPidFileName, boolean allowNotCompilable) { From 6b9887b4f518c5b26d6a9700b7ae0dea8b140164 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Mon, 30 Mar 2026 11:53:03 +0000 Subject: [PATCH 19/25] 8378902: Test compiler/vectorapi/TestVectorLibraryUnaryOpAndBinaryOp.java failed Reviewed-by: mbaesken, thartmann --- .../TestVectorLibraryUnaryOpAndBinaryOp.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestVectorLibraryUnaryOpAndBinaryOp.java b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLibraryUnaryOpAndBinaryOp.java index f7837e1abfa..731c87e69f7 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/TestVectorLibraryUnaryOpAndBinaryOp.java +++ b/test/hotspot/jtreg/compiler/vectorapi/TestVectorLibraryUnaryOpAndBinaryOp.java @@ -26,12 +26,13 @@ package compiler.vectorapi; import compiler.lib.ir_framework.*; import jdk.incubator.vector.*; +import jtreg.SkippedException; /** * @test - * @bug 8378312 + * @bug 8378312 8378902 * @library /test/lib / - * @summary VectorAPI: libraryUnaryOp and libraryBinaryOp should be intrinsified. + * @summary VectorAPI: libraryUnaryOp and libraryBinaryOp should be intrinsified. This test would be run on SVML/SLEEF supported platforms only. * @modules jdk.incubator.vector * * @run driver compiler.vectorapi.TestVectorLibraryUnaryOpAndBinaryOp @@ -53,7 +54,24 @@ public class TestVectorLibraryUnaryOpAndBinaryOp { vec.lanewise(VectorOperators.HYPOT, 1.0f); } + private static void checkVectorMathLib() { + try { + // Check jsvml first + System.loadLibrary("jsvml"); + } catch (UnsatisfiedLinkError _) { + try { + // Check sleef if jsvml not found + System.loadLibrary("sleef"); + } catch (UnsatisfiedLinkError _) { + // This test is run on unsupported platform - should be skipped + throw new SkippedException("SVML / SLEEF not found"); + } + } + } + public static void main(String[] args) { + checkVectorMathLib(); + TestFramework testFramework = new TestFramework(); testFramework.addFlags("--add-modules=jdk.incubator.vector") .start(); From 7e0a1499ee5743cb8b36ad0150fa3a538b368e27 Mon Sep 17 00:00:00 2001 From: Koushik Thirupattur Date: Mon, 30 Mar 2026 12:50:42 +0000 Subject: [PATCH 20/25] 8345954: Revisit Class Initializers and Locking in X509TrustManagerImpl Reviewed-by: weijun --- .../sun/security/provider/X509Factory.java | 83 +++++++++++-------- 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/src/java.base/share/classes/sun/security/provider/X509Factory.java b/src/java.base/share/classes/sun/security/provider/X509Factory.java index f732c7c0455..4be83d629bb 100644 --- a/src/java.base/share/classes/sun/security/provider/X509Factory.java +++ b/src/java.base/share/classes/sun/security/provider/X509Factory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -112,9 +112,10 @@ public class X509Factory extends CertificateFactorySpi { if (cert != null) { return cert; } - cert = new X509CertImpl(encoding); - addToCache(certCache, cert.getEncodedInternal(), cert); - return cert; + // Build outside lock + X509CertImpl newCert = new X509CertImpl(encoding); + byte[] enc = newCert.getEncodedInternal(); + return addIfNotPresent(certCache, enc, newCert); } /** @@ -156,7 +157,7 @@ public class X509Factory extends CertificateFactorySpi { * @throws CertificateException if failures occur while obtaining the DER * encoding for certificate data. */ - public static synchronized X509CertImpl intern(X509Certificate c) + public static X509CertImpl intern(X509Certificate c) throws CertificateException { if (c == null) { return null; @@ -168,18 +169,23 @@ public class X509Factory extends CertificateFactorySpi { } else { encoding = c.getEncoded(); } - X509CertImpl newC = getFromCache(certCache, encoding); - if (newC != null) { - return newC; + // First check under per-cache lock + X509CertImpl cached = getFromCache(certCache, encoding); + if (cached != null) { + return cached; } + + // Build outside lock + X509CertImpl newC; + byte[] enc; if (isImpl) { - newC = (X509CertImpl)c; + newC = (X509CertImpl) c; + enc = encoding; } else { newC = new X509CertImpl(encoding); - encoding = newC.getEncodedInternal(); + enc = newC.getEncodedInternal(); } - addToCache(certCache, encoding, newC); - return newC; + return addIfNotPresent(certCache, enc, newC); } /** @@ -192,7 +198,7 @@ public class X509Factory extends CertificateFactorySpi { * @throws CRLException if failures occur while obtaining the DER * encoding for CRL data. */ - public static synchronized X509CRLImpl intern(X509CRL c) + public static X509CRLImpl intern(X509CRL c) throws CRLException { if (c == null) { return null; @@ -204,39 +210,47 @@ public class X509Factory extends CertificateFactorySpi { } else { encoding = c.getEncoded(); } - X509CRLImpl newC = getFromCache(crlCache, encoding); - if (newC != null) { - return newC; + X509CRLImpl cached = getFromCache(crlCache, encoding); + if (cached != null) { + return cached; } + + X509CRLImpl newC; + byte[] enc; if (isImpl) { - newC = (X509CRLImpl)c; + newC = (X509CRLImpl) c; + enc = encoding; } else { newC = new X509CRLImpl(encoding); - encoding = newC.getEncodedInternal(); + enc = newC.getEncodedInternal(); } - addToCache(crlCache, encoding, newC); - return newC; + return addIfNotPresent(crlCache, enc, newC); } /** * Get the X509CertImpl or X509CRLImpl from the cache. */ - private static synchronized V getFromCache(Cache cache, - byte[] encoding) { - Object key = new Cache.EqualByteArray(encoding); - return cache.get(key); + private static V getFromCache(Cache cache, byte[] encoding) { + return cache.get(new Cache.EqualByteArray(encoding)); } /** * Add the X509CertImpl or X509CRLImpl to the cache. */ - private static synchronized void addToCache(Cache cache, - byte[] encoding, V value) { + private static V addIfNotPresent(Cache cache, byte[] encoding, V value) { if (encoding.length > ENC_MAX_LENGTH) { - return; + return value; } Object key = new Cache.EqualByteArray(encoding); - cache.put(key, value); + // Synchronize only to make the "check + insert" decision atomic. + synchronized (cache) { + V existing = cache.get(key); + if (existing != null) { + return existing; + } + cache.put(key, value); + return value; + } } /** @@ -389,13 +403,14 @@ public class X509Factory extends CertificateFactorySpi { try { byte[] encoding = readOneBlock(is); if (encoding != null) { - X509CRLImpl crl = getFromCache(crlCache, encoding); - if (crl != null) { - return crl; + X509CRLImpl cached = getFromCache(crlCache, encoding); + if (cached != null) { + return cached; } - crl = new X509CRLImpl(encoding); - addToCache(crlCache, crl.getEncodedInternal(), crl); - return crl; + // Build outside lock + X509CRLImpl crl = new X509CRLImpl(encoding); + byte[] enc = crl.getEncodedInternal(); + return addIfNotPresent(crlCache, enc, crl); } else { throw new IOException("Empty input"); } From d58fb1e290cb8a28a04900e132ae09002ae62937 Mon Sep 17 00:00:00 2001 From: Daisuke Yamazaki Date: Mon, 30 Mar 2026 12:58:07 +0000 Subject: [PATCH 21/25] 8374202: Simplify significand normalization in BigDecimal(double, MathContext) constructor Reviewed-by: rgiulietti --- src/java.base/share/classes/java/math/BigDecimal.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigDecimal.java b/src/java.base/share/classes/java/math/BigDecimal.java index 14d81d30c3d..6e651b4fde2 100644 --- a/src/java.base/share/classes/java/math/BigDecimal.java +++ b/src/java.base/share/classes/java/math/BigDecimal.java @@ -1026,12 +1026,11 @@ public class BigDecimal extends Number implements Comparable { return; } // Normalize - while ((significand & 1) == 0) { // i.e., significand is even - significand >>= 1; - exponent++; - } - int scl = 0; + int nTrailingZeros = Long.numberOfTrailingZeros(significand); + significand >>= nTrailingZeros; + exponent += nTrailingZeros; // Calculate intVal and scale + int scl = 0; BigInteger rb; long compactVal = sign * significand; if (exponent == 0) { From 40e6069ff0558b1d5d0e520df7f23e59369867db Mon Sep 17 00:00:00 2001 From: Dusan Balek Date: Mon, 30 Mar 2026 13:06:06 +0000 Subject: [PATCH 22/25] =?UTF-8?q?8371873:=20javac:=20U+001A=20(SUB=20/=20c?= =?UTF-8?q?ontrol-Z)=20after=20the=20last=20token=20makes=20the=20rest=20o?= =?UTF-8?q?f=20the=20file=20silently=20ignored,=20which=20contradicts=20JL?= =?UTF-8?q?S=20=C2=A73.3=E2=80=93=C2=A73.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: jlahoda --- .../sun/tools/javac/parser/JavaTokenizer.java | 4 +- .../tools/javac/lexer/AsciiSubCharTest.java | 116 ++++++++++++++++++ 2 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 test/langtools/tools/javac/lexer/AsciiSubCharTest.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java index babe372e7dc..55f2f76e358 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1000,7 +1000,7 @@ public class JavaTokenizer extends UnicodeReader { scanIdent(); } else if (digit(pos, 10) >= 0) { scanNumber(pos, 10); - } else if (is((char)EOI) || !isAvailable()) { + } else if (is((char)EOI) && position() + 1 == length() || !isAvailable()) { tk = TokenKind.EOF; pos = position(); } else { diff --git a/test/langtools/tools/javac/lexer/AsciiSubCharTest.java b/test/langtools/tools/javac/lexer/AsciiSubCharTest.java new file mode 100644 index 00000000000..0c96b744d72 --- /dev/null +++ b/test/langtools/tools/javac/lexer/AsciiSubCharTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8371873 + * @summary Check for proper handling of trailing ASCII SUB character + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit AsciiSubCharTest + */ + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class AsciiSubCharTest { + + ToolBox tb = new ToolBox(); + Path base; + + @Test + public void testTrailingAsciiSubIsIgnored() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + public class Test { + void main(String... args) { IO.println("\u001A"); } + } + \u001A""") + .run() + .writeAll(); + } + + @Test + public void testMultipleTrailingAsciiSubAreReported() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + public class Test { + void main(String... args) { IO.println("\u001A"); } + } + \u001A\u001A""") + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:4:1: compiler.err.illegal.char: \\u001a", + "Test.java:4:2: compiler.err.premature.eof", + "2 errors")); + } + + @Test + public void test8371873() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + public class Test { + void main(String... args) { IO.println("\u001A"); } + } + \u001A\u0001""") + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:4:1: compiler.err.illegal.char: \\u001a", + "Test.java:4:2: compiler.err.illegal.char: \\u0001", + "Test.java:4:3: compiler.err.premature.eof", + "3 errors")); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} From 2449dc2e807c3a4708a89e52bb16434d4a85d3d2 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Mon, 30 Mar 2026 13:06:40 +0000 Subject: [PATCH 23/25] 8377004: Java Launcher incorrectly allows inheriting a package-private main from another package Reviewed-by: jpai, alanb --- .../jdk/internal/misc/MethodFinder.java | 19 +- test/jdk/tools/launcher/InstanceMainTest.java | 224 +++++++++++++++++- test/jdk/tools/launcher/MethodFinderTest.java | 196 +++++++++++++++ 3 files changed, 423 insertions(+), 16 deletions(-) create mode 100644 test/jdk/tools/launcher/MethodFinderTest.java diff --git a/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java b/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java index 60895b8115a..1ee608f2caf 100644 --- a/src/java.base/share/classes/jdk/internal/misc/MethodFinder.java +++ b/src/java.base/share/classes/jdk/internal/misc/MethodFinder.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 @@ -27,6 +27,7 @@ package jdk.internal.misc; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Objects; import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; @@ -88,21 +89,27 @@ public class MethodFinder { mainMethod = JLA.findMethod(cls, false, "main", String[].class); } - if (mainMethod == null || !isValidMainMethod(mainMethod)) { + if (mainMethod == null || !isValidMainMethod(cls, mainMethod)) { mainMethod = JLA.findMethod(cls, false, "main"); } - if (mainMethod == null || !isValidMainMethod(mainMethod)) { + if (mainMethod == null || !isValidMainMethod(cls, mainMethod)) { return null; } return mainMethod; } - private static boolean isValidMainMethod(Method mainMethodCandidate) { + private static boolean isValidMainMethod(Class initialClass, Method mainMethodCandidate) { return mainMethodCandidate.getReturnType() == void.class && - !Modifier.isPrivate(mainMethodCandidate.getModifiers()); - + !Modifier.isPrivate(mainMethodCandidate.getModifiers()) && + (Modifier.isPublic(mainMethodCandidate.getModifiers()) || + Modifier.isProtected(mainMethodCandidate.getModifiers()) || + isInSameRuntimePackage(initialClass, mainMethodCandidate.getDeclaringClass())); } + private static boolean isInSameRuntimePackage(Class c1, Class c2) { + return Objects.equals(c1.getPackageName(), c2.getPackageName()) && + c1.getClassLoader() == c2.getClassLoader(); + } } diff --git a/test/jdk/tools/launcher/InstanceMainTest.java b/test/jdk/tools/launcher/InstanceMainTest.java index 273d56a86bd..3a2f8fe90c1 100644 --- a/test/jdk/tools/launcher/InstanceMainTest.java +++ b/test/jdk/tools/launcher/InstanceMainTest.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 @@ -28,11 +28,12 @@ import java.util.function.Consumer; /** * @test - * @bug 8329420 + * @bug 8329420 8377004 * @summary test execution priority and behavior of main methods * @run main/timeout=480 InstanceMainTest */ public class InstanceMainTest extends TestHelper { + private static String JAVA_VERSION = System.getProperty("java.specification.version"); private static final String[] SOURCES = new String[] { // static dominating with args @@ -227,11 +228,7 @@ public class InstanceMainTest extends TestHelper { private static void testExecutionOrder() throws Exception { for (TestCase testCase : EXECUTION_ORDER) { performTest(testCase.sourceCode, testCase.enablePreview(), tr -> { - if (!Objects.equals(testCase.expectedOutput, tr.testOutput)) { - throw new AssertionError("Unexpected output, " + - "expected: " + testCase.expectedOutput + - ", actual: " + tr.testOutput); - } + assertEquals(testCase.expectedOutput, tr.testOutput); }); } } @@ -350,20 +347,227 @@ public class InstanceMainTest extends TestHelper { private static void performTest(String source, boolean enablePreview, Consumer validator) throws Exception { Path mainClass = Path.of("MainClass.java"); Files.writeString(mainClass, source); - var version = System.getProperty("java.specification.version"); var previewRuntime = enablePreview ? "--enable-preview" : "-DtestNoPreview"; var previewCompile = enablePreview ? "--enable-preview" : "-XDtestNoPreview"; - var trSource = doExec(javaCmd, previewRuntime, "--source", version, "MainClass.java"); + var trSource = doExec(javaCmd, previewRuntime, "--source", JAVA_VERSION, "MainClass.java"); validator.accept(trSource); - compile(previewCompile, "--source", version, "MainClass.java"); + compile(previewCompile, "--source", JAVA_VERSION, "MainClass.java"); String cp = mainClass.toAbsolutePath().getParent().toString(); var trCompile = doExec(javaCmd, previewRuntime, "--class-path", cp, "MainClass"); validator.accept(trCompile); } + private static void testInheritance() throws Exception { + Path testInheritance = Path.of("testInheritance"); + Path src = testInheritance.resolve("src"); + Path classes = testInheritance.resolve("classes"); + Path mainClass = src.resolve("Main.java"); + Path libClass = src.resolve("p").resolve("Lib.java"); + + Files.createDirectories(libClass.getParent()); + + Files.writeString(mainClass, + """ + import p.Lib; + + public class Main extends Lib { + public void main() { + System.err.println("Main!"); + } + } + """); + + { + Files.writeString(libClass, + """ + package p; + public class Lib { + void main(String... args) { + System.err.println("Lib!"); + } + } + """); + compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString()); + var tr = doExec(javaCmd, "--class-path", classes.toString(), "Main"); + assertEquals(List.of("Main!"), tr.testOutput); + } + + { + Files.writeString(libClass, + """ + package p; + public class Lib { + protected void main(String... args) { + System.err.println("Lib!"); + } + } + """); + compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString()); + var tr = doExec(javaCmd, "--class-path", classes.toString(), "Main"); + assertEquals(List.of("Lib!"), tr.testOutput); + } + + { + Files.writeString(libClass, + """ + package p; + public class Lib { + public void main(String... args) { + System.err.println("Lib!"); + } + } + """); + compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString()); + var tr = doExec(javaCmd, "--class-path", classes.toString(), "Main"); + assertEquals(List.of("Lib!"), tr.testOutput); + } + + { + Files.writeString(mainClass, + """ + package p; + + public class Main extends Lib { + public void main() { + System.err.println("Main!"); + } + } + """); + + Files.writeString(libClass, + """ + package p; + public class Lib { + void main(String... args) { + System.err.println("Lib!"); + } + } + """); + compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString()); + var tr = doExec(javaCmd, "--class-path", classes.toString(), "p.Main"); + assertEquals(List.of("Lib!"), tr.testOutput); + } + + { + Files.writeString(mainClass, + """ + package p; + + public class Main implements Lib { + public void main() { + System.err.println("Main!"); + } + } + """); + + Files.writeString(libClass, + """ + package p; + public interface Lib { + public default void main(String... args) { + System.err.println("Lib!"); + } + } + """); + compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString()); + var tr = doExec(javaCmd, "--class-path", classes.toString(), "p.Main"); + assertEquals(List.of("Lib!"), tr.testOutput); + } + + { + Files.writeString(mainClass, + """ + package p; + + public class Main implements Lib { + public void main() { + System.err.println("Main!"); + } + } + """); + + Files.writeString(libClass, + """ + package p; + public interface Lib { + public static void main(String... args) { + System.err.println("Lib!"); + } + } + """); + compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString()); + var tr = doExec(javaCmd, "--class-path", classes.toString(), "p.Main"); + assertEquals(List.of("Main!"), tr.testOutput); + } + + { + Files.writeString(mainClass, + """ + package p; + + public class Main extends AbstractClass implements Lib { + } + abstract class AbstractClass { + public void main(String... args) { + System.err.println("Correct."); + } + } + """); + + Files.writeString(libClass, + """ + package p; + public interface Lib { + default void main(String... args) { + System.err.println("Incorrect!"); + } + } + """); + compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString()); + var tr = doExec(javaCmd, "--class-path", classes.toString(), "p.Main"); + assertEquals(List.of("Correct."), tr.testOutput); + } + + { + Files.writeString(mainClass, + """ + package p; + + public class Main extends AbstractClass implements Lib { + } + abstract class AbstractClass { + public void main() { + System.err.println("Incorrect!"); + } + } + """); + + Files.writeString(libClass, + """ + package p; + public interface Lib { + default void main(String... args) { + System.err.println("Correct."); + } + } + """); + compile("--release", JAVA_VERSION, "-d", classes.toString(), mainClass.toString(), libClass.toString()); + var tr = doExec(javaCmd, "--class-path", classes.toString(), "p.Main"); + assertEquals(List.of("Correct."), tr.testOutput); + } + } + + private static void assertEquals(List expected, List actual) { + if (!Objects.equals(expected, actual)) { + throw new AssertionError("Unexpected output, " + + "expected: " + expected + + ", actual: " + actual); + } + } public static void main(String... args) throws Exception { testMethodOrder(); testExecutionOrder(); testExecutionErrors(); + testInheritance(); } } diff --git a/test/jdk/tools/launcher/MethodFinderTest.java b/test/jdk/tools/launcher/MethodFinderTest.java new file mode 100644 index 00000000000..6e1e68d8225 --- /dev/null +++ b/test/jdk/tools/launcher/MethodFinderTest.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8377004 + * @summary Whitebox test for MethodFinder + * @modules java.base/jdk.internal.misc + * jdk.compiler + * jdk.zipfs + * @run junit MethodFinderTest + */ + +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import jdk.internal.misc.MethodFinder; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class MethodFinderTest { + + private static final String JAVA_VERSION = System.getProperty("java.specification.version"); + + @Test + public void testDistinctClassLoaders() throws Exception { + Path base = Path.of("testDistinctClassLoaders"); + Path libSrc = base.resolve("libSrc"); + Path libClasses = base.resolve("libClasses"); + Path libJava = libSrc.resolve("p").resolve("Lib.java"); + + Files.createDirectories(libJava.getParent()); + + Files.writeString(libJava, + """ + package p; + public class Lib { + void main(String... args) { + System.err.println("Lib!"); + } + } + """); + + TestHelper.compile("--release", JAVA_VERSION, "-d", libClasses.toString(), libJava.toString()); + + Path mainSrc = base.resolve("mainSrc"); + Path mainClasses = base.resolve("mainClasses"); + Path mainJava = mainSrc.resolve("p").resolve("Main.java"); + + Files.createDirectories(mainJava.getParent()); + + Files.writeString(mainJava, + """ + package p; + + public class Main extends Lib { + public void main() { + System.err.println("Main!"); + } + } + """); + + TestHelper.compile("--release", JAVA_VERSION, "--class-path", libClasses.toString(), "-d", mainClasses.toString(), mainJava.toString()); + + { + ClassLoader cl = new URLClassLoader(new URL[] { + libClasses.toUri().toURL(), + mainClasses.toUri().toURL() + }); + Class mainClass = cl.loadClass("p.Main"); + Method mainMethod = MethodFinder.findMainMethod(mainClass); + + //p.Main and p.Lib are in the same runtime package: + assertEquals("p.Lib", mainMethod.getDeclaringClass().getName()); + } + + { + ClassLoader libCl = new URLClassLoader(new URL[] { + libClasses.toUri().toURL(), + }); + ClassLoader mainCl = new URLClassLoader(new URL[] { + mainClasses.toUri().toURL() + }, libCl); + Class mainClass = mainCl.loadClass("p.Main"); + Method mainMethod = MethodFinder.findMainMethod(mainClass); + + //p.Main and p.Lib are in the different runtime packages: + assertEquals("p.Main", mainMethod.getDeclaringClass().getName()); + } + } + + @Test + public void testWrongEquals() throws Exception { + Path base = Path.of("testDistinctClassLoaders"); + Path libSrc = base.resolve("libSrc"); + Path libClasses = base.resolve("libClasses"); + Path libJava = libSrc.resolve("p").resolve("Lib.java"); + + Files.createDirectories(libJava.getParent()); + + Files.writeString(libJava, + """ + package p; + public class Lib { + void main(String... args) { + System.err.println("Lib!"); + } + } + """); + + TestHelper.compile("--release", JAVA_VERSION, "-d", libClasses.toString(), libJava.toString()); + + Path mainSrc = base.resolve("mainSrc"); + Path mainClasses = base.resolve("mainClasses"); + Path mainJava = mainSrc.resolve("p").resolve("Main.java"); + + Files.createDirectories(mainJava.getParent()); + + Files.writeString(mainJava, + """ + package p; + + public class Main extends Lib { + public void main() { + System.err.println("Main!"); + } + } + """); + + TestHelper.compile("--release", JAVA_VERSION, "--class-path", libClasses.toString(), "-d", mainClasses.toString(), mainJava.toString()); + + { + ClassLoader cl = new URLClassLoader(new URL[] { + libClasses.toUri().toURL(), + mainClasses.toUri().toURL() + }); + Class mainClass = cl.loadClass("p.Main"); + Method mainMethod = MethodFinder.findMainMethod(mainClass); + + //p.Main and p.Lib are in the same runtime package: + assertEquals("p.Lib", mainMethod.getDeclaringClass().getName()); + } + + { + class WrongEquals extends URLClassLoader { + + public WrongEquals(URL[] urls) { + super(urls); + } + + public WrongEquals(URL[] urls, ClassLoader parent) { + super(urls, parent); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof WrongEquals; + } + } + ClassLoader libCl = new WrongEquals(new URL[] { + libClasses.toUri().toURL(), + }); + ClassLoader mainCl = new WrongEquals(new URL[] { + mainClasses.toUri().toURL() + }, libCl); + Class mainClass = mainCl.loadClass("p.Main"); + Method mainMethod = MethodFinder.findMainMethod(mainClass); + + //p.Main and p.Lib are in the different runtime packages: + assertEquals("p.Main", mainMethod.getDeclaringClass().getName()); + } + } +} From 88d4f1f7ce9662155f35197157abe341ba13c673 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 30 Mar 2026 13:09:18 +0000 Subject: [PATCH 24/25] 8380795: Consider omitting type annotations from method arguments in diagnostics Co-authored-by: Liz Looney Reviewed-by: vromero --- .../com/sun/tools/javac/code/Printer.java | 43 +++++++++-------- .../failures/MethodArguments.java | 47 +++++++++++++++++++ .../failures/MethodArguments.out | 8 ++++ .../typeAnnotations/failures/p/A.java | 30 ++++++++++++ .../typeAnnotations/failures/p/B.java | 41 ++++++++++++++++ 5 files changed, 150 insertions(+), 19 deletions(-) create mode 100644 test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.java create mode 100644 test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.out create mode 100644 test/langtools/tools/javac/annotations/typeAnnotations/failures/p/A.java create mode 100644 test/langtools/tools/javac/annotations/typeAnnotations/failures/p/B.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Printer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Printer.java index d9781f19c5d..acd8a35a894 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Printer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Printer.java @@ -53,6 +53,7 @@ public abstract class Printer implements Type.Visitor, Symbol.Vi List seenCaptured = List.nil(); static final int PRIME = 997; // largest prime less than 1000 + private boolean printingMethodArgs; protected Printer() { } @@ -195,6 +196,9 @@ public abstract class Printer implements Type.Visitor, Symbol.Vi } private String printAnnotations(Type t, boolean prefix) { + if (printingMethodArgs) { + return ""; + } StringBuilder sb = new StringBuilder(); List annos = t.getAnnotationMirrors(); if (!annos.isEmpty()) { @@ -337,27 +341,28 @@ public abstract class Printer implements Type.Visitor, Symbol.Vi * @return localized string representation */ protected String printMethodArgs(List args, boolean varArgs, Locale locale) { - if (!varArgs) { - return visitTypes(args, locale); - } else { - StringBuilder buf = new StringBuilder(); - while (args.tail.nonEmpty()) { - buf.append(visit(args.head, locale)); - args = args.tail; - buf.append(','); - } - if (args.head.hasTag(TypeTag.ARRAY)) { - buf.append(visit(((ArrayType) args.head).elemtype, locale)); - if (args.head.getAnnotationMirrors().nonEmpty()) { - buf.append(' '); - buf.append(args.head.getAnnotationMirrors()); - buf.append(' '); - } - buf.append("..."); + boolean prev = printingMethodArgs; + printingMethodArgs = true; + try { + if (!varArgs) { + return visitTypes(args, locale); } else { - buf.append(visit(args.head, locale)); + StringBuilder buf = new StringBuilder(); + while (args.tail.nonEmpty()) { + buf.append(visit(args.head, locale)); + args = args.tail; + buf.append(','); + } + if (args.head.hasTag(TypeTag.ARRAY)) { + buf.append(visit(((ArrayType) args.head).elemtype, locale)); + buf.append("..."); + } else { + buf.append(visit(args.head, locale)); + } + return buf.toString(); } - return buf.toString(); + } finally { + printingMethodArgs = prev; } } diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.java b/test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.java new file mode 100644 index 00000000000..e539721855b --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.java @@ -0,0 +1,47 @@ +/* + * @test /nodynamiccopyright/ + * @summary Omit type-use annotations from diagnostics + * @compile/fail/ref=MethodArguments.out -XDrawDiagnostics MethodArguments.java p/A.java p/B.java + */ + +import java.util.List; +import p.A; +import p.B; + +public final class MethodArguments { + public static void main(String[] args) { + // error non-static.cant.be.ref: + // non-static ... cannot be referenced from a static context + B.one("bar"); + + B b = new B(); + + // error ref.ambiguous: + // reference to ... is ambiguous + // ... + // both ... and ... match + b.one(null); + + // error report.access: + // ... has private access in ... + b.two("foo"); + // ... has protected access in ... + b.three("foo"); + + // error not.def.public.cant.access: + // ... is not public in ... cannot be accessed from outside package + b.four("foo"); + } + + void five(@A String s) { + } + + void five(@A String s) { + } + + void six(List<@A String> s) { + } + + void six(List<@A String> s) { + } +} diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.out b/test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.out new file mode 100644 index 00000000000..b4ecee21403 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/MethodArguments.out @@ -0,0 +1,8 @@ +MethodArguments.java:39:8: compiler.err.already.defined: kindname.method, five(java.lang.String), kindname.class, MethodArguments +MethodArguments.java:45:8: compiler.err.already.defined: kindname.method, six(java.util.List), kindname.class, MethodArguments +MethodArguments.java:15:6: compiler.err.non-static.cant.be.ref: kindname.method, one(java.lang.String) +MethodArguments.java:23:6: compiler.err.ref.ambiguous: one, kindname.method, one(java.lang.String), p.B, kindname.method, one(java.lang.Integer), p.B +MethodArguments.java:27:6: compiler.err.report.access: two(java.lang.String), private, p.B +MethodArguments.java:29:6: compiler.err.report.access: three(java.lang.String), protected, p.B +MethodArguments.java:33:6: compiler.err.not.def.public.cant.access: four(java.lang.String), p.B +7 errors diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/p/A.java b/test/langtools/tools/javac/annotations/typeAnnotations/failures/p/A.java new file mode 100644 index 00000000000..9e3bb15dab5 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/p/A.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026, Google LLC. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package p; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE_USE) +public @interface A {} diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/failures/p/B.java b/test/langtools/tools/javac/annotations/typeAnnotations/failures/p/B.java new file mode 100644 index 00000000000..4dfe16b312d --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/failures/p/B.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026, Google LLC. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package p; + +public class B { + public void one(@A String s) { + } + + public void one(@A Integer i) { + } + + private void two(@A String s) { + } + + protected void three(@A String s) { + } + + void four(@A String s) { + } +} From 783f8f1adc4ea3ef7fd4c5ca5473aad76dfc7ed1 Mon Sep 17 00:00:00 2001 From: Christian Hagedorn Date: Mon, 30 Mar 2026 14:06:05 +0000 Subject: [PATCH 25/25] 8381320: Problemlist compiler/vectorapi/TestVectorReallocation.java Reviewed-by: thartmann --- test/hotspot/jtreg/ProblemList.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 3b871d9f4b6..7580ce4d165 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -56,6 +56,7 @@ compiler/c2/irTests/TestDuplicateBackedge.java 8318904 generic-all compiler/codecache/jmx/PoolsIndependenceTest.java 8264632 macosx-all +compiler/vectorapi/TestVectorReallocation.java 8381315 generic-x64 compiler/vectorapi/reshape/TestVectorReinterpret.java 8320897,8348519 aix-ppc64,linux-ppc64le,linux-s390x compiler/vectorapi/VectorRebracket128Test.java 8330538 generic-all