From 2a6d92fd2725ad0bc26c54275aecf591a83d20a4 Mon Sep 17 00:00:00 2001 From: Ashutosh Mehra Date: Wed, 25 Mar 2026 14:59:18 +0000 Subject: [PATCH 1/7] 8380703: Assertion failure in TestCodeEntryAlignment.java Reviewed-by: kvn, adinn --- src/hotspot/share/runtime/stubRoutines.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index 5246613738e..56d832d3fef 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -219,12 +219,12 @@ static BufferBlob* initialize_stubs(BlobId blob_id, if (STUBGEN_BLOB_FIELD_NAME(blob_name) == nullptr) { \ BlobId blob_id = BlobId:: JOIN3(stubgen, blob_name, id); \ int size = _ ## blob_name ## _code_size; \ - int max_aligned_size = 10; \ + int max_aligned_stubs = StubInfo::stub_count(blob_id); \ const char* timer_msg = "StubRoutines generation " # blob_name " stubs"; \ const char* name = "StubRoutines (" # blob_name " stubs)"; \ const char* assert_msg = "_" # blob_name "_code_size"; \ STUBGEN_BLOB_FIELD_NAME(blob_name) = \ - initialize_stubs(blob_id, size, max_aligned_size, timer_msg, \ + initialize_stubs(blob_id, size, max_aligned_stubs, timer_msg, \ name, assert_msg); \ } \ } From ca0bee78f51d5ff21cb318b9d4c8a702eca28c13 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Wed, 25 Mar 2026 15:19:32 +0000 Subject: [PATCH 2/7] 8374915: Move assertion in GenericTaskQueue<>::pop_global() Reviewed-by: tschatzl, ayang --- src/hotspot/share/gc/shared/taskqueue.inline.hpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/gc/shared/taskqueue.inline.hpp b/src/hotspot/share/gc/shared/taskqueue.inline.hpp index e77645f4fcf..b142aadc580 100644 --- a/src/hotspot/share/gc/shared/taskqueue.inline.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -279,13 +279,11 @@ typename GenericTaskQueue::PopResult GenericTaskQueue::pop_g // Increment top; if it wraps, also increment tag, to distinguish it // from any recent _age for the same top() index. idx_t new_top = increment_index(oldAge.top()); + // Don't use bottom, since a pop_local might have decremented it. + assert_not_underflow(localBot, new_top); idx_t new_tag = oldAge.tag() + ((new_top == 0) ? 1 : 0); Age newAge(new_top, new_tag); bool result = par_set_age(oldAge, newAge); - - // Note that using "bottom" here might fail, since a pop_local might - // have decremented it. - assert_not_underflow(localBot, newAge.top()); return result ? PopResult::Success : PopResult::Contended; } From cf424480f42ac220adee7034e0319cee0e9039db Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Wed, 25 Mar 2026 15:29:34 +0000 Subject: [PATCH 3/7] 8375275: Error handling to raise illegal_parameter or internal_error alert in hybrid key exchange Reviewed-by: wetmore, mpowers --- .../classes/sun/security/ssl/DHasKEM.java | 37 ++++++++++++- .../sun/security/ssl/KAKeyDerivation.java | 53 +++++++++++++++---- 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/DHasKEM.java b/src/java.base/share/classes/sun/security/ssl/DHasKEM.java index 763013f280c..ef5c5b82f06 100644 --- a/src/java.base/share/classes/sun/security/ssl/DHasKEM.java +++ b/src/java.base/share/classes/sun/security/ssl/DHasKEM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -101,7 +101,18 @@ public class DHasKEM implements KEMSpi { return new KEM.Encapsulated( sub(dh, from, to), pkEm, null); + + } catch (IllegalArgumentException e) { + // ECDH validation failure + // all-zero shared secret + throw e; + } catch (InvalidKeyException e) { + // Invalid peer public key + // Convert InvalidKeyException to an unchecked exception + throw new IllegalArgumentException("Invalid peer public key", + e); } catch (Exception e) { + // Unexpected internal failure throw new ProviderException("internal error", e); } } @@ -126,6 +137,11 @@ public class DHasKEM implements KEMSpi { PublicKey pkE = params.DeserializePublicKey(encapsulation); SecretKey dh = params.DH(algorithm, skR, pkE); return sub(dh, from, to); + + } catch (IllegalArgumentException e) { + // ECDH validation failure + // all-zero shared secret + throw e; } catch (IOException | InvalidKeyException e) { throw new DecapsulateException("Cannot decapsulate", e); } catch (Exception e) { @@ -248,7 +264,24 @@ public class DHasKEM implements KEMSpi { KeyAgreement ka = KeyAgreement.getInstance(kaAlgorithm); ka.init(skE); ka.doPhase(pkR, true); - return ka.generateSecret(alg); + SecretKey secret = ka.generateSecret(alg); + + // RFC 8446 section 7.4.2: checks for all-zero + // X25519/X448 shared secret. + if (kaAlgorithm.equals("X25519") || + kaAlgorithm.equals("X448")) { + byte[] s = secret.getEncoded(); + for (byte b : s) { + if (b != 0) { + return secret; + } + } + // Trigger ILLEGAL_PARAMETER alert + throw new IllegalArgumentException( + "All-zero shared secret"); + } + + return secret; } } } diff --git a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java index 39e82b50435..dea86351cc8 100644 --- a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package sun.security.ssl; import sun.security.util.RawKeySpec; +import javax.crypto.DecapsulateException; import javax.crypto.KDF; import javax.crypto.KEM; import javax.crypto.KeyAgreement; @@ -35,6 +36,7 @@ import javax.net.ssl.SSLHandshakeException; import java.io.IOException; import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.Provider; @@ -173,6 +175,9 @@ public class KAKeyDerivation implements SSLKeyDerivation { "encapsulation"); } + // All exceptions thrown during KEM encapsulation are mapped + // to TLS fatal alerts: + // illegal_parameter alert or internal_error alert. try { KeyFactory kf = (provider != null) ? KeyFactory.getInstance(algorithmName, provider) : @@ -189,8 +194,18 @@ public class KAKeyDerivation implements SSLKeyDerivation { SecretKey derived = deriveHandshakeSecret(algorithm, sharedSecret); return new KEM.Encapsulated(derived, enc.encapsulation(), null); - } catch (GeneralSecurityException gse) { - throw new SSLHandshakeException("Could not generate secret", gse); + } catch (IllegalArgumentException | InvalidKeyException e) { + // Peer validation failure + // ECDH all-zero shared secret (RFC 8446 section 7.4.2), + // ML-KEM encapsulation key check failure (FIPS-203 section 7.2) + throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, e); + } catch (GeneralSecurityException e) { + // Cryptographic failure, + // deriveHandshakeSecret failure. + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); + } catch (RuntimeException e) { + // unexpected provider/runtime failure + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); } finally { KeyUtil.destroySecretKeys(sharedSecret); } @@ -208,13 +223,30 @@ public class KAKeyDerivation implements SSLKeyDerivation { // Using KEM: called by the client after receiving the KEM // ciphertext (keyshare) from the server in ServerHello. // The client decapsulates it using its private key. - KEM kem = (provider != null) - ? KEM.getInstance(algorithmName, provider) - : KEM.getInstance(algorithmName); - var decapsulator = kem.newDecapsulator(localPrivateKey); - sharedSecret = decapsulator.decapsulate( - keyshare, 0, decapsulator.secretSize(), - "TlsPremasterSecret"); + + // All exceptions thrown during KEM decapsulation are mapped + // to TLS fatal alerts: + // illegal_parameter alert or internal_error alert. + try { + KEM kem = (provider != null) + ? KEM.getInstance(algorithmName, provider) + : KEM.getInstance(algorithmName); + var decapsulator = kem.newDecapsulator(localPrivateKey); + sharedSecret = decapsulator.decapsulate( + keyshare, 0, decapsulator.secretSize(), + "TlsPremasterSecret"); + } catch (IllegalArgumentException | InvalidKeyException | + DecapsulateException e) { + // Peer validation failure + // ECDH all-zero shared secret (RFC 8446 section 7.4.2) + throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, e); + } catch (GeneralSecurityException e) { + // cryptographic failure + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); + } catch (RuntimeException e) { + // unexpected provider/runtime failure + throw context.conContext.fatal(Alert.INTERNAL_ERROR, e); + } } else { // Using traditional DH-style Key Agreement KeyAgreement ka = KeyAgreement.getInstance(algorithmName); @@ -225,6 +257,7 @@ public class KAKeyDerivation implements SSLKeyDerivation { return deriveHandshakeSecret(type, sharedSecret); } catch (GeneralSecurityException gse) { + // deriveHandshakeSecret() failure throw new SSLHandshakeException("Could not generate secret", gse); } finally { KeyUtil.destroySecretKeys(sharedSecret); From 02972a5856a60f27fe6b914b38d3398bf6d2f9c7 Mon Sep 17 00:00:00 2001 From: Hai-May Chao Date: Wed, 25 Mar 2026 15:52:04 +0000 Subject: [PATCH 4/7] 8379549: sun/security/ssl/SSLSocketImpl/SSLSocketSSLEngineCloseInbound.java failed with SocketException: Connection reset by peer Reviewed-by: wetmore, syan, dcubed --- .../SSLSocketSSLEngineCloseInbound.java | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketSSLEngineCloseInbound.java b/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketSSLEngineCloseInbound.java index e084bdd3367..dc0610ce8c8 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketSSLEngineCloseInbound.java +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketSSLEngineCloseInbound.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ /* * @test * @bug 8273553 8253368 + * @library /test/lib * @summary sun.security.ssl.SSLEngineImpl.closeInbound also has similar error * of JDK-8253368 * @run main/othervm SSLSocketSSLEngineCloseInbound TLSv1.3 @@ -79,12 +80,30 @@ * read() ... ChangeCipherSpec * read() ... Finished */ -import javax.net.ssl.*; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.*; -import java.io.*; -import java.net.*; -import java.security.*; -import java.nio.*; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManagerFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.security.KeyStore; +import java.security.Security; +import java.util.concurrent.CountDownLatch; + +import jdk.test.lib.Utils; public class SSLSocketSSLEngineCloseInbound { @@ -124,6 +143,8 @@ public class SSLSocketSSLEngineCloseInbound { private ByteBuffer cTOs; // "reliable" transport client->server private ByteBuffer sTOc; // "reliable" transport server->client + private final CountDownLatch serverReadyLatch = new CountDownLatch(1); + /* * The following is to set up the keystores/trust material. */ @@ -224,7 +245,7 @@ public class SSLSocketSSLEngineCloseInbound { // server-side socket that will read try (Socket socket = serverSocket.accept()) { - socket.setSoTimeout(500); + socket.setSoTimeout((int)Utils.adjustTimeout(500)); InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); @@ -289,6 +310,8 @@ public class SSLSocketSSLEngineCloseInbound { throw new Exception("Server session is not valid"); } + // Server signals client it has finished writing + serverReadyLatch.countDown(); return; } @@ -315,6 +338,8 @@ public class SSLSocketSSLEngineCloseInbound { } } } finally { + // Release the latch if an exception is thrown. + serverReadyLatch.countDown(); if (serverException != null) { if (clientException != null) { serverException.initCause(clientException); @@ -342,7 +367,8 @@ public class SSLSocketSSLEngineCloseInbound { public void run() { // client-side socket try (SSLSocket sslSocket = (SSLSocket)sslc.getSocketFactory(). - createSocket("localhost", port)) { + createSocket(InetAddress.getLoopbackAddress(), + port)) { clientSocket = sslSocket; OutputStream os = sslSocket.getOutputStream(); @@ -365,9 +391,9 @@ public class SSLSocketSSLEngineCloseInbound { throw new Exception("Client's session is not valid"); } - // Give server a chance to read before we shutdown via - // the try-with-resources block. - Thread.sleep(2000); + // Client waits for server to finish sending data + // before shutdown. + serverReadyLatch.await(); } catch (Exception e) { clientException = e; } From 331c7540175ce1e7f992a49102eaa6ff5b24c42b Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 25 Mar 2026 16:32:44 +0000 Subject: [PATCH 5/7] 8380523: Refactor TLAB slow allocation naming Reviewed-by: stefank, jsikstro --- .../gc/shared/threadLocalAllocBuffer.cpp | 176 +++++++++--------- .../gc/shared/threadLocalAllocBuffer.hpp | 44 ++--- .../shared/threadLocalAllocBuffer.inline.hpp | 2 +- src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 4 +- src/hotspot/share/runtime/vmStructs.cpp | 7 +- 5 files changed, 116 insertions(+), 117 deletions(-) diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp index 9d995234736..f9b8694eb04 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp @@ -37,7 +37,7 @@ #include "utilities/copy.hpp" size_t ThreadLocalAllocBuffer::_max_size = 0; -unsigned int ThreadLocalAllocBuffer::_target_refills = 0; +unsigned int ThreadLocalAllocBuffer::_target_num_refills = 0; ThreadLocalAllocBuffer::ThreadLocalAllocBuffer() : _start(nullptr), @@ -48,10 +48,10 @@ ThreadLocalAllocBuffer::ThreadLocalAllocBuffer() : _desired_size(0), _refill_waste_limit(0), _allocated_before_last_gc(0), - _number_of_refills(0), + _num_refills(0), _refill_waste(0), _gc_waste(0), - _slow_allocations(0), + _num_slow_allocations(0), _allocated_size(0), _allocation_fraction(TLABAllocationWeight) { @@ -81,7 +81,7 @@ void ThreadLocalAllocBuffer::accumulate_and_reset_statistics(ThreadLocalAllocSta print_stats("gc"); - if (_number_of_refills > 0) { + if (_num_refills > 0) { // Update allocation history if a reasonable amount of eden was allocated. bool update_allocation_history = used > 0.5 * capacity; @@ -98,16 +98,16 @@ void ThreadLocalAllocBuffer::accumulate_and_reset_statistics(ThreadLocalAllocSta _allocation_fraction.sample(alloc_frac); } - stats->update_fast_allocations(_number_of_refills, + stats->update_fast_allocations(_num_refills, _allocated_size, _gc_waste, _refill_waste); } else { - assert(_number_of_refills == 0 && _refill_waste == 0 && _gc_waste == 0, + assert(_num_refills == 0 && _refill_waste == 0 && _gc_waste == 0, "tlab stats == 0"); } - stats->update_slow_allocations(_slow_allocations); + stats->update_num_slow_allocations(_num_slow_allocations); reset_statistics(); } @@ -147,7 +147,7 @@ void ThreadLocalAllocBuffer::resize() { assert(ResizeTLAB, "Should not call this otherwise"); size_t alloc = (size_t)(_allocation_fraction.average() * (Universe::heap()->tlab_capacity() / HeapWordSize)); - size_t new_size = alloc / _target_refills; + size_t new_size = alloc / _target_num_refills; new_size = clamp(new_size, min_size(), max_size()); @@ -156,24 +156,24 @@ void ThreadLocalAllocBuffer::resize() { log_trace(gc, tlab)("TLAB new size: thread: " PTR_FORMAT " [id: %2d]" " refills %d alloc: %8.6f desired_size: %zu -> %zu", p2i(thread()), thread()->osthread()->thread_id(), - _target_refills, _allocation_fraction.average(), desired_size(), aligned_new_size); + _target_num_refills, _allocation_fraction.average(), desired_size(), aligned_new_size); set_desired_size(aligned_new_size); set_refill_waste_limit(initial_refill_waste_limit()); } void ThreadLocalAllocBuffer::reset_statistics() { - _number_of_refills = 0; - _refill_waste = 0; - _gc_waste = 0; - _slow_allocations = 0; - _allocated_size = 0; + _num_refills = 0; + _refill_waste = 0; + _gc_waste = 0; + _num_slow_allocations = 0; + _allocated_size = 0; } void ThreadLocalAllocBuffer::fill(HeapWord* start, HeapWord* top, size_t new_size) { - _number_of_refills++; + _num_refills++; _allocated_size += new_size; print_stats("fill"); assert(top <= start + new_size - alignment_reserve(), "size too small"); @@ -205,7 +205,7 @@ void ThreadLocalAllocBuffer::initialize() { size_t capacity = Universe::heap()->tlab_capacity() / HeapWordSize; if (capacity > 0) { // Keep alloc_frac as float and not double to avoid the double to float conversion - float alloc_frac = desired_size() * target_refills() / (float)capacity; + float alloc_frac = desired_size() * target_num_refills() / (float)capacity; _allocation_fraction.sample(alloc_frac); } @@ -219,10 +219,10 @@ void ThreadLocalAllocBuffer::startup_initialization() { // Assuming each thread's active tlab is, on average, // 1/2 full at a GC - _target_refills = 100 / (2 * TLABWasteTargetPercent); - // We need to set initial target refills to 2 to avoid a GC which causes VM + _target_num_refills = 100 / (2 * TLABWasteTargetPercent); + // We need to set the initial target number of refills to 2 to avoid a GC which causes VM // abort during VM initialization. - _target_refills = MAX2(_target_refills, 2U); + _target_num_refills = MAX2(_target_num_refills, 2U); // During jvm startup, the main thread is initialized // before the heap is initialized. So reinitialize it now. @@ -240,10 +240,10 @@ size_t ThreadLocalAllocBuffer::initial_desired_size() { init_sz = TLABSize / HeapWordSize; } else { // Initial size is a function of the average number of allocating threads. - unsigned int nof_threads = ThreadLocalAllocStats::allocating_threads_avg(); + unsigned int num_threads = ThreadLocalAllocStats::num_allocating_threads_avg(); init_sz = (Universe::heap()->tlab_capacity() / HeapWordSize) / - (nof_threads * target_refills()); + (num_threads * target_num_refills()); init_sz = align_object_size(init_sz); } // We can't use clamp() between min_size() and max_size() here because some @@ -271,10 +271,10 @@ void ThreadLocalAllocBuffer::print_stats(const char* tag) { " slow: %dB", tag, p2i(thrd), thrd->osthread()->thread_id(), _desired_size / (K / HeapWordSize), - _slow_allocations, _refill_waste_limit * HeapWordSize, + _num_slow_allocations, _refill_waste_limit * HeapWordSize, _allocation_fraction.average(), _allocation_fraction.average() * tlab_used / K, - _number_of_refills, waste_percent, + _num_refills, waste_percent, _gc_waste * HeapWordSize, _refill_waste * HeapWordSize); } @@ -299,17 +299,17 @@ HeapWord* ThreadLocalAllocBuffer::hard_end() { return _allocation_end + alignment_reserve(); } -PerfVariable* ThreadLocalAllocStats::_perf_allocating_threads; -PerfVariable* ThreadLocalAllocStats::_perf_total_refills; -PerfVariable* ThreadLocalAllocStats::_perf_max_refills; +PerfVariable* ThreadLocalAllocStats::_perf_num_allocating_threads; +PerfVariable* ThreadLocalAllocStats::_perf_total_num_refills; +PerfVariable* ThreadLocalAllocStats::_perf_max_num_refills; PerfVariable* ThreadLocalAllocStats::_perf_total_allocated_size; PerfVariable* ThreadLocalAllocStats::_perf_total_gc_waste; PerfVariable* ThreadLocalAllocStats::_perf_max_gc_waste; PerfVariable* ThreadLocalAllocStats::_perf_total_refill_waste; PerfVariable* ThreadLocalAllocStats::_perf_max_refill_waste; -PerfVariable* ThreadLocalAllocStats::_perf_total_slow_allocations; -PerfVariable* ThreadLocalAllocStats::_perf_max_slow_allocations; -AdaptiveWeightedAverage ThreadLocalAllocStats::_allocating_threads_avg(0); +PerfVariable* ThreadLocalAllocStats::_perf_total_num_slow_allocations; +PerfVariable* ThreadLocalAllocStats::_perf_max_num_slow_allocations; +AdaptiveWeightedAverage ThreadLocalAllocStats::_num_allocating_threads_avg(0); static PerfVariable* create_perf_variable(const char* name, PerfData::Units unit, TRAPS) { ResourceMark rm; @@ -317,47 +317,47 @@ static PerfVariable* create_perf_variable(const char* name, PerfData::Units unit } void ThreadLocalAllocStats::initialize() { - _allocating_threads_avg = AdaptiveWeightedAverage(TLABAllocationWeight); - _allocating_threads_avg.sample(1); // One allocating thread at startup + _num_allocating_threads_avg = AdaptiveWeightedAverage(TLABAllocationWeight); + _num_allocating_threads_avg.sample(1); // One allocating thread at startup if (UsePerfData) { EXCEPTION_MARK; - _perf_allocating_threads = create_perf_variable("allocThreads", PerfData::U_None, CHECK); - _perf_total_refills = create_perf_variable("fills", PerfData::U_None, CHECK); - _perf_max_refills = create_perf_variable("maxFills", PerfData::U_None, CHECK); - _perf_total_allocated_size = create_perf_variable("alloc", PerfData::U_Bytes, CHECK); - _perf_total_gc_waste = create_perf_variable("gcWaste", PerfData::U_Bytes, CHECK); - _perf_max_gc_waste = create_perf_variable("maxGcWaste", PerfData::U_Bytes, CHECK); - _perf_total_refill_waste = create_perf_variable("refillWaste", PerfData::U_Bytes, CHECK); - _perf_max_refill_waste = create_perf_variable("maxRefillWaste", PerfData::U_Bytes, CHECK); - _perf_total_slow_allocations = create_perf_variable("slowAlloc", PerfData::U_None, CHECK); - _perf_max_slow_allocations = create_perf_variable("maxSlowAlloc", PerfData::U_None, CHECK); + _perf_num_allocating_threads = create_perf_variable("allocThreads", PerfData::U_None, CHECK); + _perf_total_num_refills = create_perf_variable("fills", PerfData::U_None, CHECK); + _perf_max_num_refills = create_perf_variable("maxFills", PerfData::U_None, CHECK); + _perf_total_allocated_size = create_perf_variable("alloc", PerfData::U_Bytes, CHECK); + _perf_total_gc_waste = create_perf_variable("gcWaste", PerfData::U_Bytes, CHECK); + _perf_max_gc_waste = create_perf_variable("maxGcWaste", PerfData::U_Bytes, CHECK); + _perf_total_refill_waste = create_perf_variable("refillWaste", PerfData::U_Bytes, CHECK); + _perf_max_refill_waste = create_perf_variable("maxRefillWaste", PerfData::U_Bytes, CHECK); + _perf_total_num_slow_allocations = create_perf_variable("slowAlloc", PerfData::U_None, CHECK); + _perf_max_num_slow_allocations = create_perf_variable("maxSlowAlloc", PerfData::U_None, CHECK); } } ThreadLocalAllocStats::ThreadLocalAllocStats() : - _allocating_threads(0), - _total_refills(0), - _max_refills(0), + _num_allocating_threads(0), + _total_num_refills(0), + _max_num_refills(0), _total_allocated_size(0), _total_gc_waste(0), _max_gc_waste(0), _total_refill_waste(0), _max_refill_waste(0), - _total_slow_allocations(0), - _max_slow_allocations(0) {} + _total_num_slow_allocations(0), + _max_num_slow_allocations(0) {} -unsigned int ThreadLocalAllocStats::allocating_threads_avg() { - return MAX2((unsigned int)(_allocating_threads_avg.average() + 0.5), 1U); +unsigned int ThreadLocalAllocStats::num_allocating_threads_avg() { + return MAX2((unsigned int)(_num_allocating_threads_avg.average() + 0.5), 1U); } -void ThreadLocalAllocStats::update_fast_allocations(unsigned int refills, +void ThreadLocalAllocStats::update_fast_allocations(unsigned int num_refills, size_t allocated_size, size_t gc_waste, size_t refill_waste) { - _allocating_threads += 1; - _total_refills += refills; - _max_refills = MAX2(_max_refills, refills); + _num_allocating_threads += 1; + _total_num_refills += num_refills; + _max_num_refills = MAX2(_max_num_refills, num_refills); _total_allocated_size += allocated_size; _total_gc_waste += gc_waste; _max_gc_waste = MAX2(_max_gc_waste, gc_waste); @@ -365,35 +365,35 @@ void ThreadLocalAllocStats::update_fast_allocations(unsigned int refills, _max_refill_waste = MAX2(_max_refill_waste, refill_waste); } -void ThreadLocalAllocStats::update_slow_allocations(unsigned int allocations) { - _total_slow_allocations += allocations; - _max_slow_allocations = MAX2(_max_slow_allocations, allocations); +void ThreadLocalAllocStats::update_num_slow_allocations(unsigned int num_slow_allocations) { + _total_num_slow_allocations += num_slow_allocations; + _max_num_slow_allocations = MAX2(_max_num_slow_allocations, num_slow_allocations); } void ThreadLocalAllocStats::update(const ThreadLocalAllocStats& other) { - _allocating_threads += other._allocating_threads; - _total_refills += other._total_refills; - _max_refills = MAX2(_max_refills, other._max_refills); - _total_allocated_size += other._total_allocated_size; - _total_gc_waste += other._total_gc_waste; - _max_gc_waste = MAX2(_max_gc_waste, other._max_gc_waste); - _total_refill_waste += other._total_refill_waste; - _max_refill_waste = MAX2(_max_refill_waste, other._max_refill_waste); - _total_slow_allocations += other._total_slow_allocations; - _max_slow_allocations = MAX2(_max_slow_allocations, other._max_slow_allocations); + _num_allocating_threads += other._num_allocating_threads; + _total_num_refills += other._total_num_refills; + _max_num_refills = MAX2(_max_num_refills, other._max_num_refills); + _total_allocated_size += other._total_allocated_size; + _total_gc_waste += other._total_gc_waste; + _max_gc_waste = MAX2(_max_gc_waste, other._max_gc_waste); + _total_refill_waste += other._total_refill_waste; + _max_refill_waste = MAX2(_max_refill_waste, other._max_refill_waste); + _total_num_slow_allocations += other._total_num_slow_allocations; + _max_num_slow_allocations = MAX2(_max_num_slow_allocations, other._max_num_slow_allocations); } void ThreadLocalAllocStats::reset() { - _allocating_threads = 0; - _total_refills = 0; - _max_refills = 0; - _total_allocated_size = 0; - _total_gc_waste = 0; - _max_gc_waste = 0; - _total_refill_waste = 0; - _max_refill_waste = 0; - _total_slow_allocations = 0; - _max_slow_allocations = 0; + _num_allocating_threads = 0; + _total_num_refills = 0; + _max_num_refills = 0; + _total_allocated_size = 0; + _total_gc_waste = 0; + _max_gc_waste = 0; + _total_refill_waste = 0; + _max_refill_waste = 0; + _total_num_slow_allocations = 0; + _max_num_slow_allocations = 0; } void ThreadLocalAllocStats::publish() { @@ -401,7 +401,7 @@ void ThreadLocalAllocStats::publish() { return; } - _allocating_threads_avg.sample(_allocating_threads); + _num_allocating_threads_avg.sample(_num_allocating_threads); const size_t waste = _total_gc_waste + _total_refill_waste; const double waste_percent = percent_of(waste, _total_allocated_size); @@ -409,22 +409,22 @@ void ThreadLocalAllocStats::publish() { " slow allocs: %d max %d waste: %4.1f%%" " gc: %zuB max: %zuB" " slow: %zuB max: %zuB", - _allocating_threads, _total_refills, _max_refills, - _total_slow_allocations, _max_slow_allocations, waste_percent, + _num_allocating_threads, _total_num_refills, _max_num_refills, + _total_num_slow_allocations, _max_num_slow_allocations, waste_percent, _total_gc_waste * HeapWordSize, _max_gc_waste * HeapWordSize, _total_refill_waste * HeapWordSize, _max_refill_waste * HeapWordSize); if (UsePerfData) { - _perf_allocating_threads ->set_value(_allocating_threads); - _perf_total_refills ->set_value(_total_refills); - _perf_max_refills ->set_value(_max_refills); - _perf_total_allocated_size ->set_value(_total_allocated_size); - _perf_total_gc_waste ->set_value(_total_gc_waste); - _perf_max_gc_waste ->set_value(_max_gc_waste); - _perf_total_refill_waste ->set_value(_total_refill_waste); - _perf_max_refill_waste ->set_value(_max_refill_waste); - _perf_total_slow_allocations ->set_value(_total_slow_allocations); - _perf_max_slow_allocations ->set_value(_max_slow_allocations); + _perf_num_allocating_threads ->set_value(_num_allocating_threads); + _perf_total_num_refills ->set_value(_total_num_refills); + _perf_max_num_refills ->set_value(_max_num_refills); + _perf_total_allocated_size ->set_value(_total_allocated_size); + _perf_total_gc_waste ->set_value(_total_gc_waste); + _perf_max_gc_waste ->set_value(_max_gc_waste); + _perf_total_refill_waste ->set_value(_total_refill_waste); + _perf_max_refill_waste ->set_value(_max_refill_waste); + _perf_total_num_slow_allocations ->set_value(_total_num_slow_allocations); + _perf_max_num_slow_allocations ->set_value(_max_num_slow_allocations); } } diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp index 8c99523557e..67bc149013e 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp @@ -56,13 +56,13 @@ private: size_t _refill_waste_limit; // hold onto tlab if free() is larger than this uint64_t _allocated_before_last_gc; // total bytes allocated up until the last gc - static size_t _max_size; // maximum size of any TLAB - static unsigned _target_refills; // expected number of refills between GCs + static size_t _max_size; // maximum size of any TLAB + static unsigned _target_num_refills; // expected number of refills between GCs - unsigned _number_of_refills; + unsigned _num_refills; unsigned _refill_waste; unsigned _gc_waste; - unsigned _slow_allocations; + unsigned _num_slow_allocations; size_t _allocated_size; AdaptiveWeightedAverage _allocation_fraction; // fraction of eden allocated in tlabs @@ -79,7 +79,7 @@ private: size_t initial_refill_waste_limit(); - static int target_refills() { return _target_refills; } + static int target_num_refills() { return _target_num_refills; } size_t initial_desired_size(); size_t remaining(); @@ -98,9 +98,9 @@ private: // statistics - int number_of_refills() const { return _number_of_refills; } - int gc_waste() const { return _gc_waste; } - int slow_allocations() const { return _slow_allocations; } + int num_refills() const { return _num_refills; } + int gc_waste() const { return _gc_waste; } + int num_slow_allocations() const { return _num_slow_allocations; } public: ThreadLocalAllocBuffer(); @@ -179,41 +179,41 @@ public: class ThreadLocalAllocStats : public StackObj { private: - static PerfVariable* _perf_allocating_threads; - static PerfVariable* _perf_total_refills; - static PerfVariable* _perf_max_refills; + static PerfVariable* _perf_num_allocating_threads; + static PerfVariable* _perf_total_num_refills; + static PerfVariable* _perf_max_num_refills; static PerfVariable* _perf_total_allocated_size; static PerfVariable* _perf_total_gc_waste; static PerfVariable* _perf_max_gc_waste; static PerfVariable* _perf_total_refill_waste; static PerfVariable* _perf_max_refill_waste; - static PerfVariable* _perf_total_slow_allocations; - static PerfVariable* _perf_max_slow_allocations; + static PerfVariable* _perf_total_num_slow_allocations; + static PerfVariable* _perf_max_num_slow_allocations; - static AdaptiveWeightedAverage _allocating_threads_avg; + static AdaptiveWeightedAverage _num_allocating_threads_avg; - unsigned int _allocating_threads; - unsigned int _total_refills; - unsigned int _max_refills; + unsigned int _num_allocating_threads; + unsigned int _total_num_refills; + unsigned int _max_num_refills; size_t _total_allocated_size; size_t _total_gc_waste; size_t _max_gc_waste; size_t _total_refill_waste; size_t _max_refill_waste; - unsigned int _total_slow_allocations; - unsigned int _max_slow_allocations; + unsigned int _total_num_slow_allocations; + unsigned int _max_num_slow_allocations; public: static void initialize(); - static unsigned int allocating_threads_avg(); + static unsigned int num_allocating_threads_avg(); ThreadLocalAllocStats(); - void update_fast_allocations(unsigned int refills, + void update_fast_allocations(unsigned int num_refills, size_t allocated_size, size_t gc_waste, size_t refill_waste); - void update_slow_allocations(unsigned int allocations); + void update_num_slow_allocations(unsigned int num_slow_allocations); void update(const ThreadLocalAllocStats& other); void reset(); diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp index 441686c5c4c..727467f98d0 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp @@ -82,7 +82,7 @@ void ThreadLocalAllocBuffer::record_slow_allocation(size_t obj_size) { set_refill_waste_limit(refill_waste_limit() + refill_waste_limit_increment()); - _slow_allocations++; + _num_slow_allocations++; log_develop_trace(gc, tlab)("TLAB: %s thread: " PTR_FORMAT " [id: %2d]" " obj: %zu" diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 74314b0ad61..ac532e1bb4c 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -450,8 +450,8 @@ nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \ - nonstatic_field(ThreadLocalAllocBuffer, _number_of_refills, unsigned) \ - nonstatic_field(ThreadLocalAllocBuffer, _slow_allocations, unsigned) \ + nonstatic_field(ThreadLocalAllocBuffer, _num_refills, unsigned) \ + nonstatic_field(ThreadLocalAllocBuffer, _num_slow_allocations, unsigned) \ \ nonstatic_field(SafepointMechanism::ThreadData, _polling_word, volatile uintptr_t) \ nonstatic_field(SafepointMechanism::ThreadData, _polling_page, volatile uintptr_t) \ diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 93e0ff2f3b6..48f3f078ae8 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -335,11 +335,11 @@ nonstatic_field(ThreadLocalAllocBuffer, _pf_top, HeapWord*) \ nonstatic_field(ThreadLocalAllocBuffer, _desired_size, size_t) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste_limit, size_t) \ - static_field(ThreadLocalAllocBuffer, _target_refills, unsigned) \ - nonstatic_field(ThreadLocalAllocBuffer, _number_of_refills, unsigned) \ + static_field(ThreadLocalAllocBuffer, _target_num_refills, unsigned) \ + nonstatic_field(ThreadLocalAllocBuffer, _num_refills, unsigned) \ nonstatic_field(ThreadLocalAllocBuffer, _refill_waste, unsigned) \ nonstatic_field(ThreadLocalAllocBuffer, _gc_waste, unsigned) \ - nonstatic_field(ThreadLocalAllocBuffer, _slow_allocations, unsigned) \ + nonstatic_field(ThreadLocalAllocBuffer, _num_slow_allocations, unsigned) \ nonstatic_field(VirtualSpace, _low_boundary, char*) \ nonstatic_field(VirtualSpace, _high_boundary, char*) \ nonstatic_field(VirtualSpace, _low, char*) \ @@ -2142,4 +2142,3 @@ void vmStructs_init() { VMStructs::init(); } #endif // ASSERT - From 28529282545f6b59596a445409d59398253176f1 Mon Sep 17 00:00:00 2001 From: Ana-Maria Mihalceanu Date: Wed, 25 Mar 2026 17:20:46 +0000 Subject: [PATCH 6/7] 8380663: Update jcmd man page to include AOT.end_recording diagnostic command Reviewed-by: kevinw, kvn --- src/jdk.jcmd/share/man/jcmd.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/jdk.jcmd/share/man/jcmd.md b/src/jdk.jcmd/share/man/jcmd.md index af3886a915c..23dfa67d864 100644 --- a/src/jdk.jcmd/share/man/jcmd.md +++ b/src/jdk.jcmd/share/man/jcmd.md @@ -134,6 +134,16 @@ The following commands are available: - `-all`: (Optional) Show help for all commands (BOOLEAN, false) . +`AOT.end_recording` +: Ends an in-progress AOT training and records the results to the file(s) specified by `-XX:AOTConfiguration` and/or `-XX:AOTCacheOutput`. + + Impact: Low + + **Note:** + + The JVM must be started in AOT training mode using command-line arguments such as `-XX:AOTMode=record` or `-XX:AOTCacheOutput=`. + The results of the AOT training can be an AOT configuration file, an AOT cache file, or both. + `Compiler.CodeHeap_Analytics` \[*function*\] \[*granularity*\] : Print CodeHeap analytics From 88bdbb78b2565e559d6f96cd099770951f17e8cc Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Wed, 25 Mar 2026 18:26:24 +0000 Subject: [PATCH 7/7] 8380472: Clean up test/jdk/jdk/nio/zipfs/PathOps.java shared file system usage Reviewed-by: lancea, jpai --- test/jdk/jdk/nio/zipfs/PathOps.java | 421 +++++++++++++--------------- 1 file changed, 190 insertions(+), 231 deletions(-) diff --git a/test/jdk/jdk/nio/zipfs/PathOps.java b/test/jdk/jdk/nio/zipfs/PathOps.java index a035d425a3d..86b7946eba2 100644 --- a/test/jdk/jdk/nio/zipfs/PathOps.java +++ b/test/jdk/jdk/nio/zipfs/PathOps.java @@ -21,7 +21,6 @@ * questions. */ -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -30,20 +29,20 @@ import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import java.io.IOException; -import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.ProviderMismatchException; +import java.util.Objects; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test @@ -55,47 +54,48 @@ import static org.junit.jupiter.api.Assertions.fail; */ public class PathOps { - static FileSystem fs; + // create empty JAR file, testing doesn't require any contents + static Path emptyJar; - // This test uses a static file system since some ops tested on - // Path depend on the same underlying `fs` instance @BeforeAll static void setup() throws IOException { - // create empty JAR file, test doesn't require any contents - Path emptyJar = Utils.createJarFile("empty.jar"); - fs = FileSystems.newFileSystem(emptyJar); - } - - @AfterAll - static void cleanup() throws IOException { - fs.close(); + emptyJar = Utils.createJarFile("empty.jar"); } + // Ensure NPEs are thrown for null inputs on Path ops @Test - void nullPointerTest() { - Path path = fs.getPath("foo"); - assertThrows(NullPointerException.class, () -> path.resolve((String) null)); - assertThrows(NullPointerException.class, () -> path.relativize(null)); - assertThrows(NullPointerException.class, () -> path.compareTo(null)); - assertThrows(NullPointerException.class, () -> path.startsWith((Path) null)); - assertThrows(NullPointerException.class, () -> path.endsWith((Path) null)); + void nullPointerTest() throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + Path path = fs.getPath("foo"); + assertThrows(NullPointerException.class, () -> path.resolve((String) null)); + assertThrows(NullPointerException.class, () -> path.relativize(null)); + assertThrows(NullPointerException.class, () -> path.compareTo(null)); + assertThrows(NullPointerException.class, () -> path.startsWith((Path) null)); + assertThrows(NullPointerException.class, () -> path.endsWith((Path) null)); + } } + // Ensure correct behavior when paths are provided by mismatched providers @Test - void mismatchedProvidersTest() { - Path path = fs.getPath("foo"); + void mismatchedProvidersTest() throws IOException { Path other = Paths.get("foo"); - assertThrows(ProviderMismatchException.class, () -> path.compareTo(other)); - assertThrows(ProviderMismatchException.class, () -> path.resolve(other)); - assertThrows(ProviderMismatchException.class, () -> path.relativize(other)); - assertFalse(path.startsWith(other), "providerMismatched startsWith() returns true "); - assertFalse(path.endsWith(other), "providerMismatched endsWith() returns true "); + try (var fs = FileSystems.newFileSystem(emptyJar)) { + Path path = fs.getPath("foo"); + assertThrows(ProviderMismatchException.class, () -> path.compareTo(other)); + assertThrows(ProviderMismatchException.class, () -> path.resolve(other)); + assertThrows(ProviderMismatchException.class, () -> path.relativize(other)); + assertFalse(path.startsWith(other), "providerMismatched startsWith() returns true "); + assertFalse(path.endsWith(other), "providerMismatched endsWith() returns true "); + } } + // Ensure correct construction of paths when given sequence of strings @ParameterizedTest @MethodSource - void constructionTest(String first, String[] more, String expected) { - string(getPath(first, more), expected); + void constructionTest(String first, String[] more, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + string(fs.getPath(first, more), expected); + } } static Stream constructionTest() { @@ -112,22 +112,29 @@ public class PathOps { ); } + // Ensure proper root, parent, and name components @Test - void allComponentsTest() { - var path = getPath("/a/b/c"); - root(path, "/"); - parent(path, "/a/b"); - name(path, "c"); + void allComponentsTest() throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath("/a/b/c"); + root(path, "/"); + parent(path, "/a/b"); + name(path, "c"); + } } + // Ensure correct name count for root only and empty name @ParameterizedTest @MethodSource - void nameCountTest(String first, String root, String parent, String name, int nameCount) { - var path = getPath(first); - root(path, root); - parent(path, parent); - name(path, name); - nameCount(path, nameCount); + void nameCountTest(String first, String root, String parent, String name, int nameCount) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + root(path, root); + parent(path, parent); + name(path, name); + assertNotNull(path); + assertEquals(nameCount, path.getNameCount()); + } } static Stream nameCountTest() { @@ -139,13 +146,16 @@ public class PathOps { ); } + // Ensure correct parent and name behavior for no root and name only @ParameterizedTest @MethodSource - void parentNameTest(String first, String root, String parent, String name) { - var path = getPath(first); - root(path, root); - parent(path, parent); - name(path, name); + void parentNameTest(String first, String root, String parent, String name) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + root(path, root); + parent(path, parent); + name(path, name); + } } static Stream parentNameTest() { @@ -157,10 +167,16 @@ public class PathOps { ); } + // Ensure correct (positive) `startsWith` behavior @ParameterizedTest @MethodSource - void startsWithTest(String first, String prefix) { - starts(getPath(first), prefix); + void startsWithTest(String first, String prefix) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + Path s = fs.getPath(prefix); + assertTrue(path.startsWith(s)); + } } static Stream startsWithTest() { @@ -180,10 +196,16 @@ public class PathOps { ); } + // Ensure correct (negative) `startsWith` behavior @ParameterizedTest @MethodSource - void notStartsWithTest(String first, String prefix) { - notStarts(getPath(first), prefix); + void notStartsWithTest(String first, String prefix) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + Path s = fs.getPath(prefix); + assertFalse(path.startsWith(s)); + } } static Stream notStartsWithTest() { @@ -203,10 +225,16 @@ public class PathOps { ); } + // Ensure correct (positive) `endsWith` behavior @ParameterizedTest @MethodSource - void endsWithTest(String first, String suffix) { - ends(getPath(first), suffix); + void endsWithTest(String first, String suffix) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + Path s = fs.getPath(suffix); + assertTrue(path.endsWith(s)); + } } static Stream endsWithTest() { @@ -231,10 +259,16 @@ public class PathOps { ); } + // Ensure correct (negative) `endsWith` behavior @ParameterizedTest @MethodSource - void notEndsWithTest(String first, String suffix) { - notEnds(getPath(first), suffix); + void notEndsWithTest(String first, String suffix) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + Path s = fs.getPath(suffix); + assertFalse(path.endsWith(s)); + } } static Stream notEndsWithTest() { @@ -248,10 +282,15 @@ public class PathOps { ); } + // Ensure `getName` returns correct String at index @ParameterizedTest @MethodSource - void elementTest(int index, String expected) { - element(getPath("a/b/c"), index, expected); + void elementTest(int index, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath("a/b/c"); + assertNotNull(path); + assertEquals(expected, path.getName(index).toString()); + } } static Stream elementTest() { @@ -262,22 +301,35 @@ public class PathOps { ); } + // Ensure expected behavior for absolute paths @ParameterizedTest @ValueSource(strings = {"/", "/tmp"} ) - void isAbsoluteTest(String first) { - absolute(getPath(first)); + void isAbsoluteTest(String first) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + assertTrue(path.isAbsolute()); + } } + // Ensure expected behavior for non-absolute paths @ParameterizedTest @ValueSource(strings = {"tmp", ""} ) - void notAbsoluteTest(String first) { - notAbsolute(getPath(first)); + void notAbsoluteTest(String first) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + assertFalse(path.isAbsolute()); + } } + // Ensure correct append and replacement behavior for `resolve(String)` @ParameterizedTest @MethodSource - void resolveTest(String first, String other, String expected) { - resolve(getPath(first), other, expected); + void resolveTest(String first, String other, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + resolve(fs.getPath(first), other, expected); + } } static Stream resolveTest() { @@ -298,10 +350,15 @@ public class PathOps { ); } + // Ensure correct append and replacement behavior for `resolve(Path)` @ParameterizedTest @MethodSource - void resolvePathTest(String first, String other, String expected) { - resolvePath(getPath(first), other, expected); + void resolvePathTest(String first, String other, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + assertEquals(expected, path.resolve(fs.getPath(other)).toString()); + } } static Stream resolvePathTest() { @@ -322,10 +379,13 @@ public class PathOps { ); } + // Ensure correct behavior for `resolveSibling` @ParameterizedTest @MethodSource - void resolveSiblingTest(String first, String other, String expected) { - resolveSibling(getPath(first), other, expected); + void resolveSiblingTest(String first, String other, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + resolveSibling(fs.getPath(first), other, expected); + } } static Stream resolveSiblingTest() { @@ -345,18 +405,28 @@ public class PathOps { ); } + // Checking `resolve` and `resolveSibling` behavior for empty path @Test - void resolveSiblingAndResolveTest() { - var path = getPath(""); - resolveSibling(path, "foo", "foo"); - resolveSibling(path, "/foo", "/foo"); - resolve(path, "", ""); + void resolveSiblingAndResolveTest() throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(""); + resolveSibling(path, "foo", "foo"); + resolveSibling(path, "/foo", "/foo"); + resolve(path, "", ""); + } } + // Ensure correct behavior of `relativize`. i.e. Relative path should be + // produced between two given paths @ParameterizedTest @MethodSource - void relativizeTest(String first, String other, String expected) { - relativize(getPath(first), other, expected); + void relativizeTest(String first, String other, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + Path that = fs.getPath(other); + assertEquals(expected, path.relativize(that).toString()); + } } static Stream relativizeTest() { @@ -380,10 +450,15 @@ public class PathOps { ); } + // Ensure correct behavior of `normalize`. i.e. redundant elements should be removed. @ParameterizedTest @MethodSource - void normalizeTest(String first, String expected) { - normalize(getPath(first), expected); + void normalizeTest(String first, String expected) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath(first); + assertNotNull(path); + assertEquals(expected, path.normalize().toString()); + } } static Stream normalizeTest() { @@ -409,10 +484,13 @@ public class PathOps { ); } + // Check IPE is thrown for invalid path Strings @ParameterizedTest @MethodSource - void invalidTest(String first) { - assertThrows(InvalidPathException.class, () -> getPath(first)); + void invalidTest(String first) throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + assertThrows(InvalidPathException.class, () -> fs.getPath(first)); + } } static Stream invalidTest() { @@ -426,184 +504,65 @@ public class PathOps { ); } + // Check that repeated forward slash is normalized correctly @Test - void normalizationTest() { - var path = getPath("//foo//bar"); - string(path, "/foo/bar"); - root(path, "/"); - parent(path, "/foo"); - name(path, "bar"); + void normalizationTest() throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath("//foo//bar"); + string(path, "/foo/bar"); + root(path, "/"); + parent(path, "/foo"); + name(path, "bar"); + } } - @Test - void isSameFileTest() { - isSameFile(getPath("/fileDoesNotExist"), "/fileDoesNotExist"); + @Test // Check that identical paths refer to the same file + void isSameFileTest() throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + var path = fs.getPath("/fileDoesNotExist"); + assertNotNull(path); + assertTrue(Files.isSameFile(path, fs.getPath("/fileDoesNotExist"))); + } } + // Regression test for 8139956: Ensure `relativize` of equivalent paths + // produces an empty path -> `getNameCount` returns 1 @Test - void getNameCountTest() { - // 8139956 - System.out.println("check getNameCount"); - int nc = fs.getPath("/").relativize(fs.getPath("/")).getNameCount(); - assertEquals(1, nc, "getNameCount of empty path failed"); + void getNameCountTest() throws IOException { + try (var fs = FileSystems.newFileSystem(emptyJar)) { + int nc = fs.getPath("/").relativize(fs.getPath("/")).getNameCount(); + assertEquals(1, nc, "getNameCount of empty path failed"); + } } // Utilities for testing - - static void checkPath(Path path) { - assertNotNull(path, "path is null"); - } - - static void check(Object result, String expected) { - if (result == null) { - if (expected == null) return; - } else { - // compare string representations - if (expected != null && result.toString().equals(expected)) - return; - } - fail(); - } - - static void check(Object result, boolean expected) { - check(result, Boolean.toString(expected)); - } - - static void check(Object result, int expected) { - check(result, Integer.toString(expected)); - } - static void root(Path path, String expected) { - System.out.println("check root"); - checkPath(path); - check(path.getRoot(), expected); + assertNotNull(path); + assertEquals(expected, Objects.toString(path.getRoot(), null)); } static void parent(Path path, String expected) { - System.out.println("check parent"); - checkPath(path); - check(path.getParent(), expected); + assertNotNull(path); + assertEquals(expected, Objects.toString(path.getParent(), null)); } static void name(Path path, String expected) { - System.out.println("check name"); - checkPath(path); - check(path.getFileName(), expected); - } - - static void nameCount(Path path, int expected) { - System.out.println("check nameCount"); - checkPath(path); - check(path.getNameCount(), expected); - } - - static void element(Path path, int index, String expected) { - System.out.format("check element %d\n", index); - checkPath(path); - check(path.getName(index), expected); - } - - static void subpath(Path path, int startIndex, int endIndex, String expected) { - System.out.format("test subpath(%d,%d)\n", startIndex, endIndex); - checkPath(path); - check(path.subpath(startIndex, endIndex), expected); - } - - static void starts(Path path, String prefix) { - System.out.format("test startsWith with %s\n", prefix); - checkPath(path); - Path s = fs.getPath(prefix); - check(path.startsWith(s), true); - } - - static void notStarts(Path path, String prefix) { - System.out.format("test not startsWith with %s\n", prefix); - checkPath(path); - Path s = fs.getPath(prefix); - check(path.startsWith(s), false); - } - - static void ends(Path path, String suffix) { - System.out.format("test endsWith %s\n", suffix); - checkPath(path); - Path s = fs.getPath(suffix); - check(path.endsWith(s), true); - } - - static void notEnds(Path path, String suffix) { - System.out.format("test not endsWith %s\n", suffix); - checkPath(path); - Path s = fs.getPath(suffix); - check(path.endsWith(s), false); - } - - static void absolute(Path path) { - System.out.println("check path is absolute"); - checkPath(path); - check(path.isAbsolute(), true); - } - - static void notAbsolute(Path path) { - System.out.println("check path is not absolute"); - checkPath(path); - check(path.isAbsolute(), false); + assertNotNull(path); + assertEquals(expected, Objects.toString(path.getFileName(), null)); } static void resolve(Path path, String other, String expected) { - System.out.format("test resolve %s\n", other); - checkPath(path); - check(path.resolve(other), expected); - } - - static void resolvePath(Path path, String other, String expected) { - System.out.format("test resolve %s\n", other); - checkPath(path); - check(path.resolve(fs.getPath(other)), expected); + assertNotNull(path); + assertEquals(expected, path.resolve(other).toString()); } static void resolveSibling(Path path, String other, String expected) { - System.out.format("test resolveSibling %s\n", other); - checkPath(path); - check(path.resolveSibling(other), expected); - - } - - static void relativize(Path path, String other, String expected) { - System.out.format("test relativize %s\n", other); - checkPath(path); - Path that = fs.getPath(other); - check(path.relativize(that), expected); - - } - - static void normalize(Path path, String expected) { - System.out.println("check normalized path"); - checkPath(path); - check(path.normalize(), expected); - + assertNotNull(path); + assertEquals(expected, path.resolveSibling(other).toString()); } static void string(Path path, String expected) { - System.out.println("check string representation"); - checkPath(path); - check(path, expected); - } - - static void isSameFile(Path path, String target) { - try { - System.out.println("check two paths are same"); - checkPath(path); - check(Files.isSameFile(path, fs.getPath(target)), true); - } catch (IOException ioe) { - fail(); - } - } - - static Path getPath(String s) { - return fs.getPath(s); - } - - static Path getPath(String first, String... more) { - return fs.getPath(first, more); + assertNotNull(path); + assertEquals(expected, path.toString()); } }